diff --git a/vendor/manifest b/vendor/manifest index 424bc38c..d20b4fa9 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -25,6 +25,19 @@ "revision": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a", "branch": "master" }, + { + "importpath": "github.com/apache/thrift", + "repository": "https://github.com/apache/thrift", + "revision": "b2a4d4ae21c789b689dd162deb819665567f481c", + "branch": "HEAD" + }, + { + "importpath": "github.com/apache/thrift/lib/go/thrift", + "repository": "https://github.com/apache/thrift", + "revision": "b2a4d4ae21c789b689dd162deb819665567f481c", + "branch": "HEAD", + "path": "/lib/go/thrift" + }, { "importpath": "github.com/beorn7/perks/quantile", "repository": "https://github.com/beorn7/perks", @@ -32,6 +45,18 @@ "branch": "master", "path": "/quantile" }, + { + "importpath": "github.com/codahale/hdrhistogram", + "repository": "https://github.com/codahale/hdrhistogram", + "revision": "3a0bb77429bd3a61596f5e8a3172445844342120", + "branch": "master" + }, + { + "importpath": "github.com/crossdock/crossdock-go", + "repository": "https://github.com/crossdock/crossdock-go", + "revision": "049aabb0122b03bc9bd30cab8f3f91fb60166361", + "branch": "master" + }, { "importpath": "github.com/davecgh/go-spew/spew", "repository": "https://github.com/davecgh/go-spew", @@ -261,6 +286,49 @@ "revision": "ff4a55a20a86994118644bbddc6a216da193cc13", "branch": "master" }, + { + "importpath": "github.com/uber-go/atomic", + "repository": "https://github.com/uber-go/atomic", + "revision": "54f72d32435d760d5604f17a82e2435b28dc4ba5", + "branch": "master" + }, + { + "importpath": "github.com/uber/jaeger-client-go", + "repository": "https://github.com/uber/jaeger-client-go", + "revision": "3ad49a1d839b517923a6fdac36d81cbf7b744f37", + "branch": "master" + }, + { + "importpath": "github.com/uber/jaeger-lib/metrics", + "repository": "https://github.com/uber/jaeger-lib", + "revision": "21a3da6d66fe0e278072676fdc84cd4c9ccb9b67", + "branch": "master", + "path": "/metrics" + }, + { + "importpath": "github.com/uber/tchannel-go", + "repository": "https://github.com/uber/tchannel-go", + "revision": "b3e26487e291972fda9c7301864a2ee7a319fd35", + "branch": "dev" + }, + { + "importpath": "go.uber.org/atomic", + "repository": "https://github.com/uber-go/atomic", + "revision": "54f72d32435d760d5604f17a82e2435b28dc4ba5", + "branch": "master" + }, + { + "importpath": "go.uber.org/multierr", + "repository": "https://github.com/uber-go/multierr", + "revision": "fb7d312c2c04c34f0ad621048bbb953b168f9ff6", + "branch": "master" + }, + { + "importpath": "go.uber.org/zap", + "repository": "https://github.com/uber-go/zap", + "revision": "35aad584952c3e7020db7b839f6b102de6271f89", + "branch": "master" + }, { "importpath": "golang.org/x/crypto/bcrypt", "repository": "https://go.googlesource.com/crypto", @@ -282,6 +350,13 @@ "branch": "master", "path": "/ed25519" }, + { + "importpath": "golang.org/x/net/bpf", + "repository": "https://go.googlesource.com/net", + "revision": "0a9397675ba34b2845f758fe3cd68828369c6517", + "branch": "master", + "path": "/bpf" + }, { "importpath": "golang.org/x/net/context", "repository": "https://go.googlesource.com/net", @@ -289,6 +364,34 @@ "branch": "master", "path": "/context" }, + { + "importpath": "golang.org/x/net/internal/iana", + "repository": "https://go.googlesource.com/net", + "revision": "0a9397675ba34b2845f758fe3cd68828369c6517", + "branch": "master", + "path": "/internal/iana" + }, + { + "importpath": "golang.org/x/net/internal/socket", + "repository": "https://go.googlesource.com/net", + "revision": "0a9397675ba34b2845f758fe3cd68828369c6517", + "branch": "master", + "path": "/internal/socket" + }, + { + "importpath": "golang.org/x/net/ipv4", + "repository": "https://go.googlesource.com/net", + "revision": "0a9397675ba34b2845f758fe3cd68828369c6517", + "branch": "master", + "path": "/ipv4" + }, + { + "importpath": "golang.org/x/net/ipv6", + "repository": "https://go.googlesource.com/net", + "revision": "0a9397675ba34b2845f758fe3cd68828369c6517", + "branch": "master", + "path": "/ipv6" + }, { "importpath": "gopkg.in/Shopify/sarama.v1", "repository": "https://gopkg.in/Shopify/sarama.v1", diff --git a/vendor/src/github.com/apache/thrift/CHANGES b/vendor/src/github.com/apache/thrift/CHANGES new file mode 100644 index 00000000..7b674d6f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/CHANGES @@ -0,0 +1,2366 @@ +Apache Thrift Changelog + +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/vendor/src/github.com/apache/thrift/CMakeLists.txt b/vendor/src/github.com/apache/thrift/CMakeLists.txt new file mode 100644 index 00000000..93ed8d2a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/CMakeLists.txt @@ -0,0 +1,117 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +cmake_minimum_required(VERSION 2.8.12) + +project("Apache Thrift") + +set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake") + +# 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})") + +# Some default settings +include(DefineCMakeDefaults) + +# Build time options are defined here +include(DefineOptions) +include(DefineInstallationPaths) + +# Based on the options set some platform specifics +include(DefinePlatformSpecifc) + +# Generate the config.h file +include(ConfigureChecks) + +# Package it +include(CPackConfig) + + +find_package(Threads) + +include(CTest) +if(BUILD_TESTING) + message(STATUS "Building with unittests") + + enable_testing() + # Define "make check" as alias for "make test" + add_custom_target(check COMMAND ctest) +else () + message(STATUS "Building without tests") +endif () + +if(BUILD_COMPILER) + if(NOT EXISTS ${THRIFT_COMPILER}) + set(THRIFT_COMPILER $) + endif() + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/compiler/cpp) +elseif(EXISTS ${THRIFT_COMPILER}) + add_executable(thrift-compiler IMPORTED) + set_property(TARGET thrift-compiler PROPERTY IMPORTED_LOCATION ${THRIFT_COMPILER}) +endif() + +if(BUILD_CPP) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/cpp) + if(BUILD_TUTORIALS) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tutorial/cpp) + endif() + if(BUILD_TESTING) + if(WITH_LIBEVENT AND WITH_ZLIB AND WITH_OPENSSL) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/cpp) + else() + message(WARNING "libevent and/or ZLIB and/or OpenSSL not found or disabled; will not build some tests") + endif() + endif() +endif() + +if(BUILD_C_GLIB) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/c_glib) +endif() + +if(BUILD_JAVA) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/java) +endif() + +if(BUILD_PYTHON) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/py) + if(BUILD_TESTING) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/py) + endif() +endif() + +if(BUILD_HASKELL) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/hs) + if(BUILD_TESTING) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/hs) + endif() +endif() + +PRINT_CONFIG_SUMMARY() diff --git a/vendor/src/github.com/apache/thrift/CONTRIBUTING.md b/vendor/src/github.com/apache/thrift/CONTRIBUTING.md new file mode 100644 index 00000000..316da9a0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/CONTRIBUTING.md @@ -0,0 +1,49 @@ +## How to contribute + 1. Help to review and verify existing patches + 1. Make sure your issue is not all ready in the [Jira issue tracker](http://issues.apache.org/jira/browse/THRIFT) + 1. If not, create a ticket describing the change you're proposing in the [Jira issue tracker](http://issues.apache.org/jira/browse/THRIFT) + 1. Contribute your patch using one of the two methods below + +### Contributing via a patch + +1. Check out the latest version of the source code + + * git clone https://git-wip-us.apache.org/repos/asf/thrift.git thrift + +1. Modify the source to include the improvement/bugfix + + * Remember to provide *tests* for all submited changes + * When bugfixing: add test that will isolate bug *before* applying change that fixes it + * Verify that you follow [Thrift Coding Standards](/docs/coding_standards) (you can run 'make style', which ensures proper format for some languages) + +1. Create a patch from project root directory (e.g. you@dev:~/thrift $ ): + + * git diff > ../thrift-XXX-my-new-feature.patch + +1. Attach the newly generated patch to the issue +1. Wait for other contributors or committers to review your new addition +1. Wait for a committer to commit your patch + +### Contributing via GitHub pull requests + +1. Create a fork for http://github.com/apache/thrift +1. Create a branch for your changes(best practice is issue as branch name, e.g. THRIFT-9999) +1. Modify the source to include the improvement/bugfix + + * Remember to provide *tests* for all submited changes + * When bugfixing: add test that will isolate bug *before* applying change that fixes it + * Verify that you follow [Thrift Coding Standards](/docs/coding_standards) (you can run 'make style', which ensures proper format for some languages) + * Verify that your change works on other platforms by adding a GitHub service hook to [Travis CI](http://docs.travis-ci.com/user/getting-started/#Step-one%3A-Sign-in) and [AppVeyor](http://www.appveyor.com/docs) + +1. Commit and push changes to your branch (please use issue name and description as commit title, e.g. THRIFT-9999 make it perfect) +1. Issue a pull request with the jira ticket number you are working on in it's name +1. Wait for other contributors or committers to review your new addition +1. Wait for a committer to commit your patch + +### More info + + Plenty of information on why and how to contribute is available on the Apache Software Foundation (ASF) web site. In particular, we recommend the following: + + * [Contributors Tech Guide](http://www.apache.org/dev/contributors) + * [Get involved!](http://www.apache.org/foundation/getinvolved.html) + * [Legal aspects on Submission of Contributions (Patches)](http://www.apache.org/licenses/LICENSE-2.0.html#contributions) diff --git a/vendor/src/github.com/apache/thrift/Dockerfile b/vendor/src/github.com/apache/thrift/Dockerfile new file mode 100644 index 00000000..0d7ad217 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/Dockerfile @@ -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. +# + +# Goal: provide a thrift-compiler Docker image +# +# Usage: +# docker run -v "${PWD}:/data" thrift/thrift-compiler -gen cpp -o /data/ /data/test/ThriftTest.thrift +# +# further details on docker for thrift is here build/docker/ +# +# TODO: push to apache/thrift-compiler instead of thrift/thrift-compiler + +FROM debian:jessie +MAINTAINER Apache Thrift + +ENV DEBIAN_FRONTEND noninteractive + +ADD . /thrift + +RUN buildDeps=" \ + flex \ + bison \ + g++ \ + make \ + cmake \ + curl \ + "; \ + apt-get update && apt-get install -y --no-install-recommends $buildDeps \ + && mkdir /tmp/cmake-build && cd /tmp/cmake-build \ + && cmake \ + -DBUILD_COMPILER=ON \ + -DBUILD_LIBRARIES=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_EXAMPLES=OFF \ + /thrift \ + && cmake --build . --config Release \ + && make install \ + && curl -k -sSL "https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz" -o /tmp/go.tar.gz \ + && tar xzf /tmp/go.tar.gz -C /tmp \ + && cp /tmp/go/bin/gofmt /usr/bin/gofmt \ + && apt-get purge -y --auto-remove $buildDeps \ + && apt-get clean \ + && rm -rf /tmp/* \ + && rm -rf /var/lib/apt/lists/* + +ENTRYPOINT ["thrift"] diff --git a/vendor/src/github.com/apache/thrift/LICENSE b/vendor/src/github.com/apache/thrift/LICENSE new file mode 100644 index 00000000..3b6d7d74 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/LICENSE @@ -0,0 +1,239 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------------------------------------------- +SOFTWARE DISTRIBUTED WITH THRIFT: + +The Apache Thrift software includes a number of subcomponents with +separate copyright notices and license terms. Your use of the source +code for the these subcomponents is subject to the terms and +conditions of the following licenses. + +-------------------------------------------------- +Portions of the following files are licensed under the MIT License: + + lib/erl/src/Makefile.am + +Please see doc/otp-base-license.txt for the full terms of this license. + +-------------------------------------------------- +For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components: + +# Copyright (c) 2007 Thomas Porschberg +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty provided +# the copyright notice and this notice are preserved. + +-------------------------------------------------- +For the lib/nodejs/lib/thrift/json_parse.js: + +/* + json_parse.js + 2015-05-02 + Public Domain. + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +*/ +(By Douglas Crockford ) +-------------------------------------------------- diff --git a/vendor/src/github.com/apache/thrift/Makefile.am b/vendor/src/github.com/apache/thrift/Makefile.am new file mode 100644 index 00000000..ed58265a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/Makefile.am @@ -0,0 +1,131 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +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 +endif + +if WITH_TUTORIAL +SUBDIRS += tutorial +endif + +dist-hook: + find $(distdir) -type f \( -iname ".DS_Store" -or -iname "._*" -or -iname ".gitignore" \) | xargs rm -rf + find $(distdir) -type d \( -iname ".deps" -or -iname ".libs" \) | xargs rm -rf + find $(distdir) -type d \( -iname ".svn" -or -iname ".git" \) | xargs rm -rf + +print-version: + @echo $(VERSION) + +.PHONY: precross cross +precross-%: all + $(MAKE) -C $* precross +precross: all precross-test precross-lib + +empty := +space := $(empty) $(empty) +comma := , + +CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @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@ +CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS)) + +if WITH_PY3 +CROSS_PY=$(PYTHON3) +else +CROSS_PY=$(PYTHON) +endif + +if WITH_PYTHON +crossfeature: precross + $(CROSS_PY) test/test.py --retry-count 3 --features .* --skip-known-failures --server $(CROSS_LANGS_COMMA_SEPARATED) +else +# feature test needs python build +crossfeature: +endif + +cross-%: precross crossfeature + $(CROSS_PY) test/test.py --retry-count 3 --skip-known-failures --server $(CROSS_LANGS_COMMA_SEPARATED) --client $(CROSS_LANGS_COMMA_SEPARATED) --regex "$*" + +cross: cross-.* + +TIMES = 1 2 3 +fail: precross + $(CROSS_PY) test/test.py || true + $(CROSS_PY) test/test.py --update-expected-failures=overwrite + $(foreach var,$(TIMES),test/test.py -s || true;test/test.py --update-expected-failures=merge;) + +codespell_skip_files = \ + *.jar \ + *.class \ + *.so \ + *.a \ + *.la \ + *.o \ + *.p12 \ + *OCamlMakefile \ + .keystore \ + .truststore \ + CHANGES \ + config.sub \ + configure \ + depcomp \ + libtool.m4 \ + output.* \ + rebar \ + thrift + +skipped_files = $(subst $(space),$(comma),$(codespell_skip_files)) + +style-local: + codespell --write-changes --skip=$(skipped_files) --disable-colors + +EXTRA_DIST = \ + .clang-format \ + .editorconfig \ + .travis.yml \ + appveyor.yml \ + bower.json \ + build \ + CMakeLists.txt \ + composer.json \ + contrib \ + CONTRIBUTING.md \ + debian \ + doc \ + doap.rdf \ + package.json \ + sonar-project.properties \ + Dockerfile \ + LICENSE \ + CHANGES \ + NOTICE \ + README.md \ + Thrift.podspec diff --git a/vendor/src/github.com/apache/thrift/NOTICE b/vendor/src/github.com/apache/thrift/NOTICE new file mode 100644 index 00000000..c23995a2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/NOTICE @@ -0,0 +1,5 @@ +Apache Thrift +Copyright 2006-2010 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/README.md b/vendor/src/github.com/apache/thrift/README.md new file mode 100644 index 00000000..07cd32f0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/README.md @@ -0,0 +1,166 @@ +Apache Thrift +============= + ++[![Build Status](https://travis-ci.org/apache/thrift.svg?branch=master)](https://travis-ci.org/apache/thrift) +- +[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/e2qks7enyp9gw7ma?svg=true)](https://ci.appveyor.com/project/apache/thrift) + + +Introduction +============ + +Thrift is a lightweight, language-independent software stack with an +associated code generation mechanism for RPC. Thrift provides clean +abstractions for data transport, data serialization, and application +level processing. The code generation system takes a simple definition +language as its input and generates code across programming languages that +uses the abstracted stack to build interoperable RPC clients and servers. + +Thrift is specifically designed to support non-atomic version changes +across client and server code. + +For more details on Thrift's design and implementation, take a gander at +the Thrift whitepaper included in this distribution or at the README.md files +in your particular subdirectory of interest. + +Hierarchy +========= + +thrift/ + + compiler/ + + Contains the Thrift compiler, implemented in C++. + + lib/ + + Contains the Thrift software library implementation, subdivided by + language of implementation. + + cpp/ + go/ + java/ + php/ + py/ + rb/ + + test/ + + Contains sample Thrift files and test code across the target programming + languages. + + tutorial/ + + Contains a basic tutorial that will teach you how to develop software + using Thrift. + +Requirements +============ + +See http://thrift.apache.org/docs/install for an up-to-date list of build requirements. + +Resources +========= + +More information about Thrift can be obtained on the Thrift webpage at: + + http://thrift.apache.org + +Acknowledgments +=============== + +Thrift was inspired by pillar, a lightweight RPC tool written by Adam D'Angelo, +and also by Google's protocol buffers. + +Installation +============ + +If you are building from the first time out of the source repository, you will +need to generate the configure scripts. (This is not necessary if you +downloaded a tarball.) From the top directory, do: + + ./bootstrap.sh + +Once the configure scripts are generated, thrift can be configured. +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: + + ./configure --with-boost=/usr/local + +Note that by default the thrift C++ library is typically built with debugging +symbols included. If you want to customize these options you should use the +CXXFLAGS option in configure, as such: + + ./configure CXXFLAGS='-g -O2' + ./configure CFLAGS='-g -O2' + ./configure CPPFLAGS='-DDEBUG_MY_FEATURE' + +To enable gcov required options -fprofile-arcs -ftest-coverage enable them: + + ./configure --enable-coverage + +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 +where the Python modules are installed, set the PY_PREFIX variable. +(DESTDIR is respected for Python and C++.) + +Make thrift: + + make + +From the top directory, become superuser and do: + + make install + +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 +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. + +Testing +======= + +There are a large number of client library tests that can all be run +from the top-level directory. + + 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 +language fails, the make check will continue on and provide a synopsis +at the end. + +To run the cross-language test suite, please run: + + make cross + +This will run a set of tests that use different language clients and +servers. + +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/vendor/src/github.com/apache/thrift/Thrift.podspec b/vendor/src/github.com/apache/thrift/Thrift.podspec new file mode 100644 index 00000000..2ead0e93 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/Thrift.podspec @@ -0,0 +1,18 @@ +Pod::Spec.new do |s| + s.name = "Thrift" + s.version = "0.10.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://raw.github.com/apache/thrift/thrift-0.9.0/LICENSE' } + s.author = { "The Apache Software Foundation" => "apache@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 => "thrift-0.10.0" } + s.source_files = 'lib/cocoa/src/**/*.{h,m,swift}' +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/aclocal/ac_prog_bison.m4 b/vendor/src/github.com/apache/thrift/aclocal/ac_prog_bison.m4 new file mode 100644 index 00000000..4d1198b9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ac_prog_bison.m4 @@ -0,0 +1,54 @@ +dnl +dnl Check Bison version +dnl AC_PROG_BISON([MIN_VERSION=2.4]) +dnl +dnl Will define BISON_USE_PARSER_H_EXTENSION if Automake is < 1.11 +dnl for use with .h includes. +dnl + +AC_DEFUN([AC_PROG_BISON], [ +if test "x$1" = "x" ; then + bison_required_version="2.4" +else + bison_required_version="$1" +fi + +AC_CHECK_PROG(have_prog_bison, [bison], [yes],[no]) + +AC_DEFINE_UNQUOTED([BISON_VERSION], [0.0], [Bison version if bison is not available]) + +#Do not use *.h extension for parser header files, use newer *.hh +bison_use_parser_h_extension=false + +if test "$have_prog_bison" = "yes" ; then + AC_MSG_CHECKING([for bison version >= $bison_required_version]) + bison_version=`bison --version | head -n 1 | cut '-d ' -f 4` + AC_DEFINE_UNQUOTED([BISON_VERSION], [$bison_version], [Defines bison version]) + if test "$bison_version" \< "$bison_required_version" ; then + BISON=: + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Bison version $bison_required_version or higher must be installed on the system!]) + else + AC_MSG_RESULT([yes]) + BISON=bison + AC_SUBST(BISON) + + #Verify automake version 1.11 headers for yy files are .h, > 1.12 uses .hh + automake_version=`automake --version | head -n 1 | cut '-d ' -f 4` + AC_DEFINE_UNQUOTED([AUTOMAKE_VERSION], [$automake_version], [Defines automake version]) + + if test "$automake_version" \< "1.12" ; then + #Use *.h extension for parser header file + bison_use_parser_h_extension=true + echo "Automake version < 1.12" + AC_DEFINE([BISON_USE_PARSER_H_EXTENSION], [1], [Use *.h extension for parser header file]) + fi + fi +else + BISON=: + AC_MSG_RESULT([NO]) +fi + +AM_CONDITIONAL([BISON_USE_PARSER_H_EXTENSION], [test x$bison_use_parser_h_extension = xtrue]) +AC_SUBST(BISON) +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_boost_base.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_boost_base.m4 new file mode 100644 index 00000000..b496020e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_boost_base.m4 @@ -0,0 +1,272 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro searchs +# under /usr, /usr/local, /opt and /opt/local and evaluates the +# $BOOST_ROOT environment variable. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LICENSE +# +# Copyright (c) 2008 Thomas Porschberg +# Copyright (c) 2009 Peter Adolphs +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 23 + +AC_DEFUN([AX_BOOST_BASE], +[ +AC_ARG_WITH([boost], + [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], + [use Boost library from a standard location (ARG=yes), + from the specified location (ARG=), + or disable it (ARG=no) + @<:@ARG=yes@:>@ ])], + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + ], + [want_boost="yes"]) + + +AC_ARG_WITH([boost-libdir], + AS_HELP_STRING([--with-boost-libdir=LIB_DIR], + [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), + [ + if test -d "$withval" + then + ac_boost_lib_path="$withval" + else + AC_MSG_ERROR(--with-boost-libdir expected directory name) + fi + ], + [ac_boost_lib_path=""] +) + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=ifelse([$1], ,1.20.0,$1) + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) + succeeded=no + + dnl On 64-bit systems check for system libraries in both lib64 and lib. + dnl The former is specified by FHS, but e.g. Debian does not adhere to + dnl this (as it rises problems for generic multi-arch support). + dnl The last entry in the list is chosen by default when no libraries + dnl are found, e.g. when only header-only libraries are installed! + libsubdirs="lib" + ax_arch=`uname -m` + case $ax_arch in + x86_64|ppc64|s390x|sparc64|aarch64) + libsubdirs="lib64 lib lib64" + ;; + esac + + dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give + dnl them priority over the other paths since, if libs are found there, they + dnl are almost assuredly the ones desired. + AC_REQUIRE([AC_CANONICAL_HOST]) + libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" + + case ${host_cpu} in + i?86) + libsubdirs="lib/i386-${host_os} $libsubdirs" + ;; + esac + + dnl first we check the system location for boost libraries + dnl this location ist chosen if boost libraries are installed with the --layout=system option + dnl or if you install boost with RPM + if test "$ac_boost_path" != ""; then + BOOST_CPPFLAGS="-I$ac_boost_path/include" + for ac_boost_path_tmp in $libsubdirs; do + if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then + BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" + break + fi + done + elif test "$cross_compiling" != yes; then + for ac_boost_path_tmp in $lt_sysroot/usr $lt_sysroot/usr/local $lt_sysroot/opt $lt_sysroot/opt/local ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + for libsubdir in $libsubdirs ; do + if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + dnl overwrite ld flags if we have required special directory with + dnl --with-boost-libdir parameter + if test "$ac_boost_lib_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_lib_path" + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_REQUIRE([AC_PROG_CXX]) + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + + + + dnl if we found no boost with system layout we search for boost libraries + dnl built and installed without the --layout=system option or for a staged(not installed) version + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + if test "$cross_compiling" != yes; then + for ac_boost_path in $lt_sysroot/usr $lt_sysroot/usr/local $lt_sysroot/opt $lt_sysroot/opt/local ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + if test "$ac_boost_lib_path" = ""; then + for libsubdir in $libsubdirs ; do + if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$best_path/$libsubdir" + fi + fi + + if test "x$BOOST_ROOT" != "x"; then + for libsubdir in $libsubdirs ; do + if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + else + AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) + fi + # execute ACTION-IF-NOT-FOUND (if present): + ifelse([$3], , :, [$3]) + else + AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_LDFLAGS) + AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + # execute ACTION-IF-FOUND (if present): + ifelse([$2], , :, [$2]) + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_check_openssl.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_check_openssl.m4 new file mode 100644 index 00000000..a87c5a6b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_check_openssl.m4 @@ -0,0 +1,124 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) +# +# DESCRIPTION +# +# Look for OpenSSL in a number of default spots, or in a user-selected +# spot (via --with-openssl). Sets +# +# OPENSSL_INCLUDES to the include directives required +# OPENSSL_LIBS to the -l directives required +# OPENSSL_LDFLAGS to the -L or -R flags required +# +# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately +# +# This macro sets OPENSSL_INCLUDES such that source files should use the +# openssl/ directory in include directives: +# +# #include +# +# LICENSE +# +# Copyright (c) 2009,2010 Zmanda Inc. +# Copyright (c) 2009,2010 Dustin J. Mitchell +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) +AC_DEFUN([AX_CHECK_OPENSSL], [ + found=false + AC_ARG_WITH([openssl], + [AS_HELP_STRING([--with-openssl=DIR], + [root of the OpenSSL directory])], + [ + case "$withval" in + "" | y | ye | yes | n | no) + AC_MSG_ERROR([Invalid --with-openssl value]) + ;; + *) ssldirs="$withval" + ;; + esac + ], [ + # if pkg-config is installed and openssl has installed a .pc file, + # then use that information and don't search ssldirs + AC_PATH_PROG([PKG_CONFIG], [pkg-config]) + if test x"$PKG_CONFIG" != x""; then + OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` + if test $? = 0; then + OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` + OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` + found=true + fi + fi + + # no such luck; use some default ssldirs + if ! $found; then + ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" + fi + ] + ) + + + # note that we #include , so the OpenSSL headers have to be in + # an 'openssl' subdirectory + + if ! $found; then + OPENSSL_INCLUDES= + for ssldir in $ssldirs; do + AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) + if test -f "$ssldir/include/openssl/ssl.h"; then + OPENSSL_INCLUDES="-I$ssldir/include" + OPENSSL_LDFLAGS="-L$ssldir/lib" + OPENSSL_LIBS="-lssl -lcrypto" + found=true + AC_MSG_RESULT([yes]) + break + else + AC_MSG_RESULT([no]) + fi + done + + # if the file wasn't found, well, go ahead and try the link anyway -- maybe + # it will just work! + fi + + # try the preprocessor and linker with our new flags, + # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS + + AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) + echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \ + "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD + + save_LIBS="$LIBS" + save_LDFLAGS="$LDFLAGS" + save_CPPFLAGS="$CPPFLAGS" + LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS" + LIBS="$OPENSSL_LIBS $LIBS" + CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([#include ], [SSL_new(NULL)])], + [ + AC_MSG_RESULT([yes]) + $1 + ], [ + AC_MSG_RESULT([no]) + $2 + ]) + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + + AC_SUBST([OPENSSL_INCLUDES]) + AC_SUBST([OPENSSL_LIBS]) + AC_SUBST([OPENSSL_LDFLAGS]) +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_cxx_compile_stdcxx_11.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_cxx_compile_stdcxx_11.m4 new file mode 100644 index 00000000..a9a8f584 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_cxx_compile_stdcxx_11.m4 @@ -0,0 +1,165 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++11 +# standard; if necessary, add switches to CXXFLAGS to enable support. +# +# The first argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The second argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline C++11 support is required and that the macro +# should error out if no mode with that support is found. If specified +# 'optional', then configuration proceeds regardless, after defining +# HAVE_CXX11 if and only if a supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + struct Base { + virtual void f() {} + }; + struct Child : public Base { + virtual void f() override {} + }; + + typedef check> right_angle_brackets; + + int a; + decltype(a) b; + + typedef check check_type; + check_type c; + check_type&& cr = static_cast(c); + + auto d = a; + auto l = [](){}; + // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable] + struct use_l { use_l() { l(); } }; + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this + namespace test_template_alias_sfinae { + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { + func(0); + } + } +]]) + +AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl + m4_if([$1], [], [], + [$1], [ext], [], + [$1], [noext], [], + [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl + m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], + [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], + [$2], [optional], [ax_cxx_compile_cxx11_required=false], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++11 features by default, + ax_cv_cxx_compile_cxx11, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [ax_cv_cxx_compile_cxx11=yes], + [ax_cv_cxx_compile_cxx11=no])]) + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + m4_if([$1], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++11 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$1], [ext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + AC_MSG_NOTICE([No compiler with C++11 support was found]) + else + HAVE_CXX11=1 + AC_DEFINE(HAVE_CXX11,1, + [define if the compiler supports basic C++11 syntax]) + fi + + AC_SUBST(HAVE_CXX11) + fi +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_dmd.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_dmd.m4 new file mode 100644 index 00000000..13b84b02 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_dmd.m4 @@ -0,0 +1,107 @@ +dnl @synopsis AX_DMD +dnl +dnl Test for the presence of a DMD-compatible D2 compiler, and (optionally) +dnl specified modules on the import path. +dnl +dnl If "DMD" is defined in the environment, that will be the only +dnl dmd command tested. Otherwise, a hard-coded list will be used. +dnl +dnl After AX_DMD runs, the shell variables "success" and "ax_dmd" are set to +dnl "yes" or "no", and "DMD" is set to the appropriate command. Furthermore, +dnl "dmd_optlink" will be set to "yes" or "no" depending on whether OPTLINK is +dnl used as the linker (DMD/Windows), and "dmd_of_dirsep" will be set to the +dnl directory separator to use when passing -of to DMD (OPTLINK requires a +dnl backslash). +dnl +dnl AX_CHECK_D_MODULE must be run after AX_DMD. It tests for the presence of a +dnl module in the import path of the chosen compiler, and sets the shell +dnl variable "success" to "yes" or "no". +dnl +dnl @category D +dnl @version 2011-05-31 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copyright (C) 2011 David Nadlinger +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + + +AC_DEFUN([AX_DMD], + [ + dnl Hard-coded default commands to test. + DMD_PROGS="dmd,gdmd,ldmd" + + dnl Allow the user to specify an alternative. + if test -n "$DMD" ; then + DMD_PROGS="$DMD" + fi + + AC_MSG_CHECKING(for DMD) + + # std.algorithm as a quick way to check for D2/Phobos. + echo "import std.algorithm; void main() {}" > configtest_ax_dmd.d + success=no + oIFS="$IFS" + + IFS="," + for DMD in $DMD_PROGS ; do + IFS="$oIFS" + + echo "Running \"$DMD configtest_ax_dmd.d\"" >&AS_MESSAGE_LOG_FD + if $DMD configtest_ax_dmd.d >&AS_MESSAGE_LOG_FD 2>&1 ; then + success=yes + break + fi + done + + if test "$success" != "yes" ; then + AC_MSG_RESULT(no) + DMD="" + else + AC_MSG_RESULT(yes) + fi + + ax_dmd="$success" + + # Test whether OPTLINK is used by trying if DMD accepts -L/? without + # erroring out. + if test "$success" == "yes" ; then + AC_MSG_CHECKING(whether DMD uses OPTLINK) + echo "Running \”$DMD -L/? configtest_ax_dmd.d\"" >&AS_MESSAGE_LOG_FD + if $DMD -L/? configtest_ax_dmd.d >&AS_MESSAGE_LOG_FD 2>&1 ; then + AC_MSG_RESULT(yes) + dmd_optlink="yes" + + # This actually produces double slashes in the final configure + # output, but at least it works. + dmd_of_dirsep="\\\\" + else + AC_MSG_RESULT(no) + dmd_optlink="no" + dmd_of_dirsep="/" + fi + fi + + rm -f configtest_ax_dmd* + ]) + + +AC_DEFUN([AX_CHECK_D_MODULE], + [ + AC_MSG_CHECKING(for D module [$1]) + + echo "import $1; void main() {}" > configtest_ax_dmd.d + + echo "Running \"$DMD configtest_ax_dmd.d\"" >&AS_MESSAGE_LOG_FD + if $DMD -c configtest_ax_dmd.d >&AS_MESSAGE_LOG_FD 2>&1 ; then + AC_MSG_RESULT(yes) + success=yes + else + AC_MSG_RESULT(no) + success=no + fi + + rm -f configtest_ax_dmd* + ]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_javac_and_java.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_javac_and_java.m4 new file mode 100644 index 00000000..f341f50e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_javac_and_java.m4 @@ -0,0 +1,129 @@ +dnl @synopsis AX_JAVAC_AND_JAVA +dnl @synopsis AX_CHECK_JAVA_CLASS(CLASSNAME) +dnl +dnl Test for the presence of a JDK, and (optionally) specific classes. +dnl +dnl If "JAVA" is defined in the environment, that will be the only +dnl java command tested. Otherwise, a hard-coded list will be used. +dnl Similarly for "JAVAC". +dnl +dnl AX_JAVAC_AND_JAVA does not currently support testing for a particular +dnl Java version, testing for only one of "java" and "javac", or +dnl compiling or running user-provided Java code. +dnl +dnl After AX_JAVAC_AND_JAVA runs, the shell variables "success" and +dnl "ax_javac_and_java" are set to "yes" or "no", and "JAVAC" and +dnl "JAVA" are set to the appropriate commands. +dnl +dnl AX_CHECK_JAVA_CLASS must be run after AX_JAVAC_AND_JAVA. +dnl It tests for the presence of a class based on a fully-qualified name. +dnl It sets the shell variable "success" to "yes" or "no". +dnl +dnl @category Java +dnl @version 2009-02-09 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + + +AC_DEFUN([AX_JAVAC_AND_JAVA], + [ + + dnl Hard-coded default commands to test. + JAVAC_PROGS="javac,jikes,gcj -C" + JAVA_PROGS="java,kaffe" + + dnl Allow the user to specify an alternative. + if test -n "$JAVAC" ; then + JAVAC_PROGS="$JAVAC" + fi + if test -n "$JAVA" ; then + JAVA_PROGS="$JAVA" + fi + + AC_MSG_CHECKING(for javac and java) + + echo "public class configtest_ax_javac_and_java { public static void main(String args@<:@@:>@) { } }" > configtest_ax_javac_and_java.java + success=no + oIFS="$IFS" + + IFS="," + for JAVAC in $JAVAC_PROGS ; do + IFS="$oIFS" + + echo "Running \"$JAVAC configtest_ax_javac_and_java.java\"" >&AS_MESSAGE_LOG_FD + if $JAVAC configtest_ax_javac_and_java.java >&AS_MESSAGE_LOG_FD 2>&1 ; then + + # prevent $JAVA VM issues with UTF-8 path names (THRIFT-3271) + oLC_ALL="$LC_ALL" + LC_ALL="" + + IFS="," + for JAVA in $JAVA_PROGS ; do + IFS="$oIFS" + + echo "Running \"$JAVA configtest_ax_javac_and_java\"" >&AS_MESSAGE_LOG_FD + if $JAVA configtest_ax_javac_and_java >&AS_MESSAGE_LOG_FD 2>&1 ; then + success=yes + break 2 + fi + + done + + # restore LC_ALL + LC_ALL="$oLC_ALL" + oLC_ALL="" + + fi + + done + + rm -f configtest_ax_javac_and_java.java configtest_ax_javac_and_java.class + + if test "$success" != "yes" ; then + AC_MSG_RESULT(no) + JAVAC="" + JAVA="" + else + AC_MSG_RESULT(yes) + fi + + ax_javac_and_java="$success" + + ]) + + +AC_DEFUN([AX_CHECK_JAVA_CLASS], + [ + AC_MSG_CHECKING(for Java class [$1]) + + echo "import $1; public class configtest_ax_javac_and_java { public static void main(String args@<:@@:>@) { } }" > configtest_ax_javac_and_java.java + + echo "Running \"$JAVAC configtest_ax_javac_and_java.java\"" >&AS_MESSAGE_LOG_FD + if $JAVAC configtest_ax_javac_and_java.java >&AS_MESSAGE_LOG_FD 2>&1 ; then + AC_MSG_RESULT(yes) + success=yes + else + AC_MSG_RESULT(no) + success=no + fi + + rm -f configtest_ax_javac_and_java.java configtest_ax_javac_and_java.class + ]) + + +AC_DEFUN([AX_CHECK_ANT_VERSION], + [ + AC_MSG_CHECKING(for ant version > $2) + ANT_VALID=`expr $($1 -version 2>/dev/null | sed -n 's/.*version \(@<:@0-9\.@:>@*\).*/\1/p') \>= $2` + if test "x$ANT_VALID" = "x1" ; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + ANT="" + fi + ]) + diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_lib_event.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_lib_event.m4 new file mode 100644 index 00000000..d4dcdc9a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_lib_event.m4 @@ -0,0 +1,194 @@ +dnl @synopsis AX_LIB_EVENT([MINIMUM-VERSION]) +dnl +dnl Test for the libevent library of a particular version (or newer). +dnl +dnl If no path to the installed libevent is given, the macro will first try +dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt, +dnl and /opt/libevent. +dnl If these all fail, it will try the $LIBEVENT_ROOT environment variable. +dnl +dnl This macro requires that #include works and defines u_char. +dnl +dnl This macro calls: +dnl AC_SUBST(LIBEVENT_CPPFLAGS) +dnl AC_SUBST(LIBEVENT_LDFLAGS) +dnl AC_SUBST(LIBEVENT_LIBS) +dnl +dnl And (if libevent is found): +dnl AC_DEFINE(HAVE_LIBEVENT) +dnl +dnl It also leaves the shell variables "success" and "ax_have_libevent" +dnl set to "yes" or "no". +dnl +dnl NOTE: This macro does not currently work for cross-compiling, +dnl but it can be easily modified to allow it. (grep "cross"). +dnl +dnl @category InstalledPackages +dnl @category C +dnl @version 2007-09-12 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + +dnl Input: ax_libevent_path, WANT_LIBEVENT_VERSION +dnl Output: success=yes/no +AC_DEFUN([AX_LIB_EVENT_DO_CHECK], + [ + # Save our flags. + CPPFLAGS_SAVED="$CPPFLAGS" + LDFLAGS_SAVED="$LDFLAGS" + LIBS_SAVED="$LIBS" + LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH" + + # Set our flags if we are checking a specific directory. + if test -n "$ax_libevent_path" ; then + LIBEVENT_CPPFLAGS="-I$ax_libevent_path/include" + LIBEVENT_LDFLAGS="-L$ax_libevent_path/lib" + LD_LIBRARY_PATH="$ax_libevent_path/lib:$LD_LIBRARY_PATH" + else + LIBEVENT_CPPFLAGS="" + LIBEVENT_LDFLAGS="" + fi + + # Required flag for libevent. + LIBEVENT_LIBS="-levent" + + # Prepare the environment for compilation. + CPPFLAGS="$CPPFLAGS $LIBEVENT_CPPFLAGS" + LDFLAGS="$LDFLAGS $LIBEVENT_LDFLAGS" + LIBS="$LIBS $LIBEVENT_LIBS" + export CPPFLAGS + export LDFLAGS + export LIBS + export LD_LIBRARY_PATH + + success=no + + # Compile, link, and run the program. This checks: + # - event.h is available for including. + # - event_get_version() is available for linking. + # - The event version string is lexicographically greater + # than the required version. + AC_LANG_PUSH([C]) + dnl This can be changed to AC_LINK_IFELSE if you are cross-compiling, + dnl but then the version cannot be checked. + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + const char* lib_version = event_get_version(); + const char* wnt_version = "$WANT_LIBEVENT_VERSION"; + int lib_digits; + int wnt_digits; + for (;;) { + /* If we reached the end of the want version. We have it. */ + if (*wnt_version == '\0' || *wnt_version == '-') { + return 0; + } + /* If the want version continues but the lib version does not, */ + /* we are missing a letter. We don't have it. */ + if (*lib_version == '\0' || *lib_version == '-') { + return 1; + } + /* In the 1.4 version numbering style, if there are more digits */ + /* in one version than the other, that one is higher. */ + for (lib_digits = 0; + lib_version[lib_digits] >= '0' && + lib_version[lib_digits] <= '9'; + lib_digits++) + ; + for (wnt_digits = 0; + wnt_version[wnt_digits] >= '0' && + wnt_version[wnt_digits] <= '9'; + wnt_digits++) + ; + if (lib_digits > wnt_digits) { + return 0; + } + if (lib_digits < wnt_digits) { + return 1; + } + /* If we have greater than what we want. We have it. */ + if (*lib_version > *wnt_version) { + return 0; + } + /* If we have less, we don't. */ + if (*lib_version < *wnt_version) { + return 1; + } + lib_version++; + wnt_version++; + } + return 0; + ]])], [ + success=yes + ]) + AC_LANG_POP([C]) + + # Restore flags. + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + LIBS="$LIBS_SAVED" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED" + ]) + + +AC_DEFUN([AX_LIB_EVENT], + [ + + dnl Allow search path to be overridden on the command line. + AC_ARG_WITH([libevent], + AS_HELP_STRING([--with-libevent@<:@=DIR@:>@], [use libevent [default=yes]. Optionally specify the root prefix dir where libevent is installed]), + [ + if test "x$withval" = "xno"; then + want_libevent="no" + elif test "x$withval" = "xyes"; then + want_libevent="yes" + ax_libevent_path="" + else + want_libevent="yes" + ax_libevent_path="$withval" + fi + ], + [ want_libevent="yes" ; ax_libevent_path="" ]) + + + if test "$want_libevent" = "yes"; then + WANT_LIBEVENT_VERSION=ifelse([$1], ,1.2,$1) + + AC_MSG_CHECKING(for libevent >= $WANT_LIBEVENT_VERSION) + + # Run tests. + if test -n "$ax_libevent_path"; then + AX_LIB_EVENT_DO_CHECK + else + for ax_libevent_path in "" $lt_sysroot/usr $lt_sysroot/usr/local $lt_sysroot/opt $lt_sysroot/opt/local $lt_sysroot/opt/libevent "$LIBEVENT_ROOT" ; do + AX_LIB_EVENT_DO_CHECK + if test "$success" = "yes"; then + break; + fi + done + fi + + if test "$success" != "yes" ; then + AC_MSG_RESULT(no) + LIBEVENT_CPPFLAGS="" + LIBEVENT_LDFLAGS="" + LIBEVENT_LIBS="" + else + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LIBEVENT,,[define if libevent is available]) + ax_have_libevent_[]m4_translit([$1], [.], [_])="yes" + fi + + ax_have_libevent="$success" + + AC_SUBST(LIBEVENT_CPPFLAGS) + AC_SUBST(LIBEVENT_LDFLAGS) + AC_SUBST(LIBEVENT_LIBS) + fi + + ]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_lib_zlib.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_lib_zlib.m4 new file mode 100644 index 00000000..bdb9e110 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_lib_zlib.m4 @@ -0,0 +1,173 @@ +dnl @synopsis AX_LIB_ZLIB([MINIMUM-VERSION]) +dnl +dnl Test for the libz library of a particular version (or newer). +dnl +dnl If no path to the installed zlib is given, the macro will first try +dnl using no -I or -L flags, then searches under /usr, /usr/local, /opt, +dnl and /opt/zlib. +dnl If these all fail, it will try the $ZLIB_ROOT environment variable. +dnl +dnl This macro calls: +dnl AC_SUBST(ZLIB_CPPFLAGS) +dnl AC_SUBST(ZLIB_LDFLAGS) +dnl AC_SUBST(ZLIB_LIBS) +dnl +dnl And (if zlib is found): +dnl AC_DEFINE(HAVE_ZLIB) +dnl +dnl It also leaves the shell variables "success" and "ax_have_zlib" +dnl set to "yes" or "no". +dnl +dnl NOTE: This macro does not currently work for cross-compiling, +dnl but it can be easily modified to allow it. (grep "cross"). +dnl +dnl @category InstalledPackages +dnl @category C +dnl @version 2007-09-12 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + +dnl Input: ax_zlib_path, WANT_ZLIB_VERSION +dnl Output: success=yes/no +AC_DEFUN([AX_LIB_ZLIB_DO_CHECK], + [ + # Save our flags. + CPPFLAGS_SAVED="$CPPFLAGS" + LDFLAGS_SAVED="$LDFLAGS" + LIBS_SAVED="$LIBS" + LD_LIBRARY_PATH_SAVED="$LD_LIBRARY_PATH" + + # Set our flags if we are checking a specific directory. + if test -n "$ax_zlib_path" ; then + ZLIB_CPPFLAGS="-I$ax_zlib_path/include" + ZLIB_LDFLAGS="-L$ax_zlib_path/lib" + LD_LIBRARY_PATH="$ax_zlib_path/lib:$LD_LIBRARY_PATH" + else + ZLIB_CPPFLAGS="" + ZLIB_LDFLAGS="" + fi + + # Required flag for zlib. + ZLIB_LIBS="-lz" + + # Prepare the environment for compilation. + CPPFLAGS="$CPPFLAGS $ZLIB_CPPFLAGS" + LDFLAGS="$LDFLAGS $ZLIB_LDFLAGS" + LIBS="$LIBS $ZLIB_LIBS" + export CPPFLAGS + export LDFLAGS + export LIBS + export LD_LIBRARY_PATH + + success=no + + # Compile, link, and run the program. This checks: + # - zlib.h is available for including. + # - zlibVersion() is available for linking. + # - ZLIB_VERNUM is greater than or equal to the desired version. + # - ZLIB_VERSION (defined in zlib.h) matches zlibVersion() + # (defined in the library). + AC_LANG_PUSH([C]) + dnl This can be changed to AC_LINK_IFELSE if you are cross-compiling. + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + #if ZLIB_VERNUM >= 0x$WANT_ZLIB_VERSION + #else + # error zlib is too old + #endif + ]], [[ + const char* lib_version = zlibVersion(); + const char* hdr_version = ZLIB_VERSION; + for (;;) { + if (*lib_version != *hdr_version) { + /* If this happens, your zlib header doesn't match your zlib */ + /* library. That is really bad. */ + return 1; + } + if (*lib_version == '\0') { + break; + } + lib_version++; + hdr_version++; + } + return 0; + ]])], [ + success=yes + ]) + AC_LANG_POP([C]) + + # Restore flags. + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + LIBS="$LIBS_SAVED" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_SAVED" + ]) + + +AC_DEFUN([AX_LIB_ZLIB], + [ + + dnl Allow search path to be overridden on the command line. + AC_ARG_WITH([zlib], + AS_HELP_STRING([--with-zlib@<:@=DIR@:>@], [use zlib (default is yes) - it is possible to specify an alternate root directory for zlib]), + [ + if test "x$withval" = "xno"; then + want_zlib="no" + elif test "x$withval" = "xyes"; then + want_zlib="yes" + ax_zlib_path="" + else + want_zlib="yes" + ax_zlib_path="$withval" + fi + ], + [want_zlib="yes" ; ax_zlib_path="" ]) + + + if test "$want_zlib" = "yes"; then + # Parse out the version. + zlib_version_req=ifelse([$1], ,1.2.3,$1) + zlib_version_req_major=`expr $zlib_version_req : '\([[0-9]]*\)'` + zlib_version_req_minor=`expr $zlib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + zlib_version_req_patch=`expr $zlib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test -z "$zlib_version_req_patch" ; then + zlib_version_req_patch="0" + fi + WANT_ZLIB_VERSION=`expr $zlib_version_req_major \* 1000 \+ $zlib_version_req_minor \* 100 \+ $zlib_version_req_patch \* 10` + + AC_MSG_CHECKING(for zlib >= $zlib_version_req) + + # Run tests. + if test -n "$ax_zlib_path"; then + AX_LIB_ZLIB_DO_CHECK + else + for ax_zlib_path in "" /usr /usr/local /opt /opt/zlib "$ZLIB_ROOT" ; do + AX_LIB_ZLIB_DO_CHECK + if test "$success" = "yes"; then + break; + fi + done + fi + + if test "$success" != "yes" ; then + AC_MSG_RESULT(no) + ZLIB_CPPFLAGS="" + ZLIB_LDFLAGS="" + ZLIB_LIBS="" + else + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ZLIB,,[define if zlib is available]) + fi + + ax_have_zlib="$success" + + AC_SUBST(ZLIB_CPPFLAGS) + AC_SUBST(ZLIB_LDFLAGS) + AC_SUBST(ZLIB_LIBS) + fi + + ]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_lua.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_lua.m4 new file mode 100644 index 00000000..9feb3522 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_lua.m4 @@ -0,0 +1,664 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_lua.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# +# DESCRIPTION +# +# Detect a Lua interpreter, optionally specifying a minimum and maximum +# version number. Set up important Lua paths, such as the directories in +# which to install scripts and modules (shared libraries). +# +# Also detect Lua headers and libraries. The Lua version contained in the +# header is checked to match the Lua interpreter version exactly. When +# searching for Lua libraries, the version number is used as a suffix. +# This is done with the goal of supporting multiple Lua installs (5.1, +# 5.2, and 5.3 side-by-side). +# +# A note on compatibility with previous versions: This file has been +# mostly rewritten for serial 18. Most developers should be able to use +# these macros without needing to modify configure.ac. Care has been taken +# to preserve each macro's behavior, but there are some differences: +# +# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as +# AX_PROG_LUA with no arguments. +# +# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h +# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore +# unnecessary, so it is deprecated and does not expand to anything. +# +# 3) The configure flag --with-lua-suffix no longer exists; the user +# should instead specify the LUA precious variable on the command line. +# See the AX_PROG_LUA description for details. +# +# Please read the macro descriptions below for more information. +# +# This file was inspired by Andrew Dalke's and James Henstridge's +# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 +# (serial 17). Basically, this file is a mash-up of those two files. I +# like to think it combines the best of the two! +# +# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua +# paths. Adds precious variable LUA, which may contain the path of the Lua +# interpreter. If LUA is blank, the user's path is searched for an +# suitable interpreter. +# +# If MINIMUM-VERSION is supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION will be accepted. If +# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION and less than +# TOO-BIG-VERSION will be accepted. +# +# The Lua version number, LUA_VERSION, is found from the interpreter, and +# substituted. LUA_PLATFORM is also found, but not currently supported (no +# standard representation). +# +# Finally, the macro finds four paths: +# +# luadir Directory to install Lua scripts. +# pkgluadir $luadir/$PACKAGE +# luaexecdir Directory to install Lua modules. +# pkgluaexecdir $luaexecdir/$PACKAGE +# +# These paths are found based on $prefix, $exec_prefix, Lua's +# package.path, and package.cpath. The first path of package.path +# beginning with $prefix is selected as luadir. The first path of +# package.cpath beginning with $exec_prefix is used as luaexecdir. This +# should work on all reasonable Lua installations. If a path cannot be +# determined, a default path is used. Of course, the user can override +# these later when invoking make. +# +# luadir Default: $prefix/share/lua/$LUA_VERSION +# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION +# +# These directories can be used by Automake as install destinations. The +# variable name minus 'dir' needs to be used as a prefix to the +# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. +# +# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- +# FOUND is blank, then it will default to printing an error. To prevent +# the default behavior, give ':' as an action. +# +# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_INCLUDE, which +# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If +# LUA_INCLUDE is blank, then this macro will attempt to find suitable +# flags. +# +# LUA_INCLUDE can be used by Automake to compile Lua modules or +# executables with embedded interpreters. The *_CPPFLAGS variables should +# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). +# +# This macro searches for the header lua.h (and others). The search is +# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. +# If the search is unsuccessful, then some common directories are tried. +# If the headers are then found, then LUA_INCLUDE is set accordingly. +# +# The paths automatically searched are: +# +# * /usr/include/luaX.Y +# * /usr/include/lua/X.Y +# * /usr/include/luaXY +# * /usr/local/include/luaX.Y +# * /usr/local/include/lua-X.Y +# * /usr/local/include/lua/X.Y +# * /usr/local/include/luaXY +# +# (Where X.Y is the Lua version number, e.g. 5.1.) +# +# The Lua version number found in the headers is always checked to match +# the Lua interpreter's version number. Lua headers with mismatched +# version numbers are not accepted. +# +# If headers are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_LIB, which may +# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, +# then this macro will attempt to find suitable flags. +# +# LUA_LIB can be used by Automake to link Lua modules or executables with +# embedded interpreters. The *_LIBADD and *_LDADD variables should be used +# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB). +# +# This macro searches for the Lua library. More technically, it searches +# for a library containing the function lua_load. The search is performed +# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB. +# +# If the search determines that some linker flags are missing, then those +# flags will be added to LUA_LIB. +# +# If libraries are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_READLINE: Search for readline headers and libraries. Requires the +# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the +# Autoconf Archive. +# +# If a readline compatible library is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is performed. +# +# LICENSE +# +# Copyright (c) 2015 Reuben Thomas +# Copyright (c) 2014 Tim Perkins +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 39 + +dnl ========================================================================= +dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_PROG_LUA], +[ + dnl Check for required tools. + AC_REQUIRE([AC_PROG_GREP]) + AC_REQUIRE([AC_PROG_SED]) + + dnl Make LUA a precious variable. + AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) + + dnl Find a Lua interpreter. + m4_define_default([_AX_LUA_INTERPRETER_LIST], + [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50]) + + m4_if([$1], [], + [ dnl No version check is needed. Find any Lua interpreter. + AS_IF([test "x$LUA" = 'x'], + [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) + ax_display_LUA='lua' + + AS_IF([test "x$LUA" != 'x:'], + [ dnl At least check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + ]) + ], + [ dnl A version check is needed. + AS_IF([test "x$LUA" != 'x'], + [ dnl Check if this is a Lua interpreter. + AC_MSG_CHECKING([if $LUA is a Lua interpreter]) + _AX_LUA_CHK_IS_INTRP([$LUA], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([not a Lua interpreter]) + ]) + dnl Check the version. + m4_if([$2], [], + [_ax_check_text="whether $LUA version >= $1"], + [_ax_check_text="whether $LUA version >= $1, < $2"]) + AC_MSG_CHECKING([$_ax_check_text]) + _AX_LUA_CHK_VER([$LUA], [$1], [$2], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([version is out of range for specified LUA])]) + ax_display_LUA=$LUA + ], + [ dnl Try each interpreter until we find one that satisfies VERSION. + m4_if([$2], [], + [_ax_check_text="for a Lua interpreter with version >= $1"], + [_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) + AC_CACHE_CHECK([$_ax_check_text], + [ax_cv_pathless_LUA], + [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do + test "x$ax_cv_pathless_LUA" = 'xnone' && break + _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) + _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) + done + ]) + dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA. + AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'], + [LUA=':'], + [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])]) + ax_display_LUA=$ax_cv_pathless_LUA + ]) + ]) + + AS_IF([test "x$LUA" = 'x:'], + [ dnl Run any user-specified action, or abort. + m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) + ], + [ dnl Query Lua for its version number. + AC_CACHE_CHECK([for $ax_display_LUA version], + [ax_cv_lua_version], + [ dnl Get the interpreter version in X.Y format. This should work for + dnl interpreters version 5.0 and beyond. + ax_cv_lua_version=[`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver)'`] + ]) + AS_IF([test "x$ax_cv_lua_version" = 'x'], + [AC_MSG_ERROR([invalid Lua version number])]) + AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) + AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) + + dnl The following check is not supported: + dnl At times (like when building shared libraries) you may want to know + dnl which OS platform Lua thinks this is. + AC_CACHE_CHECK([for $ax_display_LUA platform], + [ax_cv_lua_platform], + [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]]) + AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct + dnl variables so they can be overridden if need be. However, the general + dnl consensus is that you shouldn't need this ability. + AC_SUBST([LUA_PREFIX], ['${prefix}']) + AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}']) + + dnl Lua provides no way to query the script directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $prefix, then we can store scripts there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA script directory], + [ax_cv_lua_luadir], + [ AS_IF([test "x$prefix" = 'xNONE'], + [ax_lua_prefix=$ac_default_prefix], + [ax_lua_prefix=$prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` + ]) + ]) + AC_SUBST([luadir], [$ax_cv_lua_luadir]) + AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE]) + + dnl Lua provides no way to query the module directory, and instead + dnl provides LUA_PATH. However, we should be able to make a safe educated + dnl guess. If the built-in search path contains a directory which is + dnl prefixed by $exec_prefix, then we can store modules there. The first + dnl matching path will be used. + AC_CACHE_CHECK([for $ax_display_LUA module directory], + [ax_cv_lua_luaexecdir], + [ AS_IF([test "x$exec_prefix" = 'xNONE'], + [ax_lua_exec_prefix=$ax_lua_prefix], + [ax_lua_exec_prefix=$exec_prefix]) + + dnl Initialize to the default path. + ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION" + + dnl Try to find a path with the prefix. + _AX_LUA_FND_PRFX_PTH([$LUA], + [$ax_lua_exec_prefix], [module]) + AS_IF([test "x$ax_lua_prefixed_path" != 'x'], + [ dnl Fix the prefix. + _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` + ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` + ]) + ]) + AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) + AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE]) + + dnl Run any user specified action. + $3 + ]) +]) + +dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. +AC_DEFUN([AX_WITH_LUA], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]]) + AX_PROG_LUA +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_IS_INTRP], +[ + dnl A minimal Lua factorial to prove this is an interpreter. This should work + dnl for Lua interpreters version 5.0 and beyond. + _ax_lua_factorial=[`$1 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'`] + AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'], + [$2], [$3]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION], +dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_CHK_VER], +[ + dnl Check that the Lua version is within the bounds. Only the major and minor + dnl version numbers are considered. This should work for Lua interpreters + dnl version 5.0 and beyond. + _ax_lua_good_version=[`$1 -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("$2") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("$3") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'`] + AS_IF([test "x$_ax_lua_good_version" = "xyes"], + [$4], [$5]) +]) + + +dnl ========================================================================= +dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR) +dnl ========================================================================= +AC_DEFUN([_AX_LUA_FND_PRFX_PTH], +[ + dnl Get the script or module directory by querying the Lua interpreter, + dnl filtering on the given prefix, and selecting the shallowest path. If no + dnl path is found matching the prefix, the result will be an empty string. + dnl The third argument determines the type of search, it can be 'script' or + dnl 'module'. Supplying 'script' will perform the search with package.path + dnl and LUA_PATH, and supplying 'module' will search with package.cpath and + dnl LUA_CPATH. This is done for compatibility with Lua 5.0. + + ax_lua_prefixed_path=[`$1 -e ' + -- get the path based on search type + local searchtype = "$3" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$2'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "(@<:@^;@:>@+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/@<:@^/@:>@*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "@<:@^/@:>@", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end + end + end) + print(minpath)'`] +]) + + +dnl ========================================================================= +dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_HEADERS], +[ + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) + ]) + + dnl Make LUA_INCLUDE a precious variable. + AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) + + dnl Some default directories to search. + LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` + m4_define_default([_AX_LUA_INCLUDE_LIST], + [ /usr/include/lua$LUA_VERSION \ + /usr/include/lua-$LUA_VERSION \ + /usr/include/lua/$LUA_VERSION \ + /usr/include/lua$LUA_SHORT_VERSION \ + /usr/local/include/lua$LUA_VERSION \ + /usr/local/include/lua-$LUA_VERSION \ + /usr/local/include/lua/$LUA_VERSION \ + /usr/local/include/lua$LUA_SHORT_VERSION \ + ]) + + dnl Try to find the headers. + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + dnl Try some other directories if LUA_INCLUDE was not set. + AS_IF([test "x$LUA_INCLUDE" = 'x' && + test "x$ac_cv_header_lua_h" != 'xyes'], + [ dnl Try some common include paths. + for _ax_include_path in _AX_LUA_INCLUDE_LIST; do + test ! -d "$_ax_include_path" && continue + + AC_MSG_CHECKING([for Lua headers in]) + AC_MSG_RESULT([$_ax_include_path]) + + AS_UNSET([ac_cv_header_lua_h]) + AS_UNSET([ac_cv_header_lualib_h]) + AS_UNSET([ac_cv_header_lauxlib_h]) + AS_UNSET([ac_cv_header_luaconf_h]) + + _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -I$_ax_include_path" + AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) + CPPFLAGS=$_ax_lua_saved_cppflags + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ LUA_INCLUDE="-I$_ax_include_path" + break + ]) + done + ]) + + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], + [ dnl Make a program to print LUA_VERSION defined in the header. + dnl TODO It would be really nice if we could do this without compiling a + dnl program, then it would work when cross compiling. But I'm not sure how + dnl to do this reliably. For now, assume versions match when cross compiling. + + AS_IF([test "x$cross_compiling" != 'xyes'], + [ AC_CACHE_CHECK([for Lua header version], + [ax_cv_lua_header_version], + [ _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_RUN_IFELSE( + [ AC_LANG_SOURCE([[ +#include +#include +#include +int main(int argc, char ** argv) +{ + if(argc > 1) printf("%s", LUA_VERSION); + exit(EXIT_SUCCESS); +} +]]) + ], + [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ + $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"` + ], + [ax_cv_lua_header_version='unknown']) + CPPFLAGS=$_ax_lua_saved_cppflags + ]) + + dnl Compare this to the previously found LUA_VERSION. + AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) + AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], + [ AC_MSG_RESULT([yes]) + ax_header_version_match='yes' + ], + [ AC_MSG_RESULT([no]) + ax_header_version_match='no' + ]) + ], + [ AC_MSG_WARN([cross compiling so assuming header version number matches]) + ax_header_version_match='yes' + ]) + ]) + + dnl Was LUA_INCLUDE specified? + AS_IF([test "x$ax_header_version_match" != 'xyes' && + test "x$LUA_INCLUDE" != 'x'], + [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])]) + + dnl Test the final result and run user code. + AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])]) +]) + +dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. +AC_DEFUN([AX_LUA_HEADERS_VERSION], +[ + AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]]) +]) + + +dnl ========================================================================= +dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_LIBS], +[ + dnl TODO Should this macro also check various -L flags? + + dnl Check for LUA_VERSION. + AC_MSG_CHECKING([if LUA_VERSION is defined]) + AS_IF([test "x$LUA_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION]) + ]) + + dnl Make LUA_LIB a precious variable. + AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1]) + + AS_IF([test "x$LUA_LIB" != 'x'], + [ dnl Check that LUA_LIBS works. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], [], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no']) + LIBS=$_ax_lua_saved_libs + + dnl Check the result. + AS_IF([test "x$_ax_found_lua_libs" != 'xyes'], + [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])]) + ], + [ dnl First search for extra libs. + _ax_lua_extra_libs='' + + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([exp], [m]) + AC_SEARCH_LIBS([dlopen], [dl]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_exp" != 'xno' && + test "x$ac_cv_search_exp" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"]) + + AS_IF([test "x$ac_cv_search_dlopen" != 'xno' && + test "x$ac_cv_search_dlopen" != 'xnone required'], + [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"]) + + dnl Try to find the Lua libs. + _ax_lua_saved_libs=$LIBS + LIBS="$LIBS $LUA_LIB" + AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs]) + LIBS=$_ax_lua_saved_libs + + AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && + test "x$ac_cv_search_lua_load" != 'xnone required'], + [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"]) + ]) + + dnl Test the result and run user code. + AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1], + [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])]) +]) + + +dnl ========================================================================= +dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ========================================================================= +AC_DEFUN([AX_LUA_READLINE], +[ + AX_LIB_READLINE + AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' && + test "x$ac_cv_header_readline_history_h" != 'x'], + [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS" + $1 + ], + [$2]) +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_prog_haxe_version.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_prog_haxe_version.m4 new file mode 100644 index 00000000..3dee4302 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_prog_haxe_version.m4 @@ -0,0 +1,60 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_haxe_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_HAXE_VERSION([VERSION],[ACTION-IF-TRUE],[ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# Makes sure that haxe supports the version indicated. If true the shell +# commands in ACTION-IF-TRUE are executed. If not the shell commands in +# ACTION-IF-FALSE are run. The $HAXE_VERSION variable will be filled with +# the detected version. +# +# This macro uses the $HAXE variable to perform the check. If $HAXE is not +# set prior to calling this macro, the macro will fail. +# +# Example: +# +# AC_PATH_PROG([HAXE],[haxe]) +# AC_PROG_HAXE_VERSION([3.1.3],[ ... ],[ ... ]) +# +# Searches for Haxe, then checks if at least version 3.1.3 is present. +# +# LICENSE +# +# Copyright (c) 2015 Jens Geyer +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_PROG_HAXE_VERSION],[ + AC_REQUIRE([AC_PROG_SED]) + + AS_IF([test -n "$HAXE"],[ + ax_haxe_version="$1" + + AC_MSG_CHECKING([for haxe version]) + haxe_version=`$HAXE -version 2>&1 | $SED -e 's/^.* \( @<:@0-9@:>@*\.@<:@0-9@:>@*\.@<:@0-9@:>@*\) .*/\1/'` + AC_MSG_RESULT($haxe_version) + + AC_SUBST([HAXE_VERSION],[$haxe_version]) + + AX_COMPARE_VERSION([$ax_haxe_version],[le],[$haxe_version],[ + : + $2 + ],[ + : + $3 + ]) + ],[ + AC_MSG_WARN([could not find Haxe]) + $3 + ]) +]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_prog_perl_modules.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_prog_perl_modules.m4 new file mode 100644 index 00000000..11a326c9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_prog_perl_modules.m4 @@ -0,0 +1,77 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_perl_modules.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# Checks to see if the given perl modules are available. If true the shell +# commands in ACTION-IF-TRUE are executed. If not the shell commands in +# ACTION-IF-FALSE are run. Note if $PERL is not set (for example by +# calling AC_CHECK_PROG, or AC_PATH_PROG), AC_CHECK_PROG(PERL, perl, perl) +# will be run. +# +# MODULES is a space separated list of module names. To check for a +# minimum version of a module, append the version number to the module +# name, separated by an equals sign. +# +# Example: +# +# AX_PROG_PERL_MODULES( Text::Wrap Net::LDAP=1.0.3, , +# AC_MSG_WARN(Need some Perl modules) +# +# LICENSE +# +# Copyright (c) 2009 Dean Povey +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +AU_ALIAS([AC_PROG_PERL_MODULES], [AX_PROG_PERL_MODULES]) +AC_DEFUN([AX_PROG_PERL_MODULES],[dnl + +m4_define([ax_perl_modules]) +m4_foreach([ax_perl_module], m4_split(m4_normalize([$1])), + [ + m4_append([ax_perl_modules], + [']m4_bpatsubst(ax_perl_module,=,[ ])[' ]) + ]) + +# Make sure we have perl +if test -z "$PERL"; then +AC_CHECK_PROG(PERL,perl,perl) +fi + +if test "x$PERL" != x; then + ax_perl_modules_failed=0 + for ax_perl_module in ax_perl_modules; do + AC_MSG_CHECKING(for perl module $ax_perl_module) + + # Would be nice to log result here, but can't rely on autoconf internals + $PERL -e "use $ax_perl_module; exit" > /dev/null 2>&1 + if test $? -ne 0; then + AC_MSG_RESULT(no); + ax_perl_modules_failed=1 + else + AC_MSG_RESULT(ok); + fi + done + + # Run optional shell commands + if test "$ax_perl_modules_failed" = 0; then + : + $2 + else + : + $3 + fi +else + AC_MSG_WARN(could not find perl) +fi])dnl diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_signed_right_shift.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_signed_right_shift.m4 new file mode 100644 index 00000000..9c3ceb79 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_signed_right_shift.m4 @@ -0,0 +1,127 @@ +dnl @synopsis AX_SIGNED_RIGHT_SHIFT +dnl +dnl Tests the behavior of a right shift on a negative signed int. +dnl +dnl This macro calls: +dnl AC_DEFINE(SIGNED_RIGHT_SHIFT_IS) +dnl AC_DEFINE(ARITHMETIC_RIGHT_SHIFT) +dnl AC_DEFINE(LOGICAL_RIGHT_SHIFT) +dnl AC_DEFINE(UNKNOWN_RIGHT_SHIFT) +dnl +dnl SIGNED_RIGHT_SHIFT_IS will be equal to one of the other macros. +dnl It also leaves the shell variables "ax_signed_right_shift" +dnl set to "arithmetic", "logical", or "unknown". +dnl +dnl NOTE: This macro does not work for cross-compiling. +dnl +dnl @category C +dnl @version 2009-03-25 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + +AC_DEFUN([AX_SIGNED_RIGHT_SHIFT], + [ + + AC_MSG_CHECKING(the behavior of a signed right shift) + + success_arithmetic=no + AC_RUN_IFELSE([AC_LANG_PROGRAM([[]], [[ + return + /* 0xffffffff */ + -1 >> 1 != -1 || + -1 >> 2 != -1 || + -1 >> 3 != -1 || + -1 >> 4 != -1 || + -1 >> 8 != -1 || + -1 >> 16 != -1 || + -1 >> 24 != -1 || + -1 >> 31 != -1 || + /* 0x80000000 */ + (-2147483647 - 1) >> 1 != -1073741824 || + (-2147483647 - 1) >> 2 != -536870912 || + (-2147483647 - 1) >> 3 != -268435456 || + (-2147483647 - 1) >> 4 != -134217728 || + (-2147483647 - 1) >> 8 != -8388608 || + (-2147483647 - 1) >> 16 != -32768 || + (-2147483647 - 1) >> 24 != -128 || + (-2147483647 - 1) >> 31 != -1 || + /* 0x90800000 */ + -1870659584 >> 1 != -935329792 || + -1870659584 >> 2 != -467664896 || + -1870659584 >> 3 != -233832448 || + -1870659584 >> 4 != -116916224 || + -1870659584 >> 8 != -7307264 || + -1870659584 >> 16 != -28544 || + -1870659584 >> 24 != -112 || + -1870659584 >> 31 != -1 || + 0; + ]])], [ + success_arithmetic=yes + ]) + + + success_logical=no + AC_RUN_IFELSE([AC_LANG_PROGRAM([[]], [[ + return + /* 0xffffffff */ + -1 >> 1 != (signed)((unsigned)-1 >> 1) || + -1 >> 2 != (signed)((unsigned)-1 >> 2) || + -1 >> 3 != (signed)((unsigned)-1 >> 3) || + -1 >> 4 != (signed)((unsigned)-1 >> 4) || + -1 >> 8 != (signed)((unsigned)-1 >> 8) || + -1 >> 16 != (signed)((unsigned)-1 >> 16) || + -1 >> 24 != (signed)((unsigned)-1 >> 24) || + -1 >> 31 != (signed)((unsigned)-1 >> 31) || + /* 0x80000000 */ + (-2147483647 - 1) >> 1 != (signed)((unsigned)(-2147483647 - 1) >> 1) || + (-2147483647 - 1) >> 2 != (signed)((unsigned)(-2147483647 - 1) >> 2) || + (-2147483647 - 1) >> 3 != (signed)((unsigned)(-2147483647 - 1) >> 3) || + (-2147483647 - 1) >> 4 != (signed)((unsigned)(-2147483647 - 1) >> 4) || + (-2147483647 - 1) >> 8 != (signed)((unsigned)(-2147483647 - 1) >> 8) || + (-2147483647 - 1) >> 16 != (signed)((unsigned)(-2147483647 - 1) >> 16) || + (-2147483647 - 1) >> 24 != (signed)((unsigned)(-2147483647 - 1) >> 24) || + (-2147483647 - 1) >> 31 != (signed)((unsigned)(-2147483647 - 1) >> 31) || + /* 0x90800000 */ + -1870659584 >> 1 != (signed)((unsigned)-1870659584 >> 1) || + -1870659584 >> 2 != (signed)((unsigned)-1870659584 >> 2) || + -1870659584 >> 3 != (signed)((unsigned)-1870659584 >> 3) || + -1870659584 >> 4 != (signed)((unsigned)-1870659584 >> 4) || + -1870659584 >> 8 != (signed)((unsigned)-1870659584 >> 8) || + -1870659584 >> 16 != (signed)((unsigned)-1870659584 >> 16) || + -1870659584 >> 24 != (signed)((unsigned)-1870659584 >> 24) || + -1870659584 >> 31 != (signed)((unsigned)-1870659584 >> 31) || + 0; + ]])], [ + success_logical=yes + ]) + + + AC_DEFINE([ARITHMETIC_RIGHT_SHIFT], 1, [Possible value for SIGNED_RIGHT_SHIFT_IS]) + AC_DEFINE([LOGICAL_RIGHT_SHIFT], 2, [Possible value for SIGNED_RIGHT_SHIFT_IS]) + AC_DEFINE([UNKNOWN_RIGHT_SHIFT], 3, [Possible value for SIGNED_RIGHT_SHIFT_IS]) + + if test "$success_arithmetic" = "yes" && test "$success_logical" = "yes" ; then + AC_MSG_ERROR("Right shift appears to be both arithmetic and logical!") + elif test "$success_arithmetic" = "yes" ; then + ax_signed_right_shift=arithmetic + AC_DEFINE([SIGNED_RIGHT_SHIFT_IS], 1, + [Indicates the effect of the right shift operator + on negative signed integers]) + elif test "$success_logical" = "yes" ; then + ax_signed_right_shift=logical + AC_DEFINE([SIGNED_RIGHT_SHIFT_IS], 2, + [Indicates the effect of the right shift operator + on negative signed integers]) + else + ax_signed_right_shift=unknown + AC_DEFINE([SIGNED_RIGHT_SHIFT_IS], 3, + [Indicates the effect of the right shift operator + on negative signed integers]) + fi + + AC_MSG_RESULT($ax_signed_right_shift) + ]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/ax_thrift_internal.m4 b/vendor/src/github.com/apache/thrift/aclocal/ax_thrift_internal.m4 new file mode 100644 index 00000000..8c0e3cbc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/ax_thrift_internal.m4 @@ -0,0 +1,28 @@ +dnl @synopsis AX_THRIFT_GEN(SHORT_LANGUAGE, LONG_LANGUAGE, DEFAULT) +dnl @synopsis AX_THRIFT_LIB(SHORT_LANGUAGE, LONG_LANGUAGE, DEFAULT) +dnl +dnl Allow a particular language generator to be disabled. +dnl Allow a particular language library to be disabled. +dnl +dnl These macros have poor error handling and are poorly documented. +dnl They are intended only for internal use by the Thrift compiler. +dnl +dnl @version 2008-02-20 +dnl @license AllPermissive +dnl +dnl Copyright (C) 2009 David Reiss +dnl Copying and distribution of this file, with or without modification, +dnl are permitted in any medium without royalty provided the copyright +dnl notice and this notice are preserved. + +AC_DEFUN([AX_THRIFT_LIB], + [ + AC_ARG_WITH($1, + AC_HELP_STRING([--with-$1], [build the $2 library @<:@default=$3@:>@]), + [with_$1="$withval"], + [with_$1=$3] + ) + have_$1=no + dnl What we do here is going to vary from library to library, + dnl so we can't really generalize (yet!). + ]) diff --git a/vendor/src/github.com/apache/thrift/aclocal/m4_ax_compare_version.m4 b/vendor/src/github.com/apache/thrift/aclocal/m4_ax_compare_version.m4 new file mode 100644 index 00000000..74dc0fdd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/aclocal/m4_ax_compare_version.m4 @@ -0,0 +1,177 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# +# DESCRIPTION +# +# This macro compares two version strings. Due to the various number of +# minor-version numbers that can exist, and the fact that string +# comparisons are not compatible with numeric comparisons, this is not +# necessarily trivial to do in a autoconf script. This macro makes doing +# these comparisons easy. +# +# The six basic comparisons are available, as well as checking equality +# limited to a certain number of minor-version levels. +# +# The operator OP determines what type of comparison to do, and can be one +# of: +# +# eq - equal (test A == B) +# ne - not equal (test A != B) +# le - less than or equal (test A <= B) +# ge - greater than or equal (test A >= B) +# lt - less than (test A < B) +# gt - greater than (test A > B) +# +# Additionally, the eq and ne operator can have a number after it to limit +# the test to that number of minor versions. +# +# eq0 - equal up to the length of the shorter version +# ne0 - not equal up to the length of the shorter version +# eqN - equal up to N sub-version levels +# neN - not equal up to N sub-version levels +# +# When the condition is true, shell commands ACTION-IF-TRUE are run, +# otherwise shell commands ACTION-IF-FALSE are run. The environment +# variable 'ax_compare_version' is always set to either 'true' or 'false' +# as well. +# +# Examples: +# +# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) +# +# would both be true. +# +# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) +# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) +# +# would both be false. +# +# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) +# +# would be true because it is only comparing two minor versions. +# +# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) +# +# would be true because it is only comparing the lesser number of minor +# versions of the two values. +# +# Note: The characters that separate the version numbers do not matter. An +# empty string is the same as version 0. OP is evaluated by autoconf, not +# configure, so must be a string, not a variable. +# +# The author would like to acknowledge Guido Draheim whose advice about +# the m4_case and m4_ifvaln functions make this macro only include the +# portions necessary to perform the specific comparison specified by the +# OP argument in the final configure script. +# +# LICENSE +# +# Copyright (c) 2008 Tim Toolan +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +dnl ######################################################################### +AC_DEFUN([AX_COMPARE_VERSION], [ + AC_REQUIRE([AC_PROG_AWK]) + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + AS_VAR_PUSHDEF([A],[ax_compare_version_A]) + A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + AS_VAR_PUSHDEF([B],[ax_compare_version_B]) + B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ + -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ + -e 's/[[^0-9]]//g'` + + dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary + dnl # then the first line is used to determine if the condition is true. + dnl # The sed right after the echo is to remove any indented white space. + m4_case(m4_tolower($2), + [lt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [gt],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` + ], + [le],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ], + [ge],[ + ax_compare_version=`echo "x$A +x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` + ],[ + dnl Split the operator from the subversion count if present. + m4_bmatch(m4_substr($2,2), + [0],[ + # A count of zero means use the length of the shorter version. + # Determine the number of characters in A and B. + ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` + ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` + + # Set A to no more than B's length and B to no more than A's length. + A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` + B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` + ], + [[0-9]+],[ + # A count greater than zero means use only that many subversions + A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` + ], + [.+],[ + AC_WARNING( + [illegal OP numeric parameter: $2]) + ],[]) + + # Pad zeros at end of numbers to make same length. + ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" + B="$B`echo $A | sed 's/./0/g'`" + A="$ax_compare_version_tmp_A" + + # Check for equality or inequality as necessary. + m4_case(m4_tolower(m4_substr($2,0,2)), + [eq],[ + test "x$A" = "x$B" && ax_compare_version=true + ], + [ne],[ + test "x$A" != "x$B" && ax_compare_version=true + ],[ + AC_WARNING([illegal OP parameter: $2]) + ]) + ]) + + AS_VAR_POPDEF([A])dnl + AS_VAR_POPDEF([B])dnl + + dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. + if test "$ax_compare_version" = "true" ; then + m4_ifvaln([$4],[$4],[:])dnl + m4_ifvaln([$5],[else $5])dnl + fi +]) dnl AX_COMPARE_VERSION diff --git a/vendor/src/github.com/apache/thrift/appveyor.yml b/vendor/src/github.com/apache/thrift/appveyor.yml new file mode 100644 index 00000000..03ee2954 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/appveyor.yml @@ -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. +# + +# build Apache Thrift on AppVeyor - https://ci.appveyor.com + +shallow_clone: true +clone_depth: 10 + +version: '{build}' +os: +# - Windows Server 2012 R2 +- Visual Studio 2015 + +environment: + BOOST_ROOT: C:\Libraries\boost_1_59_0 + BOOST_LIBRARYDIR: C:\Libraries\boost_1_59_0\lib64-msvc-14.0 + # Unfurtunately, this version needs manual update because old versions are quickly deleted. + ANT_VERSION: 1.9.7 + +install: +- '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64' +- cd \ + # Zlib +- appveyor DownloadFile https://github.com/madler/zlib/archive/v1.2.8.tar.gz +- 7z x v1.2.8.tar.gz -so | 7z x -si -ttar > nul +- cd zlib-1.2.8 +- cmake -G "Visual Studio 14 2015 Win64" . +- cmake --build . --config release +- cd .. + # OpenSSL +- C:\Python35-x64\python %APPVEYOR_BUILD_FOLDER%\build\appveyor\download_openssl.py +- ps: Start-Process "Win64OpenSSL.exe" -ArgumentList "/silent /verysilent /sp- /suppressmsgboxes" -Wait + # Libevent +- appveyor DownloadFile https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz +- 7z x libevent-2.0.22-stable.tar.gz -so | 7z x -si -ttar > nul +- cd libevent-2.0.22-stable +- nmake -f Makefile.nmake +- mkdir lib +- move *.lib lib\ +- move WIN32-Code\event2\* include\event2\ +- move *.h include\ +- cd .. +- appveyor-retry cinst -y winflexbison +- appveyor DownloadFile http://www.us.apache.org/dist/ant/binaries/apache-ant-%ANT_VERSION%-bin.zip +- 7z x apache-ant-%ANT_VERSION%-bin.zip > nul +- cd %APPVEYOR_BUILD_FOLDER% +# TODO: Enable Haskell build +# - cinst HaskellPlatform -version 2014.2.0.0 + + +build_script: +- set PATH=C:\ProgramData\chocolatey\bin;C:\apache-ant-%ANT_VERSION%\bin;%PATH% +- set JAVA_HOME=C:\Program Files\Java\jdk1.7.0 +- set PATH=%JAVA_HOME%\bin;%PATH% +# - set PATH=%PATH%;C:\Program Files (x86)\Haskell Platform\2014.2.0.0\bin +# - set PATH=%PATH%;C:\Program Files (x86)\Haskell Platform\2014.2.0.0\lib\extralibs\bin +- set PATH=C:\Python27-x64\scripts;C:\Python27-x64;%PATH% +- pip install ipaddress backports.ssl_match_hostname tornado twisted +- mkdir cmake-build +- cd cmake-build +- cmake -G "Visual Studio 14 2015 Win64" -DWITH_SHARED_LIB=OFF -DLIBEVENT_ROOT=C:\libevent-2.0.22-stable -DZLIB_INCLUDE_DIR=C:\zlib-1.2.8 -DZLIB_LIBRARY=C:\zlib-1.2.8\release\zlibstatic.lib -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%" .. +- findstr /b /e BUILD_COMPILER:BOOL=ON CMakeCache.txt +- findstr /b /e BUILD_CPP:BOOL=ON CMakeCache.txt +- findstr /b /e BUILD_JAVA:BOOL=ON CMakeCache.txt +- findstr /b /e BUILD_PYTHON:BOOL=ON CMakeCache.txt +# - findstr /b /e BUILD_C_GLIB:BOOL=ON CMakeCache.txt +# - findstr /b /e BUILD_HASKELL:BOOL=ON CMakeCache.txt +- findstr /b /e BUILD_TESTING:BOOL=ON CMakeCache.txt +# - cmake --build . +- cmake --build . --config Release +# TODO: Fix cpack +# - cpack +# TODO: Run more tests +# CTest fails to invoke ant seemingly due to "ant.bat" v.s. "ant" (shell script) conflict. +# Currently, everything that involves OpenSSL seems to hang forever on our Appveyor setup. +# Also a few C++ tests hang (on Appveyor or on Windows in general). +- ctest -C Release --timeout 600 -VV -E "(StressTestNonBlocking|PythonTestSSLSocket|python_test$|^Java)" +# TODO make it perfect ;-r diff --git a/vendor/src/github.com/apache/thrift/bootstrap.sh b/vendor/src/github.com/apache/thrift/bootstrap.sh new file mode 100644 index 00000000..52ecda47 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/bootstrap.sh @@ -0,0 +1,54 @@ +#!/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. +# + +./cleanup.sh +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) + fi +fi + +set -e + +# libtoolize is called "glibtoolize" on OSX. +if libtoolize --version 1 >/dev/null 2>/dev/null; then + LIBTOOLIZE=libtoolize +elif glibtoolize --version 1 >/dev/null 2>/dev/null; then + LIBTOOLIZE=glibtoolize +else + echo >&2 "Couldn't find libtoolize!" + exit 1 +fi + +# we require automake 1.13 or later +# check must happen externally due to use of newer macro +AUTOMAKE_VERSION=`automake --version | grep automake | egrep -o '([0-9]{1,}\.)+[0-9]{1,}'` +if [ "$AUTOMAKE_VERSION" \< "1.13" ]; then + echo >&2 "automake version $AUTOMAKE_VERSION is too old (need 1.13 or later)" + exit 1 +fi + +autoscan +$LIBTOOLIZE --copy --automake +aclocal -I ./aclocal +autoheader +autoconf +automake --copy --add-missing --foreign diff --git a/vendor/src/github.com/apache/thrift/bower.json b/vendor/src/github.com/apache/thrift/bower.json new file mode 100644 index 00000000..9ec59fcb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/bower.json @@ -0,0 +1,16 @@ +{ + "name": "thrift", + "version": "0.10.0", + "homepage": "https://git-wip-us.apache.org/repos/asf/thrift.git", + "authors": [ + "Apache Thrift " + ], + "description": "Apache Thrift", + "main": "lib/js/src/thrift.js", + "keywords": [ + "thrift" + ], + "license": "Apache v2", + "ignore": [ + ] +} diff --git a/vendor/src/github.com/apache/thrift/build/appveyor/download_openssl.py b/vendor/src/github.com/apache/thrift/build/appveyor/download_openssl.py new file mode 100644 index 00000000..fcb72e5f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/appveyor/download_openssl.py @@ -0,0 +1,41 @@ +import urllib.request +import sys + +OUT = 'Win64OpenSSL.exe' + +URL_STR = 'https://slproweb.com/download/Win64OpenSSL-%s.exe' + +VERSION_MAJOR = 1 +VERSION_MINOR = 0 +VERSION_PATCH = 2 +VERSION_SUFFIX = 'j' +VERSION_STR = '%d_%d_%d%s' + +TRY_COUNT = 4 + + +def main(): + for patch in range(VERSION_PATCH, TRY_COUNT): + for suffix in range(TRY_COUNT): + if patch == VERSION_PATCH: + s = VERSION_SUFFIX + else: + s = 'a' + s = chr(ord(s) + suffix) + ver = VERSION_STR % (VERSION_MAJOR, VERSION_MINOR, patch, s) + url = URL_STR % ver + try: + with urllib.request.urlopen(url) as res: + if res.getcode() == 200: + with open(OUT, 'wb') as out: + out.write(res.read()) + print('successfully downloaded from ' + url) + return 0 + except urllib.error.HTTPError: + pass + print('failed to download from ' + url, file=sys.stderr) + print('could not download openssl', file=sys.stderr) + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/CPackConfig.cmake b/vendor/src/github.com/apache/thrift/build/cmake/CPackConfig.cmake new file mode 100644 index 00000000..fdc1b4e7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/CPackConfig.cmake @@ -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. +# + + +#TODO: Should we bundle system libraries for DLLs? +#include(InstallRequiredSystemLibraries) + +# For help take a look at: +# http://www.cmake.org/Wiki/CMake:CPackConfiguration + +### general settings +set(CPACK_PACKAGE_NAME "thrift") +set(CPACK_PACKAGE_VERSION "${PACKAGE_VERSION}") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Apache Thrift") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_PACKAGE_VENDOR "Apache Software Foundation") +set(CPACK_PACKAGE_CONTACT "dev@thrift.apache.org") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") +set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}") + +### versions +set(CPACK_PACKAGE_VERSION_MAJOR ${thrift_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${thrift_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${thrift_VERSION_PATCH}) + +### source generator +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;tags;cscope.*") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +### zip generator +set(CPACK_GENERATOR "ZIP") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "thrift") + + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(CPACK_GENERATOR "NSIS") + set(CPACK_NSIS_HELP_LINK "http://thrift.apache.org") + set(CPACK_NSIS_MENU_LINKS + "http://thrift.apache.org" "Apache Thrift - Web Site" + "https://issues.apache.org/jira/browse/THRIFT" "Apache Thrift - Issues") + set(CPACK_NSIS_CONTACT ${CPACK_PACKAGE_CONTACT}) + set(CPACK_NSIS_MODIFY_PATH "ON") + set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") +else() + set(CPACK_GENERATOR "DEB" ) + set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_CONTACT}) +endif() + + +include(CPack) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/ConfigureChecks.cmake b/vendor/src/github.com/apache/thrift/build/cmake/ConfigureChecks.cmake new file mode 100644 index 00000000..f6505440 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/ConfigureChecks.cmake @@ -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. +# + + +include(CheckSymbolExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckFunctionExists) + +# If AI_ADDRCONFIG is not defined we define it as 0 +check_symbol_exists(AI_ADDRCONFIG "sys/types.h;sys/socket.h;netdb.h" HAVE_AI_ADDRCONFIG) +if(NOT HAVE_AI_ADDRCONFIG) +set(AI_ADDRCONFIG 1) +endif(NOT HAVE_AI_ADDRCONFIG) + +check_include_file(arpa/inet.h HAVE_ARPA_INET_H) +check_include_file(fcntl.h HAVE_FCNTL_H) +check_include_file(getopt.h HAVE_GETOPT_H) +check_include_file(inttypes.h HAVE_INTTYPES_H) +check_include_file(netdb.h HAVE_NETDB_H) +check_include_file(netinet/in.h HAVE_NETINET_IN_H) +check_include_file(stdint.h HAVE_STDINT_H) +check_include_file(unistd.h HAVE_UNISTD_H) +check_include_file(pthread.h HAVE_PTHREAD_H) +check_include_file(sys/time.h HAVE_SYS_TIME_H) +check_include_file(sys/param.h HAVE_SYS_PARAM_H) +check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H) +check_include_file(sys/socket.h HAVE_SYS_SOCKET_H) +check_include_file(sys/stat.h HAVE_SYS_STAT_H) +check_include_file(sys/un.h HAVE_SYS_UN_H) +check_include_file(sys/poll.h HAVE_SYS_POLL_H) +check_include_file(sys/select.h HAVE_SYS_SELECT_H) +check_include_file(sched.h HAVE_SCHED_H) +check_include_file(strings.h HAVE_STRINGS_H) + +check_function_exists(gethostbyname HAVE_GETHOSTBYNAME) +check_function_exists(gethostbyname_r HAVE_GETHOSTBYNAME_R) +check_function_exists(strerror_r HAVE_STRERROR_R) +check_function_exists(sched_get_priority_max HAVE_SCHED_GET_PRIORITY_MAX) +check_function_exists(sched_get_priority_min HAVE_SCHED_GET_PRIORITY_MIN) + +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +check_cxx_source_compiles( + " + #include + int main(){char b;char *a = strerror_r(0, &b, 0); return(0);} + " + STRERROR_R_CHAR_P) + + +set(PACKAGE ${PACKAGE_NAME}) +set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") +set(VERSION ${thrift_VERSION}) + +# generate a config.h file +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/thrift/config.h") +# HACK: Some files include thrift/config.h and some config.h so we include both. This should be cleaned up. +include_directories("${CMAKE_CURRENT_BINARY_DIR}/thrift" "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/vendor/src/github.com/apache/thrift/build/cmake/DefineCMakeDefaults.cmake b/vendor/src/github.com/apache/thrift/build/cmake/DefineCMakeDefaults.cmake new file mode 100644 index 00000000..8125de9d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/DefineCMakeDefaults.cmake @@ -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. +# + + +# Always include srcdir and builddir in include path +# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in +# about every subdir +# since cmake 2.4.0 +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Put the include dirs which are in the source or build tree +# before all other include dirs, so the headers in the sources +# are preferred over the already installed ones +# since cmake 2.4.1 +set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) + +# Use colored output +# since cmake 2.4.0 +set(CMAKE_COLOR_MAKEFILE ON) + +# Define the generic version of the libraries here +set(GENERIC_LIB_VERSION "0.10.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) + +# 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 +# tree rather than installed thrift libraries. +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +# +# "rpath" support. +# See http://www.itk.org/Wiki/index.php?title=CMake_RPATH_handling +# +# On MacOSX, for shared libraries, enable rpath support. +set(CMAKE_MACOSX_RPATH TRUE) +# +# On any OS, for executables, allow linking with shared libraries in non-system +# locations and running the executables without LD_PRELOAD or similar. +# This requires the library to be built with rpath support. +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/DefineInstallationPaths.cmake b/vendor/src/github.com/apache/thrift/build/cmake/DefineInstallationPaths.cmake new file mode 100644 index 00000000..122f0f6a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/DefineInstallationPaths.cmake @@ -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. +# + + +# 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})") +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(DOC_INSTALL_DIR "share/doc" CACHE PATH "The subdirectory to install documentation files (default: share/doc)") diff --git a/vendor/src/github.com/apache/thrift/build/cmake/DefineOptions.cmake b/vendor/src/github.com/apache/thrift/build/cmake/DefineOptions.cmake new file mode 100644 index 00000000..0c853b1c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/DefineOptions.cmake @@ -0,0 +1,210 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +include(CMakeDependentOption) + +set(THRIFT_COMPILER "" CACHE FILEPATH "External Thrift compiler to use during build") + +# Additional components +option(BUILD_COMPILER "Build Thrift compiler" ON) + +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) + +# Libraries to build + +# Each language library can be enabled or disabled using the WITH_ flag. +# By default CMake checks if the required dependencies for a language are present +# 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. + +CMAKE_DEPENDENT_OPTION(WITH_BOOST_STATIC "Build with Boost static link library" OFF "NOT MSVC" ON) +set(Boost_USE_STATIC_LIBS ${WITH_BOOST_STATIC}) +if (NOT WITH_BOOST_STATIC) + add_definitions(-DBOOST_ALL_DYN_LINK) + add_definitions(-DBOOST_TEST_DYN_LINK) +endif() + +# 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 + # has no effect. + if(ZLIB_LIBRARY) + # FindZLIB.cmake does not normalize path so we need to do it ourselves. + file(TO_CMAKE_PATH ${ZLIB_LIBRARY} ZLIB_LIBRARY) + endif() + find_package(ZLIB QUIET) + CMAKE_DEPENDENT_OPTION(WITH_ZLIB "Build with ZLIB support" ON + "ZLIB_FOUND" OFF) + 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" ON + "BUILD_COMPILER;BUILD_CPP" OFF) + +# C GLib +option(WITH_C_GLIB "Build C (GLib) Thrift library" ON) +if(WITH_C_GLIB) + find_package(GLIB QUIET COMPONENTS gobject) +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) +endif() + +# Java +option(WITH_JAVA "Build Java Thrift library" ON) +if(ANDROID) + find_package(Gradle QUIET) + CMAKE_DEPENDENT_OPTION(BUILD_JAVA "Build Java library" ON + "BUILD_LIBRARIES;WITH_JAVA;GRADLE_FOUND" OFF) +else() + find_package(Java QUIET) + find_package(Ant QUIET) + CMAKE_DEPENDENT_OPTION(BUILD_JAVA "Build Java library" ON + "BUILD_LIBRARIES;WITH_JAVA;JAVA_FOUND;ANT_FOUND" OFF) +endif() + +# 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) + +# Haskell +option(WITH_HASKELL "Build Haskell Thrift library" ON) +find_package(GHC QUIET) +find_package(Cabal QUIET) +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!") +endif() + +#NOTE: C++ compiler options are defined in the lib/cpp/CMakeLists.txt + +# Visual Studio only options +if(MSVC) +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}") +endif() +endmacro(MESSAGE_DEP flag summary) + +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_DEP(PLUGIN_COMPILER_NOT_TOO_OLD "Disabled due to older compiler") +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 " Build C++ library: ${BUILD_CPP}") +MESSAGE_DEP(WITH_CPP "Disabled by WITH_CPP=OFF") +MESSAGE_DEP(Boost_FOUND "Boost headers missing") +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 " Build Java library: ${BUILD_JAVA}") +MESSAGE_DEP(WITH_JAVA "Disabled by WITH_JAVA=OFF") +if(ANDROID) + MESSAGE_DEP(GRADLE_FOUND "Gradle missing") +else() + MESSAGE_DEP(JAVA_FOUND "Java Runtime missing") + MESSAGE_DEP(ANT_FOUND "Ant missing") +endif() +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 " 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 ZLIB support: ${WITH_ZLIB}") +message(STATUS " Build with libevent support: ${WITH_LIBEVENT}") +message(STATUS " Build with Qt4 support: ${WITH_QT4}") +message(STATUS " Build with Qt5 support: ${WITH_QT5}") +message(STATUS " Build with OpenSSL support: ${WITH_OPENSSL}") +message(STATUS " Build with Boost thread support: ${WITH_BOOSTTHREADS}") +message(STATUS " Build with C++ std::thread support: ${WITH_STDTHREADS}") +message(STATUS " Build with Boost static link library: ${WITH_BOOST_STATIC}") +if(MSVC) + message(STATUS " - Enabled for Visual C++") +endif() +message(STATUS "----------------------------------------------------------") +endmacro(PRINT_CONFIG_SUMMARY) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/DefinePlatformSpecifc.cmake b/vendor/src/github.com/apache/thrift/build/cmake/DefinePlatformSpecifc.cmake new file mode 100644 index 00000000..e57ecc2f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/DefinePlatformSpecifc.cmake @@ -0,0 +1,106 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Visual Studio specific options +if(MSVC) + #For visual studio the library naming is as following: + # Dynamic libraries: + # - thrift.dll for release library + # - thriftd.dll for debug library + # + # Static libraries: + # - thriftmd.lib for /MD release build + # - thriftmt.lib for /MT release build + # + # - thriftmdd.lib for /MD debug build + # - thriftmtd.lib for /MT debug build + # + # 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 + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO + ) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() + set(STATIC_POSTFIX "mt" CACHE STRING "Set static library postfix" FORCE) + else(WITH_MT) + set(STATIC_POSTFIX "md" CACHE STRING "Set static library postfix" FORCE) + endif(WITH_MT) + + # Disable Windows.h definition of macros for min and max + add_definitions("-DNOMINMAX") + + # 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() + +elseif(UNIX) + find_program( MEMORYCHECK_COMMAND valgrind ) + set( MEMORYCHECK_COMMAND_OPTIONS "--gen-suppressions=all --leak-check=full" ) + set( MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/test/valgrind.suppress" ) +endif() + +# 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() + +# GCC and Clang. +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # FIXME -pedantic can not be used at the moment because of: https://issues.apache.org/jira/browse/THRIFT-2784 + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -Wall -Wextra -pedantic") + # FIXME enabling c++11 breaks some Linux builds on Travis by triggering a g++ bug, see + # https://travis-ci.org/apache/thrift/jobs/58017022 + # on the other hand, both MacOSX and FreeBSD need c++11 + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -Wall -Wextra") + endif() +endif() + +# If gcc older than 4.8 is detected, disable new compiler plug-in support (see THRIFT-3937) +set(PLUGIN_COMPILER_NOT_TOO_OLD ON) # simplifies messaging in DefineOptions summary +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8" AND WITH_PLUGIN) + message(STATUS "Disabling compiler plug-in support to work with older gcc compiler") + set(WITH_PLUGIN OFF) + set(PLUGIN_COMPILER_NOT_TOO_OLD OFF) +endif() + diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindAnt.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindAnt.cmake new file mode 100644 index 00000000..8b0371d9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindAnt.cmake @@ -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. +# + + +# ANT_FOUND - system has Ant +# Ant_EXECUTABLE - the Ant executable +# +# It will search the environment variable ANT_HOME if it is set + +include(FindPackageHandleStandardArgs) + +find_program(Ant_EXECUTABLE NAMES ant PATHS $ENV{ANT_HOME}/bin) +find_package_handle_standard_args(Ant DEFAULT_MSG Ant_EXECUTABLE) +mark_as_advanced(Ant_EXECUTABLE) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindCabal.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindCabal.cmake new file mode 100644 index 00000000..fed337bd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindCabal.cmake @@ -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. +# + + +# Cabal_FOUND - system has Cabal +# Cabal - the Cabal executable +# +# It will search the environment variable CABAL_HOME if it is set + +include(FindPackageHandleStandardArgs) + +find_program(CABAL NAMES cabal PATHS $ENV{HOME}/.cabal/bin $ENV{CABAL_HOME}/bin) +find_package_handle_standard_args(CABAL DEFAULT_MSG CABAL) +mark_as_advanced(CABAL) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindGHC.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindGHC.cmake new file mode 100644 index 00000000..48738472 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindGHC.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. +# + + +# GHC_FOUND - system has GHC +# GHC - the GHC executable +# RUN_HASKELL_FOUND - system has runhaskell +# RUN_HASKELL - the runhaskell executable +# +# It will search the environment variable GHC_HOME if it is set + +include(FindPackageHandleStandardArgs) + +find_program(GHC NAMES ghc PATHS $ENV{GHC_HOME}/bin) +find_package_handle_standard_args(GHC DEFAULT_MSG GHC) +mark_as_advanced(GHC) + +find_program(RUN_HASKELL NAMES runhaskell PATHS $ENV{GHC_HOME}/bin) +find_package_handle_standard_args(RUN_HASKELL DEFAULT_MSG RUN_HASKELL) +mark_as_advanced(RUN_HASKELL) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindGLIB.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindGLIB.cmake new file mode 100644 index 00000000..acbe433e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindGLIB.cmake @@ -0,0 +1,122 @@ +# - Try to find Glib and its components (gio, gobject etc) +# Once done, this will define +# +# GLIB_FOUND - system has Glib +# GLIB_INCLUDE_DIRS - the Glib include directories +# GLIB_LIBRARIES - link these to use Glib +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and Glib components can be looked for. Currently, the following +# components can be used, and they define the following variables if +# found: +# +# gio: GLIB_GIO_LIBRARIES +# gobject: GLIB_GOBJECT_LIBRARIES +# gmodule: GLIB_GMODULE_LIBRARIES +# gthread: GLIB_GTHREAD_LIBRARIES +# +# Note that the respective _INCLUDE_DIR variables are not set, since +# all headers are in the same directory as GLIB_INCLUDE_DIRS. +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``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 COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS 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. + +find_package(PkgConfig) +pkg_check_modules(PC_GLIB QUIET glib-2.0) + +find_library(GLIB_LIBRARIES + NAMES glib-2.0 + HINTS ${PC_GLIB_LIBDIR} + ${PC_GLIB_LIBRARY_DIRS} +) + +# Files in glib's main include path may include glibconfig.h, which, +# for some odd reason, is normally in $LIBDIR/glib-2.0/include. +get_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH) +find_path(GLIBCONFIG_INCLUDE_DIR + NAMES glibconfig.h + HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR} + ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0/include +) + +find_path(GLIB_INCLUDE_DIR + NAMES glib.h + HINTS ${PC_GLIB_INCLUDEDIR} + ${PC_GLIB_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0 +) + +set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR}) + +if(GLIBCONFIG_INCLUDE_DIR) + # Version detection + file(READ "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h" GLIBCONFIG_H_CONTENTS) + string(REGEX MATCH "#define GLIB_MAJOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MAJOR "${CMAKE_MATCH_1}") + string(REGEX MATCH "#define GLIB_MINOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MINOR "${CMAKE_MATCH_1}") + string(REGEX MATCH "#define GLIB_MICRO_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") + set(GLIB_VERSION_MICRO "${CMAKE_MATCH_1}") + set(GLIB_VERSION "${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}") +endif() + +# Additional Glib components. We only look for libraries, as not all of them +# have corresponding headers and all headers are installed alongside the main +# glib ones. +foreach (_component ${GLIB_FIND_COMPONENTS}) + if (${_component} STREQUAL "gio") + find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES) + elseif (${_component} STREQUAL "gobject") + find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES) + elseif (${_component} STREQUAL "gmodule") + find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES) + elseif (${_component} STREQUAL "gthread") + find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR}) + set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES) + elseif (${_component} STREQUAL "gio-unix") + # gio-unix is compiled as part of the gio library, but the include paths + # are separate from the shared glib ones. Since this is currently only used + # by WebKitGTK+ we don't go to extraordinary measures beyond pkg-config. + pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0) + endif () +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS} + VERSION_VAR GLIB_VERSION) + +mark_as_advanced( + GLIBCONFIG_INCLUDE_DIR + GLIB_GIO_LIBRARIES + GLIB_GIO_UNIX_LIBRARIES + GLIB_GMODULE_LIBRARIES + GLIB_GOBJECT_LIBRARIES + GLIB_GTHREAD_LIBRARIES + GLIB_INCLUDE_DIR + GLIB_INCLUDE_DIRS + GLIB_LIBRARIES +) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindGradle.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindGradle.cmake new file mode 100644 index 00000000..8845d697 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindGradle.cmake @@ -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. +# + + +# GRADLE_FOUND - system has Gradle +# GRADLE_EXECUTABLE - the Gradle executable +# +# It will search the environment variable ANT_HOME if it is set + +include(FindPackageHandleStandardArgs) + +find_program(GRADLE_EXECUTABLE NAMES gradle PATHS $ENV{GRADLE_HOME}/bin NO_CMAKE_FIND_ROOT_PATH) +find_package_handle_standard_args(Gradle DEFAULT_MSG GRADLE_EXECUTABLE) +mark_as_advanced(GRADLE_EXECUTABLE) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/FindLibevent.cmake b/vendor/src/github.com/apache/thrift/build/cmake/FindLibevent.cmake new file mode 100644 index 00000000..2bcd7099 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/FindLibevent.cmake @@ -0,0 +1,41 @@ +# find LibEvent +# an event notification library (http://libevent.org/) +# +# Usage: +# LIBEVENT_INCLUDE_DIRS, where to find LibEvent headers +# LIBEVENT_LIBRARIES, LibEvent libraries +# Libevent_FOUND, If false, do not try to use libevent + +set(LIBEVENT_ROOT CACHE PATH "Root directory of libevent installation") +set(LibEvent_EXTRA_PREFIXES /usr/local /opt/local "$ENV{HOME}" ${LIBEVENT_ROOT}) +foreach(prefix ${LibEvent_EXTRA_PREFIXES}) + list(APPEND LibEvent_INCLUDE_PATHS "${prefix}/include") + list(APPEND LibEvent_LIBRARIES_PATHS "${prefix}/lib") +endforeach() + +find_path(LIBEVENT_INCLUDE_DIRS event.h PATHS ${LibEvent_INCLUDE_PATHS}) +# "lib" prefix is needed on Windows +find_library(LIBEVENT_LIBRARIES NAMES event libevent PATHS ${LibEvent_LIBRARIES_PATHS}) + +if (LIBEVENT_LIBRARIES AND LIBEVENT_INCLUDE_DIRS) + set(Libevent_FOUND TRUE) + set(LIBEVENT_LIBRARIES ${LIBEVENT_LIBRARIES}) +else () + set(Libevent_FOUND FALSE) +endif () + +if (Libevent_FOUND) + if (NOT Libevent_FIND_QUIETLY) + message(STATUS "Found libevent: ${LIBEVENT_LIBRARIES}") + endif () +else () + if (LibEvent_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find libevent.") + endif () + message(STATUS "libevent NOT found.") +endif () + +mark_as_advanced( + LIBEVENT_LIBRARIES + LIBEVENT_INCLUDE_DIRS + ) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/README.md b/vendor/src/github.com/apache/thrift/build/cmake/README.md new file mode 100644 index 00000000..ebc4f7da --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/README.md @@ -0,0 +1,60 @@ +# 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 +and library detection mechanism running on as much platforms as possible +becomes required. The other aspect to simplify the release process and +package generation process. + +As nice side benefit of CMake is the generation of development environment +specific soultion files. => No solution files within source tree. + + +## Usage +just do this: + + mkdir cmake-build && cd cmake-build + cmake .. + +if you use a specific toolchain pass it to cmake, the same for options: + + cmake -DCMAKE_TOOLCHAIN_FILE=../build/cmake/mingw32-toolchain.cmake .. + cmake -DCMAKE_C_COMPILER=clang-3.5 -DCMAKE_CXX_COMPILER=clang++-3.5 .. + 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 + make check + make cross + make dist + +to generate an installer and distribution package do this: + + cpack + +## 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) +* make doc +* cpack (C++ and make dist only ?) + * thrift-compiler + * libthrift + * tutorial + * test +* merge into /README.md diff --git a/vendor/src/github.com/apache/thrift/build/cmake/ThriftMacros.cmake b/vendor/src/github.com/apache/thrift/build/cmake/ThriftMacros.cmake new file mode 100644 index 00000000..f837f948 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/ThriftMacros.cmake @@ -0,0 +1,105 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix" FORCE) + + +macro(ADD_LIBRARY_THRIFT name) + +if(WITH_SHARED_LIB) + add_library(${name} SHARED ${ARGN}) + 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} + 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) + + +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) + + +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) + + +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) + + +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) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/android-toolchain.cmake b/vendor/src/github.com/apache/thrift/build/cmake/android-toolchain.cmake new file mode 100644 index 00000000..15f3d002 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/android-toolchain.cmake @@ -0,0 +1,26 @@ +set(ANDROID_NDK "/opt/android-ndk" CACHE) +set(ANDROID_PLATFORM "android-15" CACHE) +set(ANDROID_ARCH "arch-arm" CACHE) +set(ANDROID_TOOL_ARCH "android-arm" CACHE) +set(ANDROID_CPU "armeabi-v7a" CACHE) +set(ANDROID_GCC_VERSION 4.9 CACHE) +set(HOST_ARCH linux-x86_64 CACHE) + +set(CMAKE_SYSTEM_NAME Android) +set(ANDROID_SYSROOT "${ANDROID_NDK}/platforms/${ANDROID_PLATFORM}/${ANDROID_ARCH}") +set(ANDROID_TRIPLET arm-linux-androideabi) +set(ANDROID_STL "${ANDROID_NDK}/sources/cxx-stl/gnu-libstd++/${ANDROID_GCC_VERSION}") + +set(_COMPILER_ROOT ${ANDROID_NDK}/prebuilt/${ANDROID_TRIPLET}-${ANDROID_GCC_VERSION}/prebuilt/${HOST_ARCH}) +set(CMAKE_C_COMPILER ${_COMPILER_ROOT}/bin/${ANDROID_TRIPLET}-gcc) +set(CMAKE_CXCX_COMPILER ${_COMPILER_ROOT}/bin/${ANDROID_TRIPLET}-g++) + +include_directories( + ${ANDROID_STL}/include + ${ANDROID_STL}/libs/${ANDROID_CPU}/include) + +set(CMAKE_FIND_ROOT_PATH ${ANDROID_SYSROOT}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/vendor/src/github.com/apache/thrift/build/cmake/config.h.in b/vendor/src/github.com/apache/thrift/build/cmake/config.h.in new file mode 100644 index 00000000..181ea180 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/config.h.in @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* config.h generated by CMake from config.h.in */ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* Name of package */ +#cmakedefine PACKAGE "${PACKAGE}" + +/* Define to the address where bug reports for this package should be sent. */ +#cmakedefine PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" + +/* Define to the full name of this package. */ +#cmakedefine PACKAGE_NAME "${PACKAGE_NAME}" + +/* Define to the one symbol short name of this package. */ +#cmakedefine PACKAGE_TARNAME "${PACKAGE_TARNAME}" + +/* Define to the home page for this package. */ +#cmakedefine PACKAGE_URL "${PACKAGE_URL}" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "${PACKAGE_VERSION}" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "${PACKAGE_STRING}" + +/* Version number of package */ +#define VERSION "${VERSION}" + +/************************** DEFINES *************************/ + +/* Define if the AI_ADDRCONFIG symbol is unavailable */ +#cmakedefine AI_ADDRCONFIG 0 + +/* Possible value for SIGNED_RIGHT_SHIFT_IS */ +/* TODO: This is just set to 1 for the moment + port the macro aclocal/ax_signed_right_shift.m4 to CMake to make this work */ +#define ARITHMETIC_RIGHT_SHIFT 1 + +/* Indicates the effect of the right shift operator on negative signed + integers */ +/* TODO: This is just set to 1 for the moment */ +#define SIGNED_RIGHT_SHIFT_IS 1 + +/* Use *.h extension for parser header file */ +/* TODO: This might now be necessary anymore as it is set only for automake < 1.11 + 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 + + +/************************** HEADER FILES *************************/ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SCHED_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/*************************** FUNCTIONS ***************************/ + +/* Define to 1 if you have the `gethostbyname' function. */ +#cmakedefine HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethostbyname_r' function. */ +#cmakedefine HAVE_GETHOSTBYNAME_R 1 + +/* Define to 1 if you have the `strerror_r' function. */ +#cmakedefine HAVE_STRERROR_R 1 + +/* Define to 1 if you have the `sched_get_priority_max' function. */ +#cmakedefine HAVE_SCHED_GET_PRIORITY_MAX 1 + +/* Define to 1 if you have the `sched_get_priority_min' function. */ +#cmakedefine HAVE_SCHED_GET_PRIORITY_MIN 1 + + +/* Define to 1 if strerror_r returns char *. */ +#cmakedefine STRERROR_R_CHAR_P 1 + +#endif \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/build/cmake/mingw32-toolchain.cmake b/vendor/src/github.com/apache/thrift/build/cmake/mingw32-toolchain.cmake new file mode 100644 index 00000000..864c0ebe --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/cmake/mingw32-toolchain.cmake @@ -0,0 +1,24 @@ +# CMake mingw32 cross compile toolchain file + +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Windows) + +# which compilers to use for C and C++ +SET(CMAKE_C_COMPILER i586-mingw32msvc-gcc) +SET(CMAKE_CXX_COMPILER i586-mingw32msvc-g++) +SET(CMAKE_RC_COMPILER i586-mingw32msvc-windres) + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(BUILD_SHARED_LIBS OFF) +SET(CMAKE_EXE_LINKER_FLAGS "-static") +set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-static-libgcc") +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-static-libstdc++") diff --git a/vendor/src/github.com/apache/thrift/build/docker/README.md b/vendor/src/github.com/apache/thrift/build/docker/README.md new file mode 100644 index 00000000..85cb3b2a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/README.md @@ -0,0 +1,27 @@ +# Apache Thrift Docker containers +A set of docker containers used to build and test Apache Thrift + +### Available Containers + +* Ubuntu - based on ubuntu:trusty (14.04) +* Centos - based on centos:6.6 + +## Dependencies + +* A working Docker environment. A Vagrantfile is provided which will setup an Ubuntu host and working Docker environment as well as build the Apache Thrift Docker container for testing and development + +## Usage +From the Apache Thrift code base root + +* Build + + docker build -t thrift build/docker/ubuntu + + or + + docker build -t thrift build/docker/centos + +* Run + + docker run -v $(pwd):/thrift/src -it thrift /bin/bash + diff --git a/vendor/src/github.com/apache/thrift/build/docker/Vagrantfile b/vendor/src/github.com/apache/thrift/build/docker/Vagrantfile new file mode 100644 index 00000000..5eac6e68 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/Vagrantfile @@ -0,0 +1,59 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +# +# 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. + +# Base system bootstrap script +$bootstrap_script = <<__BOOTSTRAP__ +echo "Provisioning defaults" + +sudo apt-get update -y +sudo apt-get upgrade -y + +# Install default packages +sudo apt-get install -y build-essential curl git + +# Install latest Docker version +sudo curl -sSL https://get.docker.io/gpg | sudo apt-key add - +sudo echo "deb http://get.docker.io/ubuntu docker main" > /etc/apt/sources.list.d/docker.list +sudo apt-get update -y +sudo apt-get install -y linux-image-extra-`uname -r` aufs-tools +sudo apt-get install -y lxc-docker + +echo "Finished provisioning defaults" +__BOOTSTRAP__ + +Vagrant.configure("2") do |config| + config.vm.box = "trusty64" + config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" + config.ssh.forward_agent = true + + config.vm.provider :virtualbox do |vbox| + vbox.customize ["modifyvm", :id, "--memory", "1024"] + vbox.customize ["modifyvm", :id, "--cpus", "2"] + end + + # Setup the default bootstrap script for our ubuntu base box image + config.vm.provision "shell", inline: $bootstrap_script + + # Setup the custom docker image from our Ubuntu Dockerfile + config.vm.provision "docker" do |d| + d.build_image "/vagrant/ubuntu", args: "-t thrift" + end + + # Setup the custom docker image from our Centos Dockerfile + #config.vm.provision "docker" do |d| + # d.build_image "/vagrant/centos", args: "-t thrift-centos" + #end + +end diff --git a/vendor/src/github.com/apache/thrift/build/docker/centos/Dockerfile b/vendor/src/github.com/apache/thrift/build/docker/centos/Dockerfile new file mode 100644 index 00000000..59bbfd65 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/centos/Dockerfile @@ -0,0 +1,142 @@ +# 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 Centos +# +# Known missing client libraries: +# - D +# - Haxe +# - Lua +# + +FROM centos:7 +MAINTAINER Apache Thrift + +RUN yum install -y epel-release + +# General dependencies +RUN yum install -y \ + tar \ + m4 \ + perl \ + clang \ + gcc \ + gcc-c++ \ + git \ + libtool \ + autoconf \ + make \ + bison \ + bison-devel \ + flex + +# C++ dependencies +RUN yum install -y \ + boost-devel-static \ + zlib-devel \ + openssl-devel \ + libevent-devel + +# Java Dependencies +RUN yum install -y \ + ant \ + junit \ + ant-junit \ + java-1.7.0-openjdk-devel + +# Python Dependencies +RUN yum install -y \ + python-devel \ + python-pip \ + python-setuptools \ + python-six \ + python-twisted-web && \ + pip install -U backports.ssl_match_hostname ipaddress tornado + +# Ruby Dependencies +RUN yum install -y \ + ruby \ + ruby-devel \ + rubygems && \ + gem install bundler rake + +# Perl Dependencies +RUN yum install -y \ + perl-Bit-Vector \ + perl-Class-Accessor \ + perl-ExtUtils-MakeMaker \ + perl-Test-Simple \ + perl-IO-Socket-SSL \ + perl-Net-SSLeay \ + perl-Crypt-SSLeay + +# PHP Dependencies +RUN yum install -y \ + php \ + php-devel \ + php-pear \ + re2c \ + php-phpunit-PHPUnit \ + bzip2 + +# GLibC Dependencies +RUN yum install -y glib2-devel + +# Erlang Dependencies +RUN curl -sSL http://packages.erlang-solutions.com/rpm/centos/erlang_solutions.repo -o /etc/yum.repos.d/erlang_solutions.repo && \ + yum install -y \ + erlang-kernel \ + erlang-erts \ + erlang-stdlib \ + erlang-eunit \ + erlang-rebar \ + erlang-tools + +# Go Dependencies +RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz +ENV PATH /usr/local/go/bin:$PATH + +# Haskell Dependencies +RUN yum -y install haskell-platform + +# Node.js Dependencies +RUN yum install -y \ + nodejs \ + nodejs-devel \ + npm + +# C# Dependencies +RUN yum install -y \ + mono-core \ + mono-devel \ + mono-web-devel \ + mono-extras \ + +# MinGW Dependencies +RUN yum install -y \ + mingw32-binutils \ + mingw32-crt \ + mingw32-nsis + +# CMake +RUN curl -sSL https://cmake.org/files/v3.4/cmake-3.4.0.tar.gz | tar -xz && \ + cd cmake-3.4.0 && ./bootstrap && make -j4 && make install && \ + cd .. && rm -rf cmake-3.4.0 + +# Clean up +RUN rm -rf /tmp/* && \ + yum clean all + +ENV THRIFT_ROOT /thrift +RUN mkdir -p $THRIFT_ROOT/src +COPY Dockerfile $THRIFT_ROOT/ +WORKDIR $THRIFT_ROOT/src diff --git a/vendor/src/github.com/apache/thrift/build/docker/centos6/Dockerfile b/vendor/src/github.com/apache/thrift/build/docker/centos6/Dockerfile new file mode 100644 index 00000000..aa197310 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/centos6/Dockerfile @@ -0,0 +1,54 @@ +# 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 Centos 6 +# +# This file is intended for testing old packages that are not available for +# latest Ubuntu LTS/Debian/CentOS. Currently, it is only used for Python 2.6. +# + +FROM centos:6 +MAINTAINER Apache Thrift + +RUN yum install -y epel-release && \ + yum install -y \ + autoconf \ + bison \ + bison-devel \ + clang \ + flex \ + gcc \ + gcc-c++ \ + git \ + libtool \ + m4 \ + make \ + perl \ + tar \ + python-devel \ + python-setuptools \ + python-twisted-web \ + python-pip \ + && yum clean all + +# optional dependencies +RUN pip install ipaddress backports.ssl_match_hostname tornado + +# CMake +RUN curl -sSL https://cmake.org/files/v3.4/cmake-3.4.1.tar.gz | tar -xz && \ + cd cmake-3.4.1 && ./bootstrap && make -j4 && make install && \ + cd .. && rm -rf cmake-3.4.1 + +ENV THRIFT_ROOT /thrift +RUN mkdir -p $THRIFT_ROOT/src +COPY Dockerfile $THRIFT_ROOT/ +WORKDIR $THRIFT_ROOT/src diff --git a/vendor/src/github.com/apache/thrift/build/docker/check_unmodified.sh b/vendor/src/github.com/apache/thrift/build/docker/check_unmodified.sh new file mode 100644 index 00000000..9d5fa267 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/check_unmodified.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Download prebuilt docker image and compare Dockerfile hash values + +set -ex + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DISTRO=$1 +SRC_IMG=thrift/thrift-build:$DISTRO + +function try_pull { + docker pull $SRC_IMG + cd ${SCRIPT_DIR}/$DISTRO + docker run $SRC_IMG bash -c 'cd .. && sha512sum Dockerfile' > .Dockerfile.sha512 + sha512sum -c .Dockerfile.sha512 +} + +if try_pull; then + echo Dockerfile seems identical. No need to rebuild from scratch. + docker tag thrift/thrift-build:$DISTRO thrift-build:$DISTRO +else + echo Either Dockerfile has changed or pull failure. Need to build brand new one. + exit 1 +fi diff --git a/vendor/src/github.com/apache/thrift/build/docker/debian/Dockerfile b/vendor/src/github.com/apache/thrift/build/docker/debian/Dockerfile new file mode 100644 index 00000000..29b7804d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/debian/Dockerfile @@ -0,0 +1,192 @@ +# 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 Centos +# +# Known missing client libraries: +# - None + +FROM buildpack-deps:jessie-scm +MAINTAINER Apache Thrift + +ENV DEBIAN_FRONTEND noninteractive + +# Add apt sources +# 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 && \ + sed -i /etc/apt/sources.list.d/dart_stable.list -e 's/https:/http:/g' + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# General dependencies` \ + bison \ + build-essential \ + clang \ + cmake \ + debhelper \ + flex \ + pkg-config + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# C++ dependencies` \ + libboost-dev \ + libboost-filesystem-dev \ + libboost-program-options-dev \ + libboost-system-dev \ + libboost-test-dev \ + libboost-thread-dev \ + libevent-dev \ + libssl-dev \ + qt5-default \ + qtbase5-dev \ + qtbase5-dev-tools + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Java dependencies` \ + ant \ + ant-optional \ + openjdk-7-jdk \ + maven + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Python dependencies` \ + python-all \ + python-all-dbg \ + python-all-dev \ + python-pip \ + python-setuptools \ + python-twisted \ + python-zope.interface \ + python3-all \ + python3-all-dbg \ + python3-all-dev \ + python3-setuptools \ + python3-pip + +RUN echo 'deb http://deb.debian.org/debian jessie-backports main' >> /etc/apt/sources.list \ + && apt-get update && apt-get install -y --no-install-recommends \ +`# Ruby dependencies` \ + ruby \ + ruby-bundler \ + ruby-dev \ +`# Perl dependencies` \ + libbit-vector-perl \ + libclass-accessor-class-perl \ + libcrypt-ssleay-perl \ + libio-socket-ssl-perl \ + libnet-ssleay-perl + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Php dependencies` \ + php5 \ + php5-dev \ + php5-cli \ + php-pear \ + re2c \ + phpunit \ +`# GlibC dependencies` \ + libglib2.0-dev + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Erlang dependencies` \ + erlang-base \ + erlang-eunit \ + erlang-dev \ + erlang-tools \ + rebar + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Haskell dependencies` \ + ghc \ + cabal-install \ +`# Haxe dependencies` \ + neko \ + neko-dev \ + libneko0 + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Node.js dependencies` \ + nodejs \ + nodejs-dev \ + 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 \ +`# Dart dependencies` \ + dart \ +`# Lua dependencies` \ + lua5.2 \ + lua5.2-dev \ +`# MinGW dependencies` \ + mingw32 \ + mingw32-binutils \ +`# mingw32-runtime` \ + nsis \ +`# Clean up` \ + && rm -rf /var/cache/apt/* && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /tmp/* && \ + rm -rf /var/tmp/* + +# Ruby +RUN gem install bundler --no-ri --no-rdoc + +# Python optional dependencies +RUN pip2 install -U ipaddress backports.ssl_match_hostname tornado +RUN pip3 install -U backports.ssl_match_hostname tornado + +# Go +RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz +ENV PATH /usr/local/go/bin:$PATH + +# Haxe +RUN mkdir -p /usr/lib/haxe && \ + curl http://haxe.org/website-content/downloads/3.2.0/downloads/haxe-3.2.0-linux64.tar.gz | \ + tar -C /usr/lib/haxe --strip-components=1 -xz && \ + ln -s /usr/lib/haxe/haxe /usr/bin/haxe && \ + ln -s /usr/lib/haxe/haxelib /usr/bin/haxelib && \ + mkdir -p /usr/lib/haxe/lib && \ + chmod -R 777 /usr/lib/haxe/lib && \ + haxelib setup /usr/lib/haxe/lib && \ + haxelib install hxcpp + +# D +RUN curl -sSL http://downloads.dlang.org/releases/2.x/2.070.0/dmd_2.070.0-0_amd64.deb -o /tmp/dmd_2.070.0-0_amd64.deb && \ + dpkg -i /tmp/dmd_2.070.0-0_amd64.deb && \ + rm /tmp/dmd_2.070.0-0_amd64.deb && \ + curl -sSL https://github.com/D-Programming-Deimos/openssl/archive/master.tar.gz| tar xz && \ + curl -sSL https://github.com/D-Programming-Deimos/libevent/archive/master.tar.gz| tar xz && \ + mkdir -p /usr/include/dmd/druntime/import/deimos /usr/include/dmd/druntime/import/C && \ + mv libevent-master/deimos/* openssl-master/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv libevent-master/C/* openssl-master/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf libevent-master openssl-master && \ + echo 'gcc -Wl,--no-as-needed $*' > /usr/local/bin/gcc-dmd && \ + chmod 755 /usr/local/bin/gcc-dmd && \ + echo 'CC=/usr/local/bin/gcc-dmd' >> /etc/dmd.conf + +# Dart +ENV PATH /usr/lib/dart/bin:$PATH + +# Force utf8 locale to successfully build Haskell tf-random +ENV LC_ALL C.UTF-8 + +ENV THRIFT_ROOT /thrift +RUN mkdir -p $THRIFT_ROOT/src +COPY Dockerfile $THRIFT_ROOT/ +WORKDIR $THRIFT_ROOT/src diff --git a/vendor/src/github.com/apache/thrift/build/docker/scripts/autotools.sh b/vendor/src/github.com/apache/thrift/build/docker/scripts/autotools.sh new file mode 100644 index 00000000..8388f728 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/scripts/autotools.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -ev + +./bootstrap.sh +./configure $* +make check -j3 diff --git a/vendor/src/github.com/apache/thrift/build/docker/scripts/cmake.sh b/vendor/src/github.com/apache/thrift/build/docker/scripts/cmake.sh new file mode 100644 index 00000000..6508e710 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/scripts/cmake.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -ev + +CMAKE_FLAGS=$* +MAKEPROG=make + +if ninja --version >/dev/null 2>&1; then + MAKEPROG=ninja + CMAKE_FLAGS="-GNinja $CMAKE_FLAGS" +fi + +mkdir -p cmake_build && cd cmake_build +cmake $CMAKE_FLAGS .. +for LIB in $BUILD_LIBS; do + if ! grep "^BUILD_${LIB}:BOOL=ON$" CMakeCache.txt ; then + echo "failed to configure $LIB" + exit 1 + fi +done +$MAKEPROG -j3 +cpack +ctest -VV +# was: -E "(concurrency_test|processor_test)" diff --git a/vendor/src/github.com/apache/thrift/build/docker/scripts/cross-test.sh b/vendor/src/github.com/apache/thrift/build/docker/scripts/cross-test.sh new file mode 100644 index 00000000..43581a5f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/scripts/cross-test.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -ev + +./bootstrap.sh +./configure --enable-tutorial=no +make -j3 precross + +set +e +make cross$1 + +RET=$? +if [ $RET -ne 0 ]; then + cat test/log/unexpected_failures.log +fi + +exit $RET diff --git a/vendor/src/github.com/apache/thrift/build/docker/scripts/dpkg.sh b/vendor/src/github.com/apache/thrift/build/docker/scripts/dpkg.sh new file mode 100644 index 00000000..3ba0cd48 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/scripts/dpkg.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -ev + +dpkg-buildpackage -tc -us -uc +ls -al .. diff --git a/vendor/src/github.com/apache/thrift/build/docker/scripts/make-dist.sh b/vendor/src/github.com/apache/thrift/build/docker/scripts/make-dist.sh new file mode 100644 index 00000000..5a3681e1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/scripts/make-dist.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -ev + +./bootstrap.sh +./configure $* +make dist +tar xvf thrift-*.tar.gz +cd thrift-* +./build/docker/scripts/cmake.sh diff --git a/vendor/src/github.com/apache/thrift/build/docker/ubuntu/Dockerfile b/vendor/src/github.com/apache/thrift/build/docker/ubuntu/Dockerfile new file mode 100644 index 00000000..7d245e80 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/docker/ubuntu/Dockerfile @@ -0,0 +1,209 @@ +# 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 Centos +# +# Known missing client libraries: +# - None + +FROM buildpack-deps:trusty-scm +MAINTAINER Apache Thrift + +ENV DEBIAN_FRONTEND noninteractive + +# Add apt sources +# Erlang +RUN echo 'deb http://packages.erlang-solutions.com/debian trusty contrib' > /etc/apt/sources.list.d/erlang_solutions.list && \ + curl -sSL https://packages.erlang-solutions.com/debian/erlang_solutions.asc | apt-key add - +# 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 && \ + sed -i /etc/apt/sources.list.d/dart_stable.list -e 's/https:/http:/g' + +# Consider using mirror nearby when building locally +# TODO: Provide option via --build-arg=... +# RUN sed -i /etc/apt/sources.list -e 's!http://archive.ubuntu.com/ubuntu/!http://your/mirror/!g' + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# General dependencies` \ + bison \ + build-essential \ + clang \ + cmake \ + debhelper \ + flex \ + ninja-build \ + pkg-config \ +`# Included in buildpack-deps` \ +`# autoconf` \ +`# automake` \ +`# g++` \ +`# git` \ +`# libtool` \ +`# make` + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# C++ dependencies` \ +`# libevent and OpenSSL are needed by D too` \ + libboost-dev \ + libboost-filesystem-dev \ + libboost-program-options-dev \ + libboost-system-dev \ + libboost-test-dev \ + libboost-thread-dev \ + libevent-dev \ + libssl-dev \ + qt5-default \ + qtbase5-dev \ + qtbase5-dev-tools + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Java dependencies` \ + ant \ + ant-optional \ + openjdk-7-jdk \ + maven + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Python dependencies` \ +`# TODO:` \ +`# Install twisted and zope.interface via pip. we need twisted at ./configure time, otherwise` \ +`# py.twisted tests are skipped.` \ + python-all \ + python-all-dbg \ + python-all-dev \ + python-pip \ + python-setuptools \ + python-twisted \ + python-zope.interface \ + python3-all \ + python3-all-dbg \ + python3-all-dev \ + python3-setuptools \ + python3-pip + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Ruby dependencies` \ + ruby \ + ruby-bundler \ + ruby-dev \ +`# Perl dependencies` \ + libbit-vector-perl \ + libclass-accessor-class-perl \ + libcrypt-ssleay-perl \ + libio-socket-ssl-perl \ + libnet-ssleay-perl + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Php dependencies` \ + php5 \ + php5-dev \ + php5-cli \ + php-pear \ + re2c \ + phpunit \ +`# GlibC dependencies` \ + libglib2.0-dev + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Erlang dependencies` \ + erlang-base \ + erlang-eunit \ + erlang-dev \ + erlang-tools \ + rebar + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Haskell dependencies` \ + ghc \ + cabal-install \ +`# Haxe dependencies` \ + neko \ + neko-dev \ + libneko0 + +RUN apt-get update && apt-get install -y --no-install-recommends \ +`# Node.js dependencies` \ + nodejs \ + nodejs-dev \ + nodejs-legacy + +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 \ +`# Dart dependencies` \ + dart \ +`# Lua dependencies` \ + lua5.2 \ + lua5.2-dev \ +`# MinGW dependencies` \ + mingw32 \ + mingw32-binutils \ + mingw32-runtime \ + nsis \ +`# Clean up` \ + && rm -rf /var/cache/apt/* && \ + rm -rf /var/lib/apt/lists/* && \ + rm -rf /tmp/* && \ + rm -rf /var/tmp/* + +# Ruby +RUN gem install bundler --no-ri --no-rdoc + +# Python optional dependencies +RUN pip2 install -U ipaddress backports.ssl_match_hostname tornado +RUN pip3 install -U backports.ssl_match_hostname tornado + +# Go +RUN curl -sSL https://storage.googleapis.com/golang/go1.4.3.linux-amd64.tar.gz | tar -C /usr/local/ -xz +ENV PATH /usr/local/go/bin:$PATH + +# Haxe +RUN mkdir -p /usr/lib/haxe && \ + curl http://haxe.org/website-content/downloads/3.2.0/downloads/haxe-3.2.0-linux64.tar.gz | \ + tar -C /usr/lib/haxe --strip-components=1 -xz && \ + ln -s /usr/lib/haxe/haxe /usr/bin/haxe && \ + ln -s /usr/lib/haxe/haxelib /usr/bin/haxelib && \ + mkdir -p /usr/lib/haxe/lib && \ + chmod -R 777 /usr/lib/haxe/lib && \ + haxelib setup /usr/lib/haxe/lib && \ + haxelib install hxcpp + +# Node.js +RUN curl -sSL https://www.npmjs.com/install.sh | sh + +# D +RUN curl -sSL http://downloads.dlang.org/releases/2.x/2.070.0/dmd_2.070.0-0_amd64.deb -o /tmp/dmd_2.070.0-0_amd64.deb && \ + dpkg -i /tmp/dmd_2.070.0-0_amd64.deb && \ + rm /tmp/dmd_2.070.0-0_amd64.deb && \ + curl -sSL https://github.com/D-Programming-Deimos/openssl/archive/master.tar.gz| tar xz && \ + curl -sSL https://github.com/D-Programming-Deimos/libevent/archive/master.tar.gz| tar xz && \ + mkdir -p /usr/include/dmd/druntime/import/deimos /usr/include/dmd/druntime/import/C && \ + mv libevent-master/deimos/* openssl-master/deimos/* /usr/include/dmd/druntime/import/deimos/ && \ + mv libevent-master/C/* openssl-master/C/* /usr/include/dmd/druntime/import/C/ && \ + rm -rf libevent-master openssl-master && \ + echo 'gcc -Wl,--no-as-needed $*' > /usr/local/bin/gcc-dmd && \ + chmod 755 /usr/local/bin/gcc-dmd && \ + echo 'CC=/usr/local/bin/gcc-dmd' >> /etc/dmd.conf + +# Dart +ENV PATH /usr/lib/dart/bin:$PATH + +ENV THRIFT_ROOT /thrift +RUN mkdir -p $THRIFT_ROOT/src +COPY Dockerfile $THRIFT_ROOT/ +WORKDIR $THRIFT_ROOT/src diff --git a/vendor/src/github.com/apache/thrift/build/travis/installCXXDependencies.sh b/vendor/src/github.com/apache/thrift/build/travis/installCXXDependencies.sh new file mode 100644 index 00000000..ac3edf38 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/travis/installCXXDependencies.sh @@ -0,0 +1,29 @@ +#!/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. + + +# Mainly aiming Travis CI's Ubuntu machines for now +# see what we need: http://thrift.apache.org/docs/install/ubuntu + +# General dependencies +sudo apt-add-repository "deb http://archive.ubuntu.com/ubuntu/ trusty main restricted" -y +sudo apt-get update -qq + +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 diff --git a/vendor/src/github.com/apache/thrift/build/travis/installDependencies.sh b/vendor/src/github.com/apache/thrift/build/travis/installDependencies.sh new file mode 100644 index 00000000..eab8c6b6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/build/travis/installDependencies.sh @@ -0,0 +1,66 @@ +#!/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/vendor/src/github.com/apache/thrift/cleanup.sh b/vendor/src/github.com/apache/thrift/cleanup.sh new file mode 100644 index 00000000..f110721a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/cleanup.sh @@ -0,0 +1,89 @@ +#!/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/vendor/src/github.com/apache/thrift/compiler/cpp/CMakeLists.txt b/vendor/src/github.com/apache/thrift/compiler/cpp/CMakeLists.txt new file mode 100644 index 00000000..02ed78c6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/CMakeLists.txt @@ -0,0 +1,206 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +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) + +# Create flex and bison files and build the lib parse static library +BISON_TARGET(thrifty ${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/thrifty.yy ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc) +FLEX_TARGET(thriftl ${CMAKE_CURRENT_SOURCE_DIR}/src/thrift/thriftl.ll ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc) +ADD_FLEX_BISON_DEPENDENCY(thriftl thrifty) + +# HACK: Work around the fact that bison crates a .hh file but we need a .h file +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.h + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.hh ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.h + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.hh + ) + +set(libparse_SOURCES + ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.cc + ${CMAKE_CURRENT_BINARY_DIR}/thrift/thriftl.cc + ${CMAKE_CURRENT_BINARY_DIR}/thrift/thrifty.h +) + +add_library(libparse STATIC ${libparse_SOURCES}) + +# Create the thrift compiler +set(compiler_core + src/thrift/common.cc + src/thrift/generate/t_generator.cc + src/thrift/parse/t_typedef.cc + src/thrift/parse/parse.cc + ${CMAKE_CURRENT_BINARY_DIR}/thrift/version.h +) + +set(thrift-compiler_SOURCES + src/thrift/main.cc + src/thrift/audit/t_audit.cpp +) + +# This macro adds an option THRIFT_COMPILER_${NAME} +# that allows enabling or disabling certain languages +macro(THRIFT_ADD_COMPILER name description initial) + string(TOUPPER "THRIFT_COMPILER_${name}" enabler) + set(src "src/thrift/generate/t_${name}_generator.cc") + option(${enabler} ${description} ${initial}) + if(${enabler}) + list(APPEND thrift-compiler_SOURCES ${src}) + endif() +endmacro() + +# The following compiler can be enabled or disabled +THRIFT_ADD_COMPILER(c_glib "Enable compiler for C with Glib" 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(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(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(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(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(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(xml "Enable compiler for XML" 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 ${WITH_PLUGIN}) + list(APPEND thrift-compiler_SOURCES ${compiler_core}) +endif() +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 libparse) + + 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 OUTPUT_NAME thrift) + +target_link_libraries(thrift-compiler libparse) + +install(TARGETS thrift-compiler DESTINATION "${BIN_INSTALL_DIR}") + +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() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/Makefile.am b/vendor/src/github.com/apache/thrift/compiler/cpp/Makefile.am new file mode 100644 index 00000000..a2393916 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/Makefile.am @@ -0,0 +1,203 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# 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 + +# 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 . +if WITH_TESTS +SUBDIRS += test +endif + +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 \ + src/thrift/common.cc \ + src/thrift/generate/t_generator.cc \ + src/thrift/generate/t_generator_registry.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/parse/t_base_type.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_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 + +# Specific client generator source +thrift_SOURCES += src/thrift/generate/t_c_glib_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_dart_generator.cc \ + src/thrift/generate/t_haxe_generator.cc \ + src/thrift/generate/t_csharp_generator.cc \ + 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_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_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_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_lua_generator.cc + +thrift_CPPFLAGS = -I$(srcdir)/src +thrift_CXXFLAGS = -Wall -Wextra -pedantic +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 \ + compiler.vcxproj.filters + +EXTRA_DIST = \ + coding_standards.md \ + README.md \ + CMakeLists.txt \ + test \ + $(WINDOWS_DIST) + +clean-local: + $(RM) version.h $(plugin_gen) + +src/thrift/main.cc: src/thrift/version.h + +style-local: + $(CPPSTYLE_CMD) diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/README.md b/vendor/src/github.com/apache/thrift/compiler/cpp/README.md new file mode 100644 index 00000000..90300ac0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/README.md @@ -0,0 +1,83 @@ +# Build compiler using CMake + +Use the following steps to build using cmake: + + mkdir cmake-build + cd cmake-build + cmake .. + make + + +### Create an eclipse project + + mkdir cmake-ec && cd cmake-ec + cmake -G "Eclipse CDT4 - Unix Makefiles" .. + make + +Now open the folder cmake-ec using eclipse. + + +### Cross compile using mingw32 and generate a Windows Installer with CPack + + 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 .. + cpack + +## Build on windows + +### using Git Bash +Git Bash provides flex and bison, so you just need to do this: + + mkdir cmake-vs && cd cmake-vs + cmake -DWITH_SHARED_LIB=off .. + +### using Win flex-bison + +In order to build on windows with winflexbison a few additional steps are necessary: + +1. Download winflexbison from http://sourceforge.net/projects/winflexbison/ +2. Extract the winflex bison files to for e.g. C:\winflexbison +3. Make the CMake variables point to the correct binaries. + * FLEX_EXECUTABLE = C:/winbuild/win_flex.exe + * BISON_EXECUTABLE = C:/winbuild/win_bison.exe +4. Generate a Visual Studio project: +``` +mkdir cmake-vs && cd cmake-vs +cmake -G "Visual Studio 12" -DWITH_SHARED_LIB=off .. +``` +5. Now open the folder build_vs using Visual Studio 2013. + +# Building the Thrift IDL compiler in Windows + +If you don't want to use CMake you can use the already available Visual Studio +2010 solution. +The Visual Studio project contains pre-build commands to generate the +thriftl.cc, thrifty.cc and thrifty.hh files which are necessary to build +the compiler. These depend on bison, flex and their dependencies to +work properly. +Download flex & bison as described above. +Place these binaries somewhere in the path and +rename win_flex.exe and win_bison.exe to flex.exe and bison.exe respectively. + +If this doesn't work on a system, try these manual pre-build steps. + +Open compiler.sln and remove the Pre-build commands under the project's + Properties -> Build Events -> Pre-Build Events. + +From a command prompt: +> cd thrift/compiler/cpp +> flex -osrc\thrift\thriftl.cc src\thrift\thriftl.ll +In the generated thriftl.cc, comment out #include + +Place a copy of bison.simple in thrift/compiler/cpp +> bison -y -o "src/thrift/thrifty.cc" --defines src/thrift/thrifty.yy +> move src\thrift\thrifty.cc.hh src\thrift\thrifty.hh + +Bison might generate the yacc header file "thrifty.cc.h" with just one h ".h" extension; in this case you'll have to rename to "thrifty.h". + +> move src\thrift\version.h.in src\thrift\version.h + +Download inttypes.h from the interwebs and place it in an include path +location (e.g. thrift/compiler/cpp/src). + +Build the compiler in Visual Studio. diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/coding_standards.md b/vendor/src/github.com/apache/thrift/compiler/cpp/coding_standards.md new file mode 100644 index 00000000..ea089467 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/coding_standards.md @@ -0,0 +1,4 @@ +## Compiler Coding Standards + + * When making small change / bugfix - follow style as seen in nearby code. + * When making major refactor and / or adding new feature - follow style for C++ library \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.sln b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.sln new file mode 100644 index 00000000..94961aae --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compiler", "compiler.vcxproj", "{89975A1A-F799-4556-98B8-64E30AB39A90}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {89975A1A-F799-4556-98B8-64E30AB39A90}.Debug|Win32.ActiveCfg = Debug|Win32 + {89975A1A-F799-4556-98B8-64E30AB39A90}.Debug|Win32.Build.0 = Debug|Win32 + {89975A1A-F799-4556-98B8-64E30AB39A90}.Release|Win32.ActiveCfg = Release|Win32 + {89975A1A-F799-4556-98B8-64E30AB39A90}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj new file mode 100644 index 00000000..878e2199 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj @@ -0,0 +1,249 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {89975A1A-F799-4556-98B8-64E30AB39A90} + Win32Proj + compiler + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) + thrift + $(ExecutablePath);C:\Program Files (x86)\Git\bin + + + true + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) + thrift + $(ExecutablePath);C:\Program Files (x86)\Git\bin + + + false + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) + thrift + $(ExecutablePath);C:\Program Files (x86)\Git\bin + + + false + $(ProjectDir)\src\;$(ProjectDir)\src\windows\;$(IncludePath) + thrift + $(ExecutablePath);C:\Program Files (x86)\Git\bin + + + + + + Level3 + Disabled + WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + thrift\windows\config.h + CompileAsCpp + + + Console + true + + + flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll && bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy + + + + + + + Level3 + Disabled + WIN32;MINGW;YY_NO_UNISTD_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + thrift\windows\config.h + CompileAsCpp + + + Console + true + + + flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll && bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy + + + + + + Level3 + + + MaxSpeed + true + true + WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + thrift\windows\config.h + CompileAsCpp + + + Console + true + true + true + + + flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll && bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy + + + + + + Level3 + + + MaxSpeed + true + true + WIN32;MINGW;YY_NO_UNISTD_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + thrift\windows\config.h + CompileAsCpp + + + Console + true + true + true + + + flex -o "src\\thrift\\thriftl.cc" src/thrift/thriftl.ll && bison -y -o "src\\thrift\\thrifty.cc" --defines="src\\thrift\\thrifty.hh" src/thrift/thrifty.yy + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj.filters b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj.filters new file mode 100644 index 00000000..9b14bbf1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/compiler.vcxproj.filters @@ -0,0 +1,196 @@ + + + + + + generate + + + generate + + + generate + + + generate + + + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + parse + + + + + windows + + + windows + + + + + {ae9d0a15-57ae-4f01-87a4-81f790249b83} + + + {5df016bb-591b-420a-a535-4330d9187fbf} + + + {b5c626af-afa5-433c-8e10-ee734533cb68} + + + + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + generate + + + + parse + + + + + parse + + + generate + + + generate + + + + + + + \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/Makefile.am b/vendor/src/github.com/apache/thrift/compiler/cpp/src/Makefile.am new file mode 100644 index 00000000..bc2c5cba --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/Makefile.am @@ -0,0 +1,87 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# 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 + +AM_YFLAGS = -d + +BUILT_SOURCES = thrift/thrifty.cc + +noinst_LIBRARIES = thrift/libparse.a + +thrift_libparse_a_CPPFLAGS = -I$(srcdir) +thrift_libparse_a_CXXFLAGS = -Wall -Wno-sign-compare -Wno-unused + +thrift_libparse_a_SOURCES = thrift/thrifty.yy \ + thrift/thriftl.ll + +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 diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp new file mode 100644 index 00000000..1386f3bd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.cpp @@ -0,0 +1,464 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Careful: must include globals first for extern definitions +#include "thrift/globals.h" + +#include "thrift/parse/t_program.h" +#include "thrift/parse/t_scope.h" +#include "thrift/parse/t_const.h" +#include "thrift/parse/t_field.h" + +#include "thrift/version.h" + +#include "thrift/audit/t_audit.h" + +extern int g_warn; +extern std::string g_curpath; +extern bool g_return_failure; + +void thrift_audit_warning(int level, const char* fmt, ...) { + if (g_warn < level) { + return; + } + va_list args; + printf("[Thrift Audit Warning:%s] ", g_curpath.c_str()); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +void thrift_audit_failure(const char* fmt, ...) { + va_list args; + fprintf(stderr, "[Thrift Audit Failure:%s] ", g_curpath.c_str()); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + g_return_failure = true; +} + +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++) + { + std::map::const_iterator 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()); + } + else if((newNamespaceMapIt->second) != oldNamespaceMapIt->second) + { + thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt->second).c_str()); + } + } +} + +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++) + { + int enumValue = (*oldEnumValuesIt)->get_value(); + t_enum_value* newEnumValue = newEnum->get_constant_by_value(enumValue); + if(newEnumValue != NULL) + { + std::string enumName = (*oldEnumValuesIt)->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()); + } + } + else + { + thrift_audit_failure("Enum value %d missing in %s\n", enumValue, oldEnum->get_name().c_str()); + } + + } +} + +void compare_enums(const std::vector& newEnumList, const std::vector& oldEnumList) +{ + std::map newEnumMap; + std::vector::const_iterator newEnumIt; + for(newEnumIt = newEnumList.begin(); newEnumIt != newEnumList.end(); newEnumIt++) + { + newEnumMap[(*newEnumIt)->get_name()] = *newEnumIt; + } + std::vector::const_iterator oldEnumIt; + for(oldEnumIt = oldEnumList.begin(); oldEnumIt != oldEnumList.end(); oldEnumIt++) + { + std::map::iterator newEnumMapIt; + newEnumMapIt = newEnumMap.find((*oldEnumIt)->get_name()); + + if(newEnumMapIt == newEnumMap.end()) + { + thrift_audit_warning(1, "Enum %s not found in new thrift file\n",(*oldEnumIt)->get_name().c_str()); + } + else + { + compare_enum_values(newEnumMapIt->second, *oldEnumIt); + } + } +} + +//This function returns 'true' if the two arguements are of same types. +//Returns false if they are of different type +bool compare_type(t_type* newType, t_type* oldType) +{ + //Comparing names of two types will work when the newType and oldType are basic types or structs or enums. + //However, when they are containers, get_name() returns empty for which we have to compare the type of + //their elements as well. + if((newType->get_name()).empty() && (oldType->get_name()).empty()) + { + + if(newType->is_list() && oldType->is_list()) + { + t_type* newElementType = ((t_list*)newType)->get_elem_type(); + t_type* oldElementType = ((t_list*)oldType)->get_elem_type(); + return compare_type(newElementType, oldElementType); + } + else if(newType->is_map() && oldType->is_map()) + { + t_type* newKeyType = ((t_map*)newType)->get_key_type(); + t_type* oldKeyType = ((t_map*)oldType)->get_key_type(); + + t_type* newValType = ((t_map*)newType)->get_val_type(); + t_type* oldValType = ((t_map*)oldType)->get_val_type(); + + return (compare_type(newKeyType, oldKeyType) && compare_type(newValType, oldValType)); + } + else if(newType->is_set() && oldType->is_set()) + { + t_type* newElementType = ((t_set*)newType)->get_elem_type(); + t_type* oldElementType = ((t_set*)oldType)->get_elem_type(); + return compare_type(newElementType, oldElementType); + } + else + { + return false; + } + } + else if(newType->get_name() == oldType->get_name()) + { + return true; + } + else + { + return false; + } +} + +bool compare_pair(std::pair newMapPair, std::pair oldMapPair) +{ + return compare_defaults(newMapPair.first, oldMapPair.first) && compare_defaults(newMapPair.second, oldMapPair.second); +} + +// 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->get_type() != oldStructDefault->get_type()) + { + return false; + } + + switch(newStructDefault->get_type()) + { + case t_const_value::CV_INTEGER: + return (newStructDefault->get_integer() == oldStructDefault->get_integer()); + case t_const_value::CV_DOUBLE: + return (newStructDefault->get_double() == oldStructDefault->get_double()); + case t_const_value::CV_STRING: + return (newStructDefault->get_string() == oldStructDefault->get_string()); + case t_const_value::CV_LIST: + { + const std::vector& oldDefaultList = oldStructDefault->get_list(); + const std::vector& newDefaultList = newStructDefault->get_list(); + bool defaultValuesCompare = (oldDefaultList.size() == newDefaultList.size()); + + return defaultValuesCompare && std::equal(newDefaultList.begin(), newDefaultList.end(), oldDefaultList.begin(), compare_defaults); + } + case t_const_value::CV_MAP: + { + const std::map newMap = newStructDefault->get_map(); + const std::map oldMap = oldStructDefault->get_map(); + + bool defaultValuesCompare = (oldMap.size() == newMap.size()); + + return defaultValuesCompare && std::equal(newMap.begin(), newMap.end(), oldMap.begin(), compare_pair); + } + case t_const_value::CV_IDENTIFIER: + return (newStructDefault->get_identifier() == oldStructDefault->get_identifier()); + default: + return false; + } + +} + +void compare_struct_field(t_field* newField, t_field* oldField, std::string oldStructName) +{ + t_type* newFieldType = newField->get_type(); + t_type* oldFieldType = oldField->get_type(); + if(!compare_type(newFieldType, oldFieldType)) + { + thrift_audit_failure("Struct Field Type Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str()); + } + + // A Struct member can be optional if it is mentioned explicitly, or if it is assigned with default values. + bool newStructFieldOptional = (newField->get_req() != t_field::T_REQUIRED); + bool oldStructFieldOptional = (oldField->get_req() != t_field::T_REQUIRED); + + if(newStructFieldOptional != oldStructFieldOptional) + { + thrift_audit_failure("Struct Field Requiredness Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str()); + } + if(newStructFieldOptional || oldStructFieldOptional) + { + if(!compare_defaults(newField->get_value(), oldField->get_value())) + { + thrift_audit_warning(1, "Default value changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str()); + } + } + + std::string fieldName = newField->get_name(); + if(fieldName != oldField->get_name()) + { + thrift_audit_warning(1, "Struct field name changed for Id = %d in %s\n", newField->get_key(), oldStructName.c_str()); + } + +} + +void compare_single_struct(t_struct* newStruct, t_struct* oldStruct, const std::string& oldStructName = std::string()) +{ + 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(); + + // Since we have the struct members in their ID order, comparing their IDs can be done by traversing the two member + // lists together. + while(!(oldStructMemberIt == oldStructMembersInIdOrder.end() && newStructMemberIt == newStructMembersInIdOrder.end())) + { + if(newStructMemberIt == newStructMembersInIdOrder.end() && oldStructMemberIt != oldStructMembersInIdOrder.end()) + { + // A field ID has been removed from the end. + thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str()); + oldStructMemberIt++; + } + else if(newStructMemberIt != newStructMembersInIdOrder.end() && oldStructMemberIt == oldStructMembersInIdOrder.end()) + { + //New field ID has been added to the end. + if((*newStructMemberIt)->get_req() == t_field::T_REQUIRED) + { + thrift_audit_failure("Required Struct Field Added for Id = %d in %s \n", (*newStructMemberIt)->get_key(), structName.c_str()); + } + newStructMemberIt++; + } + else if((*newStructMemberIt)->get_key() == (*oldStructMemberIt)->get_key()) + { + //Field ID found in both structs. Compare field types, default values. + compare_struct_field(*newStructMemberIt, *oldStructMemberIt, structName); + + newStructMemberIt++; + oldStructMemberIt++; + } + else if((*newStructMemberIt)->get_key() < (*oldStructMemberIt)->get_key()) + { + //New Field Id is inserted in between + //Adding fields to struct is fine, but adding them in the middle is suspicious. Error!! + thrift_audit_failure("Struct field is added in the middle with Id = %d in %s\n", (*newStructMemberIt)->get_key(), structName.c_str()); + newStructMemberIt++; + } + else if((*newStructMemberIt)->get_key() > (*oldStructMemberIt)->get_key()) + { + //A field is deleted in newStruct. + thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str()); + oldStructMemberIt++; + } + + } +} + +void compare_structs(const std::vector& newStructList, const std::vector& oldStructList) +{ + std::map newStructMap; + std::vector::const_iterator newStructListIt; + for(newStructListIt = newStructList.begin(); newStructListIt != newStructList.end(); newStructListIt++) + { + newStructMap[(*newStructListIt)->get_name()] = *newStructListIt; + } + + std::vector::const_iterator oldStructListIt; + for(oldStructListIt = oldStructList.begin(); oldStructListIt != oldStructList.end(); oldStructListIt++) + { + std::map::iterator newStructMapIt; + newStructMapIt = newStructMap.find((*oldStructListIt)->get_name()); + if(newStructMapIt == newStructMap.end()) + { + thrift_audit_failure("Struct %s not found in new thrift file\n", (*oldStructListIt)->get_name().c_str()); + } + else + { + compare_single_struct(newStructMapIt->second, *oldStructListIt); + } + } + +} + +void compare_single_function(t_function* newFunction, t_function* oldFunction) +{ + t_type* newFunctionReturnType = newFunction->get_returntype(); + + if(newFunction->is_oneway() != oldFunction->is_oneway()) + { + thrift_audit_failure("Oneway attribute changed for function %s\n",oldFunction->get_name().c_str()); + } + if(!compare_type(newFunctionReturnType, oldFunction->get_returntype())) + { + thrift_audit_failure("Return type changed for function %s\n",oldFunction->get_name().c_str()); + } + + //Compare function arguments. + compare_single_struct(newFunction->get_arglist(), oldFunction->get_arglist()); + std::string exceptionName = oldFunction->get_name(); + exceptionName += "_exception"; + compare_single_struct(newFunction->get_xceptions(), oldFunction->get_xceptions(), exceptionName); +} + +void compare_functions(const std::vector& newFunctionList, const std::vector& oldFunctionList) +{ + std::map newFunctionMap; + std::map::iterator newFunctionMapIt; + for(std::vector::const_iterator newFunctionIt = newFunctionList.begin(); + newFunctionIt != newFunctionList.end(); + newFunctionIt++) + { + newFunctionMap[(*newFunctionIt)->get_name()] = *newFunctionIt; + } + + for(std::vector::const_iterator oldFunctionIt = oldFunctionList.begin(); + oldFunctionIt != oldFunctionList.end(); + oldFunctionIt++) + { + 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()); + continue; + } + else + { + //Function is found in both thrift files. Compare return type and argument list + compare_single_function(newFunctionMapIt->second, *oldFunctionIt); + } + } + +} + +void compare_services(const std::vector& newServices, const std::vector& oldServices) +{ + std::vector::const_iterator oldServiceIt; + + std::map newServiceMap; + for(std::vector::const_iterator newServiceIt = newServices.begin(); + newServiceIt != newServices.end(); + newServiceIt++) + { + newServiceMap[(*newServiceIt)->get_name()] = *newServiceIt; + } + + + for(oldServiceIt = oldServices.begin(); oldServiceIt != oldServices.end(); oldServiceIt++) + { + const std::string oldServiceName = (*oldServiceIt)->get_name(); + std::map::iterator newServiceMapIt = newServiceMap.find(oldServiceName); + + if(newServiceMapIt == newServiceMap.end()) + { + thrift_audit_failure("New Thrift file is missing a service %s\n", oldServiceName.c_str()); + } + else + { + t_service* oldServiceExtends = (*oldServiceIt)->get_extends(); + t_service* newServiceExtends = (newServiceMapIt->second)->get_extends(); + + if(oldServiceExtends == NULL) + { + // 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) + { + thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str()); + } + else + { + std::string oldExtendsName = oldServiceExtends->get_name(); + std::string newExtendsName = newServiceExtends->get_name(); + + if( newExtendsName != oldExtendsName) + { + thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str()); + } + } + + compare_functions((newServiceMapIt->second)->get_functions(), (*oldServiceIt)->get_functions()); + } + + } + +} + +void compare_consts(const std::vector& newConst, const std::vector& oldConst) +{ + std::vector::const_iterator newConstIt; + std::vector::const_iterator oldConstIt; + + std::map newConstMap; + + for(newConstIt = newConst.begin(); newConstIt != newConst.end(); newConstIt++) + { + newConstMap[(*newConstIt)->get_name()] = *newConstIt; + } + + std::map::const_iterator newConstMapIt; + for(oldConstIt = oldConst.begin(); oldConstIt != oldConst.end(); oldConstIt++) + { + newConstMapIt = newConstMap.find((*oldConstIt)->get_name()); + if(newConstMapIt == newConstMap.end()) + { + thrift_audit_warning(1, "Constants Missing %s \n", ((*oldConstIt)->get_name()).c_str()); + } + else if(!compare_type((newConstMapIt->second)->get_type(), (*oldConstIt)->get_type())) + { + thrift_audit_warning(1, "Constant %s is of different type \n", ((*oldConstIt)->get_name()).c_str()); + } + else if(!compare_defaults((newConstMapIt->second)->get_value(), (*oldConstIt)->get_value())) + { + thrift_audit_warning(1, "Constant %s has different value\n", ((*oldConstIt)->get_name()).c_str()); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.h new file mode 100644 index 00000000..be79e312 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/audit/t_audit.h @@ -0,0 +1,14 @@ +#ifndef T_AUDIT_H +#define T_AUDIT_H + +void compare_namespace(t_program* newProgram, t_program* oldProgram); +void compare_enums(const std::vector& newEnumList, + const std::vector& oldEnumList); +bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault); +void compare_structs(const std::vector& newStructList, + const std::vector& oldStructList); +void compare_services(const std::vector& newServices, + const std::vector& oldServices); +void compare_consts(const std::vector& newConst, const std::vector& oldConst); + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.cc new file mode 100644 index 00000000..3a2b9d35 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.cc @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "thrift/common.h" +#include "thrift/parse/t_base_type.h" + +t_type* g_type_void; +t_type* g_type_string; +t_type* g_type_binary; +t_type* g_type_slist; +t_type* g_type_bool; +t_type* g_type_i8; +t_type* g_type_i16; +t_type* g_type_i32; +t_type* g_type_i64; +t_type* g_type_double; + +void initGlobals() { + g_type_void = new t_base_type("void", t_base_type::TYPE_VOID); + g_type_string = new t_base_type("string", t_base_type::TYPE_STRING); + g_type_binary = new t_base_type("string", t_base_type::TYPE_STRING); + ((t_base_type*)g_type_binary)->set_binary(true); + g_type_slist = new t_base_type("string", t_base_type::TYPE_STRING); + ((t_base_type*)g_type_slist)->set_string_list(true); + g_type_bool = new t_base_type("bool", t_base_type::TYPE_BOOL); + g_type_i8 = new t_base_type("i8", t_base_type::TYPE_I8); + g_type_i16 = new t_base_type("i16", t_base_type::TYPE_I16); + g_type_i32 = new t_base_type("i32", t_base_type::TYPE_I32); + g_type_i64 = new t_base_type("i64", t_base_type::TYPE_I64); + g_type_double = new t_base_type("double", t_base_type::TYPE_DOUBLE); +} + +void clearGlobals() { + delete g_type_void; + delete g_type_string; + delete g_type_bool; + delete g_type_i8; + delete g_type_i16; + delete g_type_i32; + delete g_type_i64; + delete g_type_double; +} + +/** + * Those are not really needed for plugins but causes link errors without + */ + +/** + * The location of the last parsed doctext comment. + */ +int g_doctext_lineno; +int g_program_doctext_lineno = 0; +PROGDOCTEXT_STATUS g_program_doctext_status = INVALID; diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.h new file mode 100644 index 00000000..69488463 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/common.h @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_COMMON_H +#define T_COMMON_H + +#include "thrift/parse/t_type.h" + +/** + * Global types for the parser to be able to reference + */ + +extern t_type* g_type_void; +extern t_type* g_type_string; +extern t_type* g_type_binary; +extern t_type* g_type_slist; +extern t_type* g_type_bool; +extern t_type* g_type_i8; +extern t_type* g_type_i16; +extern t_type* g_type_i32; +extern t_type* g_type_i64; +extern t_type* g_type_double; + +void initGlobals(); +void clearGlobals(); + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc new file mode 100644 index 00000000..5ae3f91d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_as3_generator.cc @@ -0,0 +1,2594 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * AS3 code generator. + * + */ +class t_as3_generator : public t_oop_generator { +public: + t_as3_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; + + bindable_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("bindable") == 0) { + bindable_ = true; + } else { + throw "unknown option as3:" + iter->first; + } + } + + out_dir_base_ = "gen-as3"; + } + + /** + * 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(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval = false); + std::string render_const_value(ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + /** + * Service-level generation functions + */ + + void generate_as3_struct(t_struct* tstruct, bool is_exception); + + void generate_as3_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool in_class = false, + bool is_result = false); + // removed -- equality,compare_to + void generate_as3_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_as3_validator(std::ofstream& out, t_struct* tstruct); + void generate_as3_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_as3_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_as3_struct_tostring(std::ofstream& out, t_struct* tstruct, bool bindable); + void generate_as3_meta_data_map(std::ofstream& out, t_struct* tstruct); + void generate_field_value_meta_data(std::ofstream& out, t_type* type); + std::string get_as3_type_string(t_type* type); + void generate_reflection_setters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_reflection_getters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_generic_field_getters_setters(std::ofstream& out, t_struct* tstruct); + void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); + void generate_as3_bean_boilerplate(std::ofstream& out, t_struct* tstruct, bool bindable); + + void generate_function_helpers(t_function* tfunction); + std::string get_cap_name(std::string name); + std::string generate_isset_check(t_field* field); + std::string generate_isset_check(std::string field); + void generate_isset_set(ofstream& out, t_field* field); + // removed std::string isset_field_id(t_field* field); + + void generate_service_interface(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_process_function(t_service* tservice, t_function* tfunction); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_as3_doc(std::ofstream& out, t_doc* tdoc); + + void generate_as3_doc(std::ofstream& out, t_function* tdoc); + + /** + * Helper rendering functions + */ + + std::string as3_package(); + std::string as3_type_imports(); + std::string as3_thrift_imports(); + std::string as3_thrift_gen_imports(t_struct* tstruct, string& imports); + std::string as3_thrift_gen_imports(t_service* tservice); + std::string type_name(t_type* ttype, bool in_container = false, bool in_init = false); + std::string base_type_name(t_base_type* tbase, bool in_container = false); + std::string declare_field(t_field* tfield, bool init = false); + 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); + + 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(); + } + + std::string constant_name(std::string name); + +private: + /** + * File streams + */ + + std::string package_name_; + std::ofstream f_service_; + std::string package_dir_; + + bool bindable_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_as3_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + package_name_ = program_->get_namespace("as3"); + + string dir = package_name_; + string subdir = get_out_dir(); + 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()); + } + + package_dir_ = subdir; +} + +/** + * Packages the generated file + * + * @return String of the package, i.e. "package org.apache.thriftdemo;" + */ +string t_as3_generator::as3_package() { + if (!package_name_.empty()) { + return string("package ") + package_name_ + " "; + } + return "package "; +} + +/** + * Prints standard as3 imports + * + * @return List of imports for As3 types that are used in here + */ +string t_as3_generator::as3_type_imports() { + return string() + "import org.apache.thrift.Set;\n" + "import flash.utils.ByteArray;\n" + + "import flash.utils.Dictionary;\n\n"; +} + +/** + * Prints standard as3 imports + * + * @return List of imports necessary for thrift + */ +string t_as3_generator::as3_thrift_imports() { + return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n" + + "import org.apache.thrift.protocol.*;\n\n"; +} + +/** + * Prints imports needed for a given type + * + * @return List of imports necessary for a given t_struct + */ +string t_as3_generator::as3_thrift_gen_imports(t_struct* tstruct, string& imports) { + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + // 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_) { + string package = program->get_namespace("as3"); + if (!package.empty()) { + if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) { + imports.append("import " + package + "." + (*m_iter)->get_type()->get_name() + ";\n"); + } + } + } + } + return imports; +} + +/** + * Prints imports needed for a given type + * + * @return List of imports necessary for a given t_service + */ +string t_as3_generator::as3_thrift_gen_imports(t_service* tservice) { + string imports; + const vector& functions = tservice->get_functions(); + vector::const_iterator f_iter; + + // 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_) { + string package = program->get_namespace("as3"); + if (!package.empty()) { + if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) { + imports.append("import " + package + "." + (*f_iter)->get_returntype()->get_name() + + ";\n"); + } + } + } + + as3_thrift_gen_imports((*f_iter)->get_arglist(), imports); + as3_thrift_gen_imports((*f_iter)->get_xceptions(), imports); + } + + return imports; +} + +/** + * Nothing in As3 + */ +void t_as3_generator::close_generator() { +} + +/** + * Generates a typedef. This is not done in As3, since it does + * not support arbitrary name replacements, and it'd be a wacky waste + * of overhead to make wrapper classes. + * + * @param ttypedef The type definition + */ +void t_as3_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Enums are a class with a set of static constants. + * + * @param tenum The enumeration + */ +void t_as3_generator::generate_enum(t_enum* tenum) { + // Make output file + string f_enum_name = package_dir_ + "/" + (tenum->get_name()) + ".as"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + // Comment and package it + f_enum << autogen_comment() << as3_package() << endl; + + scope_up(f_enum); + // Add as3 imports + f_enum << string() + "import org.apache.thrift.Set;" << endl << "import flash.utils.Dictionary;" + << endl; + + indent(f_enum) << "public class " << tenum->get_name() << " "; + scope_up(f_enum); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << "public static const " << (*c_iter)->get_name() << ":int = " << value << ";" + << endl; + } + + // Create a static Set with all valid values for this enum + f_enum << endl; + + indent(f_enum) << "public static const VALID_VALUES:Set = new Set("; + indent_up(); + bool firstValue = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + // populate set + f_enum << (firstValue ? "" : ", ") << (*c_iter)->get_name(); + firstValue = false; + } + indent_down(); + f_enum << ");" << endl; + + indent(f_enum) << "public static const VALUES_TO_NAMES:Dictionary = new Dictionary();" << endl; + + scope_up(f_enum); + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + indent(f_enum) << "VALUES_TO_NAMES[" << (*c_iter)->get_name() << "] = \"" + << (*c_iter)->get_name() << "\";" << endl; + } + f_enum << endl; + + scope_down(f_enum); + + scope_down(f_enum); // end class + + scope_down(f_enum); // end package + + f_enum.close(); +} + +/** + * Generates a class that holds all the constants. + */ +void t_as3_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + string f_consts_name = package_dir_ + "/" + program_name_ + "Constants.as"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + // Print header + f_consts << autogen_comment() << as3_package(); + + scope_up(f_consts); + f_consts << endl; + + f_consts << as3_type_imports(); + + indent(f_consts) << "public class " << program_name_ << "Constants {" << endl << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false); + } + indent_down(); + indent(f_consts) << "}" << endl; + scope_down(f_consts); + f_consts.close(); +} + +void t_as3_generator::print_const_value(std::ofstream& out, + string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval) { + type = get_true_type(type); + + indent(out); + if (!defval) { + out << (in_static ? "var " : "public static const "); + } + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = " << value->get_integer() << ";" << endl << endl; + } 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; + out << name << ":" << type_name(type) << " = new " << type_name(type, false, true) << "();" + << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function():void {" << endl; + indent_up(); + } + 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, name, field_type, v_iter->second); + indent(out) << name << "."; + out << v_iter->first->get_string() << " = " << val << ";" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_map()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function():void {" << endl; + indent_up(); + } + 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; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_list() || type->is_set()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function():void {" << endl; + indent_up(); + } + 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 << "." << (type->is_list() ? "push" : "add") << "(" << val << ");" + << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else { + throw "compiler error: no const of type " + type->get_name(); + } +} + +string t_as3_generator::render_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + 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) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + render << "(byte)" << value->get_integer(); + break; + case t_base_type::TYPE_I16: + render << "(short)" << value->get_integer(); + break; + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << "(double)" << 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); + render << t; + } + + return render.str(); +} + +/** + * Generates a struct definition for a thrift data type. This is a class + * with data members, read(), write(), and an inner Isset class. + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_struct(t_struct* tstruct) { + generate_as3_struct(tstruct, false); +} + +/** + * Exceptions are structs, but they inherit from Exception + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_xception(t_struct* txception) { + generate_as3_struct(txception, true); +} + +/** + * As3 struct definition. + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_struct(t_struct* tstruct, bool is_exception) { + // Make output file + string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".as"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << as3_package(); + + scope_up(f_struct); + f_struct << endl; + + string imports; + + f_struct << as3_type_imports() << as3_thrift_imports() << as3_thrift_gen_imports(tstruct, imports) + << endl; + + if (bindable_ && !is_exception) { + f_struct << "import flash.events.Event;" << endl << "import flash.events.EventDispatcher;" + << endl << "import mx.events.PropertyChangeEvent;" << endl; + } + + generate_as3_struct_definition(f_struct, tstruct, is_exception); + + scope_down(f_struct); // end of package + f_struct.close(); +} + +/** + * As3 struct definition. This has various parameters, as it could be + * generated standalone or inside another class as a helper. If it + * is a helper than it is a static class. + * + * @param tstruct The struct definition + * @param is_exception Is this an exception? + * @param in_class If inside a class, needs to be static class + * @param is_result If this is a result it needs a different writer + */ +void t_as3_generator::generate_as3_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool in_class, + bool is_result) { + generate_as3_doc(out, tstruct); + + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + bool bindable = !is_exception && !in_class && bindable_; + + indent(out) << (in_class ? "" : "public ") << (is_final ? "final " : "") << "class " + << tstruct->get_name() << " "; + + if (is_exception) { + out << "extends Error "; + } else if (bindable) { + out << "extends EventDispatcher "; + } + out << "implements TBase "; + + scope_up(out); + + indent(out) << "private static const STRUCT_DESC:TStruct = new TStruct(\"" << tstruct->get_name() + << "\");" << endl; + + // Members are public for -as3, private for -as3bean + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "private static const " << constant_name((*m_iter)->get_name()) + << "_FIELD_DESC:TField = new TField(\"" << (*m_iter)->get_name() << "\", " + << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");" + << endl; + } + + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_as3_doc(out, *m_iter); + indent(out) << "private var _" << (*m_iter)->get_name() + ":" + type_name((*m_iter)->get_type()) + << ";" << endl; + + indent(out) << "public static const " << upcase_string((*m_iter)->get_name()) + << ":int = " << (*m_iter)->get_key() << ";" << endl; + } + + out << endl; + + // Inner Isset class + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null((*m_iter)->get_type())) { + indent(out) << "private var __isset_" << (*m_iter)->get_name() << ":Boolean = false;" + << endl; + } + } + } + + out << endl; + + generate_as3_meta_data_map(out, tstruct); + + // Static initializer to populate global class to struct metadata map + indent(out) << "{" << endl; + indent_up(); + indent(out) << "FieldMetaData.addStructMetaDataMap(" << type_name(tstruct) << ", metaDataMap);" + << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + // Default constructor + 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) { + indent(out) << "this._" << (*m_iter)->get_name() << " = " + << (*m_iter)->get_value()->get_integer() << ";" << endl; + } + } + indent_down(); + indent(out) << "}" << endl << endl; + + generate_as3_bean_boilerplate(out, tstruct, bindable); + generate_generic_field_getters_setters(out, tstruct); + generate_generic_isset_method(out, tstruct); + + generate_as3_struct_reader(out, tstruct); + if (is_result) { + generate_as3_struct_result_writer(out, tstruct); + } else { + generate_as3_struct_writer(out, tstruct); + } + generate_as3_struct_tostring(out, tstruct, bindable); + generate_as3_validator(out, tstruct); + scope_down(out); + out << endl; +} + +/** + * Generates a function to read all the fields of the struct. + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_struct_reader(ofstream& out, t_struct* tstruct) { + out << indent() << "public function read(iprot:TProtocol):void {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables and read struct header + out << indent() << "var field:TField;" << endl << indent() << "iprot.readStructBegin();" << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "field = iprot.readFieldBegin();" << endl; + + // Check for field STOP marker and break + indent(out) << "if (field.type == TType.STOP) { " << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << "switch (field.id)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl; + indent_up(); + indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + generate_deserialize_field(out, *f_iter, "this."); + generate_isset_set(out, *f_iter); + indent_down(); + out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" + << endl << indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "default:" << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" + << endl << indent() << " break;" << endl; + + scope_down(out); + + // Read field end marker + indent(out) << "iprot.readFieldEnd();" << endl; + + scope_down(out); + + out << indent() << "iprot.readStructEnd();" << endl << endl; + + // in non-beans style, check for required fields of primitive type + // (which can be checked here but not in the general validate method) + out << endl << indent() << "// check for required fields of primitive type, which can't be " + "checked in the validate method" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) { + out << indent() << "if (!__isset_" << (*f_iter)->get_name() << ") {" << endl << indent() + << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '" + << (*f_iter)->get_name() + << "' was not found in serialized data! Struct: \" + toString());" << endl << indent() + << "}" << endl; + } + } + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +// generates as3 method to perform various checks +// (e.g. check that all required fields are set) +void t_as3_generator::generate_as3_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "public function validate():void {" << 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) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + if (type_can_be_null((*f_iter)->get_type())) { + indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '" + << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" + << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name() + << "' because it's a primitive and you chose the non-beans generator." << endl; + } + } + } + + // check that fields of type enum have valid values + out << indent() << "// check that fields of type enum have valid values" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + t_type* type = field->get_type(); + // if field is an enum, check that its value is valid + if (type->is_enum()) { + indent(out) << "if (" << generate_isset_check(field) << " && !" << get_enum_class_name(type) + << ".VALID_VALUES.contains(" << field->get_name() << ")){" << endl; + indent_up(); + indent(out) << "throw new TProtocolError(TProtocolError.UNKNOWN, \"The field '" + << field->get_name() << "' has been assigned the invalid value \" + " + << field->get_name() << ");" << endl; + indent_down(); + indent(out) << "}" << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to write all the fields of the struct + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_struct_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public function write(oprot:TProtocol):void {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl << endl; + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + } + // Write the struct map + out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();" + << 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 in the Isset array, and only one of them + * can be set at a time. + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_struct_result_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public function write(oprot:TProtocol):void {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << 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 << "(this." << generate_isset_check(*f_iter) << ") {" << endl; + + indent_up(); + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}"; + } + // Write the struct map + out << endl << indent() << "oprot.writeFieldStop();" << endl << indent() + << "oprot.writeStructEnd();" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_as3_generator::generate_reflection_getters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + (void)type; + (void)cap_name; + indent(out) << "case " << upcase_string(field_name) << ":" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); +} + +void t_as3_generator::generate_reflection_setters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + (void)type; + (void)cap_name; + indent(out) << "case " << upcase_string(field_name) << ":" << endl; + indent_up(); + indent(out) << "if (value == null) {" << endl; + indent(out) << " unset" << get_cap_name(field_name) << "();" << endl; + indent(out) << "} else {" << endl; + indent(out) << " this." << field_name << " = value;" << endl; + indent(out) << "}" << endl; + indent(out) << "break;" << endl << endl; + + indent_down(); +} + +void t_as3_generator::generate_generic_field_getters_setters(std::ofstream& 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; + t_type* type = get_true_type(field->get_type()); + std::string field_name = field->get_name(); + std::string cap_name = get_cap_name(field_name); + + indent_up(); + generate_reflection_setters(setter_stream, type, field_name, cap_name); + generate_reflection_getters(getter_stream, type, field_name, cap_name); + indent_down(); + } + + // create the setter + indent(out) << "public function setFieldValue(fieldID:int, value:*):void {" << endl; + indent_up(); + + indent(out) << "switch (fieldID) {" << endl; + + out << setter_stream.str(); + + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; + + // create the getter + indent(out) << "public function getFieldValue(fieldID:int):* {" << endl; + indent_up(); + + indent(out) << "switch (fieldID) {" << endl; + + out << getter_stream.str(); + + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + + indent(out) << "}" << endl; + + indent_down(); + + indent(out) << "}" << endl << endl; +} + +// Creates a generic isSet method that takes the field number as argument +void t_as3_generator::generate_generic_isset_method(std::ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // create the isSet method + indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a " + "value) and false otherwise" << endl; + indent(out) << "public function isSet(fieldID:int):Boolean {" << endl; + indent_up(); + indent(out) << "switch (fieldID) {" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << "return " << generate_isset_check(field) << ";" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a set of As3 Bean boilerplate functions (setters, getters, etc.) + * for the given struct. + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_bean_boilerplate(ofstream& out, + t_struct* tstruct, + bool bindable) { + 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 = get_cap_name(field_name); + + // Simple getter + generate_as3_doc(out, field); + indent(out) << "public function get " << field_name << "():" << type_name(type) << " {" << endl; + indent_up(); + indent(out) << "return this._" << field_name << ";" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + // Simple setter + generate_as3_doc(out, field); + std::string propName = tmp("thriftPropertyChange"); + if (bindable) { + indent(out) << "[Bindable(event=\"" << propName << "\")]" << endl; + } + indent(out) << "public function set " << field_name << "(" << field_name << ":" + << type_name(type) << "):void {" << endl; + indent_up(); + indent(out) << "this._" << field_name << " = " << field_name << ";" << endl; + generate_isset_set(out, field); + + if (bindable) { + // We have to use a custom event rather than the default, because if you use the default, + // the setter only gets called if the value has changed - this means calling + // foo.setIntValue(0) + // will not cause foo.isIntValueSet() to return true since the value of foo._intValue wasn't + // changed + // so the setter was never called. + indent(out) << "dispatchEvent(new Event(\"" << propName << "\"));" << endl; + + // However, if you just use a custom event, then collections won't be able to detect when + // elements + // in the collections have changed since they listed for PropertyChangeEvents. So, we + // dispatch both. + indent(out) << "dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE));" + << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + // Unsetter + indent(out) << "public function unset" << cap_name << "():void {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "this." << field_name << " = null;" << endl; + } else { + indent(out) << "this.__isset_" << field_name << " = false;" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + // isSet method + indent(out) << "// Returns true if field " << field_name + << " is set (has been assigned a value) and false otherwise" << endl; + indent(out) << "public function is" << get_cap_name("set") << cap_name << "():Boolean {" + << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "return this." << field_name << " != null;" << endl; + } else { + indent(out) << "return this.__isset_" << field_name << ";" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + } +} + +/** + * Generates a toString() method for the given struct + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_struct_tostring(ofstream& out, + t_struct* tstruct, + bool bindable) { + // If it's bindable, it extends EventDispatcher so toString is an override. + out << indent() << "public " << (bindable ? "override " : "") << "function toString():String {" + << endl; + indent_up(); + + out << indent() << "var ret:String = new String(\"" << tstruct->get_name() << "(\");" << endl; + out << indent() << "var first:Boolean = true;" << endl << 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) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + + t_field* field = (*f_iter); + + if (!first) { + indent(out) << "if (!first) ret += \", \";" << endl; + } + indent(out) << "ret += \"" << (*f_iter)->get_name() << ":\";" << endl; + bool can_be_null = type_can_be_null(field->get_type()); + if (can_be_null) { + indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) << " ret += \"null\";" << endl; + indent(out) << "} else {" << endl; + indent_up(); + } + + if (field->get_type()->is_base_type() && ((t_base_type*)(field->get_type()))->is_binary()) { + indent(out) << " ret += \"BINARY\";" << endl; + } else if (field->get_type()->is_enum()) { + indent(out) << "var " << field->get_name() + << "_name:String = " << get_enum_class_name(field->get_type()) + << ".VALUES_TO_NAMES[this." << (*f_iter)->get_name() << "];" << endl; + indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; + indent(out) << " ret += " << field->get_name() << "_name;" << endl; + indent(out) << " ret += \" (\";" << endl; + indent(out) << "}" << endl; + indent(out) << "ret += this." << field->get_name() << ";" << endl; + indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; + indent(out) << " ret += \")\";" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "ret += this." << (*f_iter)->get_name() << ";" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + indent(out) << "first = false;" << endl; + + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + first = false; + } + out << indent() << "ret += \")\";" << endl << indent() << "return ret;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a static map with meta data to store information such as fieldID to + * fieldName mapping + * + * @param tstruct The struct definition + */ +void t_as3_generator::generate_as3_meta_data_map(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Static Map with fieldID -> FieldMetaData mappings + indent(out) << "public static const metaDataMap:Dictionary = new Dictionary();" << endl; + + if (fields.size() > 0) { + // Populate map + scope_up(out); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + std::string field_name = field->get_name(); + indent(out) << "metaDataMap[" << upcase_string(field_name) << "] = new FieldMetaData(\"" + << field_name << "\", "; + + // Set field requirement type (required, optional, etc.) + if (field->get_req() == t_field::T_REQUIRED) { + out << "TFieldRequirementType.REQUIRED, "; + } else if (field->get_req() == t_field::T_OPTIONAL) { + out << "TFieldRequirementType.OPTIONAL, "; + } else { + out << "TFieldRequirementType.DEFAULT, "; + } + + // Create value meta data + generate_field_value_meta_data(out, field->get_type()); + out << ");" << endl; + } + scope_down(out); + } +} + +/** + * Returns a string with the as3 representation of the given thrift type + * (e.g. for the type struct it returns "TType.STRUCT") + */ +std::string t_as3_generator::get_as3_type_string(t_type* type) { + if (type->is_list()) { + return "TType.LIST"; + } else if (type->is_map()) { + return "TType.MAP"; + } else if (type->is_set()) { + return "TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.STRUCT"; + } else if (type->is_enum()) { + return "TType.I32"; + } else if (type->is_typedef()) { + return get_as3_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID: + return "TType.VOID"; + break; + case t_base_type::TYPE_STRING: + return "TType.STRING"; + break; + case t_base_type::TYPE_BOOL: + return "TType.BOOL"; + break; + case t_base_type::TYPE_I8: + return "TType.BYTE"; + break; + case t_base_type::TYPE_I16: + return "TType.I16"; + break; + case t_base_type::TYPE_I32: + return "TType.I32"; + break; + case t_base_type::TYPE_I64: + return "TType.I64"; + break; + case t_base_type::TYPE_DOUBLE: + return "TType.DOUBLE"; + break; + default: + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_as3_generator::get_as3_type_string!"); + break; // This should never happen! + } + } else { + throw std::runtime_error( + "Unknown thrift type \"" + type->get_name() + + "\" passed to t_as3_generator::get_as3_type_string!"); // This should never happen! + } +} + +void t_as3_generator::generate_field_value_meta_data(std::ofstream& out, t_type* type) { + out << endl; + indent_up(); + indent_up(); + if (type->is_struct() || type->is_xception()) { + indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type); + } else if (type->is_container()) { + if (type->is_list()) { + indent(out) << "new ListMetaData(TType.LIST, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else if (type->is_set()) { + indent(out) << "new SetMetaData(TType.SET, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else { // map + indent(out) << "new MapMetaData(TType.MAP, "; + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + generate_field_value_meta_data(out, key_type); + out << ", "; + generate_field_value_meta_data(out, val_type); + } + } else { + indent(out) << "new FieldValueMetaData(" << get_as3_type_string(type); + } + out << ")"; + indent_down(); + indent_down(); +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_as3_generator::generate_service(t_service* tservice) { + // Make interface file + string f_service_name = package_dir_ + "/" + service_name_ + ".as"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << as3_package(); + + scope_up(f_service_); + + f_service_ << endl << as3_type_imports() << as3_thrift_imports() + << as3_thrift_gen_imports(tservice); + + if (tservice->get_extends() != NULL) { + t_type* parent = tservice->get_extends(); + string parent_namespace = parent->get_program()->get_namespace("as3"); + if (!parent_namespace.empty() && parent_namespace != package_name_) { + f_service_ << "import " << type_name(parent) << ";" << endl; + } + } + + f_service_ << endl; + + generate_service_interface(tservice); + + scope_down(f_service_); + f_service_.close(); + + // Now make the implementation/client file + f_service_name = package_dir_ + "/" + service_name_ + "Impl.as"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << as3_package(); + + scope_up(f_service_); + + f_service_ << endl << as3_type_imports() << as3_thrift_imports() + << as3_thrift_gen_imports(tservice); + + if (tservice->get_extends() != NULL) { + t_type* parent = tservice->get_extends(); + string parent_namespace = parent->get_program()->get_namespace("as3"); + if (!parent_namespace.empty() && parent_namespace != package_name_) { + f_service_ << "import " << type_name(parent) << "Impl;" << endl; + } + } + + f_service_ << endl; + + generate_service_client(tservice); + scope_down(f_service_); + + f_service_ << as3_type_imports(); + f_service_ << as3_thrift_imports(); + f_service_ << as3_thrift_gen_imports(tservice); + if (!package_name_.empty()) { + f_service_ << "import " << package_name_ << ".*;" << endl; + } + + generate_service_helpers(tservice); + + f_service_.close(); + + // Now make the processor/server file + f_service_name = package_dir_ + "/" + service_name_ + "Processor.as"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << as3_package(); + + scope_up(f_service_); + + f_service_ << endl << as3_type_imports() << as3_thrift_imports() + << as3_thrift_gen_imports(tservice) << endl; + + generate_service_server(tservice); + scope_down(f_service_); + + f_service_ << as3_type_imports(); + f_service_ << as3_thrift_imports(); + f_service_ << as3_thrift_gen_imports(tservice) << endl; + if (!package_name_.empty()) { + f_service_ << "import " << package_name_ << ".*;" << endl; + } + + generate_service_helpers(tservice); + + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_as3_generator::generate_service_interface(t_service* tservice) { + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends_iface = " extends " + tservice->get_extends()->get_name(); + } + + generate_as3_doc(f_service_, tservice); + f_service_ << indent() << "public interface " << service_name_ << extends_iface << " {" << endl + << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_as3_doc(f_service_, *f_iter); + if (!(*f_iter)->is_oneway()) { + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "//function onError(Error):void;" << endl; + indent(f_service_) << "//function onSuccess():void;" << endl; + } else { + indent(f_service_) << "//function onError(Error):void;" << endl; + indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype()) + << "):void;" << endl; + } + } + indent(f_service_) << function_signature(*f_iter) << ";" << endl << endl; + } + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Generates structs for all the service args and return types + * + * @param tservice The service + */ +void t_as3_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_as3_struct_definition(f_service_, ts, false, true); + generate_function_helpers(*f_iter); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_as3_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = tservice->get_extends()->get_name(); + extends_client = " extends " + extends + "Impl"; + } + + indent(f_service_) << "public class " << service_name_ << "Impl" << extends_client + << " implements " << service_name_ << " {" << endl; + indent_up(); + + indent(f_service_) << "public function " << service_name_ << "Impl" + << "(iprot:TProtocol, oprot:TProtocol=null)" << endl; + scope_up(f_service_); + if (extends.empty()) { + f_service_ << indent() << "iprot_ = iprot;" << endl; + f_service_ << indent() << "if (oprot == null) {" << endl; + indent_up(); + f_service_ << indent() << "oprot_ = iprot;" << endl; + indent_down(); + f_service_ << indent() << "} else {" << endl; + indent_up(); + f_service_ << indent() << "oprot_ = oprot;" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << indent() << "super(iprot, oprot);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + if (extends.empty()) { + f_service_ << indent() << "protected var iprot_:TProtocol;" << endl << indent() + << "protected var oprot_:TProtocol;" << endl << endl << indent() + << "protected var seqid_:int;" << endl << endl; + + indent(f_service_) << "public function getInputProtocol():TProtocol" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.iprot_;" << endl; + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << "public function getOutputProtocol():TProtocol" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.oprot_;" << endl; + scope_down(f_service_); + f_service_ << 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) { + string funname = (*f_iter)->get_name(); + + // Open function + if (!(*f_iter)->is_oneway()) { + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "//function onError(Error):void;" << endl; + indent(f_service_) << "//function onSuccess():void;" << endl; + } else { + indent(f_service_) << "//function onError(Error):void;" << endl; + indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype()) + << "):void;" << endl; + } + } + indent(f_service_) << "public " << function_signature(*f_iter) << endl; + scope_up(f_service_); + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + string argsname = (*f_iter)->get_name() + "_args"; + vector::const_iterator fld_iter; + const vector& fields = arg_struct->get_members(); + + // Serialize the request + f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", " + << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") + << ", seqid_));" << endl << indent() << "var args:" << argsname << " = new " + << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = " + << (*fld_iter)->get_name() << ";" << endl; + } + + f_service_ << indent() << "args.write(oprot_);" << endl << indent() + << "oprot_.writeMessageEnd();" << endl; + + if ((*f_iter)->is_oneway()) { + f_service_ << indent() << "oprot_.getTransport().flush();" << endl; + } else { + f_service_ << indent() << "oprot_.getTransport().flush(function(error:Error):void {" << endl; + indent_up(); + f_service_ << indent() << "try {" << endl; + indent_up(); + string resultname = (*f_iter)->get_name() + "_result"; + f_service_ << indent() << "if (error != null) {" << endl << indent() + << " if (onError != null) onError(error);" << endl << indent() << " return;" + << endl << indent() << "}" << endl << indent() + << "var msg:TMessage = iprot_.readMessageBegin();" << endl << indent() + << "if (msg.type == TMessageType.EXCEPTION) {" << endl << indent() + << " var x:TApplicationError = TApplicationError.read(iprot_);" << endl + << indent() << " iprot_.readMessageEnd();" << endl << indent() + << " if (onError != null) onError(x);" << endl << indent() << " return;" << endl + << indent() << "}" << endl << indent() << "var result :" << resultname << " = new " + << resultname << "();" << endl << indent() << "result.read(iprot_);" << endl + << indent() << "iprot_.readMessageEnd();" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl + << indent() << " if (onSuccess != null) onSuccess(result.success);" << endl + << indent() << " return;" << endl << indent() << "}" << endl; + } + + 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) { + f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl + << indent() << " if (onError != null) onError(result." << (*x_iter)->get_name() + << ");" << endl << indent() << " return;" << endl << indent() << "}" << endl; + } + + // If you get here it's an exception, unless a void function + if ((*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (onSuccess != null) onSuccess();" << endl << indent() + << "return;" << endl; + } else { + + f_service_ << indent() << "if (onError != null) onError(new " + "TApplicationError(TApplicationError.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\"));" << endl; + } + indent_down(); + f_service_ << indent() << "} catch (e:TError) {" << endl << indent() + << " if (onError != null) onError(e);" << endl << indent() << "}" << endl; + + indent_down(); + indent(f_service_) << "});" << endl; + } + // Close function + scope_down(f_service_); + f_service_ << endl; + } + + indent_down(); + indent(f_service_) << "}" << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_as3_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_processor = " extends " + extends + "Processor"; + } + + // Generate the header portion + indent(f_service_) << "public class " << service_name_ << "Processor" << extends_processor + << " implements TProcessor {" << endl; + indent_up(); + + indent(f_service_) << "public function " << service_name_ << "Processor(iface:" << service_name_ + << ")" << endl; + scope_up(f_service_); + if (!extends.empty()) { + f_service_ << indent() << "super(iface);" << endl; + } + f_service_ << indent() << "iface_ = iface;" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "PROCESS_MAP[\"" << (*f_iter)->get_name() + << "\"] = " << (*f_iter)->get_name() << "();" << endl; + } + + scope_down(f_service_); + f_service_ << endl; + + f_service_ << indent() << "private var iface_:" << service_name_ << ";" << endl; + + if (extends.empty()) { + f_service_ << indent() << "protected const PROCESS_MAP:Dictionary = new Dictionary();" << endl; + } + + f_service_ << endl; + + // Generate the server implementation + string override = ""; + if (tservice->get_extends() != NULL) { + override = "override "; + } + indent(f_service_) << override + << "public function process(iprot:TProtocol, oprot:TProtocol):Boolean" << endl; + scope_up(f_service_); + + f_service_ << indent() << "var msg:TMessage = iprot.readMessageBegin();" << endl; + + // TODO(mcslee): validate message, was the seqid etc. legit? + // AS- If all method is oneway: + // do you have an oprot? + // do you you need nullcheck? + f_service_ + << indent() << "var fn:Function = PROCESS_MAP[msg.name];" << endl << indent() + << "if (fn == null) {" << endl << indent() << " TProtocolUtil.skip(iprot, TType.STRUCT);" + << endl << indent() << " iprot.readMessageEnd();" << endl << indent() + << " var x:TApplicationError = new TApplicationError(TApplicationError.UNKNOWN_METHOD, " + "\"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.getTransport().flush();" << endl << indent() + << " return true;" << endl << indent() << "}" << endl << indent() + << "fn.call(this,msg.seqid, iprot, oprot);" << endl; + + f_service_ << indent() << "return true;" << endl; + + scope_down(f_service_); + f_service_ << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << "}" << endl << endl; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_as3_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_as3_struct_definition(f_service_, &result, false, true, true); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_as3_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open class + indent(f_service_) << "private function " << tfunction->get_name() << "():Function {" << endl; + indent_up(); + + // Open function + indent(f_service_) << "return function(seqid:int, iprot:TProtocol, oprot:TProtocol):void" << endl; + scope_up(f_service_); + + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; + + f_service_ << indent() << "var args:" << argsname << " = 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; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << "var result:" << resultname << " = new " << resultname << "();" + << endl; + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_ << indent() << "try {" << endl; + indent_up(); + } + + // Generate the function call + 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()) { + f_service_ << "iface_." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + } else { + f_service_ << "// sorry this operation is not supported yet" << endl; + f_service_ << indent() << "throw new Error(\"This is not yet supported\");" << endl; + } + + // Set isset on success field + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void() + && !type_can_be_null(tfunction->get_returntype())) { + f_service_ << indent() << "result.set" << get_cap_name("success") << get_cap_name("isSet") + << "(true);" << endl; + } + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + f_service_ << indent() << "}"; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << (*x_iter)->get_name() << ":" + << type_name((*x_iter)->get_type(), false, false) << ") {" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << ";" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << "}"; + } + } + f_service_ << " catch (th:Error) {" << endl; + indent_up(); + f_service_ << indent() << "trace(\"Internal error processing " << tfunction->get_name() + << "\", th);" << endl << indent() + << "var x:TApplicationError = new " + "TApplicationError(TApplicationError.INTERNAL_ERROR, \"Internal error processing " + << tfunction->get_name() << "\");" << endl << indent() + << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.EXCEPTION, seqid));" << endl << indent() << "x.write(oprot);" + << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() + << "oprot.getTransport().flush();" << endl << indent() << "return;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "return;" << endl; + scope_down(f_service_); + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; + return; + } + + f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);" + << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() + << "oprot.getTransport().flush();" << endl; + + // Close function + scope_down(f_service_); + f_service_ << endl; + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Deserializes a field of any type. + * + * @param tfield The field + * @param prefix The variable name or container for this field + */ +void t_as3_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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 << " = 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 (((t_base_type*)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 As3 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()); + } +} + +/** + * Generates an unserializer for a struct, invokes read() + */ +void t_as3_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() + << prefix << ".read(iprot);" << endl; +} + +/** + * Deserializes a container by reading its size and then iterating + */ +void t_as3_generator::generate_deserialize_container(ofstream& 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"); + } + + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "var " << obj << ":TMap = iprot.readMapBegin();" << endl; + } else if (ttype->is_set()) { + indent(out) << "var " << obj << ":TSet = iprot.readSetBegin();" << endl; + } else if (ttype->is_list()) { + indent(out) << "var " << obj << ":TList = iprot.readListBegin();" << endl; + } + + indent(out) << prefix << " = new " << type_name(ttype, false, true) + // size the collection correctly + << "(" + << ");" << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (var " << i << ":int = 0; " << i << " < " << obj << ".size" + << "; " + << "++" << 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); + + // Read container end + 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); +} + +/** + * Generates code to deserialize a map + */ +void t_as3_generator::generate_deserialize_map_element(ofstream& 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; +} + +/** + * Deserializes a set element + */ +void t_as3_generator::generate_deserialize_set_element(ofstream& 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; +} + +/** + * Deserializes a list element + */ +void t_as3_generator::generate_deserialize_list_element(ofstream& 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 << ".push(" << elem << ");" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_as3_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + tfield->get_name(); + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_as3_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + out << indent() << prefix << ".write(oprot);" << endl; +} + +/** + * Serializes a container by writing its size then the elements. + * + * @param ttype The type of container + * @param prefix String prefix for fields + */ +void t_as3_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + string iter = tmp("_key"); + string counter = tmp("_sizeCounter"); + indent(out) << "var " << counter << ":int = 0;" << endl; + indent(out) << "for (var " << iter << ":* in " << prefix << ") {" << endl; + indent(out) << " " << counter << +"++;" << endl; + indent(out) << "}" << endl; + + indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) + << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << counter << "));" + << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " << prefix << ".size));" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(new TList(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));" + << endl; + } + + string iter = tmp("elem"); + if (ttype->is_map()) { + indent(out) << "for (var " << iter << ":* in " << prefix << ")"; + } else if (ttype->is_set()) { + indent(out) << "for each (var " << iter << ":* in " << prefix << ".toArray())"; + } else if (ttype->is_list()) { + indent(out) << "for each (var " << iter << ":* in " << prefix << ")"; + } + + 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); +} + +/** + * Serializes the members of a map. + */ +void t_as3_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string iter, + string map) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, &kfield, ""); + t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_as3_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_as3_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Returns a As3 type name + * + * @param ttype The type + * @param container Is the type going inside a container? + * @return As3 type name, i.e. HashMap + */ +string t_as3_generator::type_name(t_type* ttype, bool in_container, bool in_init) { + (void)in_init; + // In As3 typedefs are just resolved to their real type + ttype = get_true_type(ttype); + string prefix; + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype, in_container); + } else if (ttype->is_enum()) { + return "int"; + } else if (ttype->is_map()) { + return "Dictionary"; + } else if (ttype->is_set()) { + return "Set"; + } else if (ttype->is_list()) { + return "Array"; + } + + // Check for namespacing + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + string package = program->get_namespace("as3"); + if (!package.empty()) { + return package + "." + ttype->get_name(); + } + } + + return ttype->get_name(); +} + +/** + * Returns the AS3 type that corresponds to the thrift type. + * + * @param tbase The base type + * @param container Is it going in a As3 container? + */ +string t_as3_generator::base_type_name(t_base_type* type, bool in_container) { + (void)in_container; + 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 "ByteArray"; + } else { + return "String"; + } + case t_base_type::TYPE_BOOL: + return "Boolean"; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + return "int"; + case t_base_type::TYPE_I64: + throw "i64 is not yet supported in as3"; + case t_base_type::TYPE_DOUBLE: + return "Number"; + default: + throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_as3_generator::declare_field(t_field* tfield, bool init) { + // TODO(mcslee): do we ever need to initialize the field? + 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) { + 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 += " = 0"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype, false, true) + "()"; + } else { + result += " = new " + type_name(ttype, false, true) + "()"; + ; + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_as3_generator::function_signature(t_function* tfunction, string prefix) { + std::string arguments = argument_list(tfunction->get_arglist()); + if (!tfunction->is_oneway()) { + if (arguments != "") { + arguments += ", "; + } + arguments += "onError:Function, onSuccess:Function"; + } + + std::string result = "function " + prefix + tfunction->get_name() + "(" + arguments + "):void"; + return result; +} + +/** + * Renders a comma separated field list, with type names + */ +string t_as3_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 += (*f_iter)->get_name() + ":" + type_name((*f_iter)->get_type()); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_as3_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 "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(); +} + +/** + * Applies the correct style to a string based on the value of nocamel_style_ + */ +std::string t_as3_generator::get_cap_name(std::string name) { + name[0] = toupper(name[0]); + return name; +} + +string t_as3_generator::constant_name(string name) { + string constant_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); + + bool is_upper = isupper(character); + + if (is_upper && !is_first && !was_previous_char_upper) { + constant_name += '_'; + } + constant_name += toupper(character); + + is_first = false; + was_previous_char_upper = is_upper; + } + + return constant_name; +} + +/** + * Emits a As3Doc comment if the provided object has a doc in Thrift + */ +void t_as3_generator::generate_as3_doc(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, "/**\n", " * ", tdoc->get_doc(), " */\n"); + } +} + +/** + * Emits a As3Doc comment if the provided function object has a doc in Thrift + */ +void t_as3_generator::generate_as3_doc(ofstream& out, t_function* tfunction) { + if (tfunction->has_doc()) { + stringstream ss; + ss << tfunction->get_doc(); + 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; + ss << "\n@param " << p->get_name(); + if (p->has_doc()) { + ss << " " << p->get_doc(); + } + } + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); + } +} + +std::string t_as3_generator::generate_isset_check(t_field* field) { + return generate_isset_check(field->get_name()); +} + +std::string t_as3_generator::generate_isset_check(std::string field_name) { + return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; +} + +void t_as3_generator::generate_isset_set(ofstream& out, t_field* field) { + if (!type_can_be_null(field->get_type())) { + indent(out) << "this.__isset_" << field->get_name() << " = true;" << endl; + } +} + +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_) { + package = program->get_namespace("as3") + "."; + } + return package + type->get_name(); +} + +THRIFT_REGISTER_GENERATOR( + as3, + "AS3", + " bindable: Add [bindable] metadata to all the struct classes.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc new file mode 100644 index 00000000..7d4e4f03 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_c_glib_generator.cc @@ -0,0 +1,4562 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/* forward declarations */ +string initial_caps_to_underscores(string name); +string underscores_to_initial_caps(string name); +string to_upper_case(string name); +string to_lower_case(string name); + +/** + * C code generator, using glib for C typing. + */ +class t_c_glib_generator : public t_oop_generator { +public: + /* constructor */ + t_c_glib_generator(t_program* program, + const map& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)option_string; + std::map::const_iterator iter; + + /* set the output directory */ + this->out_dir_base_ = "gen-c_glib"; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option c_glib:" + iter->first; + } + + /* set the namespace */ + this->nspace = program_->get_namespace("c_glib"); + + if (this->nspace.empty()) { + this->nspace = ""; + this->nspace_u = ""; + this->nspace_uc = ""; + this->nspace_lc = ""; + } else { + /* replace dots with underscores */ + char* tmp = strdup(this->nspace.c_str()); + for (unsigned int i = 0; i < strlen(tmp); i++) { + if (tmp[i] == '.') { + tmp[i] = '_'; + } + } + this->nspace = string(tmp, strlen(tmp)); + free(tmp); + + /* clean up the namespace for C. + * An input of 'namespace foo' should result in: + * - nspace = foo - for thrift objects and typedefs + * - nspace_u = Foo - for internal GObject prefixes + * - nspace_uc = FOO_ - for macro prefixes + * - nspace_lc = foo_ - for filename and method prefixes + * The underscores are there since uc and lc strings are used as file and + * variable prefixes. + */ + this->nspace_u = initial_caps_to_underscores(this->nspace); + this->nspace_uc = to_upper_case(this->nspace_u) + "_"; + this->nspace_lc = to_lower_case(this->nspace_u) + "_"; + } + } + + /* initialization and destruction */ + void init_generator(); + void close_generator(); + + /* 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); + +private: + /* file streams */ + ofstream f_types_; + ofstream f_types_impl_; + ofstream f_header_; + ofstream f_service_; + + /* namespace variables */ + string nspace; + string nspace_u; + string nspace_uc; + string nspace_lc; + + /* helper functions */ + bool is_complex_type(t_type* ttype); + bool is_numeric(t_type* ttype); + string type_name(t_type* ttype, bool in_typedef = false, bool is_const = false); + string property_type_name(t_type* ttype, bool in_typedef = false, bool is_const = false); + string base_type_name(t_type* type); + string type_to_enum(t_type* type); + string constant_literal(t_type* type, t_const_value* value); + string constant_value(string name, t_type* type, t_const_value* value); + string constant_value_with_storage(string name, t_type* type, t_const_value* value); + string function_signature(t_function* tfunction); + string argument_list(t_struct* tstruct); + string xception_list(t_struct* tstruct); + string declare_field(t_field* tfield, + bool init = false, + bool pointer = false, + bool constant = false, + bool reference = false); + void declare_local_variable(ofstream& out, t_type* ttype, string& base_name, bool for_hash_table); + void declore_local_variable_for_write(ofstream& out, t_type* ttype, string& base_name); + + /* generation functions */ + void generate_const_initializer(string name, + t_type* type, + t_const_value* value, + bool top_level = false); + void generate_service_helpers(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_handler(t_service* tservice); + void generate_service_processor(t_service* tservice); + void generate_service_server(t_service* tservice); + void generate_object(t_struct* tstruct); + void generate_struct_writer(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get = "", + bool is_function = true); + void generate_struct_reader(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get = "", + bool is_function = true); + + void generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret); + void generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix, int error_ret); + void generate_serialize_container(ofstream& out, t_type* ttype, string prefix, int error_ret); + void generate_serialize_map_element(ofstream& out, + t_map* tmap, + string key, + string value, + int error_ret); + void generate_serialize_set_element(ofstream& out, t_set* tset, string element, int error_ret); + void generate_serialize_list_element(ofstream& out, + t_list* tlist, + string list, + string index, + int error_ret); + + void generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret, + bool allocate = true); + void generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + int error_ret, + bool allocate = true); + void generate_deserialize_container(ofstream& out, t_type* ttype, string prefix, int error_ret); + void generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix, int error_ret); + void generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix, int error_ret); + void generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix, + string index, + int error_ret); + + string generate_new_hash_from_type(t_type* key, t_type* value); + string generate_new_array_from_type(t_type* ttype); + + string generate_free_func_from_type(t_type* ttype); + string generate_hash_func_from_type(t_type* ttype); + string generate_cmp_func_from_type(t_type* ttype); +}; + +/** + * Prepare for file generation by opening up the necessary file + * output streams. + */ +void t_c_glib_generator::init_generator() { + /* create output directory */ + MKDIR(get_out_dir().c_str()); + + string program_name_u = initial_caps_to_underscores(program_name_); + string program_name_uc = to_upper_case(program_name_u); + string program_name_lc = to_lower_case(program_name_u); + + /* create output files */ + string f_types_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.h"; + f_types_.open(f_types_name.c_str()); + string f_types_impl_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.c"; + f_types_impl_.open(f_types_impl_name.c_str()); + + /* add thrift boilerplate headers */ + f_types_ << autogen_comment(); + f_types_impl_ << autogen_comment(); + + /* include inclusion guard */ + f_types_ << "#ifndef " << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << "#define " + << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << endl; + + /* include base types */ + f_types_ << "/* base includes */" << endl << "#include " << endl + << "#include " << endl + << "#include " << endl; + + /* include other thrift includes */ + const vector& includes = program_->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + f_types_ << "/* other thrift includes */" << endl << "#include \"" << this->nspace_lc + << initial_caps_to_underscores(includes[i]->get_name()) << "_types.h\"" << endl; + } + f_types_ << endl; + + /* 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; + } else { + f_types_ << "#include \"" << c_includes[i] << "\"" << endl; + } + } + f_types_ << endl; + + /* include math.h (for "INFINITY") in the implementation file, in case we + encounter a struct with a member of type double */ + f_types_impl_ << endl << "#include " << endl; + + // include the types file + f_types_impl_ << endl << "#include \"" << this->nspace_lc << program_name_u << "_types.h\"" + << endl << "#include " << endl << endl; + + f_types_ << "/* begin types */" << endl << endl; +} + +/** + * Finish up generation and close all file streams. + */ +void t_c_glib_generator::close_generator() { + string program_name_uc = to_upper_case(initial_caps_to_underscores(program_name_)); + + /* end the header inclusion guard */ + f_types_ << "#endif /* " << this->nspace_uc << program_name_uc << "_TYPES_H */" << endl; + + /* close output file */ + f_types_.close(); + f_types_impl_.close(); +} + +/** + * Generates a Thrift typedef in C code. For example: + * + * Thrift: + * typedef map SomeMap + * + * C: + * typedef GHashTable * ThriftSomeMap; + */ +void t_c_glib_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " << this->nspace + << ttypedef->get_symbolic() << ";" << endl << endl; +} + +/** + * Generates a C enumeration. For example: + * + * Thrift: + * enum MyEnum { + * ONE = 1, + * TWO + * } + * + * C: + * enum _ThriftMyEnum { + * THRIFT_MY_ENUM_ONE = 1, + * THRIFT_MY_ENUM_TWO + * }; + * typedef enum _ThriftMyEnum ThriftMyEnum; + */ +void t_c_glib_generator::generate_enum(t_enum* tenum) { + string name = tenum->get_name(); + string name_uc = to_upper_case(initial_caps_to_underscores(name)); + + f_types_ << indent() << "enum _" << this->nspace << name << " {" << endl; + + indent_up(); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + bool first = true; + + /* output each of the enumeration elements */ + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if (first) { + first = false; + } else { + f_types_ << "," << endl; + } + + f_types_ << indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name(); + f_types_ << " = " << (*c_iter)->get_value(); + } + + indent_down(); + f_types_ << endl << "};" << endl << "typedef enum _" << this->nspace << name << " " + << this->nspace << name << ";" << endl << endl; + + f_types_ << "/* return the name of the constant */" << endl; + f_types_ << "const char *" << endl; + f_types_ << "toString_" << name << "(int value); " << endl << endl; + ; + f_types_impl_ << "/* return the name of the constant */" << endl; + f_types_impl_ << "const char *" << endl; + f_types_impl_ << "toString_" << name << "(int value) " << endl; + f_types_impl_ << "{" << endl; + f_types_impl_ << " static __thread char buf[16] = {0};" << endl; + f_types_impl_ << " switch(value) {" << endl; + std::set done; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + // Skipping duplicate value + if (done.find(value) == done.end()) { + done.insert(value); + f_types_impl_ << " case " << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() + << ":" + << "return \"" << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() + << "\";" << endl; + } + } + f_types_impl_ << " default: g_snprintf(buf, 16, \"%d\", value); return buf;" << endl; + f_types_impl_ << " }" << endl; + f_types_impl_ << "}" << endl << endl; +} + +/** + * Generates Thrift constants in C code. + */ +void t_c_glib_generator::generate_consts(vector consts) { + f_types_ << "/* constants */" << endl; + f_types_impl_ << "/* constants */" << endl; + + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + string name_uc = to_upper_case(name); + string name_lc = to_lower_case(name); + t_type* type = (*c_iter)->get_type(); + t_const_value* value = (*c_iter)->get_value(); + + if (is_complex_type(type)) { + f_types_ << type_name(type) << indent() << this->nspace_lc << name_lc + << "_constant();" << endl; + } + + f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " " + << constant_value(name_lc, type, value) << endl; + + generate_const_initializer(name_lc, type, value, true); + } + + f_types_ << endl; + f_types_impl_ << endl; +} + +/** + * Generate Thrift structs in C code, as GObjects. Example: + * + * Thrift: + * struct Bonk + * { + * 1: string message, + * 2: i32 type + * } + * + * C GObject instance header: + * struct _ThriftBonk + * { + * GObject parent; + * + * gchar * message; + * gint32 type; + * }; + * typedef struct _ThriftBonk ThriftBonk + * // ... additional GObject boilerplate ... + */ +void t_c_glib_generator::generate_struct(t_struct* tstruct) { + f_types_ << "/* struct " << tstruct->get_name() << " */" << endl; + generate_object(tstruct); +} + +/** + * Generate C code to represent Thrift services. Creates a new GObject + * which can be used to access the service. + */ +void t_c_glib_generator::generate_service(t_service* tservice) { + string svcname_u = initial_caps_to_underscores(tservice->get_name()); + string svcname_uc = this->nspace_uc + to_upper_case(svcname_u); + string filename = this->nspace_lc + to_lower_case(svcname_u); + + // make output files + string f_header_name = get_out_dir() + filename + ".h"; + f_header_.open(f_header_name.c_str()); + + string program_name_u = initial_caps_to_underscores(program_name_); + string program_name_lc = to_lower_case(program_name_u); + + // add header file boilerplate + f_header_ << autogen_comment(); + + // add an inclusion guard + f_header_ << "#ifndef " << svcname_uc << "_H" << endl << "#define " << svcname_uc << "_H" << endl + << endl; + + // add standard includes + f_header_ << "#include " << endl << endl; + f_header_ << "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << endl; + + // if we are inheriting from another service, include its header + t_service* extends_service = tservice->get_extends(); + if (extends_service != NULL) { + f_header_ << "#include \"" << this->nspace_lc + << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\"" + << endl; + } + f_header_ << endl; + + // create the service implementation + string f_service_name = get_out_dir() + filename + ".c"; + f_service_.open(f_service_name.c_str()); + + // add the boilerplace header + f_service_ << autogen_comment(); + + // include the headers + f_service_ << "#include " << endl << "#include " << endl + << "#include " << endl << "#include \"" + << filename << ".h\"" << endl << endl; + + // generate the service-helper classes + generate_service_helpers(tservice); + + // generate the client objects + generate_service_client(tservice); + + // generate the server objects + generate_service_server(tservice); + + // end the header inclusion guard + f_header_ << "#endif /* " << svcname_uc << "_H */" << endl; + + // close the files + f_service_.close(); + f_header_.close(); +} + +/** + * + */ +void t_c_glib_generator::generate_xception(t_struct* tstruct) { + string name = tstruct->get_name(); + string name_u = initial_caps_to_underscores(name); + string name_lc = to_lower_case(name_u); + string name_uc = to_upper_case(name_u); + + generate_object(tstruct); + + f_types_ << "/* exception */" << endl + << "typedef enum" << endl + << "{" << endl; + indent_up(); + f_types_ << indent() << this->nspace_uc << name_uc << "_ERROR_CODE" << endl; + indent_down(); + f_types_ << "} " << this->nspace << name << "Error;" << endl + << endl + << "GQuark " << this->nspace_lc << name_lc + << "_error_quark (void);" << endl + << "#define " << this->nspace_uc << name_uc << "_ERROR (" + << this->nspace_lc << name_lc << "_error_quark())" << endl + << endl + << endl; + + f_types_impl_ << "/* define the GError domain for exceptions */" << endl << "#define " + << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << this->nspace_lc << name_lc + << "_error_quark\"" << endl << "GQuark" << endl << this->nspace_lc << name_lc + << "_error_quark (void)" << endl << "{" << endl + << " return g_quark_from_static_string (" << this->nspace_uc << name_uc + << "_ERROR_DOMAIN);" << endl << "}" << endl << endl; +} + +/******************** + * HELPER FUNCTIONS * + ********************/ + +/** + * Returns true if ttype is not a primitive. + */ +bool t_c_glib_generator::is_complex_type(t_type* ttype) { + ttype = get_true_type(ttype); + + return ttype->is_container() || ttype->is_struct() || ttype->is_xception(); +} + +bool t_c_glib_generator::is_numeric(t_type* ttype) { + return ttype->is_enum() || (ttype->is_base_type() && !ttype->is_string()); +} + +/** + * Maps a Thrift t_type to a C type. + */ +string t_c_glib_generator::type_name(t_type* ttype, bool in_typedef, bool is_const) { + if (ttype->is_base_type()) { + string bname = base_type_name(ttype); + + if (is_const) { + return "const " + bname; + } else { + return bname; + } + } + + if (ttype->is_container()) { + string cname; + + t_container* tcontainer = (t_container*)ttype; + if (tcontainer->has_cpp_name()) { + cname = tcontainer->get_cpp_name(); + } else if (ttype->is_map()) { + cname = "GHashTable"; + } else if (ttype->is_set()) { + // since a set requires unique elements, use a GHashTable, and + // populate the keys and values with the same data, using keys for + // the actual writes and reads. + // TODO: discuss whether or not to implement TSet, THashSet or GHashSet + cname = "GHashTable"; + } else if (ttype->is_list()) { + t_type* etype = ((t_list*)ttype)->get_elem_type(); + if (etype->is_void()) { + throw std::runtime_error("compiler error: list element type cannot be void"); + } + // TODO: investigate other implementations besides GPtrArray + cname = is_numeric(etype) ? "GArray" : "GPtrArray"; + } + + /* Omit the dereference operator if we are aliasing this type within a + typedef, to allow the type to be used more naturally in client code; + otherwise, include it */ + if (!in_typedef) { + cname += " *"; + } + + if (is_const) { + return "const " + cname; + } else { + return cname; + } + } + + // check for a namespace + string pname = this->nspace + ttype->get_name(); + + if (is_complex_type(ttype)) { + pname += " *"; + } + + if (is_const) { + return "const " + pname; + } else { + return pname; + } +} + +/** + * Maps a Thrift primitive to the type needed to hold its value when used as an + * object property. + * + * This method is needed because all integer properties of width less than 64 + * bits map to the same type, gint, as opposed to their width-specific type + * (gint8, gint16 or gint32). + */ +string t_c_glib_generator::property_type_name(t_type* ttype, bool in_typedef, bool is_const) { + string result; + + if (ttype->is_base_type()) { + switch (((t_base_type*)ttype)->get_base()) { + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + if (is_const) { + result = "const gint"; + } else { + result = "gint"; + } + break; + + default: + result = type_name(ttype, in_typedef, is_const); + } + } else { + result = type_name(ttype, in_typedef, is_const); + } + + return result; +} + +/** + * Maps a Thrift primitive to a C primitive. + */ +string t_c_glib_generator::base_type_name(t_type* type) { + if (type->is_enum()) { + return type_name(type); + } + if (!type->is_base_type()) { + throw std::invalid_argument("Only base types are suppported."); + } + t_base_type* base_type = reinterpret_cast(type); + t_base_type::t_base tbase = base_type->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + if (base_type->is_binary()) { + return "GByteArray *"; + } else { + return "gchar *"; + } + case t_base_type::TYPE_BOOL: + return "gboolean"; + case t_base_type::TYPE_I8: + return "gint8"; + case t_base_type::TYPE_I16: + return "gint16"; + case t_base_type::TYPE_I32: + return "gint32"; + case t_base_type::TYPE_I64: + return "gint64"; + case t_base_type::TYPE_DOUBLE: + return "gdouble"; + default: + throw std::logic_error("compiler error: no C base type name for base type " + + t_base_type::t_base_name(tbase)); + } +} + +/** + * Returns a member of the ThriftType C enumeration in thrift_protocol.h + * for a Thrift type. + */ +string t_c_glib_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 "T_STRING"; + case t_base_type::TYPE_BOOL: + return "T_BOOL"; + case t_base_type::TYPE_I8: + return "T_BYTE"; + case t_base_type::TYPE_I16: + return "T_I16"; + case t_base_type::TYPE_I32: + return "T_I32"; + case t_base_type::TYPE_I64: + return "T_I64"; + case t_base_type::TYPE_DOUBLE: + return "T_DOUBLE"; + } + } else if (type->is_enum()) { + return "T_I32"; + } else if (type->is_struct()) { + return "T_STRUCT"; + } else if (type->is_xception()) { + return "T_STRUCT"; + } else if (type->is_map()) { + return "T_MAP"; + } else if (type->is_set()) { + return "T_SET"; + } else if (type->is_list()) { + return "T_LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Returns a Thrift constant formatted as a literal for inclusion in C code. + */ +string t_c_glib_generator::constant_literal(t_type* type, t_const_value* value) { + ostringstream render; + + if (type->is_base_type()) { + /* primitives */ + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + + switch (tbase) { + case t_base_type::TYPE_STRING: + render << "\"" + value->get_string() + "\""; + 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: + render << value->get_double(); + break; + default: + throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); + } + } else { + t_const_value::t_const_value_type value_type = value->get_type(); + + switch (value_type) { + case t_const_value::CV_IDENTIFIER: + render << value->get_integer(); + break; + case t_const_value::CV_LIST: + render << "{ "; + { + t_type* elem_type = ((t_list*)type)->get_elem_type(); + const vector& list = value->get_list(); + vector::const_iterator list_iter; + + if (list.size() > 0) { + list_iter = list.begin(); + render << constant_literal(elem_type, *list_iter); + + while (++list_iter != list.end()) { + render << ", " << constant_literal(elem_type, *list_iter); + } + } + } + render << " }"; + break; + case t_const_value::CV_MAP: + default: + render << "NULL /* not supported */"; + } + } + + return render.str(); +} + +/** + * Returns C code that represents a Thrift constant. + */ +string t_c_glib_generator::constant_value(string name, t_type* type, t_const_value* value) { + ostringstream render; + + if (type->is_base_type()) { + /* primitives */ + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + render << "g_strdup (\"" + value->get_string() + "\")"; + break; + case t_base_type::TYPE_BOOL: + render << ((value->get_integer() != 0) ? 1 : 0); + break; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << "G_GINT64_CONSTANT (" << 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_name(type) << ")" << value->get_integer(); + } else if (is_complex_type(type)) { + render << "(" << this->nspace_lc << to_lower_case(name) << "_constant())"; + } else { + render << "NULL /* not supported */"; + } + + return render.str(); +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_c_glib_generator::function_signature(t_function* tfunction) { + t_type* ttype = tfunction->get_returntype(); + t_struct* arglist = tfunction->get_arglist(); + t_struct* xlist = tfunction->get_xceptions(); + string fname = initial_caps_to_underscores(tfunction->get_name()); + + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + return "gboolean " + this->nspace_lc + fname + " (" + this->nspace + service_name_ + "If * iface" + + (has_return ? ", " + type_name(ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arglist))) + + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError ** error)"; +} + +/** + * Renders a field list + * + * @param tstruct The struct definition + * @return Comma sepearated list of all field names in that struct + */ +string t_c_glib_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(), false, true) + " " + (*f_iter)->get_name(); + } + return result; +} + +/** + * Renders mutable exception lists + * + * @param tstruct The struct definition + * @return Comma sepearated list of all field names in that struct + */ +string t_c_glib_generator::xception_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(), false, false) + "* " + (*f_iter)->get_name(); + } + return result; +} + +/** + * Declares a field, including any necessary initialization. + */ +string t_c_glib_generator::declare_field(t_field* tfield, + bool init, + bool pointer, + bool constant, + bool reference) { + string result = ""; + if (constant) { + result += "const "; + } + result += type_name(tfield->get_type()); + if (pointer) { + result += "*"; + } + if (reference) { + result += "*"; + } + result += " " + tfield->get_name(); + if (init) { + t_type* type = get_true_type(tfield->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: + 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: + result += " = 0"; + break; + case t_base_type::TYPE_DOUBLE: + result += " = (gdouble) 0"; + break; + case t_base_type::TYPE_STRING: + result += " = NULL"; + break; + default: + throw "compiler error: no C intializer for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + result += " = (" + type_name(type) + ") 0"; + } else if (type->is_struct() || type->is_container()) { + result += " = NULL"; + } + } + + if (!reference) { + result += ";"; + } + + return result; +} + +string t_c_glib_generator::constant_value_with_storage(string fname, + t_type* etype, + t_const_value* value) { + ostringstream render; + if (is_numeric(etype)) { + render << " " << type_name(etype) << " *" << fname << " = " + << "g_new (" << base_type_name(etype) << ", 1);" << endl + << " *" << fname << " = " << constant_value(fname, (t_type*)etype, value) << ";" + << endl; + } else { + render << " " << type_name(etype) << " " << fname << " = " + << constant_value(fname, (t_type*)etype, value) << ";" << endl; + } + return render.str(); +} + +/** + * Generates C code that initializes complex constants. + */ +void t_c_glib_generator::generate_const_initializer(string name, + t_type* type, + t_const_value* value, + bool top_level) { + string name_u = initial_caps_to_underscores(name); + string name_lc = to_lower_case(name_u); + string type_u = initial_caps_to_underscores(type->get_name()); + string type_uc = to_upper_case(type_u); + string maybe_static = top_level ? "" : "static "; + + 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; + ostringstream initializers; + + // 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; + string field_name = ""; + + 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(); + field_name = (*f_iter)->get_name(); + break; + } + } + if (field_type == NULL) { + throw "type error: " + type->get_name() + " has no field " + + v_iter->first->get_string(); + } + field_name = tmp(field_name); + + generate_const_initializer(name + "_constant_" + field_name, + field_type, + v_iter->second); + initializers << " constant->" << v_iter->first->get_string() << " = " + << constant_value(name + "_constant_" + field_name, + field_type, + v_iter->second) << ";" << endl + << " constant->__isset_" << v_iter->first->get_string() + << " = TRUE;" << endl; + } + + // implement the initializer + f_types_impl_ << maybe_static << this->nspace << type->get_name() << " *" + << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static " << this->nspace << type->get_name() + << " *constant = NULL;" << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "constant = g_object_new (" << this->nspace_uc + << "TYPE_" << type_uc << ", NULL);" << endl + << initializers.str(); + scope_down(f_types_impl_); + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = NULL; + string field_name = ""; + + 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(); + field_name = (*f_iter)->get_name(); + break; + } + } + if (field_type == NULL) { + throw "type error: " + type->get_name() + " has no field " + + v_iter->first->get_string(); + } + field_name = tmp(field_name); + } + + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } else if (type->is_list()) { + string list_type = "GPtrArray *"; + string free_func + = generate_free_func_from_type(reinterpret_cast(type)->get_elem_type()); + string list_initializer = "g_ptr_array_new_with_free_func (" + free_func + ");"; + string list_appender = "g_ptr_array_add"; + bool list_variable = false; + + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + ostringstream initializers; + ostringstream appenders; + + list_initializer = generate_new_array_from_type(etype); + if (etype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + 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: + list_type = "GArray *"; + list_appender = "g_array_append_val"; + list_variable = true; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } else if (etype->is_enum()) { + list_type = "GArray *"; + list_appender = "g_array_append_val"; + list_variable = true; + } + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string fname = tmp(name); + + generate_const_initializer(fname, etype, (*v_iter)); + if (list_variable) { + initializers << " " << type_name(etype) << " " << fname << " = " + << constant_value(fname, (t_type*)etype, (*v_iter)) << ";" + << endl; + appenders << " " << list_appender << "(constant, " << fname << ");" + << endl; + } else { + appenders << " " << list_appender << "(constant, " + << constant_value(fname, (t_type*)etype, (*v_iter)) << ");" + << endl; + } + } + + f_types_impl_ << maybe_static << list_type << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static " << list_type << " constant = NULL;" + << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + if (!initializers.str().empty()) { + f_types_impl_ << initializers.str() + << endl; + } + f_types_impl_ << indent() << "constant = " << list_initializer << endl + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + ostringstream initializers; + ostringstream appenders; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string fname = tmp(name); + string ptr = is_numeric(etype) ? "*" : ""; + generate_const_initializer(fname, etype, (*v_iter)); + initializers << constant_value_with_storage(fname, (t_type*)etype, *v_iter); + appenders << " g_hash_table_insert (constant, " << fname << ", 0);" << endl; + } + + f_types_impl_ << maybe_static << "GHashTable *" << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl + << 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 + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } 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; + ostringstream initializers; + ostringstream appenders; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string fname = tmp(name); + string kname = fname + "key"; + string vname = fname + "val"; + generate_const_initializer(kname, ktype, v_iter->first); + generate_const_initializer(vname, vtype, v_iter->second); + + initializers << constant_value_with_storage(kname, (t_type*)ktype, v_iter->first); + initializers << constant_value_with_storage(vname, (t_type*)vtype, v_iter->second); + appenders << " g_hash_table_insert (constant, " << kname << ", " << vname << ");" << endl; + } + + f_types_impl_ << maybe_static << "GHashTable *" << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << initializers.str() << endl + << indent() << "constant = " << generate_new_hash_from_type(ktype, vtype) << endl + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } +} + +/** + * Generates helper classes for a service, consisting of a ThriftStruct subclass + * for the arguments to and the result from each method. + * + * @param tservice The service for which to generate helper classes + */ +void t_c_glib_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator function_iter; + + // Iterate through the service's methods + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string function_name = (*function_iter)->get_name(); + t_struct* arg_list = (*function_iter)->get_arglist(); + string arg_list_name_orig = arg_list->get_name(); + + // Generate the arguments class + arg_list->set_name(tservice->get_name() + underscores_to_initial_caps(function_name) + "Args"); + generate_struct(arg_list); + + arg_list->set_name(arg_list_name_orig); + + // Generate the result class + if (!(*function_iter)->is_oneway()) { + t_struct result(program_, + tservice->get_name() + underscores_to_initial_caps(function_name) + "Result"); + t_field success((*function_iter)->get_returntype(), "success", 0); + success.set_req(t_field::T_OPTIONAL); + if (!(*function_iter)->get_returntype()->is_void()) { + result.append(&success); + } + + t_struct* xs = (*function_iter)->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator field_iter; + for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) { + (*field_iter)->set_req(t_field::T_OPTIONAL); + result.append(*field_iter); + } + + generate_struct(&result); + } + } +} + +/** + * Generates C code that represents a Thrift service client. + */ +void t_c_glib_generator::generate_service_client(t_service* tservice) { + /* get some C friendly service names */ + string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); + string service_name_uc = to_upper_case(service_name_lc); + + string parent_service_name; + string parent_service_name_lc; + string parent_service_name_uc; + + string parent_class_name = "GObject"; + string parent_type_name = "G_TYPE_OBJECT"; + + // The service this service extends, or NULL if it extends no + // service + t_service* extends_service = tservice->get_extends(); + if (extends_service) { + // The name of the parent service + parent_service_name = extends_service->get_name(); + parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); + parent_service_name_uc = to_upper_case(parent_service_name_lc); + + // The names of the client class' parent class and type + parent_class_name = this->nspace + parent_service_name + "Client"; + parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_CLIENT"; + } + + // The base service (the topmost in the "extends" hierarchy), on + // whose client class the "input_protocol" and "output_protocol" + // properties are defined + t_service* base_service = tservice; + while (base_service->get_extends()) { + base_service = base_service->get_extends(); + } + + string base_service_name = base_service->get_name(); + string base_service_name_lc = to_lower_case(initial_caps_to_underscores(base_service_name)); + string base_service_name_uc = to_upper_case(base_service_name_lc); + + // Generate the client interface dummy object in the header. + f_header_ << "/* " << service_name_ << " service interface */" << endl << "typedef struct _" + << this->nspace << service_name_ << "If " << this->nspace << service_name_ << "If; " + << " /* dummy object */" << endl << endl; + + // Generate the client interface object in the header. + f_header_ << "struct _" << this->nspace << service_name_ << "IfInterface" << endl << "{" << endl + << " GTypeInterface parent;" << endl << endl; + + /* write out the functions for this interface */ + indent_up(); + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores((*f_iter)->get_name()); + t_type* ttype = (*f_iter)->get_returntype(); + t_struct* arglist = (*f_iter)->get_arglist(); + t_struct* xlist = (*f_iter)->get_xceptions(); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name(ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arglist))) + + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; + + indent(f_header_) << "gboolean (*" << funname << ") " << params << ";" << endl; + } + indent_down(); + + f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "IfInterface " + << this->nspace << service_name_ << "IfInterface;" << endl << endl; + + // generate all the interface boilerplate + f_header_ << "GType " << this->nspace_lc << service_name_lc << "_if_get_type (void);" << endl + << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF " + << "(" << this->nspace_lc << service_name_lc << "_if_get_type())" << endl << "#define " + << this->nspace_uc << service_name_uc << "_IF(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF, " << this->nspace << service_name_ << "If))" << endl + << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF))" << endl << "#define " << this->nspace_uc + << service_name_uc << "_IF_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), " + << this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << this->nspace + << service_name_ << "IfInterface))" << endl << endl; + + // write out all the interface function prototypes + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores((*f_iter)->get_name()); + t_type* ttype = (*f_iter)->get_returntype(); + t_struct* arglist = (*f_iter)->get_arglist(); + t_struct* xlist = (*f_iter)->get_xceptions(); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name(ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arglist))) + + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; + + f_header_ << "gboolean " << this->nspace_lc << service_name_lc << "_if_" << funname << " " + << params << ";" << endl; + } + f_header_ << endl; + + // Generate the client object instance definition in the header. + f_header_ << "/* " << service_name_ << " service client */" << endl << "struct _" << this->nspace + << service_name_ << "Client" << endl << "{" << endl << " " << parent_class_name + << " parent;" << endl; + if (!extends_service) { + // Define "input_protocol" and "output_protocol" properties only + // for base services; child service-client classes will inherit + // these + f_header_ << endl << " ThriftProtocol *input_protocol;" << endl + << " ThriftProtocol *output_protocol;" << endl; + } + f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "Client " + << this->nspace << service_name_ << "Client;" << endl << endl; + + // Generate the class definition in the header. + f_header_ << "struct _" << this->nspace << service_name_ << "ClientClass" << endl << "{" << endl + << " " << parent_class_name << "Class parent;" << endl << "};" << endl + << "typedef struct _" << this->nspace << service_name_ << "ClientClass " << this->nspace + << service_name_ << "ClientClass;" << endl << endl; + + // Create all the GObject boilerplate + f_header_ << "GType " << this->nspace_lc << service_name_lc << "_client_get_type (void);" << endl + << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT " + << "(" << this->nspace_lc << service_name_lc << "_client_get_type())" << endl + << "#define " << this->nspace_uc << service_name_uc << "_CLIENT(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "Client))" << endl + << "#define " << this->nspace_uc << service_name_uc << "_CLIENT_CLASS(c) " + << "(G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc + << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" << endl << "#define " + << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_CLIENT))" << endl << "#define " << this->nspace_uc + << service_name_uc << "_IS_CLIENT_CLASS(c) " + << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc + << "_CLIENT))" << endl << "#define " << this->nspace_uc << service_name_uc + << "_CLIENT_GET_CLASS(obj) " + << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" + << endl << endl; + + /* write out the function prototypes */ + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* make the function name C friendly */ + string funname = to_lower_case(initial_caps_to_underscores((*f_iter)->get_name())); + + t_function service_function((*f_iter)->get_returntype(), + service_name_lc + string("_client_") + funname, + (*f_iter)->get_arglist(), + (*f_iter)->get_xceptions()); + indent(f_header_) << function_signature(&service_function) << ";" << endl; + + t_function send_function(g_type_void, + service_name_lc + string("_client_send_") + funname, + (*f_iter)->get_arglist()); + indent(f_header_) << function_signature(&send_function) << ";" << endl; + + // implement recv if not a oneway service + if (!(*f_iter)->is_oneway()) { + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + service_name_lc + string("_client_recv_") + funname, + &noargs, + (*f_iter)->get_xceptions()); + indent(f_header_) << function_signature(&recv_function) << ";" << endl; + } + } + + /* write out the get/set function prototypes */ + f_header_ << "void " + service_name_lc + "_client_set_property (GObject *object, guint " + "property_id, const GValue *value, GParamSpec *pspec);" + << endl; + f_header_ << "void " + service_name_lc + "_client_get_property (GObject *object, guint " + "property_id, GValue *value, GParamSpec *pspec);" + << endl; + + f_header_ << endl; + // end of header code + + // Generate interface method implementations + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores((*f_iter)->get_name()); + t_type* ttype = (*f_iter)->get_returntype(); + t_struct* arglist = (*f_iter)->get_arglist(); + t_struct* xlist = (*f_iter)->get_xceptions(); + bool has_return = !ttype->is_void(); + bool has_args = arglist->get_members().size() == 0; + bool has_xceptions = xlist->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name(ttype) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arglist))) + + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; + + string params_without_type = string("iface, ") + (has_return ? "_return, " : ""); + + const vector& fields = arglist->get_members(); + vector::const_iterator f_iter_field; + for (f_iter_field = fields.begin(); f_iter_field != fields.end(); ++f_iter_field) { + params_without_type += (*f_iter_field)->get_name(); + params_without_type += ", "; + } + + const vector& xceptions = xlist->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + params_without_type += (*x_iter)->get_name(); + params_without_type += ", "; + } + + f_service_ << "gboolean" << endl << this->nspace_lc << service_name_lc << "_if_" << funname + << " " << params << endl << "{" << endl << " return " << this->nspace_uc + << service_name_uc << "_IF_GET_INTERFACE (iface)->" << funname << " (" + << params_without_type << "error);" << endl << "}" << endl << endl; + } + + // Generate interface boilerplate + f_service_ << "GType" << endl << this->nspace_lc << service_name_lc << "_if_get_type (void)" + << endl << "{" << endl << " static GType type = 0;" << endl << " if (type == 0)" + << endl << " {" << endl << " static const GTypeInfo type_info =" << endl << " {" + << endl << " sizeof (" << this->nspace << service_name_ << "IfInterface)," << endl + << " NULL, /* base_init */" << endl << " NULL, /* base_finalize */" << endl + << " NULL, /* class_init */" << endl << " NULL, /* class_finalize */" + << endl << " NULL, /* class_data */" << endl + << " 0, /* instance_size */" << endl << " 0, /* n_preallocs */" + << endl << " NULL, /* instance_init */" << endl + << " NULL /* value_table */" << endl << " };" << endl + << " type = g_type_register_static (G_TYPE_INTERFACE," << endl + << " \"" << this->nspace << service_name_ << "If\"," + << endl << " &type_info, 0);" << endl << " }" + << endl << " return type;" << endl << "}" << endl << endl; + + // Generate client boilerplate + f_service_ << "static void " << endl << this->nspace_lc << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" + << endl << endl << "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_ + << "Client, " << this->nspace_lc << service_name_lc << "_client," << endl + << " " << parent_type_name << ", " << endl + << " G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF," << endl + << " " << this->nspace_lc + << service_name_lc << "_if_interface_init))" << endl << endl; + + // Generate property-related code only for base services---child + // service-client classes have only properties inherited from their + // parent class + if (!extends_service) { + // Generate client properties + f_service_ << "enum _" << this->nspace << service_name_ << "ClientProperties" << endl << "{" + << endl << " PROP_0," << endl << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_INPUT_PROTOCOL," << endl << " PROP_" << this->nspace_uc + << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL" << endl << "};" << endl << endl; + + // generate property setter + f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_set_property (" + << "GObject *object, guint property_id, const GValue *value, " + << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace + << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc + << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl + << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_" + << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl + << " client->input_protocol = g_value_get_object (value);" << endl + << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL:" << endl + << " client->output_protocol = g_value_get_object (value);" << endl + << " break;" << endl << " }" << endl << "}" << endl << endl; + + // generate property getter + f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_get_property (" + << "GObject *object, guint property_id, GValue *value, " + << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace + << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc + << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl + << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_" + << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl + << " g_value_set_object (value, client->input_protocol);" << endl + << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL:" << endl + << " g_value_set_object (value, client->output_protocol);" << endl + << " break;" << endl << " }" << endl << "}" << endl << endl; + } + + // Generate client method implementations + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string name = (*f_iter)->get_name(); + string funname = initial_caps_to_underscores(name); + + // Get the struct of function call params and exceptions + t_struct* arg_struct = (*f_iter)->get_arglist(); + + // Function for sending + t_function send_function(g_type_void, + service_name_lc + string("_client_send_") + funname, + (*f_iter)->get_arglist()); + + // Open the send function + indent(f_service_) << function_signature(&send_function) << endl; + scope_up(f_service_); + + string reqType = (*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL"; + + // Serialize the request + f_service_ << indent() << "gint32 cseqid = 0;" << endl << indent() + << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc + << "_CLIENT (iface)->output_protocol;" << endl << endl << indent() + << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", " + << reqType << ", cseqid, error) < 0)" << endl << indent() << " return FALSE;" + << endl << endl; + + generate_struct_writer(f_service_, arg_struct, "", "", false); + + f_service_ << indent() << "if (thrift_protocol_write_message_end (protocol, error) < 0)" << endl + << indent() << " return FALSE;" << endl << indent() + << "if (!thrift_transport_flush (protocol->transport, error))" << endl << indent() + << " return FALSE;" << endl << indent() + << "if (!thrift_transport_write_end (protocol->transport, error))" << endl + << indent() << " return FALSE;" << endl << endl << indent() << "return TRUE;" + << endl; + + scope_down(f_service_); + f_service_ << endl; + + // Generate recv function only if not an async function + if (!(*f_iter)->is_oneway()) { + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + service_name_lc + string("_client_recv_") + funname, + &noargs, + (*f_iter)->get_xceptions()); + // Open function + indent(f_service_) << function_signature(&recv_function) << endl; + scope_up(f_service_); + + f_service_ << indent() << "gint32 rseqid;" << endl + << indent() << "gchar * fname = NULL;" << endl + << indent() << "ThriftMessageType mtype;" << endl + << indent() << "ThriftProtocol * protocol = " + << this->nspace_uc << base_service_name_uc + << "_CLIENT (iface)->input_protocol;" << endl + << indent() << "ThriftApplicationException *xception;" << endl + << endl + << indent() << "if (thrift_protocol_read_message_begin " + "(protocol, &fname, &mtype, &rseqid, error) < 0) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl + << endl + << indent() << "if (mtype == T_EXCEPTION) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "xception = g_object_new " + "(THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << endl + << indent() << "thrift_struct_read (THRIFT_STRUCT (xception), " + "protocol, NULL);" << endl + << indent() << "thrift_protocol_read_message_end " + "(protocol, NULL);" << endl + << indent() << "thrift_transport_read_end " + "(protocol->transport, NULL);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR,xception->type, " + "\"application error: %s\", xception->message);" << endl + << indent() << "g_object_unref (xception);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "} else if (mtype != T_REPLY) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " + "NULL);" << endl + << indent() << "thrift_protocol_read_message_end (protocol, " + "NULL);" << endl + << indent() << "thrift_transport_read_end (" + "protocol->transport, NULL);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR, " + "THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, " + "\"invalid message type %d, expected T_REPLY\", mtype);" + << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "} else if (strncmp (fname, \"" << name + << "\", " << name.length() << ") != 0) {" << endl; + indent_up(); + f_service_ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " + "NULL);" << endl + << indent() << "thrift_protocol_read_message_end (protocol," + "error);" << endl + << indent() << "thrift_transport_read_end (" + "protocol->transport, error);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR, " + "THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, " + "\"wrong method name %s, expected " << name + << "\", fname);" << endl + << indent() << "if (fname) g_free (fname);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl + << indent() << "if (fname) g_free (fname);" << endl + << endl; + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + { + t_struct result(program_, tservice->get_name() + "_" + (*f_iter)->get_name() + "_result"); + t_field success((*f_iter)->get_returntype(), "*_return", 0); + if (!(*f_iter)->get_returntype()->is_void()) { + result.append(&success); + } + + // add readers for exceptions, dereferencing the pointer. + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { + t_field* xception = new t_field((*x_iter)->get_type(), + "*" + (*x_iter)->get_name(), + (*x_iter)->get_key()); + result.append(xception); + } + + generate_struct_reader(f_service_, &result, "", "", false); + } + + f_service_ << indent() << "if (thrift_protocol_read_message_end (protocol, error) < 0)" + << endl << indent() << " return FALSE;" << endl << endl << indent() + << "if (!thrift_transport_read_end (protocol->transport, error))" << endl + << indent() << " return FALSE;" << endl << endl; + + // copy over any throw exceptions and return failure + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { + f_service_ << indent() << "if (*" << (*x_iter)->get_name() << " != NULL)" << endl + << indent() << "{" << endl << indent() << " g_set_error (error, " + << this->nspace_uc + << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name())) + << "_ERROR, " << this->nspace_uc + << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name())) + << "_ERROR_CODE, \"" << (*x_iter)->get_type()->get_name() << "\");" << endl + << indent() << " return FALSE;" << endl << indent() << "}" << endl; + } + // Close function + indent(f_service_) << "return TRUE;" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // Open function + t_function service_function((*f_iter)->get_returntype(), + service_name_lc + string("_client_") + funname, + (*f_iter)->get_arglist(), + (*f_iter)->get_xceptions()); + indent(f_service_) << function_signature(&service_function) << endl; + scope_up(f_service_); + + // wrap each function + f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_send_" + << funname << " (iface"; + + // Declare the function arguments + 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_ << ", " << (*fld_iter)->get_name(); + } + f_service_ << ", error))" << endl << indent() << " return FALSE;" << endl; + + // if not oneway, implement recv + if (!(*f_iter)->is_oneway()) { + string ret = (*f_iter)->get_returntype()->is_void() ? "" : "_return, "; + + const vector& xceptions = (*f_iter)->get_xceptions()->get_members(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + ret += (*x_iter)->get_name(); + ret += ", "; + } + + f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_recv_" + << funname << " (iface, " << ret << "error))" << endl << indent() + << " return FALSE;" << endl; + } + + // return TRUE which means all functions were called OK + indent(f_service_) << "return TRUE;" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // create the interface initializer + f_service_ << "static void" << endl + << this->nspace_lc << service_name_lc << "_if_interface_init (" + << this->nspace << service_name_ << "IfInterface *iface)" << endl; + scope_up(f_service_); + if (functions.size() > 0) { + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* make the function name C friendly */ + string funname = initial_caps_to_underscores((*f_iter)->get_name()); + + f_service_ << indent() << "iface->" << funname << " = " << this->nspace_lc + << service_name_lc << "_client_" << funname << ";" << endl; + } + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (iface);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // create the client instance initializer + f_service_ << "static void" << endl + << this->nspace_lc << service_name_lc << "_client_init (" + << this->nspace << service_name_ << "Client *client)" << endl; + scope_up(f_service_); + if (!extends_service) { + f_service_ << indent() << "client->input_protocol = NULL;" << endl + << indent() << "client->output_protocol = NULL;" << endl; + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (client);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // create the client class initializer + f_service_ << "static void" << endl << this->nspace_lc << service_name_lc + << "_client_class_init (" << this->nspace << service_name_ << "ClientClass *cls)" + << endl << "{" << endl; + if (!extends_service) { + f_service_ << " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl + << " GParamSpec *param_spec;" << endl << endl + << " gobject_class->set_property = " << this->nspace_lc << service_name_lc + << "_client_set_property;" << endl + << " gobject_class->get_property = " << this->nspace_lc << service_name_lc + << "_client_get_property;" << endl << endl + << " param_spec = g_param_spec_object (\"input_protocol\"," << endl + << " \"input protocol (construct)\"," << endl + << " \"Set the client input protocol\"," << endl + << " THRIFT_TYPE_PROTOCOL," << endl + << " G_PARAM_READWRITE);" << endl + << " g_object_class_install_property (gobject_class," << endl + << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl << endl + << " param_spec = g_param_spec_object (\"output_protocol\"," << endl + << " \"output protocol (construct)\"," << endl + << " \"Set the client output protocol\"," << endl + << " THRIFT_TYPE_PROTOCOL," << endl + << " G_PARAM_READWRITE);" << endl + << " g_object_class_install_property (gobject_class," << endl + << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl; + } + else { + f_service_ << " THRIFT_UNUSED_VAR (cls);" << endl; + } + f_service_ << "}" << endl << endl; +} + +/** + * Generates C code that represents a Thrift service handler. + * + * @param tservice The service for which to generate a handler. + */ +void t_c_glib_generator::generate_service_handler(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::const_iterator function_iter; + + string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); + string service_name_uc = to_upper_case(service_name_lc); + + string class_name = this->nspace + service_name_ + "Handler"; + string class_name_lc = to_lower_case(initial_caps_to_underscores(class_name)); + string class_name_uc = to_upper_case(class_name_lc); + + string parent_class_name; + string parent_type_name; + + string args_indent; + + // The service this service extends, or NULL 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' + // parent class name and type + if (extends_service) { + string parent_service_name = extends_service->get_name(); + string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); + string parent_service_name_uc = to_upper_case(parent_service_name_lc); + + parent_class_name = this->nspace + parent_service_name + "Handler"; + parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_HANDLER"; + } else { + parent_class_name = "GObject"; + parent_type_name = "G_TYPE_OBJECT"; + } + + // Generate the handler class' definition in the header file + + // Generate the handler instance definition + f_header_ << "/* " << service_name_ << " handler (abstract base class) */" << endl << "struct _" + << class_name << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << " parent;" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl + << endl; + + // Generate the handler class definition, including its class members + // (methods) + f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl; + + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string method_name = initial_caps_to_underscores((*function_iter)->get_name()); + t_type* return_type = (*function_iter)->get_returntype(); + t_struct* arg_list = (*function_iter)->get_arglist(); + t_struct* x_list = (*function_iter)->get_xceptions(); + bool has_return = !return_type->is_void(); + bool has_args = arg_list->get_members().size() == 0; + bool has_xceptions = x_list->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name(return_type) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arg_list))) + + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)"; + + indent(f_header_) << "gboolean (*" << method_name << ") " << params << ";" << endl; + } + indent_down(); + + f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name + << "Class;" << endl << endl; + + // Generate the remaining header boilerplate + f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define " + << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER " + << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc + << "(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER, " << class_name << "))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER))" << endl << "#define " << class_name_uc + << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER_CLASS(c) " + << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc + << "_HANDLER))" << endl << "#define " << this->nspace_uc << service_name_uc + << "_HANDLER_GET_CLASS(obj) " + << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << endl; + + // Generate the handler class' method definitions + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string method_name = initial_caps_to_underscores((*function_iter)->get_name()); + t_type* return_type = (*function_iter)->get_returntype(); + t_struct* arg_list = (*function_iter)->get_arglist(); + t_struct* x_list = (*function_iter)->get_xceptions(); + bool has_return = !return_type->is_void(); + bool has_args = arg_list->get_members().size() == 0; + bool has_xceptions = x_list->get_members().size() == 0; + + string params = "(" + this->nspace + service_name_ + "If *iface" + + (has_return ? ", " + type_name(return_type) + "* _return" : "") + + (has_args ? "" : (", " + argument_list(arg_list))) + + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)"; + + f_header_ << "gboolean " << class_name_lc << "_" << method_name << " " << params << ";" << endl; + } + f_header_ << endl; + + // Generate the handler's implementation in the implementation file + + // Generate the implementation boilerplate + f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" + << endl << endl; + + args_indent = string(25, ' '); + f_service_ << "G_DEFINE_TYPE_WITH_CODE (" << class_name << ", " << endl << args_indent + << class_name_lc << "," << endl << args_indent << parent_type_name << "," << endl + << args_indent << "G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF," << endl; + args_indent += string(23, ' '); + f_service_ << args_indent << class_name_lc << "_" << service_name_lc << "_if_interface_init))" + << endl << endl; + + // Generate the handler method implementations + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string function_name = (*function_iter)->get_name(); + string method_name = initial_caps_to_underscores(function_name); + t_type* return_type = (*function_iter)->get_returntype(); + t_struct* arg_list = (*function_iter)->get_arglist(); + t_struct* x_list = (*function_iter)->get_xceptions(); + + const vector& args = arg_list->get_members(); + const vector& xceptions = x_list->get_members(); + + vector::const_iterator field_iter; + + t_function implementing_function(return_type, + service_name_lc + "_handler_" + method_name, + arg_list, + x_list, + (*function_iter)->is_oneway()); + + indent(f_service_) << function_signature(&implementing_function) << endl; + scope_up(f_service_); + f_service_ << indent() << "g_return_val_if_fail (" << this->nspace_uc << "IS_" + << service_name_uc << "_HANDLER (iface), FALSE);" << endl << endl << indent() + << "return " << class_name_uc << "_GET_CLASS (iface)" + << "->" << method_name << " (iface, "; + + if (!return_type->is_void()) { + f_service_ << "_return, "; + } + for (field_iter = args.begin(); field_iter != args.end(); ++field_iter) { + f_service_ << (*field_iter)->get_name() << ", "; + } + for (field_iter = xceptions.begin(); field_iter != xceptions.end(); ++field_iter) { + f_service_ << (*field_iter)->get_name() << ", "; + } + f_service_ << "error);" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // Generate the handler interface initializer + f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)" + << endl; + scope_up(f_service_); + if (functions.size() > 0) { + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string method_name = initial_caps_to_underscores((*function_iter)->get_name()); + + f_service_ << indent() << "iface->" << method_name << " = " << class_name_lc << "_" + << method_name << ";" << endl; + } + } + else { + f_service_ << "THRIFT_UNUSED_VAR (iface);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // Generate the handler instance initializer + f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)" + << endl; + scope_up(f_service_); + f_service_ << indent() << "THRIFT_UNUSED_VAR (self);" << endl; + scope_down(f_service_); + f_service_ << endl; + + // Generate the handler class initializer + f_service_ << "static void" << endl + << class_name_lc << "_class_init (" << class_name << "Class *cls)" + << endl; + scope_up(f_service_); + if (functions.size() > 0) { + for (function_iter = functions.begin(); + function_iter != functions.end(); + ++function_iter) { + string function_name = (*function_iter)->get_name(); + string method_name = initial_caps_to_underscores(function_name); + + // All methods are pure virtual and must be implemented by subclasses + f_service_ << indent() << "cls->" << method_name << " = NULL;" << endl; + } + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (cls);" << endl; + } + scope_down(f_service_); + f_service_ << endl; +} + +/** + * Generates C code that represents a Thrift service processor. + * + * @param tservice The service for which to generate a processor + */ +void t_c_glib_generator::generate_service_processor(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::const_iterator function_iter; + + string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); + string service_name_uc = to_upper_case(service_name_lc); + + string class_name = this->nspace + service_name_ + "Processor"; + string class_name_lc = to_lower_case(initial_caps_to_underscores(class_name)); + string class_name_uc = to_upper_case(class_name_lc); + + string parent_class_name; + string parent_type_name; + + string handler_class_name = this->nspace + service_name_ + "Handler"; + string handler_class_name_lc = initial_caps_to_underscores(handler_class_name); + + string process_function_type_name = class_name + "ProcessFunction"; + string process_function_def_type_name = + class_name_lc + "_process_function_def"; + + string function_name; + string args_indent; + + // The service this service extends, or NULL if it extends no service + t_service* extends_service = tservice->get_extends(); + + // Determine the name of our parent service (if any) and the + // processor class' parent class name and type + if (extends_service) { + string parent_service_name = extends_service->get_name(); + string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); + string parent_service_name_uc = to_upper_case(parent_service_name_lc); + + parent_class_name = this->nspace + parent_service_name + "Processor"; + parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_PROCESSOR"; + } else { + parent_class_name = "ThriftDispatchProcessor"; + parent_type_name = "THRIFT_TYPE_DISPATCH_PROCESSOR"; + } + + // Generate the processor class' definition in the header file + + // Generate the processor instance definition + f_header_ << "/* " << service_name_ << " processor */" << endl << "struct _" << class_name << endl + << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << " parent;" << endl << endl << indent() + << "/* protected */" << endl << indent() + << this->nspace + service_name_ + "Handler *handler;" << endl << indent() + << "GHashTable *process_map;" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl + << endl; + + // Generate the processor class definition + f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl << indent() + << "/* protected */" << endl << indent() + << "gboolean (*dispatch_call) (ThriftDispatchProcessor *processor," << endl; + args_indent = indent() + string(27, ' '); + f_header_ << args_indent << "ThriftProtocol *in," << endl << args_indent << "ThriftProtocol *out," + << endl << args_indent << "gchar *fname," << endl << args_indent << "gint32 seqid," + << endl << args_indent << "GError **error);" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name + << "Class;" << endl << endl; + + // Generate the remaining header boilerplate + f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define " + << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR " + << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc + << "(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR, " << class_name << "))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR))" << endl << "#define " << class_name_uc + << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR_CLASS(c) " + << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc + << "_PROCESSOR))" << endl << "#define " << this->nspace_uc << service_name_uc + << "_PROCESSOR_GET_CLASS(obj) " + << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << endl; + + // Generate the processor's implementation in the implementation file + + // Generate the processor's properties enum + f_service_ << "enum _" << class_name << "Properties" << endl << "{" << endl; + indent_up(); + f_service_ << indent() << "PROP_" << class_name_uc << "_0," << endl << indent() << "PROP_" + << class_name_uc << "_HANDLER" << endl; + indent_down(); + f_service_ << "};" << endl << endl; + + // Generate the implementation boilerplate + args_indent = string(15, ' '); + f_service_ << "G_DEFINE_TYPE (" << class_name << "," << endl << args_indent << class_name_lc + << "," << endl << args_indent << parent_type_name << ")" << endl << endl; + + // Generate the processor's processing-function type + args_indent = string(process_function_type_name.length() + 23, ' '); + f_service_ << "typedef gboolean (* " << process_function_type_name << ") (" + << class_name << " *, " << endl + << args_indent << "gint32," << endl + << args_indent << "ThriftProtocol *," << endl + << args_indent << "ThriftProtocol *," << endl + << args_indent << "GError **);" << endl + << endl; + + // Generate the processor's processing-function-definition type + f_service_ << "typedef struct" << endl + << "{" << endl; + indent_up(); + f_service_ << indent() << "gchar *name;" << endl + << indent() << process_function_type_name << " function;" << endl; + indent_down(); + f_service_ << "} " << process_function_def_type_name << ";" << endl + << endl; + + // Generate forward declarations of the processor's processing functions so we + // can refer to them in the processing-function-definition struct below and + // keep all of the processor's declarations in one place + for (function_iter = functions.begin(); + function_iter != functions.end(); + ++function_iter) { + function_name = class_name_lc + "_process_" + + initial_caps_to_underscores((*function_iter)->get_name()); + + args_indent = string(function_name.length() + 2, ' '); + f_service_ << "static gboolean" << endl + << function_name << " (" + << class_name << " *," << endl + << args_indent << "gint32," << endl + << args_indent << "ThriftProtocol *," << endl + << args_indent << "ThriftProtocol *," << endl + << args_indent << "GError **);" << endl; + } + f_service_ << endl; + + // Generate the processor's processing-function definitions, if the service + // defines any methods + if (functions.size() > 0) { + f_service_ << indent() << "static " << process_function_def_type_name + << endl + << indent() << class_name_lc << "_process_function_defs[" + << functions.size() << "] = {" << endl; + indent_up(); + for (function_iter = functions.begin(); + function_iter != functions.end(); + ++function_iter) { + string service_function_name = (*function_iter)->get_name(); + string process_function_name = class_name_lc + "_process_" + + initial_caps_to_underscores(service_function_name); + + f_service_ << indent() << "{" << endl; + indent_up(); + f_service_ << indent() << "\"" << service_function_name << "\"," << endl + << indent() << process_function_name << endl; + indent_down(); + f_service_ << indent() << "}" + << (function_iter == --functions.end() ? "" : ",") << endl; + } + indent_down(); + f_service_ << indent() << "};" << endl + << endl; + } + + // Generate the processor's processing functions + for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { + string service_function_name = (*function_iter)->get_name(); + string service_function_name_ic = underscores_to_initial_caps(service_function_name); + string service_function_name_lc = initial_caps_to_underscores(service_function_name); + string service_function_name_uc = to_upper_case(service_function_name_lc); + + t_type* return_type = (*function_iter)->get_returntype(); + bool has_return_value = !return_type->is_void(); + + t_struct* arg_list = (*function_iter)->get_arglist(); + const vector& args = arg_list->get_members(); + vector::const_iterator arg_iter; + + const vector& xceptions = (*function_iter)->get_xceptions()->get_members(); + vector::const_iterator xception_iter; + + string args_class_name = this->nspace + service_name_ + service_function_name_ic + "Args"; + string args_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_" + + service_function_name_uc + "_ARGS"; + + string result_class_name = this->nspace + service_name_ + service_function_name_ic + "Result"; + string result_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_" + + service_function_name_uc + "_RESULT"; + + string handler_function_name = handler_class_name_lc + "_" + service_function_name_lc; + + function_name = class_name_lc + "_process_" + + initial_caps_to_underscores(service_function_name); + + args_indent = string(function_name.length() + 2, ' '); + f_service_ << "static gboolean" << endl << function_name << " (" << class_name << " *self," + << endl << args_indent << "gint32 sequence_id," << endl << args_indent + << "ThriftProtocol *input_protocol," << endl << args_indent + << "ThriftProtocol *output_protocol," << endl << args_indent << "GError **error)" + << endl; + scope_up(f_service_); + f_service_ << indent() << "gboolean result = TRUE;" << endl + << indent() << "ThriftTransport * transport;" << endl + << indent() << "ThriftApplicationException *xception;" << endl + << indent() << args_class_name + " * args =" << endl; + indent_up(); + f_service_ << indent() << "g_object_new (" << args_class_type << ", NULL);" << endl << endl; + indent_down(); + if ((*function_iter)->is_oneway()) { + f_service_ << indent() << "THRIFT_UNUSED_VAR (sequence_id);" << endl << indent() + << "THRIFT_UNUSED_VAR (output_protocol);" << endl << endl; + } + f_service_ << indent() << "g_object_get (input_protocol, \"transport\", " + << "&transport, NULL);" << endl << endl; + + // Read the method's arguments from the caller + f_service_ << indent() << "if ((thrift_struct_read (THRIFT_STRUCT (args), " + << "input_protocol, error) != -1) &&" << endl << indent() + << " (thrift_protocol_read_message_end (input_protocol, " + << "error) != -1) &&" << endl << indent() + << " (thrift_transport_read_end (transport, error) != FALSE))" << endl; + scope_up(f_service_); + + for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { + f_service_ << indent() << property_type_name((*arg_iter)->get_type()) << " " + << (*arg_iter)->get_name() << ";" << endl; + } + for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { + f_service_ << indent() << type_name((*xception_iter)->get_type()) << " " + << initial_caps_to_underscores((*xception_iter)->get_name()) << " = NULL;" << endl; + } + if (has_return_value) { + f_service_ << indent() << property_type_name(return_type) << " return_value;" << endl; + } + if (!(*function_iter)->is_oneway()) { + f_service_ << indent() << result_class_name << " * result_struct;" << endl; + } + f_service_ << endl; + + if (args.size() > 0) { + f_service_ << indent() << "g_object_get (args," << endl; + args_indent = indent() + string(14, ' '); + for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { + string arg_name = (*arg_iter)->get_name(); + + f_service_ << args_indent << "\"" << arg_name << "\", &" << arg_name << "," << endl; + } + f_service_ << args_indent << "NULL);" << endl << endl; + } + + if (!(*function_iter)->is_oneway()) { + f_service_ << indent() << "g_object_unref (transport);" << endl << indent() + << "g_object_get (output_protocol, \"transport\", " + << "&transport, NULL);" << endl << endl << indent() + << "result_struct = g_object_new (" << result_class_type << ", NULL);" << endl; + if (has_return_value) { + f_service_ << indent() << "g_object_get (result_struct, " + "\"success\", &return_value, NULL);" << endl; + } + f_service_ << endl; + } + + // Pass the arguments to the corresponding method in the handler + f_service_ << indent() << "if (" << handler_function_name << " (" << this->nspace_uc + << service_name_uc << "_IF (self->handler)," << endl; + args_indent = indent() + string(handler_function_name.length() + 6, ' '); + if (has_return_value) { + string return_type_name = type_name(return_type); + + f_service_ << args_indent; + + // Cast return_value if it was declared as a type other than the return + // value's actual type---this is true for integer values 32 bits or fewer + // in width, for which GLib requires a plain gint type be used when + // storing or retrieving as an object property + if (return_type_name != property_type_name(return_type)) { + if (return_type_name[return_type_name.length() - 1] != '*') { + return_type_name += ' '; + } + return_type_name += '*'; + + f_service_ << "(" << return_type_name << ")"; + } + + f_service_ << "&return_value," << endl; + } + for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { + f_service_ << args_indent << (*arg_iter)->get_name() << "," << endl; + } + for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { + f_service_ << args_indent << "&" << initial_caps_to_underscores((*xception_iter)->get_name()) + << "," << endl; + } + f_service_ << args_indent << "error) == TRUE)" << endl; + scope_up(f_service_); + + // The handler reported success; return the result, if any, to the caller + if (!(*function_iter)->is_oneway()) { + if (has_return_value) { + f_service_ << indent() << "g_object_set (result_struct, \"success\", "; + if (type_name(return_type) != property_type_name(return_type)) { + // Roundtrip cast to fix the position of sign bit. + f_service_ << "(" << property_type_name(return_type) << ")" + << "(" << type_name(return_type) << ")"; + } + 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; + indent_up(); + f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl; + args_indent = indent() + string(39, ' '); + f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent + << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent + << "error) != -1) &&" << endl << indent() + << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl; + args_indent = indent() + string(23, ' '); + f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));" + << endl; + indent_down(); + } + scope_down(f_service_); + f_service_ << indent() << "else" << endl; + scope_up(f_service_); + + // The handler reported failure; check to see if an application-defined + // exception was raised and if so, return it to the caller + f_service_ << indent(); + if (xceptions.size() > 0) { + for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { + f_service_ << "if (" << initial_caps_to_underscores((*xception_iter)->get_name()) + << " != NULL)" << endl; + scope_up(f_service_); + f_service_ << indent() << "g_object_set (result_struct," << endl; + args_indent = indent() + string(14, ' '); + f_service_ << args_indent << "\"" << (*xception_iter)->get_name() << "\", " + << (*xception_iter)->get_name() << "," << endl << args_indent << "NULL);" << endl + << endl; + f_service_ << indent() << "result =" << endl; + indent_up(); + f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl; + args_indent = indent() + string(39, ' '); + f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent + << "T_REPLY," << endl << args_indent << "sequence_id," << endl << args_indent + << "error) != -1) &&" << endl << indent() + << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << endl; + args_indent = indent() + string(23, ' '); + f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));" + << endl; + indent_down(); + scope_down(f_service_); + f_service_ << indent() << "else" << endl; + } + + scope_up(f_service_); + f_service_ << indent(); + } + + // If the handler reported failure but raised no application-defined + // exception, return a Thrift application exception with the information + // returned via GLib's own error-reporting mechanism + f_service_ << "if (*error == NULL)" << endl; + indent_up(); + f_service_ << indent() << "g_warning (\"" << service_name_ << "." + << (*function_iter)->get_name() << " implementation returned FALSE \"" << endl + << indent() << string(11, ' ') << "\"but did not set an error\");" << endl << endl; + indent_down(); + f_service_ << indent() << "xception =" << endl; + indent_up(); + f_service_ << indent() << "g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION," << endl; + args_indent = indent() + string(14, ' '); + f_service_ << args_indent << "\"type\", *error != NULL ? (*error)->code :" << endl + << args_indent << string(11, ' ') << "THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN," + << endl << args_indent << "\"message\", *error != NULL ? (*error)->message : NULL," + << endl << args_indent << "NULL);" << endl; + indent_down(); + f_service_ << indent() << "g_clear_error (error);" << endl << endl << indent() + << "result =" << endl; + indent_up(); + f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << endl; + args_indent = indent() + string(39, ' '); + f_service_ << args_indent << "\"" << service_function_name << "\"," << endl << args_indent + << "T_EXCEPTION," << endl << args_indent << "sequence_id," << endl << args_indent + << "error) != -1) &&" << endl << indent() + << " (thrift_struct_write (THRIFT_STRUCT (xception)," << endl; + args_indent = indent() + string(23, ' '); + f_service_ << args_indent << "output_protocol," << endl << args_indent << "error) != -1));" + << endl; + indent_down(); + f_service_ << endl << indent() << "g_object_unref (xception);" << endl; + + if (xceptions.size() > 0) { + scope_down(f_service_); + } + scope_down(f_service_); + f_service_ << endl; + + // Dellocate or unref retrieved argument values as necessary + for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { + string arg_name = (*arg_iter)->get_name(); + t_type* arg_type = get_true_type((*arg_iter)->get_type()); + + if (arg_type->is_base_type()) { + t_base_type* base_type = ((t_base_type*)arg_type); + + if (base_type->get_base() == t_base_type::TYPE_STRING) { + f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl; + indent_up(); + if (base_type->is_binary()) { + f_service_ << indent() << "g_byte_array_unref (" << arg_name << ");" << endl; + } else { + f_service_ << indent() << "g_free (" << arg_name << ");" << endl; + } + indent_down(); + } + } else if (arg_type->is_container()) { + f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl; + indent_up(); + + if (arg_type->is_list()) { + t_type* elem_type = ((t_list*)arg_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_ << " (" << arg_name << ");" << endl; + } else if (arg_type->is_map() || arg_type->is_set()) { + f_service_ << indent() << "g_hash_table_unref (" << arg_name << ");" << endl; + } + + indent_down(); + } else if (arg_type->is_struct()) { + f_service_ << indent() << "if (" << arg_name << " != NULL)" << endl; + indent_up(); + f_service_ << indent() << "g_object_unref (" << arg_name << ");" << endl; + indent_down(); + } + } + + if (!(*function_iter)->is_oneway()) { + f_service_ << indent() << "g_object_unref (result_struct);" << endl << endl << indent() + << "if (result == TRUE)" << endl; + indent_up(); + f_service_ << indent() << "result =" << endl; + indent_up(); + f_service_ << indent() << "((thrift_protocol_write_message_end " + << "(output_protocol, error) != -1) &&" << endl << indent() + << " (thrift_transport_write_end (transport, error) " + << "!= FALSE) &&" << endl << indent() + << " (thrift_transport_flush (transport, error) " + << "!= FALSE));" << endl; + indent_down(); + indent_down(); + } + scope_down(f_service_); + f_service_ << indent() << "else" << endl; + indent_up(); + f_service_ << indent() << "result = FALSE;" << endl; + indent_down(); + + f_service_ << endl << indent() << "g_object_unref (transport);" << endl << indent() + << "g_object_unref (args);" << endl << endl << indent() << "return result;" << endl; + scope_down(f_service_); + + f_service_ << endl; + } + + // Generate the processor's dispatch_call implementation + function_name = class_name_lc + "_dispatch_call"; + args_indent = indent() + string(function_name.length() + 2, ' '); + f_service_ << "static gboolean" << endl << function_name + << " (ThriftDispatchProcessor *dispatch_processor," << endl << args_indent + << "ThriftProtocol *input_protocol," << endl << args_indent + << "ThriftProtocol *output_protocol," << endl << args_indent << "gchar *method_name," + << endl << args_indent << "gint32 sequence_id," << endl << args_indent + << "GError **error)" << endl; + scope_up(f_service_); + f_service_ << indent() << class_name_lc << "_process_function_def *" + << "process_function_def;" << endl; + f_service_ << indent() << "gboolean dispatch_result = FALSE;" << endl << endl << indent() + << class_name << " *self = " << class_name_uc << " (dispatch_processor);" << endl; + f_service_ << indent() << parent_class_name << "Class " + "*parent_class =" << endl; + indent_up(); + f_service_ << indent() << "g_type_class_peek_parent (" << class_name_uc << "_GET_CLASS (self));" + << endl; + indent_down(); + f_service_ << endl + << indent() << "process_function_def = " + << "g_hash_table_lookup (self->process_map, method_name);" << endl + << indent() << "if (process_function_def != NULL)" << endl; + scope_up(f_service_); + args_indent = indent() + string(53, ' '); + f_service_ << indent() << "g_free (method_name);" << endl + << indent() << "dispatch_result = " + << "(*process_function_def->function) (self," << endl + << args_indent << "sequence_id," << endl + << args_indent << "input_protocol," << endl + << args_indent << "output_protocol," << endl + << args_indent << "error);" << endl; + scope_down(f_service_); + f_service_ << indent() << "else" << endl; + scope_up(f_service_); + + // Method name not recognized; chain up to our parent processor---note the + // top-most implementation of this method, in ThriftDispatchProcessor itself, + // will return an application exception to the caller if no class in the + // hierarchy recognizes the method name + f_service_ << indent() << "dispatch_result = parent_class->dispatch_call " + "(dispatch_processor," << endl; + args_indent = indent() + string(47, ' '); + f_service_ << args_indent << "input_protocol," << endl << args_indent << "output_protocol," + << endl << args_indent << "method_name," << endl << args_indent << "sequence_id," + << endl << args_indent << "error);" << endl; + scope_down(f_service_); + f_service_ << endl << indent() << "return dispatch_result;" << endl; + scope_down(f_service_); + f_service_ << endl; + + // Generate the processor's property setter + function_name = class_name_lc + "_set_property"; + args_indent = string(function_name.length() + 2, ' '); + f_service_ << "static void" << endl << function_name << " (GObject *object," << endl + << args_indent << "guint property_id," << endl << args_indent << "const GValue *value," + << endl << args_indent << "GParamSpec *pspec)" << endl; + scope_up(f_service_); + f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl + << endl << indent() << "switch (property_id)" << endl; + scope_up(f_service_); + f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl; + indent_up(); + f_service_ << indent() << "if (self->handler != NULL)" << endl; + indent_up(); + f_service_ << indent() << "g_object_unref (self->handler);" << endl; + indent_down(); + f_service_ << indent() << "self->handler = g_value_get_object (value);" << endl << indent() + << "g_object_ref (self->handler);" << endl; + if (extends_service) { + // Chain up to set the handler in every superclass as well + f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)->" + << endl; + indent_up(); + f_service_ << indent() << "set_property (object, property_id, value, pspec);" << endl; + indent_down(); + } + f_service_ << indent() << "break;" << endl; + indent_down(); + f_service_ << indent() << "default:" << endl; + indent_up(); + f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" + << endl << indent() << "break;" << endl; + indent_down(); + scope_down(f_service_); + scope_down(f_service_); + f_service_ << endl; + + // Generate processor's property getter + function_name = class_name_lc + "_get_property"; + args_indent = string(function_name.length() + 2, ' '); + f_service_ << "static void" << endl << function_name << " (GObject *object," << endl + << args_indent << "guint property_id," << endl << args_indent << "GValue *value," + << endl << args_indent << "GParamSpec *pspec)" << endl; + scope_up(f_service_); + f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl + << endl << indent() << "switch (property_id)" << endl; + scope_up(f_service_); + f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << endl; + indent_up(); + f_service_ << indent() << "g_value_set_object (value, self->handler);" << endl << indent() + << "break;" << endl; + indent_down(); + f_service_ << indent() << "default:" << endl; + indent_up(); + f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" + << endl << indent() << "break;" << endl; + indent_down(); + scope_down(f_service_); + scope_down(f_service_); + f_service_ << endl; + + // Generator the processor's dispose function + f_service_ << "static void" << endl << class_name_lc << "_dispose (GObject *gobject)" << endl; + scope_up(f_service_); + f_service_ << indent() << class_name << " *self = " << class_name_uc << " (gobject);" << endl + << endl << indent() << "if (self->handler != NULL)" << endl; + scope_up(f_service_); + f_service_ << indent() << "g_object_unref (self->handler);" << endl << indent() + << "self->handler = NULL;" << endl; + scope_down(f_service_); + f_service_ << endl << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)" + "->dispose (gobject);" + << endl; + scope_down(f_service_); + f_service_ << endl; + + // Generate processor finalize function + f_service_ << "static void" << endl << class_name_lc << "_finalize (GObject *gobject)" << endl; + scope_up(f_service_); + f_service_ << indent() << this->nspace << service_name_ << "Processor *self = " << this->nspace_uc + << service_name_uc << "_PROCESSOR (gobject);" << endl << endl << indent() + << "thrift_safe_hash_table_destroy (self->process_map);" << endl << endl << indent() + << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)" + "->finalize (gobject);" << endl; + scope_down(f_service_); + f_service_ << endl; + + // Generate processor instance initializer + f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)" + << endl; + scope_up(f_service_); + if (functions.size() > 0) { + f_service_ << indent() << "guint index;" << endl + << endl; + } + f_service_ << indent() << "self->handler = NULL;" << endl << indent() + << "self->process_map = " + "g_hash_table_new (g_str_hash, g_str_equal);" << endl; + if (functions.size() > 0) { + args_indent = string(21, ' '); + f_service_ << endl + << indent() << "for (index = 0; index < " + << functions.size() << "; index += 1)" << endl; + indent_up(); + f_service_ << indent() << "g_hash_table_insert (self->process_map," << endl + << indent() << args_indent + << class_name_lc << "_process_function_defs[index].name," << endl + << indent() << args_indent + << "&" << class_name_lc << "_process_function_defs[index]" << ");" + << endl; + indent_down(); + } + scope_down(f_service_); + f_service_ << endl; + + // Generate processor class initializer + f_service_ << "static void" << endl << class_name_lc << "_class_init (" << class_name + << "Class *cls)" << endl; + scope_up(f_service_); + f_service_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl + << indent() << "ThriftDispatchProcessorClass *dispatch_processor_class =" << endl; + indent_up(); + f_service_ << indent() << "THRIFT_DISPATCH_PROCESSOR_CLASS (cls);" << endl; + indent_down(); + f_service_ << indent() << "GParamSpec *param_spec;" << endl << endl << indent() + << "gobject_class->dispose = " << class_name_lc << "_dispose;" << endl << indent() + << "gobject_class->finalize = " << class_name_lc << "_finalize;" << endl << indent() + << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl + << indent() << "gobject_class->get_property = " << class_name_lc << "_get_property;" + << endl << endl << indent() + << "dispatch_processor_class->dispatch_call = " << class_name_lc << "_dispatch_call;" + << endl << indent() << "cls->dispatch_call = " << class_name_lc << "_dispatch_call;" + << endl << endl << indent() << "param_spec = g_param_spec_object (\"handler\"," + << endl; + args_indent = indent() + string(34, ' '); + f_service_ << args_indent << "\"Service handler implementation\"," << endl << args_indent + << "\"The service handler implementation \"" << endl << args_indent + << "\"to which method calls are dispatched.\"," << endl << args_indent + << this->nspace_uc + "TYPE_" + service_name_uc + "_HANDLER," << endl << args_indent + << "G_PARAM_READWRITE);" << endl; + f_service_ << indent() << "g_object_class_install_property (gobject_class," << endl; + args_indent = indent() + string(33, ' '); + f_service_ << args_indent << "PROP_" << class_name_uc << "_HANDLER," << endl << args_indent + << "param_spec);" << endl; + scope_down(f_service_); +} + +/** + * Generates C code that represents a Thrift service server. + */ +void t_c_glib_generator::generate_service_server(t_service* tservice) { + (void)tservice; + // Generate the service's handler class + generate_service_handler(tservice); + + // Generate the service's processor class + generate_service_processor(tservice); +} + +/** + * Generates C code to represent a THrift structure as a GObject. + */ +void t_c_glib_generator::generate_object(t_struct* tstruct) { + string name = tstruct->get_name(); + string name_u = initial_caps_to_underscores(name); + string name_uc = to_upper_case(name_u); + + string class_name = this->nspace + name; + string class_name_lc = to_lower_case(initial_caps_to_underscores(class_name)); + string class_name_uc = to_upper_case(class_name_lc); + + string function_name; + string args_indent; + + // write the instance definition + f_types_ << "struct _" << this->nspace << name << endl << "{ " << endl + << " ThriftStruct parent; " << endl << endl << " /* public */" << endl; + + // for each field, add a member variable + vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + f_types_ << " " << type_name(t) << " " << (*m_iter)->get_name() << ";" << endl; + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + f_types_ << " gboolean __isset_" << (*m_iter)->get_name() << ";" << endl; + } + } + + // close the structure definition and create a typedef + f_types_ << "};" << endl << "typedef struct _" << this->nspace << name << " " << this->nspace + << name << ";" << endl << endl; + + // write the class definition + f_types_ << "struct _" << this->nspace << name << "Class" << endl << "{" << endl + << " ThriftStructClass parent;" << endl << "};" << endl << "typedef struct _" + << this->nspace << name << "Class " << this->nspace << name << "Class;" << endl << endl; + + // write the standard GObject boilerplate + f_types_ << "GType " << this->nspace_lc << name_u << "_get_type (void);" << endl << "#define " + << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u + << "_get_type())" << endl << "#define " << this->nspace_uc << name_uc + << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc + << ", " << this->nspace << name << "))" << endl << "#define " << this->nspace_uc + << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_" + << name_uc << ", " << this->nspace << name << "Class))" << endl << "#define " + << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), " + << this->nspace_uc << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc + << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc + << "TYPE_" << name_uc << "))" << endl << "#define " << this->nspace_uc << name_uc + << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" + << name_uc << ", " << this->nspace << name << "Class))" << endl << endl; + + // start writing the object implementation .c file + + // generate properties enum + if (members.size() > 0) { + f_types_impl_ << "enum _" << class_name << "Properties" << endl << "{" << endl; + indent_up(); + f_types_impl_ << indent() << "PROP_" << class_name_uc << "_0"; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string member_name_uc + = to_upper_case(to_lower_case(initial_caps_to_underscores((*m_iter)->get_name()))); + + f_types_impl_ << "," << endl << indent() << "PROP_" << class_name_uc << "_" << member_name_uc; + } + f_types_impl_ << endl; + indent_down(); + f_types_impl_ << "};" << endl << endl; + } + + // generate struct I/O methods + string this_get = this->nspace + name + " * this_object = " + this->nspace_uc + name_uc + + "(object);"; + generate_struct_reader(f_types_impl_, tstruct, "this_object->", this_get); + generate_struct_writer(f_types_impl_, tstruct, "this_object->", this_get); + + // generate property setter and getter + if (members.size() > 0) { + // generate property setter + function_name = class_name_lc + "_set_property"; + args_indent = string(function_name.length() + 2, ' '); + f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl + << args_indent << "guint property_id," << endl << args_indent + << "const GValue *value," << endl << args_indent << "GParamSpec *pspec)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl + << endl << indent() << "switch (property_id)" << endl; + scope_up(f_types_impl_); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* member = (*m_iter); + string member_name = member->get_name(); + string member_name_uc + = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); + t_type* member_type = get_true_type(member->get_type()); + + string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; + + f_types_impl_ << indent() << "case " << property_identifier + ":" << endl; + indent_up(); + + if (member_type->is_base_type()) { + t_base_type* base_type = ((t_base_type*)member_type); + string assign_function_name; + + if (base_type->get_base() == t_base_type::TYPE_STRING) { + string release_function_name; + + f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl; + indent_up(); + + if (base_type->is_binary()) { + release_function_name = "g_byte_array_unref"; + assign_function_name = "g_value_dup_boxed"; + } else { + release_function_name = "g_free"; + assign_function_name = "g_value_dup_string"; + } + + f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");" + << endl; + indent_down(); + } else { + switch (base_type->get_base()) { + case t_base_type::TYPE_BOOL: + assign_function_name = "g_value_get_boolean"; + break; + + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + assign_function_name = "g_value_get_int"; + break; + + case t_base_type::TYPE_I64: + assign_function_name = "g_value_get_int64"; + break; + + case t_base_type::TYPE_DOUBLE: + assign_function_name = "g_value_get_double"; + break; + + default: + throw "compiler error: " + "unrecognized base type \"" + base_type->get_name() + "\" " + "for struct member \"" + + member_name + "\""; + break; + } + } + + f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name + << " (value);" << endl; + } else if (member_type->is_enum()) { + f_types_impl_ << indent() << "self->" << member_name << " = g_value_get_int (value);" + << endl; + } else if (member_type->is_container()) { + string release_function_name; + string assign_function_name; + + if (member_type->is_list()) { + t_type* elem_type = ((t_list*)member_type)->get_elem_type(); + + // Lists of base types other than strings are represented as GArrays; + // all others as GPtrArrays + if (is_numeric(elem_type)) { + release_function_name = "g_array_unref"; + } else { + release_function_name = "g_ptr_array_unref"; + } + + assign_function_name = "g_value_dup_boxed"; + } else if (member_type->is_set() || member_type->is_map()) { + release_function_name = "g_hash_table_unref"; + assign_function_name = "g_value_dup_boxed"; + } + + f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl; + indent_up(); + f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");" + << endl; + indent_down(); + f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name + << " (value);" << endl; + } else if (member_type->is_struct() || member_type->is_xception()) { + f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << endl; + indent_up(); + f_types_impl_ << indent() << "g_object_unref (self->" << member_name << ");" << endl; + indent_down(); + f_types_impl_ << indent() << "self->" << member_name << " = g_value_dup_object (value);" + << endl; + } + + if (member->get_req() != t_field::T_REQUIRED) { + f_types_impl_ << indent() << "self->__isset_" << member_name << " = TRUE;" << endl; + } + + f_types_impl_ << indent() << "break;" << endl << endl; + indent_down(); + } + f_types_impl_ << indent() << "default:" << endl; + indent_up(); + f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" + << endl << indent() << "break;" << endl; + indent_down(); + scope_down(f_types_impl_); + scope_down(f_types_impl_); + f_types_impl_ << endl; + + // generate property getter + function_name = class_name_lc + "_get_property"; + args_indent = string(function_name.length() + 2, ' '); + f_types_impl_ << "static void" << endl << function_name << " (GObject *object," << endl + << args_indent << "guint property_id," << endl << args_indent << "GValue *value," + << endl << args_indent << "GParamSpec *pspec)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << endl + << endl << indent() << "switch (property_id)" << endl; + scope_up(f_types_impl_); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* member = (*m_iter); + string member_name = (*m_iter)->get_name(); + string member_name_uc + = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); + t_type* member_type = get_true_type(member->get_type()); + + string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; + + string setter_function_name; + + if (member_type->is_base_type()) { + t_base_type* base_type = ((t_base_type*)member_type); + + switch (base_type->get_base()) { + case t_base_type::TYPE_BOOL: + setter_function_name = "g_value_set_boolean"; + break; + + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + setter_function_name = "g_value_set_int"; + break; + + case t_base_type::TYPE_I64: + setter_function_name = "g_value_set_int64"; + break; + + case t_base_type::TYPE_DOUBLE: + setter_function_name = "g_value_set_double"; + break; + + case t_base_type::TYPE_STRING: + if (base_type->is_binary()) { + setter_function_name = "g_value_set_boxed"; + } else { + setter_function_name = "g_value_set_string"; + } + break; + + default: + throw "compiler error: " + "unrecognized base type \"" + base_type->get_name() + "\" " + "for struct member \"" + + member_name + "\""; + break; + } + } else if (member_type->is_enum()) { + setter_function_name = "g_value_set_int"; + } else if (member_type->is_struct() || member_type->is_xception()) { + setter_function_name = "g_value_set_object"; + } else if (member_type->is_container()) { + setter_function_name = "g_value_set_boxed"; + } else { + throw "compiler error: " + "unrecognized type for struct member \"" + member_name + "\""; + } + + f_types_impl_ << indent() << "case " << property_identifier + ":" << endl; + indent_up(); + f_types_impl_ << indent() << setter_function_name << " (value, self->" << member_name << ");" + << endl << indent() << "break;" << endl << endl; + indent_down(); + } + f_types_impl_ << indent() << "default:" << endl; + indent_up(); + f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" + << endl << indent() << "break;" << endl; + indent_down(); + scope_down(f_types_impl_); + scope_down(f_types_impl_); + f_types_impl_ << endl; + } + + // generate the instance init function + + f_types_impl_ << "static void " << endl << this->nspace_lc << name_u << "_instance_init (" + << this->nspace << name << " * object)" << endl << "{" << endl; + indent_up(); + + // generate default-value structures for container-type members + bool constant_declaration_output = false; + bool string_list_constant_output = false; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* member = *m_iter; + t_const_value* member_value = member->get_value(); + + if (member_value != NULL) { + string member_name = member->get_name(); + t_type* member_type = get_true_type(member->get_type()); + + if (member_type->is_list()) { + const vector& list = member_value->get_list(); + t_type* elem_type = ((t_list*)member_type)->get_elem_type(); + + // Generate an array with the list literal + indent(f_types_impl_) << "static " << type_name(elem_type, false, true) << " __default_" + << member_name << "[" << list.size() << "] = " << endl; + indent_up(); + f_types_impl_ << indent() << constant_literal(member_type, member_value) << ";" << endl; + indent_down(); + + constant_declaration_output = true; + + // If we are generating values for a pointer array (i.e. a list of + // strings), set a flag so we know to also declare an index variable to + // use in pre-populating the array + if (elem_type->is_string()) { + string_list_constant_output = true; + } + } + + // TODO: Handle container types other than list + } + } + if (constant_declaration_output) { + if (string_list_constant_output) { + indent(f_types_impl_) << "unsigned int list_index;" << endl; + } + + f_types_impl_ << endl; + } + + // satisfy compilers with -Wall turned on + indent(f_types_impl_) << "/* satisfy -Wall */" << endl << indent() + << "THRIFT_UNUSED_VAR (object);" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (t->is_base_type()) { + string dval = " = "; + if (t->is_enum()) { + dval += "(" + type_name(t) + ")"; + } + t_const_value* cv = (*m_iter)->get_value(); + if (cv != NULL) { + dval += constant_value("", t, cv); + } else { + dval += t->is_string() ? "NULL" : "0"; + } + indent(f_types_impl_) << "object->" << (*m_iter)->get_name() << dval << ";" << endl; + } else if (t->is_struct()) { + string name = (*m_iter)->get_name(); + string type_name_uc + = to_upper_case(initial_caps_to_underscores((*m_iter)->get_type()->get_name())); + indent(f_types_impl_) << "object->" << name << " = g_object_new (" << this->nspace_uc + << "TYPE_" << type_name_uc << ", NULL);" << endl; + } else if (t->is_xception()) { + string name = (*m_iter)->get_name(); + indent(f_types_impl_) << "object->" << name << " = NULL;" << endl; + } else if (t->is_container()) { + string name = (*m_iter)->get_name(); + string init_function; + t_type* etype = NULL; + + if (t->is_map()) { + t_type* key = ((t_map*)t)->get_key_type(); + t_type* value = ((t_map*)t)->get_val_type(); + 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); + } else if (t->is_list()) { + etype = ((t_list*)t)->get_elem_type(); + init_function = generate_new_array_from_type(etype); + } + + indent(f_types_impl_) << "object->" << name << " = " << init_function << endl; + + // Pre-populate the container with the specified default values, if any + if ((*m_iter)->get_value()) { + t_const_value* member_value = (*m_iter)->get_value(); + + if (t->is_list()) { + const vector& list = member_value->get_list(); + + if (is_numeric(etype)) { + indent(f_types_impl_) << + "g_array_append_vals (object->" << name << ", &__default_" << + name << ", " << list.size() << ");" << endl; + } + else { + indent(f_types_impl_) << + "for (list_index = 0; list_index < " << list.size() << "; " << + "list_index += 1)" << endl; + indent_up(); + indent(f_types_impl_) << + "g_ptr_array_add (object->" << name << "," << endl << + indent() << string(17, ' ') << "g_strdup (__default_" << + name << "[list_index]));" << endl; + indent_down(); + } + } + + // TODO: Handle container types other than list + } + } + + /* if not required, initialize the __isset variable */ + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + indent(f_types_impl_) << "object->__isset_" << (*m_iter)->get_name() << " = FALSE;" << endl; + } + } + + indent_down(); + f_types_impl_ << "}" << endl << endl; + + /* create the destructor */ + f_types_impl_ << "static void " << endl << this->nspace_lc << name_u + << "_finalize (GObject *object)" << endl << "{" << endl; + indent_up(); + + f_types_impl_ << indent() << this->nspace << name << " *tobject = " << this->nspace_uc << name_uc + << " (object);" << endl << endl; + + f_types_impl_ << indent() << "/* satisfy -Wall in case we don't use tobject */" << endl + << indent() << "THRIFT_UNUSED_VAR (tobject);" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (t->is_container()) { + string name = (*m_iter)->get_name(); + if (t->is_map() || t->is_set()) { + f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl; + f_types_impl_ << indent() << "{" << endl; + indent_up(); + f_types_impl_ << indent() << "g_hash_table_destroy (tobject->" << name << ");" << endl; + f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl; + indent_down(); + f_types_impl_ << indent() << "}" << endl; + } else if (t->is_list()) { + t_type* etype = ((t_list*)t)->get_elem_type(); + string destructor_function = "g_ptr_array_unref"; + + if (etype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + 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: + destructor_function = "g_array_unref"; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } else if (etype->is_enum()) { + destructor_function = "g_array_unref"; + } + + f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl; + f_types_impl_ << indent() << "{" << endl; + indent_up(); + f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ");" << endl; + f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl; + indent_down(); + f_types_impl_ << indent() << "}" << endl; + } + } else if (t->is_struct() || t->is_xception()) { + string name = (*m_iter)->get_name(); + // TODO: g_clear_object needs glib >= 2.28 + // f_types_impl_ << indent() << "g_clear_object (&(tobject->" << name << "));" << endl; + // does g_object_unref the trick? + f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl; + f_types_impl_ << indent() << "{" << endl; + indent_up(); + f_types_impl_ << indent() << "g_object_unref(tobject->" << name << ");" << endl; + f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl; + indent_down(); + f_types_impl_ << indent() << "}" << endl; + } else if (t->is_string()) { + string name = (*m_iter)->get_name(); + f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << endl; + f_types_impl_ << indent() << "{" << endl; + indent_up(); + f_types_impl_ << indent() << generate_free_func_from_type(t) << "(tobject->" << name << ");" + << endl; + f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << endl; + indent_down(); + f_types_impl_ << indent() << "}" << endl; + } + } + + indent_down(); + f_types_impl_ << "}" << endl << endl; + + // generate the class init function + + f_types_impl_ << "static void" << endl << class_name_lc << "_class_init (" << class_name + << "Class * cls)" << endl; + scope_up(f_types_impl_); + + f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl + << indent() << "ThriftStructClass *struct_class = " + << "THRIFT_STRUCT_CLASS (cls);" << endl << endl << indent() + << "struct_class->read = " << class_name_lc << "_read;" << endl << indent() + << "struct_class->write = " << class_name_lc << "_write;" << endl << endl + << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;" + << endl; + if (members.size() > 0) { + f_types_impl_ << indent() << "gobject_class->get_property = " << class_name_lc + << "_get_property;" << endl << indent() + << "gobject_class->set_property = " << class_name_lc << "_set_property;" << endl; + + // install a property for each member + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* member = (*m_iter); + string member_name = member->get_name(); + string member_name_uc + = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); + t_type* member_type = get_true_type(member->get_type()); + t_const_value* member_value = member->get_value(); + + string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; + + f_types_impl_ << endl << indent() << "g_object_class_install_property" << endl; + indent_up(); + args_indent = indent() + ' '; + f_types_impl_ << indent() << "(gobject_class," << endl << args_indent << property_identifier + << "," << endl << args_indent; + + if (member_type->is_base_type()) { + t_base_type::t_base base_type = ((t_base_type*)member_type)->get_base(); + + if (base_type == t_base_type::TYPE_STRING) { + if (((t_base_type*)member_type)->is_binary()) { + args_indent += string(20, ' '); + f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << "G_TYPE_BYTE_ARRAY," << endl << args_indent << "G_PARAM_READWRITE));" + << endl; + } else { + args_indent += string(21, ' '); + f_types_impl_ << "g_param_spec_string (\"" << member_name << "\"," << endl + << args_indent << "NULL," << endl << args_indent << "NULL," << endl + << args_indent + << ((member_value != NULL) ? "\"" + member_value->get_string() + "\"" + : "NULL") << "," << endl << args_indent + << "G_PARAM_READWRITE));" << endl; + } + } else if (base_type == t_base_type::TYPE_BOOL) { + args_indent += string(22, ' '); + f_types_impl_ << "g_param_spec_boolean (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << (((member_value != NULL) && (member_value->get_integer() != 0)) + ? "TRUE" + : "FALSE") << "," << endl << args_indent << "G_PARAM_READWRITE));" + << endl; + } else if ((base_type == t_base_type::TYPE_I8) || (base_type == t_base_type::TYPE_I16) + || (base_type == t_base_type::TYPE_I32) || (base_type == t_base_type::TYPE_I64) + || (base_type == t_base_type::TYPE_DOUBLE)) { + string param_spec_function_name = "g_param_spec_int"; + string min_value; + string max_value; + ostringstream default_value; + + switch (base_type) { + case t_base_type::TYPE_I8: + min_value = "G_MININT8"; + max_value = "G_MAXINT8"; + break; + + case t_base_type::TYPE_I16: + min_value = "G_MININT16"; + max_value = "G_MAXINT16"; + break; + + case t_base_type::TYPE_I32: + min_value = "G_MININT32"; + max_value = "G_MAXINT32"; + break; + + case t_base_type::TYPE_I64: + param_spec_function_name = "g_param_spec_int64"; + min_value = "G_MININT64"; + max_value = "G_MAXINT64"; + break; + + case t_base_type::TYPE_DOUBLE: + param_spec_function_name = "g_param_spec_double"; + min_value = "-INFINITY"; + max_value = "INFINITY"; + break; + + default: + throw "compiler error: " + "unrecognized base type \"" + member_type->get_name() + "\" " + "for struct member \"" + + member_name + "\""; + break; + } + + if (member_value != NULL) { + default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double() + : member_value->get_integer()); + } else { + default_value << "0"; + } + + args_indent += string(param_spec_function_name.length() + 2, ' '); + f_types_impl_ << param_spec_function_name << " (\"" << member_name << "\"," << endl + << args_indent << "NULL," << endl << args_indent << "NULL," << endl + << args_indent << min_value << "," << endl << args_indent << max_value + << "," << endl << args_indent << default_value.str() << "," << endl + << args_indent << "G_PARAM_READWRITE));" << endl; + } + + indent_down(); + } 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; + + args_indent += string(18, ' '); + f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << min_value << "," << endl << args_indent << max_value << "," << endl + << args_indent << min_value << "," << endl << args_indent + << "G_PARAM_READWRITE));" << endl; + indent_down(); + } else if (member_type->is_struct() || member_type->is_xception()) { + string param_type = this->nspace_uc + "TYPE_" + + to_upper_case(initial_caps_to_underscores(member_type->get_name())); + + args_indent += string(20, ' '); + f_types_impl_ << "g_param_spec_object (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl; + indent_down(); + } else if (member_type->is_list()) { + t_type* elem_type = ((t_list*)member_type)->get_elem_type(); + string param_type; + + if (elem_type->is_base_type() && !elem_type->is_string()) { + param_type = "G_TYPE_ARRAY"; + } else { + param_type = "G_TYPE_PTR_ARRAY"; + } + + args_indent += string(20, ' '); + f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << param_type << "," << endl << args_indent << "G_PARAM_READWRITE));" << endl; + indent_down(); + } else if (member_type->is_set() || member_type->is_map()) { + args_indent += string(20, ' '); + f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << endl << args_indent + << "NULL," << endl << args_indent << "NULL," << endl << args_indent + << "G_TYPE_HASH_TABLE," << endl << args_indent << "G_PARAM_READWRITE));" + << endl; + indent_down(); + } + } + } + scope_down(f_types_impl_); + f_types_impl_ << endl; + + f_types_impl_ << "GType" << endl << this->nspace_lc << name_u << "_get_type (void)" << endl << "{" + << endl << " static GType type = 0;" << endl << endl << " if (type == 0) " << endl + << " {" << endl << " static const GTypeInfo type_info = " << endl << " {" + << endl << " sizeof (" << this->nspace << name << "Class)," << endl + << " NULL, /* base_init */" << endl << " NULL, /* base_finalize */" + << endl << " (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init," + << endl << " NULL, /* class_finalize */" << endl + << " NULL, /* class_data */" << endl << " sizeof (" << this->nspace + << name << ")," << endl << " 0, /* n_preallocs */" << endl + << " (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init," + << endl << " NULL, /* value_table */" << endl << " };" << endl << endl + << " type = g_type_register_static (THRIFT_TYPE_STRUCT, " << endl + << " \"" << this->nspace << name << "Type\"," + << endl << " &type_info, 0);" << endl << " }" + << endl << endl << " return type;" << endl << "}" << endl << endl; +} + +/** + * Generates functions to write Thrift structures to a stream. + */ +void t_c_glib_generator::generate_struct_writer(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get, + bool is_function) { + string name = tstruct->get_name(); + string name_u = initial_caps_to_underscores(name); + string name_uc = to_upper_case(name_u); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + int error_ret = 0; + + if (is_function) { + error_ret = -1; + indent(out) << "static gint32" << endl << this->nspace_lc << name_u + << "_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" + << endl; + } + indent(out) << "{" << endl; + indent_up(); + + out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << endl; + + indent(out) << this_get << endl; + // satisfy -Wall in the case of an empty struct + if (!this_get.empty()) { + indent(out) << "THRIFT_UNUSED_VAR (this_object);" << endl; + } + + out << indent() << "if ((ret = thrift_protocol_write_struct_begin (protocol, \"" << name + << "\", error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl + << indent() << "xfer += ret;" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL) { + indent(out) << "if (this_object->__isset_" << (*f_iter)->get_name() << " == TRUE) {" << endl; + indent_up(); + } + + out << indent() << "if ((ret = thrift_protocol_write_field_begin (protocol, " + << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " + << (*f_iter)->get_key() << ", error)) < 0)" << endl << indent() << " return " << error_ret + << ";" << endl << indent() << "xfer += ret;" << endl; + generate_serialize_field(out, *f_iter, this_name, "", error_ret); + out << indent() << "if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" + << endl; + + if ((*f_iter)->get_req() == t_field::T_OPTIONAL) { + indent_down(); + indent(out) << "}" << endl; + } + } + + // write the struct map + out << indent() << "if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl + << indent() << "if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl + << endl; + + if (is_function) { + indent(out) << "return xfer;" << endl; + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates code to read Thrift structures from a stream. + */ +void t_c_glib_generator::generate_struct_reader(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get, + bool is_function) { + string name = tstruct->get_name(); + string name_u = initial_caps_to_underscores(name); + string name_uc = to_upper_case(name_u); + int error_ret = 0; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + if (is_function) { + error_ret = -1; + indent(out) << "/* reads a " << name_u << " object */" << endl << "static gint32" << endl + << this->nspace_lc << name_u + << "_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << endl; + } + + indent(out) << "{" << endl; + indent_up(); + + // declare stack temp variables + out << indent() << "gint32 ret;" << endl << indent() << "gint32 xfer = 0;" << endl << indent() + << "gchar *name = NULL;" << endl << indent() << "ThriftType ftype;" << endl << indent() + << "gint16 fid;" << endl << indent() << "guint32 len = 0;" << endl << indent() + << "gpointer data = NULL;" << endl << indent() << this_get << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent(out) << "gboolean isset_" << (*f_iter)->get_name() << " = FALSE;" << endl; + } + } + + out << endl; + + // satisfy -Wall in case we don't use some variables + out << indent() << "/* satisfy -Wall in case these aren't used */" << endl << indent() + << "THRIFT_UNUSED_VAR (len);" << endl << indent() << "THRIFT_UNUSED_VAR (data);" << endl; + + if (!this_get.empty()) { + out << indent() << "THRIFT_UNUSED_VAR (this_object);" << endl; + } + out << endl; + + // read the beginning of the structure marker + out << indent() << "/* read the struct begin marker */" << endl << indent() + << "if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)" << endl + << indent() << "{" << endl << indent() << " if (name) g_free (name);" << endl << indent() + << " return " << error_ret << ";" << endl << indent() << "}" << endl << indent() + << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent() + << "name = NULL;" << endl << endl; + + // read the struct fields + out << indent() << "/* read the struct fields */" << endl << indent() << "while (1)" << endl; + scope_up(out); + + // read beginning field marker + out << indent() << "/* read the beginning of a field */" << endl << indent() + << "if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, &fid, error)) < 0)" + << endl << indent() << "{" << endl << indent() << " if (name) g_free (name);" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "}" << endl << indent() + << "xfer += ret;" << endl << indent() << "if (name) g_free (name);" << endl << indent() + << "name = NULL;" << endl << endl; + + // check for field STOP marker + out << indent() << "/* break if we get a STOP field */" << endl << indent() + << "if (ftype == T_STOP)" << endl << indent() << "{" << endl << indent() << " break;" << endl + << indent() << "}" << endl << endl; + + // switch depending on the field type + indent(out) << "switch (fid)" << endl; + + // start switch + scope_up(out); + + // generate deserialization code for known types + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ")" << endl; + indent(out) << "{" << endl; + + indent_up(); + // generate deserialize field + generate_deserialize_field(out, *f_iter, this_name, "", error_ret, false); + indent_down(); + + out << indent() << "} else {" << endl << indent() + << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent() + << " return " << error_ret << ";" << endl << indent() << " xfer += ret;" << endl + << indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + // create the default case + out << indent() << "default:" << endl << indent() + << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << endl << indent() + << " return " << error_ret << ";" << endl << indent() << " xfer += ret;" << endl + << indent() << " break;" << endl; + + // end switch + scope_down(out); + + // read field end marker + out << indent() << "if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl; + + // end while loop + scope_down(out); + out << endl; + + // read the end of the structure + out << indent() << "if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl + << endl; + + // if a required field is missing, throw an error + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + out << indent() << "if (!isset_" << (*f_iter)->get_name() << ")" << endl << indent() << "{" + << endl << indent() << " g_set_error (error, THRIFT_PROTOCOL_ERROR," << endl << indent() + << " THRIFT_PROTOCOL_ERROR_INVALID_DATA," << endl << indent() + << " \"missing field\");" << endl << indent() << " return -1;" << endl + << indent() << "}" << endl << endl; + } + } + + if (is_function) { + indent(out) << "return xfer;" << endl; + } + + // end the function/structure + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_c_glib_generator::generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret) { + t_type* type = get_true_type(tfield->get_type()); + string name = prefix + tfield->get_name() + suffix; + + 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, error_ret); + } else if (type->is_container()) { + generate_serialize_container(out, type, name, error_ret); + } else if (type->is_base_type() || type->is_enum()) { + indent(out) << "if ((ret = thrift_protocol_write_"; + + 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_BOOL: + out << "bool (protocol, " << name; + break; + case t_base_type::TYPE_I8: + out << "byte (protocol, " << name; + break; + case t_base_type::TYPE_I16: + out << "i16 (protocol, " << name; + break; + case t_base_type::TYPE_I32: + out << "i32 (protocol, " << name; + break; + case t_base_type::TYPE_I64: + out << "i64 (protocol, " << name; + break; + case t_base_type::TYPE_DOUBLE: + out << "double (protocol, " << name; + break; + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + out << "binary (protocol, " << name << " ? ((GByteArray *) " << name << ")->data : NULL, " + << name << " ? ((GByteArray *) " << name << ")->len : 0"; + } else { + out << "string (protocol, " << name; + } + break; + default: + throw "compiler error: no C writer for base type " + t_base_type::t_base_name(tbase) + name; + } + } else { + out << "i32 (protocol, (gint32) " << name; + } + out << ", error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl + << indent() << "xfer += ret;" << endl << endl; + } else { + throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + name + "' TYPE '" + + type_name(type)); + } +} + +void t_c_glib_generator::generate_serialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + int error_ret) { + (void)tstruct; + out << indent() << "if ((ret = thrift_struct_write (THRIFT_STRUCT (" << prefix + << "), protocol, error)) < 0)" << endl << indent() << " return " << error_ret << ";" << endl + << indent() << "xfer += ret;" << endl << endl; +} + +void t_c_glib_generator::generate_serialize_container(ofstream& out, + t_type* ttype, + string prefix, + int error_ret) { + scope_up(out); + + if (ttype->is_map()) { + t_type* tkey = ((t_map*)ttype)->get_key_type(); + t_type* tval = ((t_map*)ttype)->get_val_type(); + string tkey_name = type_name(tkey); + string tval_name = type_name(tval); + string tkey_ptr; + string tval_ptr; + string keyname = tmp("key"); + string valname = tmp("val"); + + declore_local_variable_for_write(out, tkey, keyname); + declore_local_variable_for_write(out, tval, valname); + + /* If either the key or value type is a typedef, find its underlying type so + we can correctly determine how to generate a pointer to it */ + tkey = get_true_type(tkey); + tval = get_true_type(tval); + + tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; + tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*"; + + /* + * Some ugliness here. To maximize backwards compatibility, we + * avoid using GHashTableIter and instead get a GList of all keys, + * then copy it into a array on the stack, and free it. + * This is because we may exit early before we get a chance to free the + * GList. + */ + out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl + << indent() << tkey_name << tkey_ptr << "* keys;" << endl + << indent() << "int i = 0, key_count;" << endl + << endl + << indent() << "if ((ret = thrift_protocol_write_map_begin (protocol, " + << type_to_enum(tkey) << ", " << type_to_enum(tval) << ", " << prefix << " ? " + << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0" + << ", error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << indent() << "if (" << prefix << ")" << endl + << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix + << ", thrift_hash_table_get_keys, &key_list);" << endl + << indent() << "key_count = g_list_length (key_list);" << endl + << indent() << "keys = g_newa (" << tkey_name << tkey_ptr + << ", key_count);" << endl + << indent() << "for (iter = g_list_first (key_list); iter; " + "iter = iter->next)" << endl; + indent_up(); + out << indent() << "keys[i++] = (" << tkey_name << tkey_ptr + << ") iter->data;" << endl; + indent_down(); + out << indent() << "g_list_free (key_list);" << endl + << endl + << indent() << "for (i = 0; i < key_count; ++i)" << endl; + scope_up(out); + out << indent() << keyname << " = keys[i];" << endl + << indent() << valname << " = (" << tval_name << tval_ptr + << ") g_hash_table_lookup (((GHashTable *) " << prefix + << "), (gpointer) " << keyname << ");" << endl + << endl; + generate_serialize_map_element(out, + (t_map*)ttype, + tkey_ptr + " " + keyname, + tval_ptr + " " + valname, + error_ret); + scope_down(out); + out << indent() << "if ((ret = thrift_protocol_write_map_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl; + } else if (ttype->is_set()) { + t_type* telem = ((t_set*)ttype)->get_elem_type(); + string telem_name = type_name(telem); + string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*"; + out << indent() << "GList *key_list = NULL, *iter = NULL;" << endl + << indent() << telem_name << telem_ptr << "* keys;" << endl + << indent() << "int i = 0, key_count;" << endl + << indent() << telem_name << telem_ptr << " elem;" << endl + << indent() << "gpointer value;" << endl + << indent() << "THRIFT_UNUSED_VAR (value);" << endl + << endl + << indent() << "if ((ret = thrift_protocol_write_set_begin (protocol, " + << type_to_enum(telem) << ", " << prefix << " ? " + << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0" + << ", error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << indent() << "if (" << prefix << ")" << endl + << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix + << ", thrift_hash_table_get_keys, &key_list);" << endl + << indent() << "key_count = g_list_length (key_list);" << endl + << indent() << "keys = g_newa (" << telem_name << telem_ptr + << ", key_count);" << endl + << indent() << "for (iter = g_list_first (key_list); iter; " + "iter = iter->next)" << endl; + indent_up(); + out << indent() << "keys[i++] = (" << telem_name << telem_ptr + << ") iter->data;" << endl; + indent_down(); + out << indent() << "g_list_free (key_list);" << endl + << endl + << indent() << "for (i = 0; i < key_count; ++i)" << endl; + scope_up(out); + out << indent() << "elem = keys[i];" << endl + << indent() << "value = (gpointer) g_hash_table_lookup " + "(((GHashTable *) " << prefix << "), (gpointer) elem);" << endl + << endl; + generate_serialize_set_element(out, + (t_set*)ttype, + telem_ptr + "elem", + error_ret); + scope_down(out); + out << indent() << "if ((ret = thrift_protocol_write_set_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl; + } else if (ttype->is_list()) { + string length = "(" + prefix + " ? " + prefix + "->len : 0)"; + string i = tmp("i"); + out << indent() << "guint " << i << ";" << endl + << endl + << indent() << "if ((ret = thrift_protocol_write_list_begin (protocol, " + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", (gint32) " + << length << ", error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << indent() << "for (" << i << " = 0; " << i << " < " << length << "; " + << i << "++)" << endl; + scope_up(out); + generate_serialize_list_element(out, (t_list*)ttype, prefix, i, error_ret); + scope_down(out); + out << indent() << "if ((ret = thrift_protocol_write_list_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl; + } + + scope_down(out); +} + +void t_c_glib_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string key, + string value, + int error_ret) { + t_field kfield(tmap->get_key_type(), key); + generate_serialize_field(out, &kfield, "", "", error_ret); + + t_field vfield(tmap->get_val_type(), value); + generate_serialize_field(out, &vfield, "", "", error_ret); +} + +void t_c_glib_generator::generate_serialize_set_element(ofstream& out, + t_set* tset, + string element, + int error_ret) { + t_field efield(tset->get_elem_type(), element); + generate_serialize_field(out, &efield, "", "", error_ret); +} + +void t_c_glib_generator::generate_serialize_list_element(ofstream& out, + t_list* tlist, + string list, + string index, + int error_ret) { + t_type* ttype = get_true_type(tlist->get_elem_type()); + + // cast to non-const + string cast = ""; + string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + index + ")"; + + if (ttype->is_void()) { + throw std::runtime_error("compiler error: list element type cannot be void"); + } else if (is_numeric(ttype)) { + name = "g_array_index (" + list + ", " + base_type_name(ttype) + ", " + index + ")"; + } else if (ttype->is_string()) { + cast = "(gchar*)"; + } else if (ttype->is_map() || ttype->is_set()) { + cast = "(GHashTable*)"; + } else if (ttype->is_list()) { + t_type* etype = ((t_list*)ttype)->get_elem_type(); + if (etype->is_void()) { + throw std::runtime_error("compiler error: list element type cannot be void"); + } + cast = is_numeric(etype) ? "(GArray*)" : "(GPtrArray*)"; + } + + t_field efield(ttype, "(" + cast + name + ")"); + generate_serialize_field(out, &efield, "", "", error_ret); +} + +/* deserializes a field of any type. */ +void t_c_glib_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret, + bool allocate) { + t_type* type = get_true_type(tfield->get_type()); + + if (type->is_void()) { + throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + + tfield->get_name()); + } + + string name = prefix + tfield->get_name() + suffix; + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, name, error_ret, allocate); + } else if (type->is_container()) { + generate_deserialize_container(out, type, name, error_ret); + } else if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + if (tbase == t_base_type::TYPE_STRING) { + indent(out) << "if (" << name << " != NULL)" << endl << indent() << "{" << endl; + indent_up(); + indent(out) << "g_free(" << name << ");" << endl << indent() << name << " = NULL;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + indent(out) << "if ((ret = thrift_protocol_read_"; + + 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 (((t_base_type*)type)->is_binary()) { + out << "binary (protocol, &data, &len"; + } else { + out << "string (protocol, &" << name; + } + break; + case t_base_type::TYPE_BOOL: + out << "bool (protocol, &" << name; + break; + case t_base_type::TYPE_I8: + out << "byte (protocol, &" << name; + break; + case t_base_type::TYPE_I16: + out << "i16 (protocol, &" << name; + break; + case t_base_type::TYPE_I32: + out << "i32 (protocol, &" << name; + break; + case t_base_type::TYPE_I64: + out << "i64 (protocol, &" << name; + break; + case t_base_type::TYPE_DOUBLE: + out << "double (protocol, &" << name; + break; + default: + throw "compiler error: no C reader for base type " + t_base_type::t_base_name(tbase) + name; + } + out << ", error)) < 0)" << endl; + out << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" + << endl; + + // load the byte array with the data + if (tbase == t_base_type::TYPE_STRING && ((t_base_type*)type)->is_binary()) { + indent(out) << name << " = g_byte_array_new();" << endl; + indent(out) << "g_byte_array_append (" << name << ", (guint8 *) data, (guint) len);" << endl; + indent(out) << "g_free (data);" << endl; + } + } else if (type->is_enum()) { + string t = tmp("ecast"); + out << indent() << "gint32 " << t << ";" << endl << indent() + << "if ((ret = thrift_protocol_read_i32 (protocol, &" << t << ", error)) < 0)" << endl + << indent() << " return " << error_ret << ";" << endl << indent() << "xfer += ret;" << endl + << indent() << name << " = (" << type_name(type) << ")" << t << ";" << endl; + } else { + throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + tfield->get_name() + "' TYPE '" + + type_name(type)); + } + + // if the type is not required and this is a thrift struct (no prefix), + // set the isset variable. if the type is required, then set the + // local variable indicating the value was set, so that we can do // validation later. + if (tfield->get_req() != t_field::T_REQUIRED && prefix != "") { + indent(out) << prefix << "__isset_" << tfield->get_name() << suffix << " = TRUE;" << endl; + } else if (tfield->get_req() == t_field::T_REQUIRED && prefix != "") { + indent(out) << "isset_" << tfield->get_name() << " = TRUE;" << endl; + } +} + +void t_c_glib_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + int error_ret, + bool allocate) { + string name_uc = to_upper_case(initial_caps_to_underscores(tstruct->get_name())); + if (tstruct->is_xception()) { + out << indent() << "/* This struct is an exception */" << endl; + allocate = true; + } + + if (allocate) { + out << indent() << "if ( " << prefix << " != NULL)" << endl << indent() << "{" << endl; + indent_up(); + out << indent() << "g_object_unref (" << prefix << ");" << endl; + indent_down(); + out << indent() << "}" << endl << indent() << prefix << " = g_object_new (" << this->nspace_uc + << "TYPE_" << name_uc << ", NULL);" << endl; + } + out << indent() << "if ((ret = thrift_struct_read (THRIFT_STRUCT (" << prefix + << "), protocol, error)) < 0)" << endl << indent() << "{" << endl; + indent_up(); + if (allocate) { + indent(out) << "g_object_unref (" << prefix << ");" << endl; + if (tstruct->is_xception()) { + indent(out) << prefix << " = NULL;" << endl; + } + } + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "}" << endl << indent() << "xfer += ret;" << endl; +} + +void t_c_glib_generator::generate_deserialize_container(ofstream& out, + t_type* ttype, + string prefix, + int error_ret) { + scope_up(out); + + if (ttype->is_map()) { + out << indent() << "guint32 size;" << endl + << indent() << "guint32 i;" << endl + << indent() << "ThriftType key_type;" << endl + << indent() << "ThriftType value_type;" << endl + << endl + << indent() << "/* read the map begin marker */" << endl + << indent() << "if ((ret = thrift_protocol_read_map_begin (protocol, " + "&key_type, &value_type, &size, error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << endl; + + // iterate over map elements + out << indent() << "/* iterate through each of the map's fields */" << endl + << indent() << "for (i = 0; i < size; i++)" << endl; + scope_up(out); + generate_deserialize_map_element(out, (t_map*)ttype, prefix, error_ret); + scope_down(out); + out << endl; + + // read map end + out << indent() << "/* read the map end marker */" << endl + << indent() << "if ((ret = thrift_protocol_read_map_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl; + } else if (ttype->is_set()) { + out << indent() << "guint32 size;" << endl + << indent() << "guint32 i;" << endl + << indent() << "ThriftType element_type;" << endl + << endl + << indent() << "if ((ret = thrift_protocol_read_set_begin (protocol, " + "&element_type, &size, error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << endl; + + // iterate over the elements + out << indent() << "/* iterate through the set elements */" << endl + << indent() << "for (i = 0; i < size; ++i)" << endl; + scope_up(out); + generate_deserialize_set_element(out, (t_set*)ttype, prefix, error_ret); + scope_down(out); + + // read set end + out << indent() << "if ((ret = thrift_protocol_read_set_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << endl; + } else if (ttype->is_list()) { + out << indent() << "guint32 size;" << endl + << indent() << "guint32 i;" << endl + << indent() << "ThriftType element_type;" << endl + << endl + << indent() << "if ((ret = thrift_protocol_read_list_begin (protocol, " + "&element_type,&size, error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl + << endl; + + // iterate over the elements + out << indent() << "/* iterate through list elements */" << endl + << indent() << "for (i = 0; i < size; i++)" << endl; + scope_up(out); + generate_deserialize_list_element(out, + (t_list*)ttype, + prefix, + "i", + error_ret); + scope_down(out); + + // read list end + out << indent() << "if ((ret = thrift_protocol_read_list_end (protocol, " + "error)) < 0)" << endl; + indent_up(); + out << indent() << "return " << error_ret << ";" << endl; + indent_down(); + out << indent() << "xfer += ret;" << endl; + } + + scope_down(out); +} + +void t_c_glib_generator::declare_local_variable(ofstream& out, t_type* ttype, string& name, bool for_hash_table) { + string tname = type_name(ttype); + + /* If the given type is a typedef, find its underlying type so we + can correctly determine how to generate a pointer to it */ + ttype = get_true_type(ttype); + string ptr = !is_numeric(ttype) ? "" : "*"; + + if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + out << indent() << tname << ptr << " " << name << " = " + << generate_new_hash_from_type(tmap->get_key_type(), tmap->get_val_type()) << endl; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + out << indent() << tname << ptr << " " << name << " = " + << generate_new_array_from_type(tlist->get_elem_type()) << endl; + } else if (for_hash_table && ttype->is_enum()) { + out << indent() << tname << " " << name << ";" << endl; + } else { + out << indent() << tname << ptr << " " << name + << (ptr != "" ? " = g_new (" + tname + ", 1)" : " = NULL") << ";" << endl; + } +} + +void t_c_glib_generator::declore_local_variable_for_write(ofstream& out, + t_type* ttype, + string& name) { + string tname = type_name(ttype); + ttype = get_true_type(ttype); + string ptr = ttype->is_string() || !ttype->is_base_type() ? " " : "* "; + string init_val = ttype->is_enum() ? "" : " = NULL"; + out << indent() << tname << ptr << name << init_val << ";" << endl; +} + +void t_c_glib_generator::generate_deserialize_map_element(ofstream& out, + t_map* tmap, + string prefix, + int error_ret) { + t_type* tkey = tmap->get_key_type(); + t_type* tval = tmap->get_val_type(); + string keyname = tmp("key"); + string valname = tmp("val"); + + declare_local_variable(out, tkey, keyname, true); + declare_local_variable(out, tval, valname, true); + + /* If either the key or value type is a typedef, find its underlying + type so we can correctly determine how to generate a pointer to + it */ + tkey = get_true_type(tkey); + tval = get_true_type(tval); + + string tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; + string tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*"; + + // deserialize the fields of the map element + t_field fkey(tkey, tkey_ptr + keyname); + generate_deserialize_field(out, &fkey, "", "", error_ret); + t_field fval(tval, tval_ptr + valname); + generate_deserialize_field(out, &fval, "", "", error_ret); + + indent(out) << "if (" << prefix << " && " << keyname << ")" << endl; + indent_up(); + indent(out) << "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << keyname + << ", (gpointer) " << valname << ");" << endl; + indent_down(); +} + +void t_c_glib_generator::generate_deserialize_set_element(ofstream& out, + t_set* tset, + string prefix, + int error_ret) { + t_type* telem = tset->get_elem_type(); + string elem = tmp("_elem"); + string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*"; + + declare_local_variable(out, telem, elem, true); + + t_field felem(telem, telem_ptr + elem); + generate_deserialize_field(out, &felem, "", "", error_ret); + + indent(out) << "if (" << prefix << " && " << elem << ")" << endl; + indent_up(); + indent(out) << "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) " << elem + << ", (gpointer) " << elem << ");" << endl; + indent_down(); +} + +void t_c_glib_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix, + string index, + int error_ret) { + (void)index; + t_type* ttype = get_true_type(tlist->get_elem_type()); + string elem = tmp("_elem"); + string telem_ptr = !is_numeric(ttype) ? "" : "*"; + + declare_local_variable(out, ttype, elem, false); + + t_field felem(ttype, telem_ptr + elem); + generate_deserialize_field(out, &felem, "", "", error_ret); + + if (ttype->is_void()) { + 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; + } 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) + return "NULL"; + + 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 determine hash type"; + 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: + return "g_free"; + case t_base_type::TYPE_STRING: + if (((t_base_type*)ttype)->is_binary()) { + return "thrift_string_free"; + } + return "g_free"; + default: + throw "compiler error: no hash table info for type"; + } + } else if (ttype->is_enum()) { + return "NULL"; + } else if (ttype->is_map() || ttype->is_set()) { + return "(GDestroyNotify) thrift_safe_hash_table_destroy"; + } else if (ttype->is_struct()) { + return "g_object_unref"; + } else if (ttype->is_list()) { + t_type* etype = ((t_list*)ttype)->get_elem_type(); + if (etype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_VOID: + throw "compiler error: cannot determine array type"; + 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: + return "(GDestroyNotify) g_array_unref"; + case t_base_type::TYPE_STRING: + return "(GDestroyNotify) g_ptr_array_unref"; + default: + throw "compiler error: no array info for type"; + } + } else if (etype->is_container() || etype->is_struct()) { + return "(GDestroyNotify) g_ptr_array_unref"; + ; + } else if (etype->is_enum()) { + return "(GDestroyNotify) g_array_unref"; + } + printf("Type not expected inside the array: %s\n", etype->get_name().c_str()); + throw "Type not expected inside array"; + } else if (ttype->is_typedef()) { + return generate_free_func_from_type(((t_typedef*)ttype)->get_type()); + } + printf("Type not expected: %s\n", ttype->get_name().c_str()); + throw "Type not expected"; +} + +string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) { + if (ttype == NULL) + return "NULL"; + + 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 determine hash type"; + break; + case t_base_type::TYPE_BOOL: + return "thrift_boolean_hash"; + case t_base_type::TYPE_I8: + return "thrift_int8_hash"; + case t_base_type::TYPE_I16: + return "thrift_int16_hash"; + case t_base_type::TYPE_I32: + return "g_int_hash"; + case t_base_type::TYPE_I64: + return "g_int64_hash"; + case t_base_type::TYPE_DOUBLE: + return "g_double_hash"; + case t_base_type::TYPE_STRING: + return "g_str_hash"; + default: + throw "compiler error: no hash table info for type"; + } + } else if (ttype->is_enum()) { + return "g_direct_hash"; + } else if (ttype->is_container() || ttype->is_struct()) { + return "g_direct_hash"; + } else if (ttype->is_typedef()) { + return generate_hash_func_from_type(((t_typedef*)ttype)->get_type()); + } + printf("Type not expected: %s\n", ttype->get_name().c_str()); + throw "Type not expected"; +} + +string t_c_glib_generator::generate_cmp_func_from_type(t_type* ttype) { + if (ttype == NULL) + return "NULL"; + + 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 determine hash type"; + break; + case t_base_type::TYPE_BOOL: + return "thrift_boolean_equal"; + case t_base_type::TYPE_I8: + return "thrift_int8_equal"; + case t_base_type::TYPE_I16: + return "thrift_int16_equal"; + case t_base_type::TYPE_I32: + return "g_int_equal"; + case t_base_type::TYPE_I64: + return "g_int64_equal"; + case t_base_type::TYPE_DOUBLE: + return "g_double_equal"; + case t_base_type::TYPE_STRING: + return "g_str_equal"; + default: + throw "compiler error: no hash table info for type"; + } + } else if (ttype->is_enum()) { + return "g_direct_equal"; + } else if (ttype->is_container() || ttype->is_struct()) { + return "g_direct_equal"; + } else if (ttype->is_typedef()) { + return generate_cmp_func_from_type(((t_typedef*)ttype)->get_type()); + } + printf("Type not expected: %s\n", ttype->get_name().c_str()); + throw "Type not expected"; +} + +string t_c_glib_generator::generate_new_hash_from_type(t_type* key, t_type* value) { + string hash_func = generate_hash_func_from_type(key); + string cmp_func = generate_cmp_func_from_type(key); + string key_free_func = generate_free_func_from_type(key); + string value_free_func = generate_free_func_from_type(value); + + return "g_hash_table_new_full (" + hash_func + ", " + cmp_func + ", " + key_free_func + ", " + + value_free_func + ");"; +} + +string t_c_glib_generator::generate_new_array_from_type(t_type* ttype) { + if (ttype->is_void()) { + throw std::runtime_error("compiler error: cannot determine array type"); + } else if (is_numeric(ttype)) { + return "g_array_new (0, 1, sizeof (" + base_type_name(ttype) + "));"; + } else { + string free_func = generate_free_func_from_type(ttype); + return "g_ptr_array_new_with_free_func (" + free_func + ");"; + } +} + +/*************************************** + * UTILITY FUNCTIONS * + ***************************************/ + +/** + * Upper case a string. Wraps boost's string utility. + */ +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. + */ +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); +} + +/** + * Makes a string friendly to C code standards by lowercasing and adding + * underscores, with the exception of the first character. For example: + * + * Input: "ZomgCamelCase" + * Output: "zomg_camel_case" + */ +string initial_caps_to_underscores(string name) { + string ret; + const char* tmp = name.c_str(); + int pos = 0; + + /* the first character isn't underscored if uppercase, just lowercased */ + ret += tolower(tmp[pos]); + pos++; + for (unsigned int i = pos; i < name.length(); i++) { + char lc = tolower(tmp[i]); + if (lc != tmp[i]) { + ret += '_'; + } + ret += lc; + } + + return ret; +} + +/** + * Performs the reverse operation of initial_caps_to_underscores: The first + * character of the string is made uppercase, along with each character that + * follows an underscore (which is removed). Useful for converting Thrift + * service-method names into GObject-style class names. + * + * Input: "zomg_camel_case" + * Output: "ZomgCamelCase" + */ +string underscores_to_initial_caps(string name) { + string ret; + const char* tmp = name.c_str(); + bool uppercase_next = true; + + for (unsigned int i = 0; i < name.length(); i++) { + char c = tmp[i]; + if (c == '_') { + uppercase_next = true; + } else { + if (uppercase_next) { + ret += toupper(c); + uppercase_next = false; + } else { + ret += c; + } + } + } + + return ret; +} + +/* register this generator with the main program */ +THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc new file mode 100644 index 00000000..eba94c0a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cocoa_generator.cc @@ -0,0 +1,3301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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::ofstream& out, + t_struct* tstruct, + bool is_xception = false); + void generate_cocoa_struct_implementation(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_result = false); + void generate_cocoa_struct_initializer_signature(std::ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_init_with_coder_method(ofstream& out, + t_struct* tstruct, + bool is_exception); + void generate_cocoa_struct_encode_with_coder_method(ofstream& out, + t_struct* tstruct, + bool is_exception); + void generate_cocoa_struct_copy_method(ofstream& out, + t_struct* tstruct, + bool is_exception); + void generate_cocoa_struct_hash_method(ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_is_equal_method(ofstream& out, + t_struct* tstruct, + bool is_exception); + void generate_cocoa_struct_field_accessor_implementations(std::ofstream& out, + t_struct* tstruct, + bool is_exception); + void generate_cocoa_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_validator(std::ofstream& out, t_struct* tstruct); + void generate_cocoa_struct_description(std::ofstream& 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::ofstream& out, t_service* tservice); + void generate_cocoa_service_async_protocol(std::ofstream& out, t_service* tservice); + + void generate_cocoa_service_client_interface(std::ofstream& out, t_service* tservice); + void generate_cocoa_service_client_async_interface(std::ofstream& out, t_service* tservice); + + void generate_cocoa_service_client_send_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_cocoa_service_client_send_function_invocation(ofstream& out, t_function* tfunction); + void generate_cocoa_service_client_send_async_function_invocation(ofstream& out, + t_function* tfunction, + string failureBlockName); + void generate_cocoa_service_client_recv_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_cocoa_service_client_implementation(std::ofstream& out, t_service* tservice); + void generate_cocoa_service_client_async_implementation(std::ofstream& out, t_service* tservice); + + void generate_cocoa_service_server_interface(std::ofstream& out, t_service* tservice); + void generate_cocoa_service_server_implementation(std::ofstream& 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::ofstream& out, t_field* tfield, std::string fieldName); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string fieldName = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& 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_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 + */ + + std::ofstream f_header_; + std::ofstream 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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( + ofstream& 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( + ofstream& 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( + ofstream& 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( + ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 (((t_base_type*)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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 (((t_base_type*)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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 ((t_base_type*)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_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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc new file mode 100644 index 00000000..cbe8da22 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_cpp_generator.cc @@ -0,0 +1,4374 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::string; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * C++ code generator. This is legitimacy incarnate. + * + */ +class t_cpp_generator : public t_oop_generator { +public: + t_cpp_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; + + + gen_pure_enums_ = false; + use_include_prefix_ = false; + gen_cob_style_ = false; + gen_no_client_completion_ = false; + gen_no_default_operators_ = false; + gen_templates_ = false; + gen_templates_only_ = false; + gen_moveable_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("pure_enums") == 0) { + gen_pure_enums_ = true; + } else if( iter->first.compare("include_prefix") == 0) { + use_include_prefix_ = true; + } else if( iter->first.compare("cob_style") == 0) { + gen_cob_style_ = true; + } else if( iter->first.compare("no_client_completion") == 0) { + gen_no_client_completion_ = true; + } else if( iter->first.compare("no_default_operators") == 0) { + gen_no_default_operators_ = true; + } else if( iter->first.compare("templates") == 0) { + gen_templates_ = true; + gen_templates_only_ = (iter->second == "only"); + } else if( iter->first.compare("moveable_types") == 0) { + gen_moveable_ = true; + } else { + throw "unknown option cpp:" + iter->first; + } + } + + out_dir_base_ = "gen-cpp"; + } + + /** + * 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_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_cpp_struct(t_struct* tstruct, bool is_exception); + + void generate_service(t_service* tservice); + + void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); + std::string render_const_value(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + void generate_struct_declaration(std::ofstream& out, + t_struct* tstruct, + bool is_exception = false, + bool pointers = false, + bool read = true, + bool write = true, + bool swap = false, + bool is_user_struct = false); + void generate_struct_definition(std::ofstream& out, + std::ofstream& force_cpp_out, + t_struct* tstruct, + bool setters = true); + void generate_copy_constructor(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_move_constructor(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_constructor_helper(std::ofstream& out, + t_struct* tstruct, + bool is_excpetion, + bool is_move); + void generate_assignment_operator(std::ofstream& out, t_struct* tstruct); + void generate_move_assignment_operator(std::ofstream& out, t_struct* tstruct); + void generate_assignment_helper(std::ofstream& out, t_struct* tstruct, bool is_move); + void generate_struct_reader(std::ofstream& out, t_struct* tstruct, bool pointers = false); + void generate_struct_writer(std::ofstream& out, t_struct* tstruct, bool pointers = false); + void generate_struct_result_writer(std::ofstream& out, t_struct* tstruct, bool pointers = false); + void generate_struct_swap(std::ofstream& out, t_struct* tstruct); + void generate_struct_print_method(std::ofstream& out, t_struct* tstruct); + void generate_exception_what_method(std::ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + + void generate_service_interface(t_service* tservice, string style); + void generate_service_interface_factory(t_service* tservice, string style); + void generate_service_null(t_service* tservice, string style); + void generate_service_multiface(t_service* tservice); + void generate_service_helpers(t_service* tservice); + void generate_service_client(t_service* tservice, string style); + void generate_service_processor(t_service* tservice, string style); + void generate_service_skeleton(t_service* tservice); + void generate_process_function(t_service* tservice, + t_function* tfunction, + string style, + bool specialized = false); + void generate_function_helpers(t_service* tservice, t_function* tfunction); + void generate_service_async_skeleton(t_service* tservice); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + std::string suffix = ""); + + void generate_deserialize_struct(std::ofstream& out, + t_struct* tstruct, + std::string prefix = "", + bool pointer = false); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix, + bool push_back, + std::string index); + + void generate_serialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + std::string suffix = ""); + + void generate_serialize_struct(std::ofstream& out, + t_struct* tstruct, + std::string prefix = "", + bool pointer = false); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, t_map* tmap, std::string iter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_function_call(ostream& out, + t_function* tfunction, + string target, + string iface, + string arg_prefix); + /* + * Helper rendering functions + */ + + std::string namespace_prefix(std::string ns); + std::string namespace_open(std::string ns); + std::string namespace_close(std::string ns); + std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false); + std::string base_type_name(t_base_type::t_base tbase); + std::string declare_field(t_field* tfield, + bool init = false, + bool pointer = false, + bool constant = false, + bool reference = false); + std::string function_signature(t_function* tfunction, + std::string style, + std::string prefix = "", + bool name_params = true); + std::string cob_function_signature(t_function* tfunction, + std::string prefix = "", + bool name_params = true); + std::string argument_list(t_struct* tstruct, bool name_params = true, bool start_comma = false); + std::string type_to_enum(t_type* ttype); + + void generate_enum_constant_list(std::ofstream& f, + const vector& constants, + const char* prefix, + const char* suffix, + bool include_values); + + void generate_struct_ostream_operator(std::ofstream& f, t_struct* tstruct); + void generate_struct_print_method_decl(std::ofstream& f, t_struct* tstruct); + void generate_exception_what_method_decl(std::ofstream& f, + t_struct* tstruct, + bool external = false); + + bool is_reference(t_field* tfield) { return tfield->get_reference(); } + + bool is_complex_type(t_type* ttype) { + ttype = get_true_type(ttype); + + return ttype->is_container() || ttype->is_struct() || ttype->is_xception() + || (ttype->is_base_type() + && (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING)); + } + + void set_use_include_prefix(bool use_include_prefix) { use_include_prefix_ = use_include_prefix; } + +private: + /** + * Returns the include prefix to use for a file generated by program, or the + * empty string if no include prefix should be used. + */ + std::string get_include_prefix(const t_program& program) const; + + /** + * True if we should generate pure enums for Thrift enums, instead of wrapper classes. + */ + bool gen_pure_enums_; + + /** + * True if we should generate templatized reader/writer methods. + */ + bool gen_templates_; + + /** + * True iff we should generate process function pointers for only templatized + * reader/writer methods. + */ + bool gen_templates_only_; + + /** + * True if we should generate move constructors & assignment operators. + */ + bool gen_moveable_; + + /** + * True iff we should use a path prefix in our #include statements for other + * thrift-generated header files. + */ + bool use_include_prefix_; + + /** + * True if we should generate "Continuation OBject"-style classes as well. + */ + bool gen_cob_style_; + + /** + * True if we should omit calls to completion__() in CobClient class. + */ + bool gen_no_client_completion_; + + /** + * True if we should omit generating the default opeartors ==, != and <. + */ + bool gen_no_default_operators_; + + /** + * Strings for namespace, computed once up front then used directly + */ + + std::string ns_open_; + std::string ns_close_; + + /** + * File streams, stored here to avoid passing them as parameters to every + * function. + */ + + std::ofstream f_types_; + std::ofstream f_types_impl_; + std::ofstream f_types_tcc_; + std::ofstream f_header_; + std::ofstream f_service_; + std::ofstream f_service_tcc_; + + // The ProcessorGenerator is used to generate parts of the code, + // so it needs access to many of our protected members and methods. + // + // TODO: The code really should be cleaned up so that helper methods for + // writing to the output files are separate from the generator classes + // themselves. + friend class ProcessorGenerator; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + */ +void t_cpp_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Make output file + string f_types_name = get_out_dir() + program_name_ + "_types.h"; + f_types_.open(f_types_name.c_str()); + + string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp"; + f_types_impl_.open(f_types_impl_name.c_str()); + + if (gen_templates_) { + // If we don't open the stream, it appears to just discard data, + // which is fine. + string f_types_tcc_name = get_out_dir() + program_name_ + "_types.tcc"; + f_types_tcc_.open(f_types_tcc_name.c_str()); + } + + // Print header + f_types_ << autogen_comment(); + f_types_impl_ << autogen_comment(); + f_types_tcc_ << autogen_comment(); + + // Start ifndef + f_types_ << "#ifndef " << program_name_ << "_TYPES_H" << endl << "#define " << program_name_ + << "_TYPES_H" << endl << endl; + f_types_tcc_ << "#ifndef " << program_name_ << "_TYPES_TCC" << endl << "#define " << program_name_ + << "_TYPES_TCC" << endl << endl; + + // Include base types + f_types_ << "#include " << endl + << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << "#include " << endl + << endl; + // Include C++xx compatibility header + 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() + << "_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() + << "_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; + } else { + f_types_ << "#include \"" << cpp_includes[i] << "\"" << endl; + } + } + f_types_ << endl; + + // Include the types file + f_types_impl_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.h\"" << endl << endl; + f_types_tcc_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.h\"" << endl << endl; + + // The swap() code needs for std::swap() + f_types_impl_ << "#include " << endl; + // for operator<< + f_types_impl_ << "#include " << endl << endl; + f_types_impl_ << "#include " << endl << endl; + + // Open namespace + ns_open_ = namespace_open(program_->get_namespace("cpp")); + ns_close_ = namespace_close(program_->get_namespace("cpp")); + + f_types_ << ns_open_ << endl << endl; + + f_types_impl_ << ns_open_ << endl << endl; + + f_types_tcc_ << ns_open_ << endl << endl; +} + +/** + * Closes the output files. + */ +void t_cpp_generator::close_generator() { + // Close namespace + f_types_ << ns_close_ << endl << endl; + f_types_impl_ << ns_close_ << endl; + f_types_tcc_ << ns_close_ << endl << endl; + + // Include the types.tcc file from the types header file, + // so clients don't have to explicitly include the tcc file. + // TODO(simpkins): Make this a separate option. + if (gen_templates_) { + f_types_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.tcc\"" << endl << endl; + } + + // Close ifndef + f_types_ << "#endif" << endl; + f_types_tcc_ << "#endif" << endl; + + // Close output file + f_types_.close(); + f_types_impl_.close(); + f_types_tcc_.close(); +} + +/** + * Generates a typedef. This is just a simple 1-liner in C++ + * + * @param ttypedef The type definition + */ +void t_cpp_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " + << ttypedef->get_symbolic() << ";" << endl << endl; +} + +void t_cpp_generator::generate_enum_constant_list(std::ofstream& f, + const vector& constants, + const char* prefix, + const char* suffix, + bool include_values) { + f << " {" << endl; + indent_up(); + + vector::const_iterator c_iter; + bool first = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if (first) { + first = false; + } else { + f << "," << endl; + } + indent(f) << prefix << (*c_iter)->get_name() << suffix; + if (include_values) { + f << " = " << (*c_iter)->get_value(); + } + } + + f << endl; + indent_down(); + indent(f) << "};" << endl; +} + +/** + * Generates code for an enumerated type. In C++, this is essentially the same + * as the thrift definition itself, using the enum keyword in C++. + * + * @param tenum The enumeration + */ +void t_cpp_generator::generate_enum(t_enum* tenum) { + vector constants = tenum->get_constants(); + + std::string enum_name = tenum->get_name(); + if (!gen_pure_enums_) { + enum_name = "type"; + f_types_ << indent() << "struct " << tenum->get_name() << " {" << endl; + indent_up(); + } + f_types_ << indent() << "enum " << enum_name; + + generate_enum_constant_list(f_types_, constants, "", "", true); + + if (!gen_pure_enums_) { + indent_down(); + f_types_ << "};" << endl; + } + + f_types_ << endl; + + /** + Generate a character array of enum names for debugging purposes. + */ + std::string prefix = ""; + if (!gen_pure_enums_) { + prefix = tenum->get_name() + "::"; + } + + f_types_impl_ << indent() << "int _k" << tenum->get_name() << "Values[] ="; + generate_enum_constant_list(f_types_impl_, constants, prefix.c_str(), "", false); + + f_types_impl_ << indent() << "const char* _k" << tenum->get_name() << "Names[] ="; + generate_enum_constant_list(f_types_impl_, constants, "\"", "\"", false); + + f_types_ << indent() << "extern const std::map _" << tenum->get_name() + << "_VALUES_TO_NAMES;" << endl << endl; + + f_types_impl_ << indent() << "const std::map _" << tenum->get_name() + << "_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; +} + +/** + * 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 f_consts; + f_consts.open(f_consts_name.c_str()); + + string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp"; + ofstream f_consts_impl; + f_consts_impl.open(f_consts_impl_name.c_str()); + + // 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; + + 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_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_impl << endl << ns_close_ << endl << endl; +} + +/** + * 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_cpp_generator::print_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + type = get_true_type(type); + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + indent(out) << name << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + indent(out) << name << " = (" << type_name(type) << ")" << value->get_integer() << ";" << endl + << endl; + } 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; + bool is_nonrequired_field = false; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = NULL; + 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()) { + field_type = (*f_iter)->get_type(); + is_nonrequired_field = (*f_iter)->get_req() != t_field::T_REQUIRED; + } + } + if (field_type == NULL) { + 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); + indent(out) << name << "." << v_iter->first->get_string() << " = " << val << ";" << endl; + if (is_nonrequired_field) { + indent(out) << name << ".__isset." << v_iter->first->get_string() << " = true;" << endl; + } + } + out << endl; + } 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 << ".insert(std::make_pair(" << key << ", " << val << "));" << endl; + } + out << endl; + } else if (type->is_list()) { + t_type* etype = ((t_list*)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 << ".push_back(" << val << ");" << endl; + } + out << endl; + } else if (type->is_set()) { + t_type* 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 << ".insert(" << val << ");" << endl; + } + out << endl; + } else { + throw "INVALID TYPE IN print_const_value: " + type->get_name(); + } +} + +/** + * + */ +string t_cpp_generator::render_const_value(ofstream& 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: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "LL"; + 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_name(type) << ")" << value->get_integer(); + } else { + string t = tmp("tmp"); + indent(out) << type_name(type) << " " << t << ";" << endl; + print_const_value(out, t, type, value); + render << t; + } + + return render.str(); +} + +void t_cpp_generator::generate_forward_declaration(t_struct* tstruct) { + // Forward declare struct def + f_types_ << indent() << "class " << tstruct->get_name() << ";" << endl << endl; +} + +/** + * Generates a struct definition for a thrift data type. This is a class + * with data members and a read/write() function, plus a mirroring isset + * inner class. + * + * @param tstruct The struct definition + */ +void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) { + generate_struct_declaration(f_types_, tstruct, is_exception, false, true, true, true, true); + generate_struct_definition(f_types_impl_, f_types_impl_, tstruct); + + std::ofstream& out = (gen_templates_ ? f_types_tcc_ : f_types_impl_); + generate_struct_reader(out, tstruct); + generate_struct_writer(out, tstruct); + generate_struct_swap(f_types_impl_, tstruct); + generate_copy_constructor(f_types_impl_, tstruct, is_exception); + if (gen_moveable_) { + generate_move_constructor(f_types_impl_, tstruct, is_exception); + } + generate_assignment_operator(f_types_impl_, tstruct); + if (gen_moveable_) { + generate_move_assignment_operator(f_types_impl_, tstruct); + } + generate_struct_print_method(f_types_impl_, tstruct); + if (is_exception) { + generate_exception_what_method(f_types_impl_, tstruct); + } +} + +void t_cpp_generator::generate_copy_constructor(ofstream& out, + t_struct* tstruct, + bool is_exception) { + generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/false); +} + +void t_cpp_generator::generate_move_constructor(ofstream& out, + t_struct* tstruct, + bool is_exception) { + generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/true); +} + +namespace { +// Helper to convert a variable to rvalue, if move is enabled +std::string maybeMove(std::string const& other, bool move) { + if (move) { + return "std::move(" + other + ")"; + } + return other; +} +} + +void t_cpp_generator::generate_constructor_helper(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_move) { + + std::string tmp_name = tmp("other"); + + indent(out) << tstruct->get_name() << "::" << tstruct->get_name(); + + if (is_move) { + out << "( " << tstruct->get_name() << "&& "; + } else { + out << "(const " << tstruct->get_name() << "& "; + } + out << tmp_name << ") "; + if (is_exception) + out << ": TException() "; + out << "{" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + + // eliminate compiler unused warning + if (members.empty()) + indent(out) << "(void) " << tmp_name << ";" << endl; + + vector::const_iterator f_iter; + bool has_nonrequired_fields = false; + for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) { + if ((*f_iter)->get_req() != t_field::T_REQUIRED) + has_nonrequired_fields = true; + indent(out) << (*f_iter)->get_name() << " = " + << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl; + } + + if (has_nonrequired_fields) { + indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl; + } + + indent_down(); + indent(out) << "}" << endl; +} + +void t_cpp_generator::generate_assignment_operator(ofstream& out, t_struct* tstruct) { + generate_assignment_helper(out, tstruct, /*is_move=*/false); +} + +void t_cpp_generator::generate_move_assignment_operator(ofstream& out, t_struct* tstruct) { + generate_assignment_helper(out, tstruct, /*is_move=*/true); +} + +void t_cpp_generator::generate_assignment_helper(ofstream& out, t_struct* tstruct, bool is_move) { + std::string tmp_name = tmp("other"); + + indent(out) << tstruct->get_name() << "& " << tstruct->get_name() << "::operator=("; + + if (is_move) { + out << tstruct->get_name() << "&& "; + } else { + out << "const " << tstruct->get_name() << "& "; + } + out << tmp_name << ") {" << endl; + + indent_up(); + + const vector& members = tstruct->get_members(); + + // eliminate compiler unused warning + if (members.empty()) + indent(out) << "(void) " << tmp_name << ";" << endl; + + vector::const_iterator f_iter; + bool has_nonrequired_fields = false; + for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) { + if ((*f_iter)->get_req() != t_field::T_REQUIRED) + has_nonrequired_fields = true; + indent(out) << (*f_iter)->get_name() << " = " + << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl; + } + if (has_nonrequired_fields) { + indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl; + } + + indent(out) << "return *this;" << endl; + indent_down(); + indent(out) << "}" << endl; +} + +/** + * Writes the struct declaration into the header file + * + * @param out Output stream + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_declaration(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool pointers, + bool read, + bool write, + bool swap, + bool is_user_struct) { + string extends = ""; + if (is_exception) { + extends = " : public ::apache::thrift::TException"; + } else { + if (is_user_struct && !gen_templates_) { + extends = " : public virtual ::apache::thrift::TBase"; + } + } + + // Get members + vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + + // Write the isset structure declaration outside the class. This makes + // the generated code amenable to processing by SWIG. + // We only declare the struct if it gets used in the class. + + // Isset struct has boolean fields, but only for non-required fields. + bool has_nonrequired_fields = false; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_REQUIRED) + has_nonrequired_fields = true; + } + + if (has_nonrequired_fields && (!pointers || read)) { + + out << indent() << "typedef struct _" << tstruct->get_name() << "__isset {" << endl; + indent_up(); + + indent(out) << "_" << tstruct->get_name() << "__isset() "; + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() == t_field::T_REQUIRED) { + continue; + } + string isSet = ((*m_iter)->get_value() != NULL) ? "true" : "false"; + if (first) { + first = false; + out << ": " << (*m_iter)->get_name() << "(" << isSet << ")"; + } else { + out << ", " << (*m_iter)->get_name() << "(" << isSet << ")"; + } + } + out << " {}" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + indent(out) << "bool " << (*m_iter)->get_name() << " :1;" << endl; + } + } + + indent_down(); + indent(out) << "} _" << tstruct->get_name() << "__isset;" << endl; + } + + out << endl; + + // Open struct def + out << indent() << "class " << tstruct->get_name() << extends << " {" << endl << indent() + << " public:" << endl << endl; + indent_up(); + + if (!pointers) { + // Copy constructor + indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&);" << endl; + + // Move constructor + if (gen_moveable_) { + indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&);" << endl; + } + + // Assignment Operator + indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&);" + << endl; + + // Move assignment operator + if (gen_moveable_) { + indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&);" << endl; + } + + // Default constructor + indent(out) << tstruct->get_name() << "()"; + + bool init_ctor = false; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (t->is_base_type() || t->is_enum() || is_reference(*m_iter)) { + string dval; + if (t->is_enum()) { + dval += "(" + type_name(t) + ")"; + } + dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0"; + t_const_value* cv = (*m_iter)->get_value(); + if (cv != NULL) { + dval = render_const_value(out, (*m_iter)->get_name(), t, cv); + } + if (!init_ctor) { + init_ctor = true; + out << " : "; + out << (*m_iter)->get_name() << "(" << dval << ")"; + } else { + out << ", " << (*m_iter)->get_name() << "(" << dval << ")"; + } + } + } + out << " {" << endl; + indent_up(); + // TODO(dreiss): When everything else in Thrift is perfect, + // do more of these in the initializer list. + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + + if (!t->is_base_type()) { + t_const_value* cv = (*m_iter)->get_value(); + if (cv != NULL) { + print_const_value(out, (*m_iter)->get_name(), t, cv); + } + } + } + scope_down(out); + } + + if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { + out << endl << indent() << "virtual ~" << tstruct->get_name() << "() throw();" << endl; + } + + // Declare all fields + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << declare_field(*m_iter, + false, + (pointers && !(*m_iter)->get_type()->is_xception()), + !read) << endl; + } + + // Add the __isset data member if we need it, using the definition from above + if (has_nonrequired_fields && (!pointers || read)) { + out << endl << indent() << "_" << tstruct->get_name() << "__isset __isset;" << endl; + } + + // Create a setter function for each field + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (pointers) { + continue; + } + if (is_reference((*m_iter))) { + out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(boost::shared_ptr<" + << type_name((*m_iter)->get_type(), false, false) << ">"; + out << " val);" << endl; + } else { + out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(" + << type_name((*m_iter)->get_type(), false, true); + out << " val);" << endl; + } + } + out << endl; + + if (!pointers) { + // Should we generate default operators? + if (!gen_no_default_operators_) { + // Generate an equality testing operator. Make it inline since the compiler + // will do a better job than we would when deciding whether to inline it. + out << indent() << "bool operator == (const " << tstruct->get_name() << " & " + << (members.size() > 0 ? "rhs" : "/* rhs */") << ") const" << endl; + scope_up(out); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + // Most existing Thrift code does not use isset or optional/required, + // so we treat "default" fields as required. + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + out << indent() << "if (!(" << (*m_iter)->get_name() << " == rhs." + << (*m_iter)->get_name() << "))" << endl << indent() << " return false;" << endl; + } else { + out << indent() << "if (__isset." << (*m_iter)->get_name() << " != rhs.__isset." + << (*m_iter)->get_name() << ")" << endl << indent() << " return false;" << endl + << indent() << "else if (__isset." << (*m_iter)->get_name() << " && !(" + << (*m_iter)->get_name() << " == rhs." << (*m_iter)->get_name() << "))" << endl + << indent() << " return false;" << endl; + } + } + indent(out) << "return true;" << endl; + scope_down(out); + out << indent() << "bool operator != (const " << tstruct->get_name() << " &rhs) const {" + << endl << indent() << " return !(*this == rhs);" << endl << indent() << "}" << endl + << endl; + + // Generate the declaration of a less-than operator. This must be + // implemented by the application developer if they wish to use it. (They + // will get a link error if they try to use it without an implementation.) + out << indent() << "bool operator < (const " << tstruct->get_name() << " & ) const;" << endl + << endl; + } + } + + if (read) { + if (gen_templates_) { + out << indent() << "template " << endl << indent() + << "uint32_t read(Protocol_* iprot);" << endl; + } else { + out << indent() << "uint32_t read(" + << "::apache::thrift::protocol::TProtocol* iprot);" << endl; + } + } + if (write) { + if (gen_templates_) { + out << indent() << "template " << endl << indent() + << "uint32_t write(Protocol_* oprot) const;" << endl; + } else { + out << indent() << "uint32_t write(" + << "::apache::thrift::protocol::TProtocol* oprot) const;" << endl; + } + } + out << endl; + + if (is_user_struct) { + out << indent() << "virtual "; + generate_struct_print_method_decl(out, NULL); + out << ";" << endl; + } + + // std::exception::what() + if (is_exception) { + out << indent() << "mutable std::string thriftTExceptionMessageHolder_;" << endl; + out << indent(); + generate_exception_what_method_decl(out, tstruct, false); + out << ";" << endl; + } + + indent_down(); + indent(out) << "};" << endl << endl; + + if (swap) { + // Generate a namespace-scope swap() function + out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() + << " &b);" << endl << endl; + } + + if (is_user_struct) { + generate_struct_ostream_operator(out, tstruct); + } +} + +void t_cpp_generator::generate_struct_definition(ofstream& out, + ofstream& force_cpp_out, + t_struct* tstruct, + bool setters) { + // Get members + vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + + // Destructor + if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { + force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name() + << "() throw() {" << endl; + indent_up(); + + indent_down(); + force_cpp_out << indent() << "}" << endl << endl; + } + + // Create a setter function for each field + 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() << "(boost::shared_ptr<" + << type_name((*m_iter)->get_type(), false, false) << ">"; + out << " val) {" << endl; + } else { + out << endl << indent() << "void " << tstruct->get_name() << "::__set_" + << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true); + out << " val) {" << endl; + } + indent_up(); + out << indent() << "this->" << (*m_iter)->get_name() << " = val;" << endl; + indent_down(); + + // assume all fields are required except optional fields. + // for optional fields change __isset.name to true + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + if (is_optional) { + out << indent() << indent() << "__isset." << (*m_iter)->get_name() << " = true;" << endl; + } + out << indent() << "}" << endl; + } + } + out << endl; +} + +/** + * Makes a helper function to gen a struct reader. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_reader(ofstream& out, t_struct* tstruct, bool pointers) { + if (gen_templates_) { + out << indent() << "template " << endl << indent() << "uint32_t " + << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl; + } + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables + out << endl + << indent() << "apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);" << endl + << indent() << "uint32_t xfer = 0;" << endl + << indent() << "std::string fname;" << endl + << indent() << "::apache::thrift::protocol::TType ftype;" << endl + << indent() << "int16_t fid;" << endl + << endl + << indent() << "xfer += iprot->readStructBegin(fname);" << endl + << endl + << indent() << "using ::apache::thrift::protocol::TProtocolException;" << endl + << endl; + + // 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 ((*f_iter)->get_req() == t_field::T_REQUIRED) + indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; + } + out << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "xfer += iprot->readFieldBegin(fname, ftype, fid);" << endl; + + // Check for field STOP marker + out << indent() << "if (ftype == ::apache::thrift::protocol::T_STOP) {" << endl << indent() + << " break;" << endl << indent() << "}" << endl; + + if (fields.empty()) { + out << indent() << "xfer += iprot->skip(ftype);" << endl; + } else { + // Switch statement on the field we are reading + indent(out) << "switch (fid)" << 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 (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + const char* isset_prefix = ((*f_iter)->get_req() != t_field::T_REQUIRED) ? "this->__isset." + : "isset_"; + +#if 0 + // This code throws an exception if the same field is encountered twice. + // We've decided to leave it out for performance reasons. + // TODO(dreiss): Generate this code and "if" it out to make it easier + // for people recompiling thrift to include it. + out << + indent() << "if (" << isset_prefix << (*f_iter)->get_name() << ")" << endl << + indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl; +#endif + + if (pointers && !(*f_iter)->get_type()->is_xception()) { + generate_deserialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_deserialize_field(out, *f_iter, "this->"); + } + out << indent() << isset_prefix << (*f_iter)->get_name() << " = true;" << endl; + indent_down(); + out << indent() << "} else {" << endl << indent() << " xfer += iprot->skip(ftype);" << endl + << + // TODO(dreiss): Make this an option when thrift structs + // have a common base class. + // indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl << + indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "default:" << endl << indent() << " xfer += iprot->skip(ftype);" << endl + << indent() << " break;" << endl; + + scope_down(out); + } //!fields.empty() + // Read field end marker + indent(out) << "xfer += iprot->readFieldEnd();" << endl; + + scope_down(out); + + out << endl << indent() << "xfer += iprot->readStructEnd();" << endl; + + // Throw if any required fields are missing. + // We do this after reading the struct end so that + // there might possibly be a chance of continuing. + out << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) + out << indent() << "if (!isset_" << (*f_iter)->get_name() << ')' << endl << indent() + << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl; + } + + indent(out) << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates the write function. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_writer(ofstream& out, t_struct* tstruct, bool pointers) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + if (gen_templates_) { + out << indent() << "template " << endl << indent() << "uint32_t " + << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl; + } + indent_up(); + + out << indent() << "uint32_t xfer = 0;" << endl; + + indent(out) << "apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);" << endl; + indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool check_if_set = (*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_type()->is_xception(); + if (check_if_set) { + out << endl << indent() << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl; + indent_up(); + } else { + out << endl; + } + + // Write field header + out << indent() << "xfer += oprot->writeFieldBegin(" + << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " + << (*f_iter)->get_key() << ");" << endl; + // Write field contents + if (pointers && !(*f_iter)->get_type()->is_xception()) { + generate_serialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_serialize_field(out, *f_iter, "this->"); + } + // Write field closer + indent(out) << "xfer += oprot->writeFieldEnd();" << endl; + if (check_if_set) { + indent_down(); + indent(out) << '}'; + } + } + + out << endl; + + // Write the struct map + out << indent() << "xfer += oprot->writeFieldStop();" << endl << indent() + << "xfer += oprot->writeStructEnd();" << endl << indent() + << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Struct writer for result of a function, which can have only one of its + * fields set and does a conditional if else look up into the __isset field + * of the struct. + * + * @param out Output stream + * @param tstruct The result struct + */ +void t_cpp_generator::generate_struct_result_writer(ofstream& out, + t_struct* tstruct, + bool pointers) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + if (gen_templates_) { + out << indent() << "template " << endl << indent() << "uint32_t " + << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl; + } + indent_up(); + + out << endl << indent() << "uint32_t xfer = 0;" << endl << endl; + + indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << 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 << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl; + + indent_up(); + + // Write field header + out << indent() << "xfer += oprot->writeFieldBegin(" + << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " + << (*f_iter)->get_key() << ");" << endl; + // Write field contents + if (pointers) { + generate_serialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_serialize_field(out, *f_iter, "this->"); + } + // Write field closer + indent(out) << "xfer += oprot->writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}"; + } + + // Write the struct map + out << endl << indent() << "xfer += oprot->writeFieldStop();" << endl << indent() + << "xfer += oprot->writeStructEnd();" << endl << indent() << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates the swap function. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_swap(ofstream& out, t_struct* tstruct) { + 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 + // use based on the argument types. If none is found in the arguments' + // namespaces, fall back to ::std::swap(). + out << indent() << "using ::std::swap;" << endl; + + 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; + + 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 (has_nonrequired_fields) { + 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; + } + + scope_down(out); + out << endl; +} + +void t_cpp_generator::generate_struct_ostream_operator(std::ofstream& out, t_struct* tstruct) { + out << "inline std::ostream& operator<<(std::ostream& out, const " + << tstruct->get_name() + << "& obj)" << endl; + scope_up(out); + out << indent() << "obj.printTo(out);" << endl + << indent() << "return out;" << endl; + scope_down(out); + out << endl; +} + +void t_cpp_generator::generate_struct_print_method_decl(std::ofstream& out, t_struct* tstruct) { + out << "void "; + if (tstruct) { + out << tstruct->get_name() << "::"; + } + out << "printTo(std::ostream& out) const"; +} + +void t_cpp_generator::generate_exception_what_method_decl(std::ofstream& out, + t_struct* tstruct, + bool external) { + out << "const char* "; + if (external) { + out << tstruct->get_name() << "::"; + } + out << "what() const throw()"; +} + +namespace struct_ostream_operator_generator { +void generate_required_field_value(std::ofstream& out, const t_field* field) { + out << " << to_string(" << field->get_name() << ")"; +} + +void generate_optional_field_value(std::ofstream& out, const t_field* field) { + out << "; (__isset." << field->get_name() << " ? (out"; + generate_required_field_value(out, field); + out << ") : (out << \"\"))"; +} + +void generate_field_value(std::ofstream& out, const t_field* field) { + if (field->get_req() == t_field::T_OPTIONAL) + generate_optional_field_value(out, field); + else + generate_required_field_value(out, field); +} + +void generate_field_name(std::ofstream& out, const t_field* field) { + out << "\"" << field->get_name() << "=\""; +} + +void generate_field(std::ofstream& out, const t_field* field) { + generate_field_name(out, field); + generate_field_value(out, field); +} + +void generate_fields(std::ofstream& out, + const vector& fields, + const std::string& indent) { + const vector::const_iterator beg = fields.begin(); + const vector::const_iterator end = fields.end(); + + for (vector::const_iterator it = beg; it != end; ++it) { + out << indent << "out << "; + + if (it != beg) { + out << "\", \" << "; + } + + generate_field(out, *it); + out << ";" << endl; + } +} +} + +/** + * Generates operator<< + */ +void t_cpp_generator::generate_struct_print_method(std::ofstream& out, t_struct* tstruct) { + out << indent(); + generate_struct_print_method_decl(out, tstruct); + out << " {" << endl; + + indent_up(); + + out << indent() << "using ::apache::thrift::to_string;" << endl; + out << indent() << "out << \"" << tstruct->get_name() << "(\";" << endl; + struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent()); + out << indent() << "out << \")\";" << endl; + + indent_down(); + out << "}" << endl << endl; +} + +/** + * Generates what() method for exceptions + */ +void t_cpp_generator::generate_exception_what_method(std::ofstream& out, t_struct* tstruct) { + out << indent(); + generate_exception_what_method_decl(out, tstruct, true); + out << " {" << endl; + + indent_up(); + out << indent() << "try {" << endl; + + indent_up(); + out << indent() << "std::stringstream ss;" << endl; + out << indent() << "ss << \"TException - service has thrown: \" << *this;" << endl; + out << indent() << "this->thriftTExceptionMessageHolder_ = ss.str();" << endl; + out << indent() << "return this->thriftTExceptionMessageHolder_.c_str();" << endl; + indent_down(); + + out << indent() << "} catch (const std::exception&) {" << endl; + + indent_up(); + out << indent() << "return \"TException - service has thrown: " << tstruct->get_name() << "\";" + << endl; + indent_down(); + + out << indent() << "}" << endl; + + indent_down(); + out << "}" << endl << endl; +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_cpp_generator::generate_service(t_service* tservice) { + string svcname = tservice->get_name(); + + // Make output files + string f_header_name = get_out_dir() + svcname + ".h"; + f_header_.open(f_header_name.c_str()); + + // Print header file includes + f_header_ << autogen_comment(); + f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl + << endl; + if (gen_cob_style_) { + f_header_ << "#include " << endl << // TMemoryBuffer + "#include " << endl + << "namespace apache { namespace thrift { namespace async {" << endl + << "class TAsyncChannel;" << endl << "}}}" << endl; + } + f_header_ << "#include " << endl; + if (gen_cob_style_) { + 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) { + f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program())) + << extends_service->get_name() << ".h\"" << endl; + } + + f_header_ << endl << ns_open_ << endl << endl; + + f_header_ << "#ifdef _WIN32\n" + " #pragma warning( push )\n" + " #pragma warning (disable : 4250 ) //inheriting methods via dominance \n" + "#endif\n\n"; + + // Service implementation file includes + string f_service_name = get_out_dir() + svcname + ".cpp"; + f_service_.open(f_service_name.c_str()); + f_service_ << autogen_comment(); + f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl; + if (gen_cob_style_) { + f_service_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl; + } + if (gen_templates_) { + f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" + << endl; + + string f_service_tcc_name = get_out_dir() + svcname + ".tcc"; + f_service_tcc_.open(f_service_tcc_name.c_str()); + f_service_tcc_ << autogen_comment(); + f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" + << endl; + + f_service_tcc_ << "#ifndef " << svcname << "_TCC" << endl << "#define " << svcname << "_TCC" + << endl << endl; + + if (gen_cob_style_) { + f_service_tcc_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl; + } + } + + f_service_ << endl << ns_open_ << endl << endl; + f_service_tcc_ << endl << ns_open_ << endl << endl; + + // Generate all the components + generate_service_interface(tservice, ""); + generate_service_interface_factory(tservice, ""); + generate_service_null(tservice, ""); + generate_service_helpers(tservice); + generate_service_client(tservice, ""); + generate_service_processor(tservice, ""); + generate_service_multiface(tservice); + generate_service_skeleton(tservice); + generate_service_client(tservice, "Concurrent"); + + // Generate all the cob components + if (gen_cob_style_) { + generate_service_interface(tservice, "CobCl"); + generate_service_interface(tservice, "CobSv"); + generate_service_interface_factory(tservice, "CobSv"); + generate_service_null(tservice, "CobSv"); + generate_service_client(tservice, "Cob"); + generate_service_processor(tservice, "Cob"); + generate_service_async_skeleton(tservice); + } + + f_header_ << "#ifdef _WIN32\n" + " #pragma warning( pop )\n" + "#endif\n\n"; + + // Close the namespace + f_service_ << ns_close_ << endl << endl; + f_service_tcc_ << ns_close_ << endl << endl; + f_header_ << ns_close_ << endl << endl; + + // TODO(simpkins): Make this a separate option + if (gen_templates_) { + f_header_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" << endl + << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.tcc\"" << endl << endl; + } + + f_header_ << "#endif" << endl; + f_service_tcc_ << "#endif" << endl; + + // Close the files + f_service_tcc_.close(); + f_service_.close(); + f_header_.close(); +} + +/** + * Generates helper functions for a service. Basically, this generates types + * for all the arguments and results to functions. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + string name_orig = ts->get_name(); + + // TODO(dreiss): Why is this stuff not in generate_function_helpers? + ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args"); + generate_struct_declaration(f_header_, ts, false); + generate_struct_definition(out, f_service_, ts, false); + generate_struct_reader(out, ts); + generate_struct_writer(out, ts); + ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs"); + generate_struct_declaration(f_header_, ts, false, true, false, true); + generate_struct_definition(out, f_service_, ts, false); + generate_struct_writer(out, ts, true); + ts->set_name(name_orig); + + generate_function_helpers(tservice, *f_iter); + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_interface(t_service* tservice, string style) { + + string service_if_name = service_name_ + style + "If"; + if (style == "CobCl") { + // Forward declare the client. + string client_name = service_name_ + "CobClient"; + if (gen_templates_) { + client_name += "T"; + service_if_name += "T"; + indent(f_header_) << "template " << endl; + } + indent(f_header_) << "class " << client_name << ";" << endl << endl; + } + + string extends = ""; + if (tservice->get_extends() != NULL) { + 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 + // parent services were also generated with templates enabled. + extends += "T"; + } + } + + if (style == "CobCl" && gen_templates_) { + f_header_ << "template " << endl; + } + f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl; + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if ((*f_iter)->has_doc()) + f_header_ << endl; + generate_java_doc(f_header_, *f_iter); + f_header_ << indent() << "virtual " << function_signature(*f_iter, style) << " = 0;" << endl; + } + indent_down(); + f_header_ << "};" << endl << endl; + + if (style == "CobCl" && gen_templates_) { + // generate a backwards-compatible typedef for clients that do not + // know about the new template-style code + f_header_ << "typedef " << service_if_name << "< ::apache::thrift::protocol::TProtocol> " + << service_name_ << style << "If;" << endl << endl; + } +} + +/** + * Generates a service interface factory. + * + * @param tservice The service to generate an interface factory for. + */ +void t_cpp_generator::generate_service_interface_factory(t_service* tservice, string style) { + string service_if_name = service_name_ + style + "If"; + + // Figure out the name of the upper-most parent class. + // Getting everything to work out properly with inheritance is annoying. + // Here's what we're doing for now: + // + // - All handlers implement getHandler(), but subclasses use covariant return + // types to return their specific service interface class type. We have to + // use raw pointers because of this; shared_ptr<> can't be used for + // covariant return types. + // + // - Since we're not using shared_ptr<>, we also provide a releaseHandler() + // function that must be called to release a pointer to a handler obtained + // via getHandler(). + // + // releaseHandler() always accepts a pointer to the upper-most parent class + // type. This is necessary since the parent versions of releaseHandler() + // may accept any of the parent types, not just the most specific subclass + // 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) { + base_service = base_service->get_extends(); + } + string base_if_name = type_name(base_service) + style + "If"; + + // Generate the abstract factory class + string factory_name = service_if_name + "Factory"; + string extends; + if (tservice->get_extends() != NULL) { + extends = " : virtual public " + type_name(tservice->get_extends()) + style + "IfFactory"; + } + + f_header_ << "class " << factory_name << extends << " {" << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << "typedef " << service_if_name << " Handler;" << endl << endl << indent() + << "virtual ~" << factory_name << "() {}" << endl << endl << indent() << "virtual " + << service_if_name << "* getHandler(" + << "const ::apache::thrift::TConnectionInfo& connInfo) = 0;" << endl << indent() + << "virtual void releaseHandler(" << base_if_name << "* /* handler */) = 0;" << endl; + + indent_down(); + f_header_ << "};" << endl << endl; + + // Generate the singleton factory class + string singleton_factory_name = service_if_name + "SingletonFactory"; + f_header_ << "class " << singleton_factory_name << " : virtual public " << factory_name << " {" + << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << singleton_factory_name << "(const boost::shared_ptr<" << service_if_name + << ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~" + << singleton_factory_name << "() {}" << endl << endl << indent() << "virtual " + << service_if_name << "* getHandler(" + << "const ::apache::thrift::TConnectionInfo&) {" << endl << indent() + << " return iface_.get();" << endl << indent() << "}" << endl << indent() + << "virtual void releaseHandler(" << base_if_name << "* /* handler */) {}" << endl; + + f_header_ << endl << " protected:" << endl << indent() << "boost::shared_ptr<" << service_if_name + << "> iface_;" << endl; + + indent_down(); + f_header_ << "};" << endl << endl; +} + +/** + * Generates a null implementation of the service. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_null(t_service* tservice, string style) { + string extends = ""; + if (tservice->get_extends() != NULL) { + extends = " , virtual public " + type_name(tservice->get_extends()) + style + "Null"; + } + f_header_ << "class " << service_name_ << style << "Null : virtual public " << service_name_ + << style << "If" << extends << " {" << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << "virtual ~" << service_name_ << style << "Null() {}" << endl; + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_header_ << indent() << function_signature(*f_iter, style, "", false) << " {" << endl; + indent_up(); + + t_type* returntype = (*f_iter)->get_returntype(); + t_field returnfield(returntype, "_return"); + + if (style == "") { + if (returntype->is_void() || is_complex_type(returntype)) { + f_header_ << indent() << "return;" << endl; + } else { + f_header_ << indent() << declare_field(&returnfield, true) << endl << indent() + << "return _return;" << endl; + } + } else if (style == "CobSv") { + if (returntype->is_void()) { + f_header_ << indent() << "return cob();" << endl; + } else { + t_field returnfield(returntype, "_return"); + f_header_ << indent() << declare_field(&returnfield, true) << endl << indent() + << "return cob(_return);" << endl; + } + + } else { + throw "UNKNOWN STYLE"; + } + + indent_down(); + f_header_ << indent() << "}" << endl; + } + indent_down(); + f_header_ << "};" << endl << endl; +} + +void t_cpp_generator::generate_function_call(ostream& out, + t_function* tfunction, + string target, + string iface, + string arg_prefix) { + bool first = true; + t_type* ret_type = get_true_type(tfunction->get_returntype()); + out << indent(); + if (!tfunction->is_oneway() && !ret_type->is_void()) { + if (is_complex_type(ret_type)) { + first = false; + out << iface << "->" << tfunction->get_name() << "(" << target; + } else { + out << target << " = " << iface << "->" << tfunction->get_name() << "("; + } + } else { + out << iface << "->" << tfunction->get_name() << "("; + } + const std::vector& fields = tfunction->get_arglist()->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + out << ", "; + } + out << arg_prefix << (*f_iter)->get_name(); + } + out << ");" << endl; +} + +void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) { + string svcname = tservice->get_name(); + + // Service implementation file includes + string f_skeleton_name = get_out_dir() + svcname + "_async_server.skeleton.cpp"; + + string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp")); + + ofstream f_skeleton; + f_skeleton.open(f_skeleton_name.c_str()); + f_skeleton << "// This autogenerated skeleton file illustrates one way to adapt a synchronous" + << endl << "// interface into an asynchronous interface. You should copy it to another" + << endl + << "// 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 + << "using namespace ::apache::thrift;" << endl + << "using namespace ::apache::thrift::protocol;" << endl + << "using namespace ::apache::thrift::transport;" << endl + << "using namespace ::apache::thrift::async;" << endl << endl + << "using boost::shared_ptr;" << endl << endl; + + // the following code would not compile: + // using namespace ; + // using namespace ::; + if ((!ns.empty()) && (ns.compare(" ::") != 0)) { + f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << 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 + << "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; + indent_up(); + + t_type* returntype = (*f_iter)->get_returntype(); + t_field returnfield(returntype, "_return"); + + string target = returntype->is_void() ? "" : "_return"; + if (!returntype->is_void()) { + f_skeleton << indent() << declare_field(&returnfield, true) << endl; + } + generate_function_call(f_skeleton, *f_iter, target, "syncHandler_", ""); + f_skeleton << indent() << "return cob(" << target << ");" << endl; + + scope_down(f_skeleton); + } + f_skeleton << endl << " protected:" << endl << indent() << "std::auto_ptr<" << svcname + << "Handler> syncHandler_;" << endl; + indent_down(); + f_skeleton << "};" << endl << endl; +} + +/** + * Generates a multiface, which is a single server that just takes a set + * of objects implementing the interface and calls them all, returning the + * value of the last one to be called. + * + * @param tservice The service to generate a multiserver for. + */ +void t_cpp_generator::generate_service_multiface(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_multiface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_multiface = ", public " + extends + "Multiface"; + } + + string list_type = string("std::vector >"; + + // Generate the header portion + f_header_ << "class " << service_name_ << "Multiface : " + << "virtual public " << service_name_ << "If" << extends_multiface << " {" << endl + << " public:" << endl; + indent_up(); + f_header_ << indent() << service_name_ << "Multiface(" << list_type + << "& ifaces) : ifaces_(ifaces) {" << endl; + if (!extends.empty()) { + f_header_ << indent() + << " std::vector >::iterator iter;" + << endl << indent() << " for (iter = ifaces.begin(); iter != ifaces.end(); ++iter) {" + << endl << indent() << " " << extends << "Multiface::add(*iter);" << endl + << indent() << " }" << endl; + } + f_header_ << indent() << "}" << endl << indent() << "virtual ~" << service_name_ + << "Multiface() {}" << endl; + indent_down(); + + // Protected data members + f_header_ << " protected:" << endl; + indent_up(); + f_header_ << indent() << list_type << " ifaces_;" << endl << indent() << service_name_ + << "Multiface() {}" << endl << indent() << "void add(boost::shared_ptr<" + << service_name_ << "If> iface) {" << endl; + if (!extends.empty()) { + f_header_ << indent() << " " << extends << "Multiface::add(iface);" << endl; + } + f_header_ << indent() << " ifaces_.push_back(iface);" << endl << indent() << "}" << endl; + indent_down(); + + f_header_ << indent() << " public:" << endl; + indent_up(); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* arglist = (*f_iter)->get_arglist(); + const vector& args = arglist->get_members(); + vector::const_iterator a_iter; + + string call = string("ifaces_[i]->") + (*f_iter)->get_name() + "("; + bool first = true; + if (is_complex_type((*f_iter)->get_returntype())) { + call += "_return"; + first = false; + } + for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) { + if (first) { + first = false; + } else { + call += ", "; + } + call += (*a_iter)->get_name(); + } + call += ")"; + + f_header_ << indent() << function_signature(*f_iter, "") << " {" << endl; + indent_up(); + f_header_ << indent() << "size_t sz = ifaces_.size();" << endl << indent() << "size_t i = 0;" + << endl << indent() << "for (; i < (sz - 1); ++i) {" << endl; + indent_up(); + f_header_ << indent() << call << ";" << endl; + indent_down(); + f_header_ << indent() << "}" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + if (is_complex_type((*f_iter)->get_returntype())) { + f_header_ << indent() << call << ";" << endl << indent() << "return;" << endl; + } else { + f_header_ << indent() << "return " << call << ";" << endl; + } + } else { + f_header_ << indent() << call << ";" << endl; + } + + indent_down(); + f_header_ << indent() << "}" << endl << endl; + } + + indent_down(); + f_header_ << indent() << "};" << endl << endl; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_client(t_service* tservice, string style) { + string ifstyle; + if (style == "Cob") { + ifstyle = "CobCl"; + } + + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + string template_header, template_suffix, short_suffix, protocol_type, _this; + string const prot_factory_type = "::apache::thrift::protocol::TProtocolFactory"; + if (gen_templates_) { + template_header = "template \n"; + short_suffix = "T"; + template_suffix = "T"; + protocol_type = "Protocol_"; + _this = "this->"; + } else { + protocol_type = "::apache::thrift::protocol::TProtocol"; + } + string prot_ptr = "boost::shared_ptr< " + protocol_type + ">"; + string client_suffix = "Client" + template_suffix; + string if_suffix = "If"; + if (style == "Cob") { + if_suffix += template_suffix; + } + + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + // 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()); + extends_client = ", public " + extends + style + client_suffix; + } + + // Generate the header portion + if (style == "Concurrent") { + f_header_ << "// The \'concurrent\' client is a thread safe client that correctly handles\n" + "// out of order responses. It is slower than the regular client, so should\n" + "// only be used when you need to share a connection among multiple threads\n"; + } + f_header_ << template_header << "class " << service_name_ << style << "Client" << short_suffix + << " : " + << "virtual public " << service_name_ << ifstyle << if_suffix << extends_client << " {" + << endl << " public:" << endl; + + indent_up(); + if (style != "Cob") { + f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr + << " prot) "; + + if (extends.empty()) { + 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() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr + << " iprot, " << prot_ptr << " oprot) "; + if (extends.empty()) { + 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; + } + + // create the setProtocol methods + if (extends.empty()) { + f_header_ << " private:" << endl; + // 1: one parameter + f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " prot) {" + << endl; + f_header_ << indent() << "setProtocol" << short_suffix << "(prot,prot);" << endl; + f_header_ << indent() << "}" << endl; + // 2: two parameter + f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " iprot, " + << prot_ptr << " oprot) {" << endl; + + f_header_ << indent() << " piprot_=iprot;" << endl << indent() << " poprot_=oprot;" << endl + << indent() << " iprot_ = iprot.get();" << endl << indent() + << " oprot_ = oprot.get();" << endl; + + f_header_ << indent() << "}" << endl; + f_header_ << " public:" << endl; + } + + // Generate getters for the protocols. + // Note that these are not currently templated for simplicity. + // TODO(simpkins): should they be templated? + f_header_ << indent() + << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" + << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}" + << endl; + + f_header_ << indent() + << "boost::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 << "(" + << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, " + << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl; + if (extends.empty()) { + f_header_ << indent() << " channel_(channel)," << endl << indent() + << " itrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl + << indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer())," + << endl; + 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_(boost::dynamic_pointer_cast(" + << "protocolFactory->getProtocol(itrans_)))," << endl << indent() + << " poprot_(boost::dynamic_pointer_cast(" + << "protocolFactory->getProtocol(otrans_))) {" << endl; + // Throw a TException if either dynamic cast failed. + f_header_ << indent() << " if (!piprot_ || !poprot_) {" << endl << indent() + << " throw ::apache::thrift::TException(\"" + << "TProtocolFactory returned unexpected protocol type in " << service_name_ + << style << "Client" << short_suffix << " constructor\");" << endl << indent() + << " }" << endl; + } else { + f_header_ << indent() << " piprot_(protocolFactory->getProtocol(itrans_))," << endl + << indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" << endl; + } + f_header_ << indent() << " iprot_ = piprot_.get();" << endl << indent() + << " oprot_ = poprot_.get();" << endl << indent() << "}" << endl; + } else { + f_header_ << indent() << " " << extends << style << client_suffix + << "(channel, protocolFactory) {}" << endl; + } + } + + if (style == "Cob") { + f_header_ << indent() + << "boost::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; + } + } + + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++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()) { + // concurrent clients need to move the seqid from the send function to the + // recv function. Oneway methods don't have a recv function, so we don't need to + // move the seqid for them. Attempting to do so would result in a seqid leak. + t_function send_function(g_type_i32, /*returning seqid*/ + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + indent(f_header_) << function_signature(&send_function, "") << ";" << endl; + } else { + t_function send_function(g_type_void, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + indent(f_header_) << function_signature(&send_function, "") << ";" << endl; + } + if (!(*f_iter)->is_oneway()) { + if (style == "Concurrent") { + t_field seqIdArg(g_type_i32, "seqid"); + t_struct seqIdArgStruct(program_); + seqIdArgStruct.append(&seqIdArg); + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &seqIdArgStruct); + indent(f_header_) << function_signature(&recv_function, "") << ";" << endl; + } else { + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + indent(f_header_) << function_signature(&recv_function, "") << ";" << endl; + } + } + } + indent_down(); + + if (extends.empty()) { + f_header_ << " protected:" << endl; + indent_up(); + + if (style == "Cob") { + f_header_ << indent() + << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel_;" << endl + << indent() + << "boost::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> itrans_;" << endl + << indent() + << "boost::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;" + << endl; + } + f_header_ << + indent() << prot_ptr << " piprot_;" << endl << + indent() << prot_ptr << " poprot_;" << endl << + indent() << protocol_type << "* iprot_;" << endl << + indent() << protocol_type << "* oprot_;" << endl; + + if (style == "Concurrent") { + f_header_ << + indent() << "::apache::thrift::async::TConcurrentClientSyncInfo sync_;"< " << service_name_ << style + << "Client;" << endl << endl; + } + + string scope = service_name_ + style + client_suffix + "::"; + + // Generate client method implementations + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string seqIdCapture; + string seqIdUse; + string seqIdCommaUse; + if (style == "Concurrent" && !(*f_iter)->is_oneway()) { + seqIdCapture = "int32_t seqid = "; + seqIdUse = "seqid"; + seqIdCommaUse = ", seqid"; + } + + string funname = (*f_iter)->get_name(); + + // Open function + if (gen_templates_) { + indent(out) << template_header; + } + indent(out) << function_signature(*f_iter, ifstyle, scope) << endl; + scope_up(out); + indent(out) << seqIdCapture << "send_" << funname << "("; + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + // Declare the function arguments + 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) { + if (first) { + first = false; + } else { + out << ", "; + } + out << (*fld_iter)->get_name(); + } + out << ");" << endl; + + if (style != "Cob") { + if (!(*f_iter)->is_oneway()) { + out << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + if (is_complex_type((*f_iter)->get_returntype())) { + out << "recv_" << funname << "(_return" << seqIdCommaUse << ");" << endl; + } else { + out << "return recv_" << funname << "(" << seqIdUse << ");" << endl; + } + } else { + out << "recv_" << funname << "(" << seqIdUse << ");" << endl; + } + } + } else { + if (!(*f_iter)->is_oneway()) { + out << indent() << _this << "channel_->sendAndRecvMessage(" + << "tcxx::bind(cob, this), " << _this << "otrans_.get(), " << _this << "itrans_.get());" + << endl; + } else { + out << indent() << _this << "channel_->sendMessage(" + << "tcxx::bind(cob, this), " << _this << "otrans_.get());" << endl; + } + } + scope_down(out); + out << endl; + + // if (style != "Cob") // TODO(dreiss): Libify the client and don't generate this for cob-style + if (true) { + t_type* send_func_return_type = g_type_void; + if (style == "Concurrent" && !(*f_iter)->is_oneway()) { + send_func_return_type = g_type_i32; + } + // Function for sending + t_function send_function(send_func_return_type, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + + // Open the send function + if (gen_templates_) { + indent(out) << template_header; + } + indent(out) << function_signature(&send_function, "", scope) << endl; + scope_up(out); + + // Function arguments and results + string argsname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs"; + string resultname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_presult"; + + string cseqidVal = "0"; + if (style == "Concurrent") { + if (!(*f_iter)->is_oneway()) { + cseqidVal = "this->sync_.generateSeqId()"; + } + } + // Serialize the request + out << + indent() << "int32_t cseqid = " << cseqidVal << ";" << endl; + if(style == "Concurrent") { + out << + indent() << "::apache::thrift::async::TConcurrentSendSentry sentry(&this->sync_);" << endl; + } + if (style == "Cob") { + out << + indent() << _this << "otrans_->resetBuffer();" << endl; + } + out << + indent() << _this << "oprot_->writeMessageBegin(\"" << + (*f_iter)->get_name() << + "\", ::apache::thrift::protocol::" << ((*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL") << + ", cseqid);" << endl << endl << + indent() << argsname << " args;" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + out << indent() << "args." << (*fld_iter)->get_name() << " = &" << (*fld_iter)->get_name() + << ";" << endl; + } + + out << indent() << "args.write(" << _this << "oprot_);" << endl << endl << indent() << _this + << "oprot_->writeMessageEnd();" << endl << indent() << _this + << "oprot_->getTransport()->writeEnd();" << endl << indent() << _this + << "oprot_->getTransport()->flush();" << endl; + + if (style == "Concurrent") { + out << endl << indent() << "sentry.commit();" << endl; + + if (!(*f_iter)->is_oneway()) { + out << indent() << "return cseqid;" << endl; + } + } + scope_down(out); + out << endl; + + // Generate recv function only if not an oneway function + if (!(*f_iter)->is_oneway()) { + t_struct noargs(program_); + + t_field seqIdArg(g_type_i32, "seqid"); + t_struct seqIdArgStruct(program_); + seqIdArgStruct.append(&seqIdArg); + + t_struct* recv_function_args = &noargs; + if (style == "Concurrent") { + recv_function_args = &seqIdArgStruct; + } + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + recv_function_args); + // Open the recv function + if (gen_templates_) { + indent(out) << template_header; + } + indent(out) << function_signature(&recv_function, "", scope) << endl; + scope_up(out); + + out << endl << + indent() << "int32_t rseqid = 0;" << endl << + indent() << "std::string fname;" << endl << + indent() << "::apache::thrift::protocol::TMessageType mtype;" << endl; + if(style == "Concurrent") { + out << + 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; + } + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << "bool completed = false;" << endl << endl << indent() << "try {"; + indent_up(); + } + out << endl; + if (style == "Concurrent") { + out << + indent() << "while(true) {" << endl << + indent() << " if(!this->sync_.getPending(fname, mtype, rseqid)) {" << endl; + indent_up(); + indent_up(); + } + out << + indent() << _this << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl; + if (style == "Concurrent") { + scope_down(out); + out << indent() << "if(seqid == rseqid) {" << endl; + indent_up(); + } + out << + indent() << "if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {" << endl << + indent() << " ::apache::thrift::TApplicationException x;" << endl << + indent() << " x.read(" << _this << "iprot_);" << endl << + indent() << " " << _this << "iprot_->readMessageEnd();" << endl << + indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << " sentry.commit();" << endl; + } + out << + indent() << " throw x;" << endl << + indent() << "}" << endl << + indent() << "if (mtype != ::apache::thrift::protocol::T_REPLY) {" << endl << + indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl << + indent() << " " << _this << "iprot_->readMessageEnd();" << endl << + indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(false);" + << endl; + } + out << + indent() << "}" << endl << + indent() << "if (fname.compare(\"" << (*f_iter)->get_name() << "\") != 0) {" << endl << + indent() << " " << _this << "iprot_->skip(" << "::apache::thrift::protocol::T_STRUCT);" << endl << + indent() << " " << _this << "iprot_->readMessageEnd();" << endl << + indent() << " " << _this << "iprot_->getTransport()->readEnd();" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(false);" + << endl; + } + if (style == "Concurrent") { + out << endl << + indent() << " // in a bad state, don't commit" << endl << + indent() << " using ::apache::thrift::protocol::TProtocolException;" << endl << + indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl; + } + out << indent() << "}" << endl; + + if (!(*f_iter)->get_returntype()->is_void() + && !is_complex_type((*f_iter)->get_returntype())) { + t_field returnfield((*f_iter)->get_returntype(), "_return"); + out << indent() << declare_field(&returnfield) << endl; + } + + out << indent() << resultname << " result;" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + out << indent() << "result.success = &_return;" << endl; + } + + out << indent() << "result.read(" << _this << "iprot_);" << endl << indent() << _this + << "iprot_->readMessageEnd();" << endl << indent() << _this + << "iprot_->getTransport()->readEnd();" << endl << endl; + + // Careful, only look for _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + if (is_complex_type((*f_iter)->get_returntype())) { + out << + indent() << "if (result.__isset.success) {" << endl; + out << + indent() << " // _return pointer has now been filled" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << " sentry.commit();" << endl; + } + out << + indent() << " return;" << endl << + indent() << "}" << endl; + } else { + out << indent() << "if (result.__isset.success) {" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << " sentry.commit();" << endl; + } + out << indent() << " return _return;" << endl << indent() << "}" << endl; + } + } + + 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 << indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl; + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << " completed = true;" << endl << indent() << " completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << " sentry.commit();" << endl; + } + out << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl << indent() + << "}" << endl; + } + + // We only get here if we are a void function + if ((*f_iter)->get_returntype()->is_void()) { + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << "completed = true;" << endl << indent() << "completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << "sentry.commit();" << endl; + } + indent(out) << "return;" << endl; + } else { + if (style == "Cob" && !gen_no_client_completion_) { + out << indent() << "completed = true;" << endl << indent() << "completed__(true);" + << endl; + } + if (style == "Concurrent") { + out << indent() << "// in a bad state, don't commit" << endl; + } + out << indent() << "throw " + "::apache::thrift::TApplicationException(::apache::thrift::" + "TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name() + << " failed: unknown result\");" << endl; + } + if (style == "Concurrent") { + indent_down(); + indent_down(); + out << + indent() << " }" << endl << + indent() << " // seqid != 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() << "} // end while(true)" << endl; + } + if (style == "Cob" && !gen_no_client_completion_) { + indent_down(); + out << indent() << "} catch (...) {" << endl << indent() << " if (!completed) {" << endl + << indent() << " completed__(false);" << endl << indent() << " }" << endl + << indent() << " throw;" << endl << indent() << "}" << endl; + } + // Close function + scope_down(out); + out << endl; + } + } + } +} + +class ProcessorGenerator { +public: + ProcessorGenerator(t_cpp_generator* generator, t_service* service, const string& style); + + void run() { + generate_class_definition(); + + // Generate the dispatchCall() function + generate_dispatch_call(false); + if (generator_->gen_templates_) { + generate_dispatch_call(true); + } + + // Generate all of the process subfunctions + generate_process_functions(); + + generate_factory(); + } + + void generate_class_definition(); + void generate_dispatch_call(bool template_protocol); + void generate_process_functions(); + void generate_factory(); + +protected: + std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false) { + return generator_->type_name(ttype, in_typedef, arg); + } + + std::string indent() { return generator_->indent(); } + std::ostream& indent(std::ostream& os) { return generator_->indent(os); } + + void indent_up() { generator_->indent_up(); } + void indent_down() { generator_->indent_down(); } + + t_cpp_generator* generator_; + t_service* service_; + std::ofstream& f_header_; + std::ofstream& f_out_; + string service_name_; + string style_; + string pstyle_; + string class_name_; + string if_name_; + string factory_class_name_; + string finish_cob_; + string finish_cob_decl_; + string ret_type_; + string call_context_; + string cob_arg_; + string call_context_arg_; + string call_context_decl_; + string template_header_; + string template_suffix_; + string typename_str_; + string class_suffix_; + string extends_; +}; + +ProcessorGenerator::ProcessorGenerator(t_cpp_generator* generator, + t_service* service, + const string& style) + : generator_(generator), + service_(service), + f_header_(generator->f_header_), + f_out_(generator->gen_templates_ ? generator->f_service_tcc_ : generator->f_service_), + service_name_(generator->service_name_), + style_(style) { + if (style_ == "Cob") { + pstyle_ = "Async"; + class_name_ = service_name_ + pstyle_ + "Processor"; + if_name_ = service_name_ + "CobSvIf"; + + finish_cob_ = "tcxx::function cob, "; + finish_cob_decl_ = "tcxx::function, "; + cob_arg_ = "cob, "; + ret_type_ = "void "; + } else { + class_name_ = service_name_ + "Processor"; + if_name_ = service_name_ + "If"; + + ret_type_ = "bool "; + // TODO(edhall) callContext should eventually be added to TAsyncProcessor + call_context_ = ", void* callContext"; + call_context_arg_ = ", callContext"; + call_context_decl_ = ", void*"; + } + + factory_class_name_ = class_name_ + "Factory"; + + if (generator->gen_templates_) { + template_header_ = "template \n"; + template_suffix_ = ""; + typename_str_ = "typename "; + class_name_ += "T"; + factory_class_name_ += "T"; + } + + if (service_->get_extends() != NULL) { + extends_ = type_name(service_->get_extends()) + pstyle_ + "Processor"; + if (generator_->gen_templates_) { + // TODO(simpkins): If gen_templates_ is enabled, we currently assume all + // parent services were also generated with templates enabled. + extends_ += "T"; + } + } +} + +void ProcessorGenerator::generate_class_definition() { + // Generate the dispatch methods + vector functions = service_->get_functions(); + vector::iterator f_iter; + + string parent_class; + if (service_->get_extends() != NULL) { + parent_class = extends_; + } else { + if (style_ == "Cob") { + parent_class = "::apache::thrift::async::TAsyncDispatchProcessor"; + } else { + parent_class = "::apache::thrift::TDispatchProcessor"; + } + + if (generator_->gen_templates_) { + parent_class += "T"; + } + } + + // Generate the header portion + f_header_ << template_header_ << "class " << class_name_ << " : public " << parent_class << " {" + << endl; + + // Protected data members + f_header_ << " protected:" << endl; + indent_up(); + f_header_ << indent() << "boost::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, " + << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl; + if (generator_->gen_templates_) { + f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_ + << "Protocol_* iprot, Protocol_* oprot, " + << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl; + } + indent_down(); + + // Process function declarations + f_header_ << " private:" << endl; + indent_up(); + + // Declare processMap_ + f_header_ << indent() << "typedef void (" << class_name_ << "::*" + << "ProcessFunction)(" << finish_cob_decl_ << "int32_t, " + << "::apache::thrift::protocol::TProtocol*, " + << "::apache::thrift::protocol::TProtocol*" << call_context_decl_ << ");" << endl; + if (generator_->gen_templates_) { + f_header_ << indent() << "typedef void (" << class_name_ << "::*" + << "SpecializedProcessFunction)(" << finish_cob_decl_ << "int32_t, " + << "Protocol_*, Protocol_*" << call_context_decl_ << ");" << endl << indent() + << "struct ProcessFunctions {" << endl << indent() << " ProcessFunction generic;" + << endl << indent() << " SpecializedProcessFunction specialized;" << endl << indent() + << " ProcessFunctions(ProcessFunction g, " + << "SpecializedProcessFunction s) :" << endl << indent() << " generic(g)," << endl + << indent() << " specialized(s) {}" << endl << indent() + << " ProcessFunctions() : generic(NULL), specialized(NULL) " + << "{}" << endl << indent() << "};" << endl << indent() + << "typedef std::map " + << "ProcessMap;" << endl; + } else { + f_header_ << indent() << "typedef std::map " + << "ProcessMap;" << endl; + } + f_header_ << indent() << "ProcessMap processMap_;" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_ + << "int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, " + "::apache::thrift::protocol::TProtocol* oprot" << call_context_ << ");" + << endl; + if (generator_->gen_templates_) { + indent(f_header_) << "void process_" << (*f_iter)->get_name() << "(" << finish_cob_ + << "int32_t seqid, Protocol_* iprot, Protocol_* oprot" << call_context_ + << ");" << endl; + } + if (style_ == "Cob") { + // XXX Factor this out, even if it is a pain. + string ret_arg = ((*f_iter)->get_returntype()->is_void() + ? "" + : ", const " + type_name((*f_iter)->get_returntype()) + "& _return"); + f_header_ << indent() << "void return_" << (*f_iter)->get_name() + << "(tcxx::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() + << "(tcxx::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() + << "(tcxx::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() + << "(tcxx::function cob, int32_t seqid, " + << "Protocol_* oprot, void* ctx, " + << "::apache::thrift::TDelayedException* _throw);" << endl; + } + } + } + + f_header_ << " public:" << endl << indent() << class_name_ << "(boost::shared_ptr<" << if_name_ + << "> iface) :" << endl; + if (!extends_.empty()) { + f_header_ << indent() << " " << extends_ << "(iface)," << endl; + } + f_header_ << indent() << " iface_(iface) {" << endl; + indent_up(); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_header_ << indent() << "processMap_[\"" << (*f_iter)->get_name() << "\"] = "; + if (generator_->gen_templates_) { + f_header_ << "ProcessFunctions(" << endl; + if (generator_->gen_templates_only_) { + indent(f_header_) << " NULL," << endl; + } else { + indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << "," + << endl; + } + indent(f_header_) << " &" << class_name_ << "::process_" << (*f_iter)->get_name() << ")"; + } else { + f_header_ << "&" << class_name_ << "::process_" << (*f_iter)->get_name(); + } + f_header_ << ";" << endl; + } + + indent_down(); + f_header_ << indent() << "}" << endl << endl << indent() << "virtual ~" << class_name_ << "() {}" + << endl; + indent_down(); + f_header_ << "};" << endl << endl; + + if (generator_->gen_templates_) { + // Generate a backwards compatible typedef, for callers who don't know + // about the new template-style code. + // + // We can't use TProtocol as the template parameter, since ProcessorT + // provides overloaded versions of most methods, one of which accepts + // TProtocol pointers, and one which accepts Protocol_ pointers. This + // results in a compile error if instantiated with Protocol_ == TProtocol. + // Therefore, we define TDummyProtocol solely so we can use it as the + // template parameter here. + f_header_ << "typedef " << class_name_ << "< ::apache::thrift::protocol::TDummyProtocol > " + << service_name_ << pstyle_ << "Processor;" << endl << endl; + } +} + +void ProcessorGenerator::generate_dispatch_call(bool template_protocol) { + string protocol = "::apache::thrift::protocol::TProtocol"; + string function_suffix; + if (template_protocol) { + protocol = "Protocol_"; + // We call the generic version dispatchCall(), and the specialized + // version dispatchCallTemplated(). We can't call them both + // dispatchCall(), since this will cause the compiler to issue a warning if + // a service that doesn't use templates inherits from a service that does + // use templates: the compiler complains that the subclass only implements + // the generic version of dispatchCall(), and hides the templated version. + // Using different names for the two functions prevents this. + function_suffix = "Templated"; + } + + f_out_ << template_header_ << ret_type_ << class_name_ << template_suffix_ << "::dispatchCall" + << function_suffix << "(" << finish_cob_ << protocol << "* iprot, " << protocol + << "* oprot, " + << "const std::string& fname, int32_t seqid" << call_context_ << ") {" << endl; + indent_up(); + + // HOT: member function pointer map + f_out_ << indent() << typename_str_ << "ProcessMap::iterator pfn;" << endl << indent() + << "pfn = processMap_.find(fname);" << endl << indent() + << "if (pfn == processMap_.end()) {" << endl; + if (extends_.empty()) { + f_out_ << indent() << " iprot->skip(::apache::thrift::protocol::T_STRUCT);" << endl << indent() + << " iprot->readMessageEnd();" << endl << indent() + << " iprot->getTransport()->readEnd();" << endl << indent() + << " ::apache::thrift::TApplicationException " + "x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, \"Invalid method name: " + "'\"+fname+\"'\");" << endl << indent() + << " oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);" + << endl << indent() << " x.write(oprot);" << endl << indent() + << " oprot->writeMessageEnd();" << endl << indent() + << " oprot->getTransport()->writeEnd();" << endl << indent() + << " oprot->getTransport()->flush();" << endl << indent() + << (style_ == "Cob" ? " return cob(true);" : " return true;") << endl; + } else { + f_out_ << indent() << " return " << extends_ << "::dispatchCall(" + << (style_ == "Cob" ? "cob, " : "") << "iprot, oprot, fname, seqid" << call_context_arg_ + << ");" << endl; + } + f_out_ << indent() << "}" << endl; + if (template_protocol) { + f_out_ << indent() << "(this->*(pfn->second.specialized))"; + } else { + if (generator_->gen_templates_only_) { + // TODO: This is a null pointer, so nothing good will come from calling + // it. Throw an exception instead. + f_out_ << indent() << "(this->*(pfn->second.generic))"; + } else if (generator_->gen_templates_) { + f_out_ << indent() << "(this->*(pfn->second.generic))"; + } else { + f_out_ << indent() << "(this->*(pfn->second))"; + } + } + f_out_ << "(" << cob_arg_ << "seqid, iprot, oprot" << call_context_arg_ << ");" << endl; + + // TODO(dreiss): return pfn ret? + if (style_ == "Cob") { + f_out_ << indent() << "return;" << endl; + } else { + f_out_ << indent() << "return true;" << endl; + } + + indent_down(); + f_out_ << "}" << endl << endl; +} + +void ProcessorGenerator::generate_process_functions() { + vector functions = service_->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if (generator_->gen_templates_) { + generator_->generate_process_function(service_, *f_iter, style_, false); + generator_->generate_process_function(service_, *f_iter, style_, true); + } else { + generator_->generate_process_function(service_, *f_iter, style_, false); + } + } +} + +void ProcessorGenerator::generate_factory() { + string if_factory_name = if_name_ + "Factory"; + + // Generate the factory class definition + f_header_ << template_header_ << "class " << factory_class_name_ << " : public ::apache::thrift::" + << (style_ == "Cob" ? "async::TAsyncProcessorFactory" : "TProcessorFactory") << " {" + << endl << " public:" << endl; + indent_up(); + + f_header_ << indent() << factory_class_name_ << "(const ::boost::shared_ptr< " << if_factory_name + << " >& handlerFactory) :" << endl << indent() + << " handlerFactory_(handlerFactory) {}" << endl << endl << indent() + << "::boost::shared_ptr< ::apache::thrift::" + << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " + << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);" << endl; + + f_header_ << endl << " protected:" << endl << indent() << "::boost::shared_ptr< " + << if_factory_name << " > handlerFactory_;" << endl; + + indent_down(); + f_header_ << "};" << endl << endl; + + // If we are generating templates, output a typedef for the plain + // factory name. + if (generator_->gen_templates_) { + f_header_ << "typedef " << factory_class_name_ + << "< ::apache::thrift::protocol::TDummyProtocol > " << service_name_ << pstyle_ + << "ProcessorFactory;" << endl << endl; + } + + // Generate the getProcessor() method + f_out_ << template_header_ << indent() << "::boost::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() << "::boost::shared_ptr< " + << if_name_ << " > handler(" + << "handlerFactory_->getHandler(connInfo), cleanup);" << endl << indent() + << "::boost::shared_ptr< ::apache::thrift::" + << (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > " + << "processor(new " << class_name_ << template_suffix_ << "(handler));" << endl << indent() + << "return processor;" << endl; + + indent_down(); + f_out_ << indent() << "}" << endl << endl; +} + +/** + * Generates a service processor definition. + * + * @param tservice The service to generate a processor for. + */ +void t_cpp_generator::generate_service_processor(t_service* tservice, string style) { + ProcessorGenerator generator(this, tservice, style); + generator.run(); +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function* tfunction) { + if (tfunction->is_oneway()) { + return; + } + + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + + t_struct result(program_, tservice->get_name() + "_" + 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_struct_declaration(f_header_, &result, false); + generate_struct_definition(out, f_service_, &result, false); + generate_struct_reader(out, &result); + generate_struct_result_writer(out, &result); + + result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult"); + generate_struct_declaration(f_header_, &result, false, true, true, gen_cob_style_); + generate_struct_definition(out, f_service_, &result, false); + generate_struct_reader(out, &result, true); + if (gen_cob_style_) { + generate_struct_writer(out, &result, true); + } +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_cpp_generator::generate_process_function(t_service* tservice, + t_function* tfunction, + string style, + bool specialized) { + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + string service_func_name = "\"" + tservice->get_name() + "." + tfunction->get_name() + "\""; + + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + + string prot_type = (specialized ? "Protocol_" : "::apache::thrift::protocol::TProtocol"); + string class_suffix; + if (gen_templates_) { + class_suffix = "T"; + } + + // I tried to do this as one function. I really did. But it was too hard. + if (style != "Cob") { + // Open function + if (gen_templates_) { + out << indent() << "template " << endl; + } + const bool unnamed_oprot_seqid = tfunction->is_oneway() && !(gen_templates_ && !specialized); + out << "void " << tservice->get_name() << "Processor" << class_suffix << "::" + << "process_" << tfunction->get_name() << "(" + << "int32_t" << (unnamed_oprot_seqid ? ", " : " seqid, ") << prot_type << "* iprot, " + << prot_type << "*" << (unnamed_oprot_seqid ? ", " : " oprot, ") << "void* callContext)" + << endl; + scope_up(out); + + string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args"; + string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result"; + + if (tfunction->is_oneway() && !unnamed_oprot_seqid) { + out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl; + } + + out << indent() << "void* ctx = NULL;" << endl << indent() + << "if (this->eventHandler_.get() != NULL) {" << 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() + << " 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() + << " this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << endl + << indent() << "}" << endl << endl; + + // Declare result + if (!tfunction->is_oneway()) { + out << indent() << resultname << " result;" << endl; + } + + // Try block for functions with exceptions + out << indent() << "try {" << endl; + indent_up(); + + // Generate the function call + bool first = true; + out << indent(); + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + if (is_complex_type(tfunction->get_returntype())) { + first = false; + out << "iface_->" << tfunction->get_name() << "(result.success"; + } else { + out << "result.success = iface_->" << tfunction->get_name() << "("; + } + } else { + out << "iface_->" << tfunction->get_name() << "("; + } + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + out << ", "; + } + out << "args." << (*f_iter)->get_name(); + } + out << ");" << endl; + + // Set isset on success field + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + out << indent() << "result.__isset.success = true;" << endl; + } + + indent_down(); + out << indent() << "}"; + + if (!tfunction->is_oneway()) { + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() + << ") {" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() + << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" + << endl; + indent_down(); + out << indent() << "}"; + } else { + out << "}"; + } + } + } + + if (!tfunction->is_oneway()) { + out << " catch (const std::exception& e) {" << endl; + } else { + out << " catch (const std::exception&) {" << endl; + } + + indent_up(); + out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl + << indent() << "}" << endl; + + if (!tfunction->is_oneway()) { + out << endl << indent() << "::apache::thrift::TApplicationException x(e.what());" << endl + << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name() + << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent() + << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl + << indent() << "oprot->getTransport()->writeEnd();" << endl << indent() + << "oprot->getTransport()->flush();" << endl; + } + out << indent() << "return;" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + out << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl + << indent() << "}" << endl << endl << indent() << "return;" << endl; + indent_down(); + out << "}" << endl << endl; + return; + } + + // Serialize the result into a struct + out << indent() << "if (this->eventHandler_.get() != NULL) {" << 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() + << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl + << indent() << "}" << endl; + + // Close function + scope_down(out); + out << endl; + } + + // Cob style. + else { + // Processor entry point. + // TODO(edhall) update for callContext when TEventServer is ready + if (gen_templates_) { + out << indent() << "template " << endl; + } + out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_" + << tfunction->get_name() << "(tcxx::function cob, int32_t seqid, " + << prot_type << "* iprot, " << prot_type << "* oprot)" << endl; + scope_up(out); + + // TODO(simpkins): we could try to consoldate this + // with the non-cob code above + if (gen_templates_ && !specialized) { + // If these are instances of Protocol_, instead of any old TProtocol, + // use the specialized process function instead. + out << indent() << "Protocol_* _iprot = dynamic_cast(iprot);" << endl << indent() + << "Protocol_* _oprot = dynamic_cast(oprot);" << endl << indent() + << "if (_iprot && _oprot) {" << endl << indent() << " return process_" + << tfunction->get_name() << "(cob, seqid, _iprot, _oprot);" << endl << indent() << "}" + << endl << indent() << "T_GENERIC_PROTOCOL(this, iprot, _iprot);" << endl << indent() + << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl << endl; + } + + if (tfunction->is_oneway()) { + out << indent() << "(void) seqid;" << endl << indent() << "(void) oprot;" << endl; + } + + 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() << "}" << 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() + << " 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() + << " 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() + << " 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() + << " this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << endl + << indent() << "}" << endl; + } + // TODO(dreiss): Figure out a strategy for exceptions in async handlers. + out << indent() << "freer.unregister();" << endl; + if (tfunction->is_oneway()) { + // No return. Just hand off our cob. + // TODO(dreiss): Call the cob immediately? + out << indent() << "iface_->" << tfunction->get_name() << "(" + << "tcxx::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 = ", tcxx::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)(tcxx::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)(tcxx::function " + << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, " + << "::apache::thrift::TDelayedException* _throw) =" << endl; + out << indent() << " &" << tservice->get_name() << "AsyncProcessor" << class_suffix + << "::throw_" << tfunction->get_name() << ";" << endl; + } + + out << indent() << "iface_->" << tfunction->get_name() << "(" << endl; + indent_up(); + indent_up(); + out << indent() << "tcxx::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder + << ")"; + if (!xceptions.empty()) { + out << ',' << endl << indent() << "tcxx::bind(throw_fn, this, cob, seqid, oprot, " + << "ctx, tcxx::placeholders::_1)"; + } + } + + // XXX Whitespace cleanup. + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << ',' << endl << indent() << "args." << (*f_iter)->get_name(); + } + out << ");" << endl; + indent_down(); + indent_down(); + scope_down(out); + out << endl; + + // Normal return. + if (!tfunction->is_oneway()) { + string ret_arg_decl, ret_arg_name; + if (!tfunction->get_returntype()->is_void()) { + ret_arg_decl = ", const " + type_name(tfunction->get_returntype()) + "& _return"; + ret_arg_name = ", _return"; + } + if (gen_templates_) { + out << indent() << "template " << endl; + } + out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_" + << tfunction->get_name() << "(tcxx::function cob, int32_t seqid, " + << prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << endl; + scope_up(out); + + if (gen_templates_ && !specialized) { + // If oprot is a Protocol_ instance, + // use the specialized return function instead. + out << indent() << "Protocol_* _oprot = dynamic_cast(oprot);" << endl + << indent() << "if (_oprot) {" << endl << indent() << " return return_" + << tfunction->get_name() << "(cob, seqid, _oprot, ctx" << ret_arg_name << ");" << endl + << indent() << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" + << endl << endl; + } + + out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_presult result;" + << endl; + if (!tfunction->get_returntype()->is_void()) { + // The const_cast here is unfortunate, but it would be a pain to avoid, + // and we only do a write with this struct, which is const-safe. + out << indent() << "result.success = const_cast<" << type_name(tfunction->get_returntype()) + << "*>(&_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 + << indent() << "}" << endl << indent() + << "::apache::thrift::TProcessorContextFreer freer(" + << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl + << indent() << "if (this->eventHandler_.get() != NULL) {" << 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() + << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl + << indent() << "}" << endl << indent() << "return cob(true);" << endl; + scope_down(out); + out << endl; + } + + // Exception return. + if (!tfunction->is_oneway() && !xceptions.empty()) { + if (gen_templates_) { + out << indent() << "template " << endl; + } + out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_" + << tfunction->get_name() << "(tcxx::function cob, int32_t seqid, " + << prot_type << "* oprot, void* ctx, " + << "::apache::thrift::TDelayedException* _throw)" << endl; + scope_up(out); + + if (gen_templates_ && !specialized) { + // If oprot is a Protocol_ instance, + // use the specialized throw function instead. + out << indent() << "Protocol_* _oprot = dynamic_cast(oprot);" << endl + << indent() << "if (_oprot) {" << endl << indent() << " return throw_" + << tfunction->get_name() << "(cob, seqid, _oprot, ctx, _throw);" << endl << indent() + << "}" << endl << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << endl + << endl; + } + + // Get the event handler context + out << endl << indent() << "if (this->eventHandler_.get() != NULL) {" << endl << indent() + << " ctx = this->eventHandler_->getContext(" << service_func_name << ", NULL);" << endl + << indent() << "}" << endl << indent() + << "::apache::thrift::TProcessorContextFreer freer(" + << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << endl << endl; + + // Throw the TDelayedException, and catch the result + out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;" + << endl << endl << indent() << "try {" << endl; + indent_up(); + out << indent() << "_throw->throw_it();" << endl << indent() << "return cob(false);" + << endl; // Is this possible? TBD. + indent_down(); + out << indent() << '}'; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() + << ") {" << endl; + indent_up(); + out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() + << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" + << endl; + scope_down(out); + } + + // 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() + << " this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << endl + << indent() << "}" << endl << endl << indent() + << "::apache::thrift::TApplicationException x(e.what());" << endl << indent() + << "oprot->writeMessageBegin(\"" << tfunction->get_name() + << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl << indent() + << "x.write(oprot);" << endl << indent() << "oprot->writeMessageEnd();" << endl + << indent() << "oprot->getTransport()->writeEnd();" << endl << indent() + << "oprot->getTransport()->flush();" << endl << + // We pass true to the cob here, since we did successfully write a + // response, even though it is an exception response. + // It looks like the argument is currently ignored, anyway. + indent() << "return cob(true);" << endl; + scope_down(out); + + // Serialize the result into a struct + out << indent() << "if (this->eventHandler_.get() != NULL) {" << 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() + << " this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << endl + << indent() << "}" << endl << indent() << "return cob(true);" << endl; + scope_down(out); + out << endl; + } // for each function + } // cob style +} + +/** + * Generates a skeleton file of a server + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_skeleton(t_service* tservice) { + string svcname = tservice->get_name(); + + // Service implementation file includes + string f_skeleton_name = get_out_dir() + svcname + "_server.skeleton.cpp"; + + string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp")); + + ofstream f_skeleton; + f_skeleton.open(f_skeleton_name.c_str()); + f_skeleton << "// This autogenerated skeleton file illustrates how to build a server." << endl + << "// You should copy it to another filename to avoid overwriting it." << endl << endl + << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << 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 + << "using namespace ::apache::thrift::server;" << endl << endl + << "using boost::shared_ptr;" << endl << endl; + + // the following code would not compile: + // using namespace ; + // using namespace ::; + if ((!ns.empty()) && (ns.compare(" ::") != 0)) { + 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 << indent() << "int main(int argc, char **argv) {" << endl; + indent_up(); + f_skeleton + << indent() << "int port = 9090;" << endl << indent() << "shared_ptr<" << svcname + << "Handler> handler(new " << svcname << "Handler());" << endl << indent() + << "shared_ptr processor(new " << svcname << "Processor(handler));" << endl + << indent() << "shared_ptr serverTransport(new TServerSocket(port));" + << endl << indent() + << "shared_ptr transportFactory(new TBufferedTransportFactory());" << endl + << indent() << "shared_ptr protocolFactory(new TBinaryProtocolFactory());" + << endl << endl << indent() + << "TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);" + << endl << indent() << "server.serve();" << endl << indent() << "return 0;" << endl; + indent_down(); + f_skeleton << "}" << endl << endl; + + // Close the files + f_skeleton.close(); +} + +/** + * Deserializes a field of any type. + */ +void t_cpp_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix) { + 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(); + } + + string name = prefix + tfield->get_name() + suffix; + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, name, is_reference(tfield)); + } else if (type->is_container()) { + generate_deserialize_container(out, type, name); + } else if (type->is_base_type()) { + indent(out) << "xfer += iprot->"; + 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 (((t_base_type*)type)->is_binary()) { + out << "readBinary(" << name << ");"; + } else { + out << "readString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "readBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "readByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "readI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "readI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "readI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "readDouble(" << name << ");"; + break; + default: + throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + name; + } + out << endl; + } else if (type->is_enum()) { + string t = tmp("ecast"); + out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t + << ");" << endl << indent() << name << " = (" << type_name(type) << ")" << t << ";" << 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 variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_cpp_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + bool pointer) { + if (pointer) { + indent(out) << "if (!" << prefix << ") { " << endl; + indent(out) << " " << prefix << " = boost::shared_ptr<" << type_name(tstruct) << ">(new " + << type_name(tstruct) << ");" << endl; + indent(out) << "}" << endl; + indent(out) << "xfer += " << prefix << "->read(iprot);" << endl; + indent(out) << "bool wasSet = false;" << endl; + const vector& members = tstruct->get_members(); + vector::const_iterator f_iter; + for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) { + + indent(out) << "if (" << prefix << "->__isset." << (*f_iter)->get_name() + << ") { wasSet = true; }" << endl; + } + indent(out) << "if (!wasSet) { " << prefix << ".reset(); }" << endl; + } else { + indent(out) << "xfer += " << prefix << ".read(iprot);" << endl; + } +} + +void t_cpp_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_container* tcontainer = (t_container*)ttype; + bool use_push = tcontainer->has_cpp_name(); + + indent(out) << prefix << ".clear();" << endl << indent() << "uint32_t " << size << ";" << endl; + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << "::apache::thrift::protocol::TType " << ktype << ";" << endl << indent() + << "::apache::thrift::protocol::TType " << vtype << ";" << endl << indent() + << "xfer += iprot->readMapBegin(" << ktype << ", " << vtype << ", " << size << ");" << endl; + } else if (ttype->is_set()) { + out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent() + << "xfer += iprot->readSetBegin(" << etype << ", " << size << ");" << endl; + } else if (ttype->is_list()) { + out << indent() << "::apache::thrift::protocol::TType " << etype << ";" << endl << indent() + << "xfer += iprot->readListBegin(" << etype << ", " << size << ");" << endl; + if (!use_push) { + indent(out) << prefix << ".resize(" << size << ");" << endl; + } + } + + // For loop iterates over elements + string i = tmp("_i"); + out << indent() << "uint32_t " << 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, 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, use_push, i); + } + + scope_down(out); + + // Read container end + if (ttype->is_map()) { + indent(out) << "xfer += iprot->readMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "xfer += iprot->readSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "xfer += iprot->readListEnd();" << endl; + } + + scope_down(out); +} + +/** + * Generates code to deserialize a map + */ +void t_cpp_generator::generate_deserialize_map_element(ofstream& 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); + + out << indent() << declare_field(&fkey) << endl; + + generate_deserialize_field(out, &fkey); + indent(out) << declare_field(&fval, false, false, false, true) << " = " << prefix << "[" << key + << "];" << endl; + + generate_deserialize_field(out, &fval); +} + +void t_cpp_generator::generate_deserialize_set_element(ofstream& 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 << ".insert(" << elem << ");" << endl; +} + +void t_cpp_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix, + bool use_push, + string index) { + if (use_push) { + 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 << ".push_back(" << elem << ");" << endl; + } else { + t_field felem(tlist->get_elem_type(), prefix + "[" + index + "]"); + generate_deserialize_field(out, &felem); + } +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_cpp_generator::generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix) { + t_type* type = get_true_type(tfield->get_type()); + + string name = prefix + tfield->get_name() + suffix; + + // Do nothing for void types + 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, is_reference(tfield)); + } else if (type->is_container()) { + generate_serialize_container(out, type, name); + } else if (type->is_base_type() || type->is_enum()) { + + indent(out) << "xfer += oprot->"; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no C++ writer for base type " + t_base_type::t_base_name(tbase) + + name; + } + } else if (type->is_enum()) { + out << "writeI32((int32_t)" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", + name.c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_cpp_generator::generate_serialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + bool pointer) { + if (pointer) { + indent(out) << "if (" << prefix << ") {" << endl; + indent(out) << " xfer += " << prefix << "->write(oprot); " << endl; + indent(out) << "} else {" + << "oprot->writeStructBegin(\"" << tstruct->get_name() << "\"); " << endl; + indent(out) << " oprot->writeStructEnd();" << endl; + indent(out) << " oprot->writeFieldStop();" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "xfer += " << prefix << ".write(oprot);" << endl; + } +} + +void t_cpp_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + indent(out) << "xfer += oprot->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) + << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "static_cast(" << prefix << ".size()));" << endl; + } else if (ttype->is_set()) { + indent(out) << "xfer += oprot->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " + << "static_cast(" << prefix << ".size()));" << endl; + } else if (ttype->is_list()) { + indent(out) << "xfer += oprot->writeListBegin(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " + << "static_cast(" << prefix << ".size()));" << endl; + } + + string iter = tmp("_iter"); + out << indent() << type_name(ttype) << "::const_iterator " << iter << ";" << endl << indent() + << "for (" << iter << " = " << prefix << ".begin(); " << iter << " != " << prefix + << ".end(); ++" << iter << ")" << endl; + scope_up(out); + if (ttype->is_map()) { + generate_serialize_map_element(out, (t_map*)ttype, iter); + } 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) << "xfer += oprot->writeMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "xfer += oprot->writeSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "xfer += oprot->writeListEnd();" << endl; + } + + scope_down(out); +} + +/** + * Serializes the members of a map. + * + */ +void t_cpp_generator::generate_serialize_map_element(ofstream& out, t_map* tmap, string iter) { + t_field kfield(tmap->get_key_type(), iter + "->first"); + generate_serialize_field(out, &kfield, ""); + + t_field vfield(tmap->get_val_type(), iter + "->second"); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_cpp_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), "(*" + iter + ")"); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_cpp_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), "(*" + iter + ")"); + generate_serialize_field(out, &efield, ""); +} + +/** + * Makes a :: prefix for a namespace + * + * @param ns The namespace, w/ periods in it + * @return Namespaces + */ +string t_cpp_generator::namespace_prefix(string ns) { + // Always start with "::", to avoid possible name collisions with + // other names in one of the current namespaces. + // + // We also need a leading space, in case the name is used inside of a + // template parameter. "MyTemplate<::foo::Bar>" is not valid C++, + // since "<:" is an alternative token for "[". + string result = " ::"; + + if (ns.size() == 0) { + return result; + } + string::size_type loc; + while ((loc = ns.find(".")) != string::npos) { + result += ns.substr(0, loc); + result += "::"; + ns = ns.substr(loc + 1); + } + if (ns.size() > 0) { + result += ns + "::"; + } + return result; +} + +/** + * Opens namespace. + * + * @param ns The namespace, w/ periods in it + * @return Namespaces + */ +string t_cpp_generator::namespace_open(string ns) { + if (ns.size() == 0) { + return ""; + } + string result = ""; + string separator = ""; + string::size_type loc; + while ((loc = ns.find(".")) != string::npos) { + result += separator; + result += "namespace "; + result += ns.substr(0, loc); + result += " {"; + separator = " "; + ns = ns.substr(loc + 1); + } + if (ns.size() > 0) { + result += separator + "namespace " + ns + " {"; + } + return result; +} + +/** + * Closes namespace. + * + * @param ns The namespace, w/ periods in it + * @return Namespaces + */ +string t_cpp_generator::namespace_close(string ns) { + if (ns.size() == 0) { + return ""; + } + string result = "}"; + string::size_type loc; + while ((loc = ns.find(".")) != string::npos) { + result += "}"; + ns = ns.substr(loc + 1); + } + result += " // namespace"; + return result; +} + +/** + * Returns a C++ type name + * + * @param ttype The type + * @return String of the type name, i.e. std::set + */ +string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) { + if (ttype->is_base_type()) { + string bname = base_type_name(((t_base_type*)ttype)->get_base()); + std::map::iterator it = ttype->annotations_.find("cpp.type"); + if (it != ttype->annotations_.end()) { + bname = it->second; + } + + if (!arg) { + return bname; + } + + if (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING) { + return "const " + bname + "&"; + } else { + return "const " + bname; + } + } + + // Check for a custom overloaded C++ name + if (ttype->is_container()) { + string cname; + + t_container* tcontainer = (t_container*)ttype; + if (tcontainer->has_cpp_name()) { + cname = tcontainer->get_cpp_name(); + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + cname = "std::map<" + type_name(tmap->get_key_type(), in_typedef) + ", " + + type_name(tmap->get_val_type(), in_typedef) + "> "; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + cname = "std::set<" + type_name(tset->get_elem_type(), in_typedef) + "> "; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + cname = "std::vector<" + type_name(tlist->get_elem_type(), in_typedef) + "> "; + } + + if (arg) { + return "const " + cname + "&"; + } else { + return cname; + } + } + + string class_prefix; + if (in_typedef && (ttype->is_struct() || ttype->is_xception())) { + class_prefix = "class "; + } + + // Check if it needs to be namespaced + string pname; + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + pname = class_prefix + namespace_prefix(program->get_namespace("cpp")) + ttype->get_name(); + } else { + pname = class_prefix + ttype->get_name(); + } + + if (ttype->is_enum() && !gen_pure_enums_) { + pname += "::type"; + } + + if (arg) { + if (is_complex_type(ttype)) { + return "const " + pname + "&"; + } else { + return "const " + pname; + } + } else { + return pname; + } +} + +/** + * Returns the C++ type that corresponds to the thrift type. + * + * @param tbase The base type + * @return Explicit C++ type, i.e. "int32_t" + */ +string t_cpp_generator::base_type_name(t_base_type::t_base tbase) { + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + return "std::string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_I8: + return "int8_t"; + case t_base_type::TYPE_I16: + return "int16_t"; + case t_base_type::TYPE_I32: + return "int32_t"; + case t_base_type::TYPE_I64: + return "int64_t"; + case t_base_type::TYPE_DOUBLE: + return "double"; + default: + throw "compiler error: no C++ base type name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + * @return Field declaration, i.e. int x = 0; + */ +string t_cpp_generator::declare_field(t_field* tfield, + bool init, + bool pointer, + bool constant, + bool reference) { + // TODO(mcslee): do we ever need to initialize the field? + string result = ""; + if (constant) { + result += "const "; + } + result += type_name(tfield->get_type()); + if (is_reference(tfield)) { + result = "boost::shared_ptr<" + result + ">"; + } + if (pointer) { + result += "*"; + } + if (reference) { + result += "&"; + } + result += " " + tfield->get_name(); + if (init) { + t_type* type = get_true_type(tfield->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: + case t_base_type::TYPE_STRING: + 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; + default: + throw "compiler error: no C++ initializer for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + result += " = (" + type_name(type) + ")0"; + } + } + if (!reference) { + result += ";"; + } + return result; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_cpp_generator::function_signature(t_function* tfunction, + string style, + string prefix, + bool name_params) { + t_type* ttype = tfunction->get_returntype(); + t_struct* arglist = tfunction->get_arglist(); + bool has_xceptions = !tfunction->get_xceptions()->get_members().empty(); + + if (style == "") { + if (is_complex_type(ttype)) { + return "void " + prefix + tfunction->get_name() + "(" + type_name(ttype) + + (name_params ? "& _return" : "& /* _return */") + + argument_list(arglist, name_params, true) + ")"; + } else { + return type_name(ttype) + " " + prefix + tfunction->get_name() + "(" + + argument_list(arglist, name_params) + ")"; + } + } else if (style.substr(0, 3) == "Cob") { + string cob_type; + string exn_cob; + if (style == "CobCl") { + cob_type = "(" + service_name_ + "CobClient"; + if (gen_templates_) { + cob_type += "T"; + } + cob_type += "* client)"; + } else if (style == "CobSv") { + cob_type = (ttype->is_void() ? "()" : ("(" + type_name(ttype) + " const& _return)")); + if (has_xceptions) { + exn_cob + = ", tcxx::function /* exn_cob */"; + } + } else { + throw "UNKNOWN STYLE"; + } + + return "void " + prefix + tfunction->get_name() + "(tcxx::function cob" + + exn_cob + argument_list(arglist, name_params, true) + ")"; + } else { + throw "UNKNOWN STYLE"; + } +} + +/** + * Renders a field list + * + * @param tstruct The struct definition + * @return Comma sepearated list of all field names in that struct + */ +string t_cpp_generator::argument_list(t_struct* tstruct, bool name_params, bool start_comma) { + string result = ""; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool first = !start_comma; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += type_name((*f_iter)->get_type(), false, true) + " " + + (name_params ? (*f_iter)->get_name() : "/* " + (*f_iter)->get_name() + " */"); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + * + * @param type Thrift Type + * @return String of C++ code to definition of that type constant + */ +string t_cpp_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 "::apache::thrift::protocol::T_STRING"; + case t_base_type::TYPE_BOOL: + return "::apache::thrift::protocol::T_BOOL"; + case t_base_type::TYPE_I8: + return "::apache::thrift::protocol::T_BYTE"; + case t_base_type::TYPE_I16: + return "::apache::thrift::protocol::T_I16"; + case t_base_type::TYPE_I32: + return "::apache::thrift::protocol::T_I32"; + case t_base_type::TYPE_I64: + return "::apache::thrift::protocol::T_I64"; + case t_base_type::TYPE_DOUBLE: + return "::apache::thrift::protocol::T_DOUBLE"; + } + } else if (type->is_enum()) { + return "::apache::thrift::protocol::T_I32"; + } else if (type->is_struct()) { + return "::apache::thrift::protocol::T_STRUCT"; + } else if (type->is_xception()) { + return "::apache::thrift::protocol::T_STRUCT"; + } else if (type->is_map()) { + return "::apache::thrift::protocol::T_MAP"; + } else if (type->is_set()) { + return "::apache::thrift::protocol::T_SET"; + } else if (type->is_list()) { + return "::apache::thrift::protocol::T_LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +string t_cpp_generator::get_include_prefix(const t_program& program) const { + string include_prefix = program.get_include_prefix(); + if (!use_include_prefix_ || (include_prefix.size() > 0 && include_prefix[0] == '/')) { + // if flag is turned off or this is absolute path, return empty prefix + return ""; + } + + string::size_type last_slash = string::npos; + if ((last_slash = include_prefix.rfind("/")) != string::npos) { + return include_prefix.substr(0, last_slash) + + (get_program()->is_out_path_absolute() ? "/" : "/" + out_dir_base_ + "/"); + } + + return ""; +} + +THRIFT_REGISTER_GENERATOR( + cpp, + "C++", + " cob_style: Generate \"Continuation OBject\"-style classes.\n" + " no_client_completion:\n" + " Omit calls to completion__() in CobClient class.\n" + " no_default_operators:\n" + " Omits generation of default operators ==, != and <\n" + " templates: Generate templatized reader/writer methods.\n" + " pure_enums: Generate pure enums instead of wrapper classes.\n" + " include_prefix: Use full include paths in generated files.\n" + " moveable_types: Generate move constructors and assignment operators.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc new file mode 100644 index 00000000..ae3c48b0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_csharp_generator.cc @@ -0,0 +1,3202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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::ofstream; +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(ofstream& out, t_field* tfield, bool isPublic, bool generateIsset); + void generate_csharp_property(ofstream& out, + t_field* tfield, + bool isPublic, + bool includeIsset = true, + std::string fieldPrefix = ""); + bool print_const_value(std::ofstream& 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::ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + void print_const_constructor(std::ofstream& out, std::vector consts); + void print_const_def_value(std::ofstream& 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::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool in_class = false, + bool is_result = false); + void generate_csharp_union_definition(std::ofstream& out, t_struct* tunion); + void generate_csharp_union_class(std::ofstream& out, t_struct* tunion, t_field* tfield); + void generate_csharp_wcffault(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_tostring(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_equals(std::ofstream& out, t_struct* tstruct); + void generate_csharp_struct_hashcode(std::ofstream& out, t_struct* tstruct); + void generate_csharp_union_reader(std::ofstream& 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::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool is_propertyless = false); + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + void generate_deserialize_list_element(std::ofstream& out, t_list* list, std::string prefix = ""); + void generate_serialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool is_element = false, + bool is_propertyless = false); + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_csharp_doc(std::ofstream& out, t_field* field); + void generate_csharp_doc(std::ofstream& out, t_doc* tdoc); + void generate_csharp_doc(std::ofstream& out, t_function* tdoc); + void generate_csharp_docstring_comment(std::ofstream& out, string contents); + + void start_csharp_namespace(std::ofstream& out); + void end_csharp_namespace(std::ofstream& 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_; + std::ofstream 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(ofstream& out) { + if (!namespace_name_.empty()) { + out << "namespace " << namespace_name_ << "\n"; + scope_up(out); + } +} + +void t_csharp_generator::end_csharp_namespace(ofstream& 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 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 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::ofstream& 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::ofstream& 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::ofstream& 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(ofstream& 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 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(ofstream& 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()) << " " << (*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)) << " = " << (*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(ofstream& 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(ofstream& 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(); + indent(out) << "throw new TProtocolException(TProtocolException.INVALID_DATA);" << 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(ofstream& 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)); + 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(); + } + } + 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(ofstream& 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(ofstream& 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 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::ofstream& 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::ofstream& 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(ofstream& 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_base_type() && (((t_base_type*)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(ofstream& 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; + } + // 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; + } + + // "Normal" Synchronous invoke + generate_csharp_doc(f_service_, *f_iter); + indent(f_service_) << "public " << function_signature(*f_iter) << endl; + scope_up(f_service_); + + if (!async_) { + indent(f_service_) << "#if !SILVERLIGHT" << endl; + 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; + + indent(f_service_) << "#else" << endl; + } + + // Silverlight synchronous invoke + 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; + + 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; + if (!async_) { + indent(f_service_) << "#else" << endl; + indent(f_service_) << "public " << function_signature(&send_function) << endl; + indent(f_service_) << "#endif" << 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; + ; + + if (!async_) { + indent(f_service_) << "#if SILVERLIGHT" << endl; + } + indent(f_service_) << "return oprot_.Transport.BeginFlush(callback, state);" << endl; + if (!async_) { + indent(f_service_) << "#else" << endl; + indent(f_service_) << "oprot_.Transport.Flush();" << endl; + indent(f_service_) << "#endif" << endl; + } + + cleanup_member_name_mapping(arg_struct); + scope_down(f_service_); + 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 + ".Processor, "; + } + + 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::ofstream& 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(ofstream& 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 (((t_base_type*)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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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 (((t_base_type*)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(ofstream& out, + t_struct* tstruct, + string prefix) { + (void)tstruct; + out << indent() << prefix << ".Write(oprot);" << endl; +} + +void t_csharp_generator::generate_serialize_container(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& 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(ofstream& out, + t_field* tfield, + bool isPublic, + bool generateIsset) { + generate_csharp_property(out, tfield, isPublic, generateIsset, "_"); +} +void t_csharp_generator::generate_csharp_property(ofstream& 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; + } + } + 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; + 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)) { + 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(ofstream& out, string contents) { + generate_docstring_comment(out, "/// \n", "/// ", contents, "/// \n"); +} + +void t_csharp_generator::generate_csharp_doc(ofstream& 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(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_csharp_docstring_comment(out, tdoc->get_doc()); + } +} + +void t_csharp_generator::generate_csharp_doc(ofstream& 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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc new file mode 100644 index 00000000..48166814 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_d_generator.cc @@ -0,0 +1,728 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::ostringstream; +using std::set; +using std::string; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * D code generator. + * + * generate_*() functions are called by the base class to emit code for the + * given entity, print_*() functions write a piece of code to the passed + * stream, and render_*() return a string containing the D representation of + * the passed entity. + */ +class t_d_generator : public t_oop_generator { +public: + t_d_generator(t_program* program, + const std::map& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)option_string; + std::map::const_iterator iter; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option d:" + iter->first; + } + + out_dir_base_ = "gen-d"; + } + +protected: + virtual void init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + string dir = program_->get_namespace("d"); + string subdir = get_out_dir(); + 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.empty()) { + subdir = subdir + "/" + dir; + MKDIR(subdir.c_str()); + } + + package_dir_ = subdir + "/"; + + // Make output file + string f_types_name = package_dir_ + program_name_ + "_types.d"; + f_types_.open(f_types_name.c_str()); + + // Print header + f_types_ << autogen_comment() << "module " << render_package(*program_) << program_name_ + << "_types;" << endl << endl; + + print_default_imports(f_types_); + + // Include type modules from other imported programs. + const vector& includes = program_->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + f_types_ << "import " << render_package(*(includes[i])) << includes[i]->get_name() + << "_types;" << endl; + } + if (!includes.empty()) + f_types_ << endl; + } + + virtual void close_generator() { + // Close output file + f_types_.close(); + } + + virtual void generate_consts(std::vector consts) { + if (!consts.empty()) { + string f_consts_name = package_dir_ + program_name_ + "_constants.d"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + f_consts << autogen_comment() << "module " << render_package(*program_) << program_name_ + << "_constants;" << endl << endl; + + print_default_imports(f_consts); + + f_consts << "import " << render_package(*get_program()) << program_name_ << "_types;" << endl + << endl; + + 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(); + indent(f_consts) << "immutable(" << render_type_name(type) << ") " << name << ";" << endl; + } + + f_consts << endl << "static this() {" << endl; + indent_up(); + + bool first = true; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if (first) { + first = false; + } else { + f_consts << endl; + } + t_type* type = (*c_iter)->get_type(); + indent(f_consts) << (*c_iter)->get_name() << " = "; + if (!is_immutable_type(type)) { + f_consts << "cast(immutable(" << render_type_name(type) << ")) "; + } + f_consts << render_const_value(type, (*c_iter)->get_value()) << ";" << endl; + } + indent_down(); + indent(f_consts) << "}" << endl; + } + } + + virtual void generate_typedef(t_typedef* ttypedef) { + f_types_ << indent() << "alias " << render_type_name(ttypedef->get_type()) << " " + << ttypedef->get_symbolic() << ";" << endl << endl; + } + + virtual void generate_enum(t_enum* tenum) { + vector constants = tenum->get_constants(); + + string enum_name = tenum->get_name(); + f_types_ << indent() << "enum " << enum_name << " {" << endl; + + indent_up(); + + vector::const_iterator c_iter; + bool first = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if (first) { + first = false; + } else { + f_types_ << "," << endl; + } + indent(f_types_) << (*c_iter)->get_name(); + f_types_ << " = " << (*c_iter)->get_value(); + } + + f_types_ << endl; + indent_down(); + indent(f_types_) << "}" << endl; + + f_types_ << endl; + } + + virtual void generate_struct(t_struct* tstruct) { + print_struct_definition(f_types_, tstruct, false); + } + + virtual void generate_xception(t_struct* txception) { + print_struct_definition(f_types_, txception, true); + } + + virtual void generate_service(t_service* tservice) { + string svc_name = tservice->get_name(); + + // Service implementation file includes + string f_servicename = package_dir_ + svc_name + ".d"; + std::ofstream f_service; + f_service.open(f_servicename.c_str()); + f_service << autogen_comment() << "module " << render_package(*program_) << svc_name << ";" + << endl << endl; + + print_default_imports(f_service); + + f_service << "import " << 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; + } + + f_service << endl; + + string extends = ""; + if (tservice->get_extends() != NULL) { + extends = " : " + render_type_name(tservice->get_extends()); + } + + f_service << indent() << "interface " << svc_name << extends << " {" << endl; + indent_up(); + + // Collect all the exception types service methods can throw so we can + // emit the necessary aliases later. + set exception_types; + + // Print the method signatures. + vector functions = tservice->get_functions(); + vector::iterator fn_iter; + for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { + f_service << indent(); + print_function_signature(f_service, *fn_iter); + f_service << ";" << endl; + + const vector& exceptions = (*fn_iter)->get_xceptions()->get_members(); + vector::const_iterator ex_iter; + for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { + exception_types.insert((*ex_iter)->get_type()); + } + } + + // Alias the exception types into the current scope. + if (!exception_types.empty()) + f_service << endl; + set::const_iterator et_iter; + for (et_iter = exception_types.begin(); et_iter != exception_types.end(); ++et_iter) { + indent(f_service) << "alias " << render_package(*(*et_iter)->get_program()) + << (*et_iter)->get_program()->get_name() << "_types" + << "." << (*et_iter)->get_name() << " " << (*et_iter)->get_name() << ";" + << endl; + } + + // Write the method metadata. + ostringstream meta; + indent_up(); + bool first = true; + for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { + if ((*fn_iter)->get_arglist()->get_members().empty() + && (*fn_iter)->get_xceptions()->get_members().empty() && !(*fn_iter)->is_oneway()) { + continue; + } + + if (first) { + first = false; + } else { + meta << ","; + } + + meta << endl << indent() << "TMethodMeta(`" << (*fn_iter)->get_name() << "`, " << endl; + indent_up(); + indent(meta) << "["; + + bool first = true; + const vector& params = (*fn_iter)->get_arglist()->get_members(); + vector::const_iterator p_iter; + for (p_iter = params.begin(); p_iter != params.end(); ++p_iter) { + if (first) { + first = false; + } else { + meta << ", "; + } + + meta << "TParamMeta(`" << (*p_iter)->get_name() << "`, " << (*p_iter)->get_key(); + + t_const_value* cv = (*p_iter)->get_value(); + if (cv != NULL) { + meta << ", q{" << render_const_value((*p_iter)->get_type(), cv) << "}"; + } + meta << ")"; + } + + meta << "]"; + + if (!(*fn_iter)->get_xceptions()->get_members().empty() || (*fn_iter)->is_oneway()) { + meta << "," << endl << indent() << "["; + + bool first = true; + const vector& exceptions = (*fn_iter)->get_xceptions()->get_members(); + vector::const_iterator ex_iter; + for (ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter) { + if (first) { + first = false; + } else { + meta << ", "; + } + + meta << "TExceptionMeta(`" << (*ex_iter)->get_name() << "`, " << (*ex_iter)->get_key() + << ", `" << (*ex_iter)->get_type()->get_name() << "`)"; + } + + meta << "]"; + } + + if ((*fn_iter)->is_oneway()) { + meta << "," << endl << indent() << "TMethodType.ONEWAY"; + } + + indent_down(); + meta << endl << indent() << ")"; + } + indent_down(); + + string meta_str(meta.str()); + if (!meta_str.empty()) { + f_service << endl << indent() << "enum methodMeta = [" << meta_str << endl << indent() << "];" + << endl; + } + + indent_down(); + indent(f_service) << "}" << endl; + + // Server skeleton generation. + string f_skeletonname = package_dir_ + svc_name + "_server.skeleton.d"; + std::ofstream f_skeleton; + f_skeleton.open(f_skeletonname.c_str()); + print_server_skeleton(f_skeleton, tservice); + f_skeleton.close(); + } + +private: + /** + * 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(); + + out << "/*" << endl + << " * This auto-generated skeleton file illustrates how to build a server. If you" << endl + << " * intend to customize it, you should edit a copy with another file name to " << endl + << " * avoid overwriting it when running the generator again." << endl << " */" << endl + << "module " << render_package(*tservice->get_program()) << svc_name << "_server;" << endl + << endl << "import std.stdio;" << endl << "import thrift.codegen.processor;" << endl + << "import thrift.protocol.binary;" << endl << "import thrift.server.simple;" << endl + << "import thrift.server.transport.socket;" << endl << "import thrift.transport.buffered;" + << endl << "import thrift.util.hashset;" << endl << endl << "import " + << render_package(*tservice->get_program()) << svc_name << ";" << endl << "import " + << render_package(*get_program()) << program_name_ << "_types;" << endl << endl << endl + << "class " << svc_name << "Handler : " << svc_name << " {" << endl; + + indent_up(); + out << indent() << "this() {" << 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) { + out << indent(); + print_function_signature(out, *f_iter); + out << " {" << endl; + + indent_up(); + + out << indent() << "// Your implementation goes here." << endl << indent() << "writeln(\"" + << (*f_iter)->get_name() << " called\");" << endl; + + t_base_type* rt = (t_base_type*)(*f_iter)->get_returntype(); + if (rt->get_base() != t_base_type::TYPE_VOID) { + indent(out) << "return typeof(return).init;" << endl; + } + + indent_down(); + + out << indent() << "}" << endl << endl; + } + + indent_down(); + out << "}" << endl << endl; + + out << indent() << "void main() {" << endl; + indent_up(); + out << indent() << "auto protocolFactory = new TBinaryProtocolFactory!();" << endl << indent() + << "auto processor = new TServiceProcessor!" << svc_name << "(new " << svc_name + << "Handler);" << endl << indent() << "auto serverTransport = new TServerSocket(9090);" + << endl << indent() << "auto transportFactory = new TBufferedTransportFactory;" << endl + << indent() << "auto server = new TSimpleServer(" << endl << indent() + << " processor, serverTransport, transportFactory, protocolFactory);" << endl << indent() + << "server.serve();" << endl; + indent_down(); + out << "}" << endl; + } + + /** + * Writes the definition of a struct or an exception type to out. + */ + void print_struct_definition(ostream& out, t_struct* tstruct, bool is_exception) { + const vector& members = tstruct->get_members(); + + if (is_exception) { + indent(out) << "class " << tstruct->get_name() << " : TException {" << endl; + } else { + indent(out) << "struct " << 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() << ";" + << endl; + } + + if (!members.empty()) + indent(out) << endl; + indent(out) << "mixin TStructHelpers!("; + + if (!members.empty()) { + // If there are any fields, construct the TFieldMeta array to pass to + // TStructHelpers. We can't just pass an empty array if not because [] + // doesn't pass the TFieldMeta[] constraint. + out << "["; + indent_up(); + + bool first = true; + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (first) { + first = false; + } else { + out << ","; + } + out << endl; + + indent(out) << "TFieldMeta(`" << (*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) { + out << ", q{" << render_const_value((*m_iter)->get_type(), cv) << "}"; + } + out << ")"; + } + + indent_down(); + out << endl << indent() << "]"; + } + + out << ");" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; + } + + /** + * Prints the D function signature (including return type) for the given + * method. + */ + void print_function_signature(ostream& out, t_function* fn) { + out << render_type_name(fn->get_returntype()) << " " << fn->get_name() << "("; + + const vector& fields = fn->get_arglist()->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 { + out << ", "; + } + out << render_type_name((*f_iter)->get_type(), true) << " " << (*f_iter)->get_name(); + } + + out << ")"; + } + + /** + * Returns the D representation of value. The result is guaranteed to be a + * single expression; for complex types, immediately called delegate + * literals are used to achieve this. + */ + string render_const_value(t_type* type, t_const_value* value) { + // Resolve any typedefs. + type = get_true_type(type); + + ostringstream out; + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << ((value->get_integer() > 0) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + out << "cast(" << render_type_name(type) << ")" << value->get_integer(); + break; + case t_base_type::TYPE_I32: + out << value->get_integer(); + break; + case t_base_type::TYPE_I64: + out << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out << "cast(" << render_type_name(type) << ")" << value->get_integer(); + } else { + out << "{" << endl; + indent_up(); + + indent(out) << render_type_name(type) << " v;" << endl; + if (type->is_struct() || type->is_xception()) { + indent(out) << "v = " << (type->is_xception() ? "new " : "") << render_type_name(type) + << "();" << endl; + + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(field_type, v_iter->second); + indent(out) << "v.set!`" << v_iter->first->get_string() << "`(" << val << ");" << endl; + } + } 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(ktype, v_iter->first); + string val = render_const_value(vtype, v_iter->second); + indent(out) << "v["; + if (!is_immutable_type(ktype)) { + out << "cast(immutable(" << render_type_name(ktype) << "))"; + } + out << key << "] = " << val << ";" << endl; + } + } else if (type->is_list()) { + t_type* etype = ((t_list*)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(etype, *v_iter); + indent(out) << "v ~= " << val << ";" << endl; + } + } else if (type->is_set()) { + t_type* 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(etype, *v_iter); + indent(out) << "v ~= " << val << ";" << endl; + } + } else { + throw "Compiler error: Invalid type in render_const_value: " + type->get_name(); + } + indent(out) << "return v;" << endl; + + indent_down(); + indent(out) << "}()"; + } + + return out.str(); + } + + /** + * Returns the D package to which modules for program are written (with a + * trailing dot, if not empty). + */ + string render_package(const t_program& program) const { + string package = program.get_namespace("d"); + if (package.size() == 0) + return ""; + return package + "."; + } + + /** + * Returns the name of the D repesentation of ttype. + * + * If isArg is true, a const reference to the type will be returned for + * structs. + */ + string render_type_name(const t_type* ttype, bool isArg = false) const { + 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: + return "void"; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_I8: + return "byte"; + case t_base_type::TYPE_I16: + return "short"; + case t_base_type::TYPE_I32: + return "int"; + case t_base_type::TYPE_I64: + return "long"; + case t_base_type::TYPE_DOUBLE: + return "double"; + default: + throw "Compiler error: No D type name for base type " + t_base_type::t_base_name(tbase); + } + } + + if (ttype->is_container()) { + t_container* tcontainer = (t_container*)ttype; + if (tcontainer->has_cpp_name()) { + return tcontainer->get_cpp_name(); + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + t_type* ktype = tmap->get_key_type(); + + string name = render_type_name(tmap->get_val_type()) + "["; + if (!is_immutable_type(ktype)) { + name += "immutable("; + } + name += render_type_name(ktype); + if (!is_immutable_type(ktype)) { + name += ")"; + } + name += "]"; + return name; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + return "HashSet!(" + render_type_name(tset->get_elem_type()) + ")"; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + return render_type_name(tlist->get_elem_type()) + "[]"; + } + } + + if (ttype->is_struct() && isArg) { + return "ref const(" + ttype->get_name() + ")"; + } else { + return ttype->get_name(); + } + } + + /** + * Returns the D TReq enum member corresponding to req. + */ + string render_req(t_field::e_req req) const { + switch (req) { + case t_field::T_OPT_IN_REQ_OUT: + return "TReq.OPT_IN_REQ_OUT"; + case t_field::T_OPTIONAL: + return "TReq.OPTIONAL"; + case t_field::T_REQUIRED: + return "TReq.REQUIRED"; + default: { + std::stringstream ss; + ss << "Compiler error: Invalid requirement level " << req; + throw ss.str(); + } + } + } + + /** + * Writes the default list of imports (which are written to every generated + * module) to f. + */ + void print_default_imports(ostream& out) { + indent(out) << "import thrift.base;" << endl << "import thrift.codegen.base;" << endl + << "import thrift.util.hashset;" << endl << endl; + } + + /** + * Returns whether type is »intrinsically immutable«, in the sense that + * a value of that type is implicitly castable to immutable(type), and it is + * allowed for AA keys without an immutable() qualifier. + */ + bool is_immutable_type(t_type* type) const { + t_type* ttype = get_true_type(type); + return ttype->is_base_type() || ttype->is_enum(); + } + + /* + * File streams, stored here to avoid passing them as parameters to every + * function. + */ + ofstream f_types_; + ofstream f_header_; + + string package_dir_; +}; + +THRIFT_REGISTER_GENERATOR(d, "D", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc new file mode 100644 index 00000000..19bbb7b2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_dart_generator.cc @@ -0,0 +1,2516 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 +static const string endl2 = "\n\n"; + +/** + * Use the current Thrift version for static libraries. When releasing, update + * the version in these files. + * - lib/dart/pubspec.yaml + * - test/dart/test_client/pubspec.yaml + * - tutorial/dart/client/pubspec.yaml + * - tutorial/dart/console_client/pubspec.yaml + * - tutorial/dart/server/pubspec.yaml + * See https://thrift.apache.org/docs/committers/HowToVersion + */ +static const string dart_thrift_version = THRIFT_VERSION; + +/* forward declarations */ +string initial_caps_to_underscores(string name); + +/** + * Dart code generator + * + */ +class t_dart_generator : public t_oop_generator { +public: + t_dart_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; + + library_name_ = ""; + library_prefix_ = ""; + package_prefix_ = ""; + pubspec_lib_ = ""; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("library_name") == 0) { + library_name_ = (iter->second); + } else if( iter->first.compare("library_prefix") == 0) { + library_prefix_ = (iter->second) + "."; + package_prefix_ = replace_all(library_prefix_, ".", "/"); + } else if( iter->first.compare("pubspec_lib") == 0) { + pubspec_lib_ = (iter->second); + } else { + throw "unknown option dart:" + iter->first; + } + } + + out_dir_base_ = "gen-dart"; + } + + void scope_up(std::ostream& out, std::string prefix=" ") { + out << prefix << "{" << endl; + indent_up(); + } + + void scope_down(std::ostream& out, std::string postfix=endl) { + indent_down(); + indent(out) << "}" << postfix; + } + + string replace_all(string contents, string search, string repl) { + string str(contents); + + size_t slen = search.length(); + size_t rlen = repl.length(); + size_t incr = (rlen > 0) ? rlen : 1; + + if (slen > 0) { + size_t found = str.find(search); + while ((found != string::npos) && (found < str.length())) { + str.replace(found, slen, repl); + found = str.find(search, found + incr); + } + } + + return str; + } + + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + 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); + + /** + * 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(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval = false); + std::string render_const_value(ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + /** + * Service-level generation functions + */ + + void generate_dart_struct(t_struct* tstruct, bool is_exception); + + void generate_dart_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_result = false, + string export_file_name = ""); + void generate_dart_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_dart_validator(std::ofstream& out, t_struct* tstruct); + void generate_dart_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_dart_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_dart_struct_tostring(std::ofstream& out, t_struct* tstruct); + std::string get_dart_type_string(t_type* type); + void generate_generic_field_getters(std::ofstream& out, t_struct* tstruct); + void generate_generic_field_setters(std::ofstream& out, t_struct* tstruct); + void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); + void generate_dart_bean_boilerplate(std::ofstream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + std::string init_value(t_field* tfield); + std::string get_cap_name(std::string name); + std::string get_member_name(std::string name); + std::string get_args_class_name(std::string name); + std::string get_result_class_name(std::string name); + std::string get_file_name(std::string name); + std::string get_constants_class_name(std::string name); + std::string generate_isset_check(t_field* field); + std::string generate_isset_check(std::string field); + void generate_isset_set(ofstream& out, t_field* field); + + void generate_service_interface(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_process_function(t_service* tservice, t_function* tfunction); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_dart_doc(std::ofstream& out, t_doc* tdoc); + + void generate_dart_doc(std::ofstream& out, t_function* tdoc); + + /** + * Helper rendering functions + */ + + std::string find_library_name(t_program* program); + std::string dart_library(string file_name); + std::string service_imports(); + std::string dart_thrift_imports(); + std::string type_name(t_type* ttype); + std::string base_type_name(t_base_type* tbase); + std::string declare_field(t_field* tfield, bool init = false); + std::string function_signature(t_function* tfunction); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + std::string get_ttype_class_name(t_type* ttype); + + 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(); + } + + vector split(const string& s, char delim) { + vector elems; + stringstream ss(s); + string item; + while (getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; + } + + std::string constant_name(std::string name); + +private: + std::ofstream f_service_; + + std::string library_name_; + std::string library_prefix_; + std::string package_prefix_; + std::string pubspec_lib_; + + std::string base_dir_; + std::string src_dir_; + std::string library_exports_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_dart_generator::init_generator() { + MKDIR(get_out_dir().c_str()); + + if (library_name_.empty()) { + library_name_ = find_library_name(program_); + } + + string subdir = get_out_dir() + "/" + library_name_; + MKDIR(subdir.c_str()); + base_dir_ = subdir; + + if (library_prefix_.empty()) { + subdir += "/lib"; + MKDIR(subdir.c_str()); + subdir += "/src"; + MKDIR(subdir.c_str()); + src_dir_ = subdir; + } else { + src_dir_ = base_dir_; + } +} + +string t_dart_generator::find_library_name(t_program* program) { + string name = program->get_namespace("dart"); + if (name.empty()) { + name = program->get_name(); + } + name = replace_all(name, ".", "_"); + name = replace_all(name, "-", "_"); + return name; +} + +/** + * The Dart library + * + * @return String of the library, e.g. "library myservice;" + */ +string t_dart_generator::dart_library(string file_name) { + string out = "library " + library_prefix_ + library_name_; + if (!file_name.empty()) { + if (library_prefix_.empty()) { + out += ".src." + file_name; + } else { + out += "." + file_name; + } + } + return out + ";\n"; +} + +/** + * Prints imports for services + * + * @return List of imports for services + */ +string t_dart_generator::service_imports() { + return "import 'dart:async';" + endl; +} + +/** + * Prints standard dart imports + * + * @return List of imports necessary for thrift + */ +string t_dart_generator::dart_thrift_imports() { + string imports = "import 'dart:typed_data' show Uint8List;" + endl + + "import 'package:thrift/thrift.dart';" + endl; + + // add import for this library + if (package_prefix_.empty()) { + imports += "import 'package:" + library_name_ + "/" + library_name_ + ".dart';" + endl; + } else { + imports += "import 'package:" + package_prefix_ + library_name_ + ".dart';" + endl; + } + + // 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]); + string named_import = "t_" + include_name; + if (package_prefix_.empty()) { + imports += "import 'package:" + include_name + "/" + include_name + ".dart' as " + named_import + ";" + endl; + } else { + imports += "import 'package:" + package_prefix_ + include_name + ".dart' as " + named_import + ";" + endl; + } + } + + return imports; +} + +/** + * Not used + */ +void t_dart_generator::close_generator() { + generate_dart_library(); + + if (library_prefix_.empty()) { + generate_dart_pubspec(); + } +} + +void t_dart_generator::generate_dart_library() { + string f_library_name; + if (library_prefix_.empty()) { + f_library_name = base_dir_ + "/lib/" + library_name_ + ".dart"; + } else { + f_library_name = get_out_dir() + "/" + library_name_ + ".dart"; + } + + ofstream f_library; + f_library.open(f_library_name.c_str()); + + f_library << autogen_comment() << endl; + f_library << "library " << library_prefix_ << library_name_ << ";" << endl2; + f_library << library_exports_; + + f_library.close(); +} + +void t_dart_generator::export_class_to_library(string file_name, string class_name) { + string subdir; + if (library_prefix_.empty()) { + subdir = "src"; + } else { + subdir = library_name_; + } + library_exports_ += "export '" + subdir + "/" + file_name + ".dart' show " + class_name + ";" + endl; +} + +void t_dart_generator::generate_dart_pubspec() { + string f_pubspec_name = base_dir_ + "/pubspec.yaml"; + ofstream f_pubspec; + f_pubspec.open(f_pubspec_name.c_str()); + + indent(f_pubspec) << "name: " << library_name_ << endl; + indent(f_pubspec) << "version: 0.0.1" << endl; + indent(f_pubspec) << "description: Autogenerated by Thrift Compiler" << endl; + f_pubspec << endl; + + indent(f_pubspec) << "environment:" << endl; + indent_up(); + indent(f_pubspec) << "sdk: ^1.12.0" << endl; + indent_down(); + f_pubspec << endl; + + indent(f_pubspec) << "dependencies:" << endl; + indent_up(); + + if (pubspec_lib_.empty()) { + // default to relative path within working directory, which works for tests + indent(f_pubspec) << "thrift: # ^" << dart_thrift_version << endl; + indent_up(); + indent(f_pubspec) << "path: ../../../../lib/dart" << endl; + 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; + } + } + + // 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]); + indent(f_pubspec) << include_name << ":" << endl; + indent_up(); + indent(f_pubspec) << "path: ../" << include_name << endl; + indent_down(); + } + + indent_down(); + f_pubspec << endl; + + f_pubspec.close(); +} + +/** + * Not used + * + * @param ttypedef The type definition + */ +void t_dart_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Enums are a class with a set of static constants. + * + * @param tenum The enumeration + */ +void t_dart_generator::generate_enum(t_enum* tenum) { + // Make output file + string file_name = get_file_name(tenum->get_name()); + + string f_enum_name = src_dir_ + "/" + file_name + ".dart"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + // Comment and add library + f_enum << autogen_comment() << dart_library(file_name) << endl; + + string class_name = tenum->get_name(); + export_class_to_library(file_name, class_name); + f_enum << "class " << class_name; + scope_up(f_enum); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << "static const int " << (*c_iter)->get_name() << " = " << value << ";" + << endl; + } + + // Create a static Set with all valid values for this enum + f_enum << endl; + + indent(f_enum) << "static final Set VALID_VALUES = new Set.from([" << endl; + indent_up(); + bool firstValue = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + // populate set + indent(f_enum) << (firstValue ? "" : ", "); + f_enum << (*c_iter)->get_name() << endl; + firstValue = false; + } + indent_down(); + indent(f_enum) << "]);" << endl; + + indent(f_enum) << "static final Map VALUES_TO_NAMES = {" << endl; + indent_up(); + firstValue = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + indent(f_enum) << (firstValue ? "" : ", "); + f_enum << (*c_iter)->get_name() << ": '" << (*c_iter)->get_name() << "'" << endl; + firstValue = false; + } + indent_down(); + indent(f_enum) << "};" << endl; + + scope_down(f_enum); // end class + + f_enum.close(); +} + +/** + * Generates a class that holds all the constants. + */ +void t_dart_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + string class_name = get_constants_class_name(program_name_); + string file_name = get_file_name(class_name); + + string f_consts_name = src_dir_ + "/" + file_name + ".dart"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + // Print header + f_consts << autogen_comment() << dart_library(file_name) << endl; + f_consts << dart_thrift_imports() << endl; + + export_class_to_library(file_name, class_name); + indent(f_consts) << "class " << class_name; + scope_up(f_consts); + + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false); + f_consts << endl; + } + + scope_down(f_consts); + + f_consts.close(); +} + +void t_dart_generator::print_const_value(std::ofstream& out, + string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval) { + type = get_true_type(type); + + indent(out); + if (!defval) { + out << (in_static ? "var " : "static final "); + } + if (type->is_base_type()) { + if (!defval) { + out << type_name(type) << " "; + } + string v2 = render_const_value(out, name, type, value); + out << name; + out << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + if (!defval) { + out << type_name(type) << " "; + } + out << name; + out << " = " << value->get_integer() << ";" << endl << endl; + } 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; + 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; + 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, name, field_type, v_iter->second); + out << endl; + indent(out) << ".." << v_iter->first->get_string() << " = " << val; + } + indent_down(); + out << ";" << endl; + } else if (type->is_map()) { + if (!defval) { + out << type_name(type) << " "; + } + out << name << " ="; + scope_up(out); + + 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) << key << ": " << val << "," << endl; + } + scope_down(out, ";" + endl); + + out << endl; + } else if (type->is_list() || type->is_set()) { + if (!defval) { + out << type_name(type) << " "; + } + out << name << " = "; + t_type* etype; + if (type->is_list()) { + out << "[" << endl; + etype = ((t_list*)type)->get_elem_type(); + } else { + out << "new " << type_name(type) << ".from([" << endl; + etype = ((t_set*)type)->get_elem_type(); + } + const vector& val = value->get_list(); + vector::const_iterator v_iter; + + indent_up(); + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string val = render_const_value(out, name, etype, *v_iter); + indent(out) << val << "," << endl; + } + indent_down(); + + if (type->is_list()) { + indent(out) << "];" << endl; + } else { + indent(out) << "]);" << endl; + } + + } else { + throw "compiler error: no const of type " + type->get_name(); + } +} + +string t_dart_generator::render_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + 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) ? "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 << value->get_integer(); + } else { + string t = tmp("tmp"); + print_const_value(out, t, type, value, true); + out << endl; + render << t; + } + + return render.str(); +} + +/** + * Generates a struct definition for a thrift data type. This is a class + * with data members, read(), write(), and an inner Isset class. + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_struct(t_struct* tstruct) { + generate_dart_struct(tstruct, false); +} + +/** + * Exceptions are structs, but they inherit from Exception + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_xception(t_struct* txception) { + generate_dart_struct(txception, true); +} + +/** + * Dart struct definition. + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_struct(t_struct* tstruct, bool is_exception) { + string file_name = get_file_name(tstruct->get_name()); + string f_struct_name = src_dir_ + "/" + file_name + ".dart"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << dart_library(file_name) << endl; + + string imports; + + f_struct << dart_thrift_imports() << endl; + + generate_dart_struct_definition(f_struct, tstruct, is_exception, false, file_name); + + f_struct.close(); +} + +/** + * Dart struct definition. This has various parameters, as it could be + * generated standalone or inside another class as a helper. If it + * is a helper than it is a static class. + * + * @param tstruct The struct definition + * @param is_exception Is this an exception? + * @param in_class If inside a class, needs to be static class + * @param is_result If this is a result it needs a different writer + */ +void t_dart_generator::generate_dart_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_result, + string export_file_name) { + generate_dart_doc(out, tstruct); + + string class_name = tstruct->get_name(); + if (!export_file_name.empty()) { + export_class_to_library(export_file_name, class_name); + } + indent(out) << "class " << class_name << " "; + + if (is_exception) { + out << "extends Error "; + } + out << "implements TBase"; + scope_up(out); + + indent(out) << "static final TStruct _STRUCT_DESC = new TStruct(\"" << class_name + << "\");" << endl; + + // Members are public for -dart, private for -dartbean + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "static final TField _" << constant_name((*m_iter)->get_name()) + << "_FIELD_DESC = new TField(\"" << (*m_iter)->get_name() << "\", " + << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");" + << endl; + } + + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_dart_doc(out, *m_iter); + indent(out) << type_name((*m_iter)->get_type()) + " _" + << get_member_name((*m_iter)->get_name()) << init_value(*m_iter) << ";" << endl; + + indent(out) << "static const int " << upcase_string((*m_iter)->get_name()) + << " = " << (*m_iter)->get_key() << ";" << endl; + } + + out << endl; + + // Inner Isset class + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null((*m_iter)->get_type())) { + string field_name = get_member_name((*m_iter)->get_name()); + indent(out) << "bool __isset_" << field_name << " = false;" << endl; + } + } + } + + out << endl; + + // Default constructor + indent(out) << tstruct->get_name() << "()"; + 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) { + print_const_value(out, + "this." + get_member_name((*m_iter)->get_name()), + t, + (*m_iter)->get_value(), + true, + true); + } + } + scope_down(out); + out << endl; + + generate_dart_bean_boilerplate(out, tstruct); + generate_generic_field_getters(out, tstruct); + generate_generic_field_setters(out, tstruct); + generate_generic_isset_method(out, tstruct); + + generate_dart_struct_reader(out, tstruct); + if (is_result) { + generate_dart_struct_result_writer(out, tstruct); + } else { + generate_dart_struct_writer(out, tstruct); + } + generate_dart_struct_tostring(out, tstruct); + generate_dart_validator(out, tstruct); + scope_down(out); + out << endl; +} + +/** + * Generates a function to read all the fields of the struct. + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_struct_reader(ofstream& out, t_struct* tstruct) { + indent(out) << "read(TProtocol iprot)"; + scope_up(out); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables and read struct header + indent(out) << "TField field;" << endl; + indent(out) << "iprot.readStructBegin();" << endl; + + // Loop over reading in fields + indent(out) << "while (true)"; + scope_up(out); + + // Read beginning field marker + indent(out) << "field = iprot.readFieldBegin();" << endl; + + // Check for field STOP marker and break + indent(out) << "if (field.type == TType.STOP)"; + scope_up(out); + indent(out) << "break;" << endl; + scope_down(out); + + // Switch statement on the field we are reading + indent(out) << "switch (field.id)"; + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl; + indent_up(); + + indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ")"; + scope_up(out); + + generate_deserialize_field(out, *f_iter, "this."); + generate_isset_set(out, *f_iter); + + scope_down(out, " else"); + scope_up(out); + indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl; + scope_down(out); + + indent(out) << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + indent(out) << "default:" << endl; + indent_up(); + indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl; + indent(out) << "break;" << endl; + indent_down(); + + scope_down(out); + + // Read field end marker + indent(out) << "iprot.readFieldEnd();" << endl; + + scope_down(out); + + indent(out) << "iprot.readStructEnd();" << endl2; + + // in non-beans style, check for required fields of primitive type + // (which can be checked here but not in the general validate method) + indent(out) << "// check for required fields of primitive type, which can't be " + "checked in the validate method" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) { + string field_name = get_member_name((*f_iter)->get_name()); + indent(out) << "if (!__isset_" << field_name << ")"; + scope_up(out); + indent(out) << " throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '" + << field_name + << "' was not found in serialized data! Struct: \" + toString());" << endl; + scope_down(out, endl2); + } + } + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl; + + scope_down(out, endl2); +} + +// generates dart method to perform various checks +// (e.g. check that all required fields are set) +void t_dart_generator::generate_dart_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "validate()"; + scope_up(out); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "// check for required fields" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + string field_name = get_member_name((*f_iter)->get_name()); + if (type_can_be_null((*f_iter)->get_type())) { + indent(out) << "if (" << field_name << " == null)"; + scope_up(out); + indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"Required field '" + << field_name << "' was not present! Struct: \" + toString());" + << endl; + scope_down(out); + } else { + indent(out) << "// alas, we cannot check '" << field_name + << "' because it's a primitive and you chose the non-beans generator." << endl; + } + } + } + + // check that fields of type enum have valid values + indent(out) << "// check that fields of type enum have valid values" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + t_type* type = field->get_type(); + // if field is an enum, check that its value is valid + if (type->is_enum()) { + string field_name = get_member_name(field->get_name()); + indent(out) << "if (" << generate_isset_check(field) << " && !" << get_ttype_class_name(type) + << ".VALID_VALUES.contains(" << field_name << "))"; + scope_up(out); + indent(out) << "throw new TProtocolError(TProtocolErrorType.UNKNOWN, \"The field '" + << field_name << "' has been assigned the invalid value " + << "$" << field_name << "\");" << endl; + scope_down(out); + } + } + + scope_down(out, endl2); +} + +/** + * Generates a function to write all the fields of the struct + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_struct_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "write(TProtocol oprot)"; + scope_up(out); + + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl2; + + indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string field_name = get_member_name((*f_iter)->get_name()); + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ")"; + scope_up(out); + } + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + indent(out) << "if (this." << field_name << " != null)"; + scope_up(out); + } + + indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + if (null_allowed) { + scope_down(out); + } + if (could_be_unset) { + scope_down(out); + } + } + // Write the struct map + indent(out) << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();" + << endl; + + scope_down(out, endl2); +} + +/** + * 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 in the Isset array, and only one of them + * can be set at a time. + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_struct_result_writer(ofstream& out, t_struct* tstruct) { + indent(out) << "write(TProtocol oprot)"; + scope_up(out); + + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << "oprot.writeStructBegin(_STRUCT_DESC);" << endl2; + + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + indent(out) << "if "; + } else { + out << " else if "; + } + + out << "(this." << generate_isset_check(*f_iter) << ")"; + scope_up(out); + + indent(out) << "oprot.writeFieldBegin(_" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + scope_down(out, ""); + } + out << endl; + + // Write the struct map + indent(out) << "oprot.writeFieldStop();" << endl << indent() + << "oprot.writeStructEnd();" << endl; + + scope_down(out, endl2); +} + +void t_dart_generator::generate_generic_field_getters(std::ofstream& out, + t_struct* tstruct) { + // create the getter + indent(out) << "getFieldValue(int fieldID)"; + scope_up(out); + + indent(out) << "switch (fieldID)"; + scope_up(out); + + 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 = get_member_name(field->get_name()); + + indent(out) << "case " << upcase_string(field_name) << ":" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent_up(); + indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl; + indent_down(); + + scope_down(out); // switch + scope_down(out, endl2); // method +} + +void t_dart_generator::generate_generic_field_setters(std::ofstream& out, + t_struct* tstruct) { + + // create the setter + indent(out) << "setFieldValue(int fieldID, Object value)"; + scope_up(out); + + indent(out) << "switch (fieldID)"; + scope_up(out); + + // 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 = get_member_name(field->get_name()); + + indent(out) << "case " << upcase_string(field_name) << ":" << endl; + indent_up(); + + indent(out) << "if (value == null)"; + scope_up(out); + indent(out) << "unset" << get_cap_name(field_name) << "();" << endl; + + scope_down(out, " else"); + scope_up(out); + indent(out) << "this." << field_name << " = value;" << endl; + scope_down(out); + + indent(out) << "break;" << endl; + + indent_down(); + out << endl; + } + + indent(out) << "default:" << endl; + indent_up(); + indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl; + indent_down(); + + scope_down(out); // switch + scope_down(out, endl2); // method +} + +// Creates a generic isSet method that takes the field number as argument +void t_dart_generator::generate_generic_isset_method(std::ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // create the isSet method + indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a " + "value) and false otherwise" << endl; + indent(out) << "bool isSet(int fieldID)"; + scope_up(out); + + indent(out) << "switch (fieldID)"; + scope_up(out); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << "return " << generate_isset_check(field) << ";" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent_up(); + indent(out) << "throw new ArgumentError(\"Field $fieldID doesn't exist!\");" << endl; + indent_down(); + + scope_down(out); // switch + scope_down(out, endl2); // method +} + +/** + * Generates a set of Dart Bean boilerplate functions (setters, getters, etc.) + * for the given struct. + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_bean_boilerplate(ofstream& out, + t_struct* tstruct) { + 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 = get_member_name(field->get_name()); + std::string cap_name = get_cap_name(field_name); + + indent(out) << "// " << field_name << endl; + + // Simple getter + generate_dart_doc(out, field); + indent(out) << type_name(type) << " get " << field_name << " => this._" << field_name << ";" << endl2; + + // Simple setter + generate_dart_doc(out, field); + indent(out) << "set " << field_name << "(" << type_name(type) << " " << field_name << ")"; + scope_up(out); + indent(out) << "this._" << field_name << " = " << field_name << ";" << endl; + generate_isset_set(out, field); + scope_down(out, endl2); + + // isSet method + indent(out) << "bool is" << get_cap_name("set") << cap_name << "()"; + if (type_can_be_null(type)) { + out << " => this." << field_name << " != null;" << endl2; + } else { + out << " => this.__isset_" << field_name << ";" << endl2; + } + + // Unsetter + indent(out) << "unset" << cap_name << "()"; + scope_up(out); + if (type_can_be_null(type)) { + indent(out) << "this." << field_name << " = null;" << endl; + } else { + indent(out) << "this.__isset_" << field_name << " = false;" << endl; + } + scope_down(out, endl2); + } +} + +/** + * Generates a toString() method for the given struct + * + * @param tstruct The struct definition + */ +void t_dart_generator::generate_dart_struct_tostring(ofstream& out, + t_struct* tstruct) { + indent(out) << "String toString()"; + scope_up(out); + + indent(out) << "StringBuffer ret = new StringBuffer(\"" + << tstruct->get_name() << "(\");" << endl2; + + 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) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ")"; + scope_up(out); + } + + t_field* field = (*f_iter); + std::string field_name = get_member_name(field->get_name()); + + if (!first) { + indent(out) << "ret.write(\", \");" << endl; + } + indent(out) << "ret.write(\"" << field_name << ":\");" << endl; + bool can_be_null = type_can_be_null(field->get_type()); + if (can_be_null) { + indent(out) << "if (this." << field_name << " == null)"; + scope_up(out); + indent(out) << "ret.write(\"null\");" << endl; + scope_down(out, " else"); + scope_up(out); + } + + if (field->get_type()->is_base_type() && ((t_base_type*)(field->get_type()))->is_binary()) { + indent(out) << "ret.write(\"BINARY\");" << endl; + } else if (field->get_type()->is_enum()) { + indent(out) << "String " << field_name << "_name = " + << get_ttype_class_name(field->get_type()) + << ".VALUES_TO_NAMES[this." << field_name << "];" << endl; + indent(out) << "if (" << field_name << "_name != null)"; + scope_up(out); + indent(out) << "ret.write(" << field_name << "_name);" << endl; + indent(out) << "ret.write(\" (\");" << endl; + scope_down(out); + indent(out) << "ret.write(this." << field_name << ");" << endl; + indent(out) << "if (" << field_name << "_name != null)"; + scope_up(out); + indent(out) << "ret.write(\")\");" << endl; + scope_down(out); + } else { + indent(out) << "ret.write(this." << field_name << ");" << endl; + } + + if (can_be_null) { + scope_down(out); + } + if (could_be_unset) { + scope_down(out); + } + + out << endl; + first = false; + } + + indent(out) << "ret.write(\")\");" << endl2; + + indent(out) << "return ret.toString();" << endl; + + scope_down(out, endl2); +} + +/** + * Returns a string with the dart representation of the given thrift type + * (e.g. for the type struct it returns "TType.STRUCT") + */ +std::string t_dart_generator::get_dart_type_string(t_type* type) { + if (type->is_list()) { + return "TType.LIST"; + } else if (type->is_map()) { + return "TType.MAP"; + } else if (type->is_set()) { + return "TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.STRUCT"; + } else if (type->is_enum()) { + return "TType.I32"; + } else if (type->is_typedef()) { + return get_dart_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID: + return "TType.VOID"; + break; + case t_base_type::TYPE_STRING: + return "TType.STRING"; + break; + case t_base_type::TYPE_BOOL: + return "TType.BOOL"; + break; + case t_base_type::TYPE_I8: + return "TType.BYTE"; + break; + case t_base_type::TYPE_I16: + return "TType.I16"; + break; + case t_base_type::TYPE_I32: + return "TType.I32"; + break; + case t_base_type::TYPE_I64: + return "TType.I64"; + break; + case t_base_type::TYPE_DOUBLE: + return "TType.DOUBLE"; + break; + default: + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_dart_generator::get_dart_type_string!"); + break; // This should never happen! + } + } else { + throw std::runtime_error( + "Unknown thrift type \"" + type->get_name() + + "\" passed to t_dart_generator::get_dart_type_string!"); // This should never happen! + } +} + +void t_dart_generator::generate_service(t_service* tservice) { + string file_name = get_file_name(service_name_); + string f_service_name = src_dir_ + "/" + file_name + ".dart"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << dart_library(file_name) << endl; + f_service_ << service_imports() << dart_thrift_imports() << endl; + f_service_ << endl; + + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_dart_generator::generate_service_interface(t_service* tservice) { + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends_iface = " extends " + get_ttype_class_name(tservice->get_extends()); + } + + generate_dart_doc(f_service_, tservice); + + string class_name = service_name_; + export_class_to_library(get_file_name(service_name_), class_name); + indent(f_service_) << "abstract class " << class_name << extends_iface; + scope_up(f_service_); + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << endl; + generate_dart_doc(f_service_, *f_iter); + indent(f_service_) << function_signature(*f_iter) << ";" << endl; + } + + scope_down(f_service_, endl2); +} + +/** + * Generates structs for all the service args and return types + * + * @param tservice The service + */ +void t_dart_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_dart_struct_definition(f_service_, ts, false, false); + generate_function_helpers(*f_iter); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_dart_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = get_ttype_class_name(tservice->get_extends()); + extends_client = " extends " + extends + "Client"; + } + + string class_name = service_name_ + "Client"; + export_class_to_library(get_file_name(service_name_), class_name); + indent(f_service_) << "class " << class_name << extends_client + << " implements " << service_name_; + scope_up(f_service_); + f_service_ << endl; + + indent(f_service_) << class_name << "(TProtocol iprot, [TProtocol oprot = null])"; + + if (!extends.empty()) { + indent_up(); + f_service_ << endl; + indent(f_service_) << ": super(iprot, oprot);" << endl; + indent_down(); + } else { + scope_up(f_service_); + indent(f_service_) << "_iprot = iprot;" << endl; + indent(f_service_) << "_oprot = (oprot == null) ? iprot : oprot;" << endl; + scope_down(f_service_); + } + f_service_ << endl; + + if (extends.empty()) { + indent(f_service_) << "TProtocol _iprot;" << endl2; + indent(f_service_) << "TProtocol get iprot => _iprot;" << endl2; + indent(f_service_) << "TProtocol _oprot;" << endl2; + indent(f_service_) << "TProtocol get oprot => _oprot;" << endl2; + indent(f_service_) << "int _seqid = 0;" << endl2; + indent(f_service_) << "int get seqid => _seqid;" << endl2; + indent(f_service_) << "int nextSeqid() => ++_seqid;" << endl2; + } + + // 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) { + // Open function + indent(f_service_) << function_signature(*f_iter) << " async"; + scope_up(f_service_); + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + string argsname = get_args_class_name((*f_iter)->get_name()); + vector::const_iterator fld_iter; + const vector& fields = arg_struct->get_members(); + + // Serialize the request + indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << (*f_iter)->get_name() << "\", " + << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") + << ", nextSeqid()));" << endl; + indent(f_service_) << argsname << " args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + string arg_field_name = get_member_name((*fld_iter)->get_name()); + indent(f_service_) << "args." << arg_field_name << " = " + << arg_field_name << ";" << endl; + } + + indent(f_service_) << "args.write(oprot);" << endl; + indent(f_service_) << "oprot.writeMessageEnd();" << endl2; + + indent(f_service_) << "await oprot.transport.flush();" << endl2; + + if (!(*f_iter)->is_oneway()) { + indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl; + indent(f_service_) << "if (msg.type == TMessageType.EXCEPTION)"; + scope_up(f_service_); + indent(f_service_) << "TApplicationError error = TApplicationError.read(iprot);" << endl; + indent(f_service_) << "iprot.readMessageEnd();" << endl; + indent(f_service_) << "throw error;" << endl; + scope_down(f_service_, endl2); + + string result_class = get_result_class_name((*f_iter)->get_name()); + indent(f_service_) << result_class << " result = new " << result_class << "();" << endl; + indent(f_service_) << "result.read(iprot);" << endl; + indent(f_service_) << "iprot.readMessageEnd();" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "if (result." << generate_isset_check("success") << ")"; + scope_up(f_service_); + indent(f_service_) << "return result.success;" << endl; + scope_down(f_service_, endl2); + } + + 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) { + string result_field_name = get_member_name((*x_iter)->get_name()); + indent(f_service_) << "if (result." << result_field_name << " != null)"; + scope_up(f_service_); + indent(f_service_) << "throw result." << result_field_name << ";" << endl; + scope_down(f_service_); + } + + // If you get here it's an exception, unless a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "throw new TApplicationError(TApplicationErrorType.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + } + } + + scope_down(f_service_, endl2); + } + + scope_down(f_service_, endl2); +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_dart_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // typedef + indent(f_service_) << "typedef void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);" << endl2; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = get_ttype_class_name(tservice->get_extends()); + extends_processor = " extends " + extends + "Processor"; + } + + // Generate the header portion + string class_name = service_name_ + "Processor"; + export_class_to_library(get_file_name(service_name_), class_name); + indent(f_service_) << "class " << class_name << extends_processor << " implements TProcessor"; + scope_up(f_service_); + + indent(f_service_) << class_name << "(" << service_name_ << " iface)"; + if (!extends.empty()) { + indent_up(); + f_service_ << endl; + indent(f_service_) << ": super(iface)"; + indent_down(); + } + scope_up(f_service_); + + if (extends.empty()) { + indent(f_service_) << "iface_ = iface;" << endl; + } + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_) << "PROCESS_MAP[\"" << (*f_iter)->get_name() + << "\"] = " << get_member_name((*f_iter)->get_name()) << ";" << endl; + } + scope_down(f_service_, endl2); + + indent(f_service_) << service_name_ << " iface_;" << endl; + + if (extends.empty()) { + indent(f_service_) << "final Map PROCESS_MAP = {};" << endl; + } + + f_service_ << endl; + + // Generate the server implementation + indent(f_service_) << "bool process(TProtocol iprot, TProtocol oprot)"; + scope_up(f_service_); + indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << endl; + indent(f_service_) << "ProcessFunction fn = PROCESS_MAP[msg.name];" << endl; + indent(f_service_) << "if (fn == null)"; + scope_up(f_service_); + indent(f_service_) << "TProtocolUtil.skip(iprot, TType.STRUCT);" << endl; + indent(f_service_) << "iprot.readMessageEnd();" << endl; + indent(f_service_) << "TApplicationError x = new TApplicationError(TApplicationErrorType.UNKNOWN_METHOD, " + "\"Invalid method name: '\"+msg.name+\"'\");" << endl; + indent(f_service_) << "oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));" << endl; + indent(f_service_) << "x.write(oprot);" << endl; + indent(f_service_) << "oprot.writeMessageEnd();" << endl; + indent(f_service_) << "oprot.transport.flush();" << endl; + indent(f_service_) << "return true;" << endl; + scope_down(f_service_); + indent(f_service_) << "fn(msg.seqid, iprot, oprot);" << endl; + indent(f_service_) << "return true;" << endl; + scope_down(f_service_, endl2); // process function + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + scope_down(f_service_, endl2); // class +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_dart_generator::generate_function_helpers(t_function* tfunction) { + if (tfunction->is_oneway()) { + return; + } + + t_struct result(program_, get_result_class_name(tfunction->get_name())); + 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_dart_struct_definition(f_service_, &result, false, true); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_dart_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + + bool await_result = (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()); + + indent(f_service_) << get_member_name(tfunction->get_name()) << "(int seqid, TProtocol iprot, TProtocol oprot)"; + if (await_result) { + f_service_ << " async"; + } + scope_up(f_service_); + + string argsname = get_args_class_name(tfunction->get_name()); + string resultname = get_result_class_name(tfunction->get_name()); + + indent(f_service_) << argsname << " args = new " << argsname << "();" << endl; + indent(f_service_) << "args.read(iprot);" << endl; + indent(f_service_) << "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()) { + indent(f_service_) << resultname << " result = new " << resultname << "();" << endl; + } + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent(f_service_) << "try"; + scope_up(f_service_); + } + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + f_service_ << indent(); + if (await_result) { + f_service_ << "result.success = await "; + } + f_service_ << "iface_." << get_member_name(tfunction->get_name()) << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << get_member_name((*f_iter)->get_name()); + } + f_service_ << ");" << endl; + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + string result_field_name = get_member_name((*x_iter)->get_name()); + scope_down(f_service_, ""); + f_service_ << " on " << type_name((*x_iter)->get_type()) + << " catch(" << result_field_name << ")"; + scope_up(f_service_); + if (!tfunction->is_oneway()) { + indent(f_service_) << "result." << result_field_name << " = " + << result_field_name << ";" << endl; + } + } + scope_down(f_service_, " "); + f_service_ << "catch (th)"; + scope_up(f_service_); + indent(f_service_) << "// Internal error" << endl; + indent(f_service_) << "TApplicationError x = new " + "TApplicationError(TApplicationErrorType.INTERNAL_ERROR, \"Internal error processing " + << tfunction->get_name() << "\");" << endl; + indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.EXCEPTION, seqid));" << endl; + indent(f_service_) << "x.write(oprot);" << endl; + indent(f_service_) << "oprot.writeMessageEnd();" << endl; + indent(f_service_) << "oprot.transport.flush();" << endl; + indent(f_service_) << "return;" << endl; + scope_down(f_service_); + } + + if (tfunction->is_oneway()) { + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid));" << endl; + indent(f_service_) << "result.write(oprot);" << endl; + indent(f_service_) << "oprot.writeMessageEnd();" << endl; + indent(f_service_) << "oprot.transport.flush();" << endl; + } + + scope_down(f_service_, endl2); +} + +/** + * Deserializes a field of any type. + * + * @param tfield The field + * @param prefix The variable name or container for this field + */ +void t_dart_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = get_true_type(tfield->get_type()); + string field_name = get_member_name(tfield->get_name()); + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + field_name; + } + + string name = prefix + field_name; + + 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 << " = 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 (((t_base_type*)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 Dart 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", + field_name.c_str(), + type_name(type).c_str()); + } +} + +/** + * Generates an unserializer for a struct, invokes read() + */ +void t_dart_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + indent(out) << prefix << " = new " << type_name(tstruct) << "();" << endl; + indent(out) << prefix << ".read(iprot);" << endl; +} + +/** + * Deserializes a container by reading its size and then iterating + */ +void t_dart_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + indent(out); + 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"); + } + + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "TMap " << obj << " = iprot.readMapBegin();" << endl; + } else if (ttype->is_set()) { + indent(out) << "TSet " << obj << " = iprot.readSetBegin();" << endl; + } else if (ttype->is_list()) { + indent(out) << "TList " << obj << " = iprot.readListBegin();" << endl; + } + + indent(out) << prefix << " = new " << type_name(ttype) << "();" << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".length" + << "; " + << "++" << i << ")"; + 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); + + // Read container end + 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); +} + +/** + * Generates code to deserialize a map + */ +void t_dart_generator::generate_deserialize_map_element(ofstream& 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; +} + +/** + * Deserializes a set element + */ +void t_dart_generator::generate_deserialize_set_element(ofstream& 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; +} + +/** + * Deserializes a list element + */ +void t_dart_generator::generate_deserialize_list_element(ofstream& 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; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_dart_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = get_true_type(tfield->get_type()); + string field_name = get_member_name(tfield->get_name()); + + // Do nothing for void types + if (type->is_void()) { + throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + field_name; + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + field_name); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + field_name); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + field_name; + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + field_name.c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_dart_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << prefix << ".write(oprot);" << endl; +} + +/** + * Serializes a container by writing its size then the elements. + * + * @param ttype The type of container + * @param prefix String prefix for fields + */ +void t_dart_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + indent(out); + scope_up(out, ""); + + if (ttype->is_map()) { + string iter = tmp("_key"); + 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 << ".length));" + << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " << prefix << ".length));" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(new TList(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));" + << endl; + } + + string iter = tmp("elem"); + if (ttype->is_map()) { + indent(out) << "for (var " << iter << " in " << prefix << ".keys)"; + } else if (ttype->is_set() || ttype->is_list()) { + indent(out) << "for (var " << iter << " in " << prefix << ")"; + } + + 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); +} + +/** + * Serializes the members of a map. + */ +void t_dart_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string iter, + string map) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, &kfield, ""); + t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_dart_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_dart_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Returns a Dart type name + * + * @param ttype The type + * @return Dart type name, i.e. Map + */ +string t_dart_generator::type_name(t_type* ttype) { + ttype = get_true_type(ttype); + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype); + } else if (ttype->is_enum()) { + return "int"; + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + return "Map<" + type_name(tmap->get_key_type()) + ", " + + type_name(tmap->get_val_type()) + ">"; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + return "Set<" + type_name(tset->get_elem_type()) + ">"; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + return "List<" + type_name(tlist->get_elem_type()) + ">"; + } + + return get_ttype_class_name(ttype); +} + +/** + * Returns the Dart type that corresponds to the thrift type. + * + * @param tbase The base type + */ +string t_dart_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 "Uint8List"; + } else { + return "String"; + } + case t_base_type::TYPE_BOOL: + return "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: + return "int"; + case t_base_type::TYPE_DOUBLE: + return "double"; + default: + throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_dart_generator::declare_field(t_field* tfield, bool init) { + string field_name = get_member_name(tfield->get_name()); + 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) { + ofstream dummy; + result += " = " + render_const_value(dummy, field_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 += " = 0.0"; + break; + } + + } else if (ttype->is_enum()) { + result += " = 0"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype) + "()"; + } else { + result += " = new " + type_name(ttype) + "()"; + ; + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_dart_generator::function_signature(t_function* tfunction) { + std::string arguments = argument_list(tfunction->get_arglist()); + + std::string returntype; + if (tfunction->get_returntype()->is_void()) { + returntype = "Future"; + } else { + returntype = "Future<" + type_name(tfunction->get_returntype()) + ">"; + } + + std::string result = returntype + " " + get_member_name(tfunction->get_name()) + + "(" + arguments + ")"; + return result; +} + +/** + * Renders a comma separated field list, with type names + */ +string t_dart_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 += ", "; + } + string field_name = get_member_name((*f_iter)->get_name()); + result += type_name((*f_iter)->get_type()) + " " + field_name; + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_dart_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 "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(); +} + +std::string t_dart_generator::init_value(t_field* field) { + // Do not initialize optional fields + if (field->get_req() == t_field::T_OPTIONAL) { + return ""; + } + + t_type* ttype = field->get_type(); + + // Get the actual type for a typedef + if (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + // Only consider base types for default initialization + if (!ttype->is_base_type()) { + return ""; + } + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + + // Initialize bools, ints, and doubles with sane defaults + string result; + switch (tbase) { + 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 = " = 0.0"; + break; + case t_base_type::TYPE_VOID: + case t_base_type::TYPE_STRING: + result = ""; + break; + } + + return result; +} + +std::string t_dart_generator::get_cap_name(std::string name) { + name[0] = toupper(name[0]); + return name; +} + +std::string t_dart_generator::get_member_name(std::string name) { + name[0] = tolower(name[0]); + return name; +} + +std::string t_dart_generator::get_args_class_name(std::string name) { + return name + "_args"; +} + +std::string t_dart_generator::get_result_class_name(std::string name) { + return name + "_result"; +} + +std::string t_dart_generator::get_file_name(std::string name) { + // e.g. change APIForFileIO to api_for_file_io + + string ret; + const char* tmp = name.c_str(); + bool is_prev_lc = true; + bool is_current_lc = tmp[0] == tolower(tmp[0]); + bool is_next_lc = false; + + for (unsigned int i = 0; i < name.length(); i++) { + char lc = tolower(tmp[i]); + + if (i == name.length() - 1) { + is_next_lc = false; + } else { + is_next_lc = (tmp[i+1] == tolower(tmp[i+1])); + } + + if (i != 0 && !is_current_lc && (is_prev_lc || is_next_lc)) { + ret += "_"; + } + ret += lc; + + is_prev_lc = is_current_lc; + is_current_lc = is_next_lc; + } + + return ret; +} + +std::string t_dart_generator::get_constants_class_name(std::string name) { + // e.g. change my_great_model to MyGreatModelConstants + string ret; + const char* tmp = name.c_str(); + bool is_prev_underscore = true; + + for (unsigned int i = 0; i < name.length(); i++) { + if (tmp[i] == '_') { + is_prev_underscore = true; + } else { + if (is_prev_underscore) { + ret += toupper(tmp[i]); + } else { + ret += tmp[i]; + } + + is_prev_underscore = false; + } + } + + return ret + "Constants"; +} + +string t_dart_generator::constant_name(string name) { + string constant_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); + + bool is_upper = isupper(character); + + if (is_upper && !is_first && !was_previous_char_upper) { + constant_name += '_'; + } + constant_name += toupper(character); + + is_first = false; + was_previous_char_upper = is_upper; + } + + return constant_name; +} + +/** + * Emits a doc comment if the provided object has a doc in Thrift + */ +void t_dart_generator::generate_dart_doc(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, "", "/// ", tdoc->get_doc(), ""); + } +} + +/** + * Emits a doc comment if the provided function object has a doc in Thrift + */ +void t_dart_generator::generate_dart_doc(ofstream& out, t_function* tfunction) { + if (tfunction->has_doc()) { + stringstream ss; + ss << tfunction->get_doc(); + 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; + string field_name = get_member_name(p->get_name()); + ss << "\n@param " << field_name; + if (p->has_doc()) { + ss << " " << p->get_doc(); + } + } + generate_docstring_comment(out, "", "/// ", ss.str(), ""); + } +} + +std::string t_dart_generator::generate_isset_check(t_field* field) { + string field_name = get_member_name(field->get_name()); + return generate_isset_check(field_name); +} + +std::string t_dart_generator::generate_isset_check(std::string field_name) { + return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; +} + +void t_dart_generator::generate_isset_set(ofstream& out, t_field* field) { + if (!type_can_be_null(field->get_type())) { + string field_name = get_member_name(field->get_name()); + indent(out) << "this.__isset_" << field_name << " = true;" << endl; + } +} + +std::string t_dart_generator::get_ttype_class_name(t_type* ttype) { + if (program_ == ttype->get_program()) { + return ttype->get_name(); + } else { + string named_import = "t_" + find_library_name(ttype->get_program()); + return named_import + "." + ttype->get_name(); + } +} + +THRIFT_REGISTER_GENERATOR( + dart, + "Dart", + " library_name: Optional override for library name.\n" + " library_prefix: Generate code that can be used within an existing library.\n" + " Use a dot-separated string, e.g. \"my_parent_lib.src.gen\"\n" + " pubspec_lib: Optional override for thrift lib dependency in pubspec.yaml,\n" + " e.g. \"thrift: 0.x.x\". Use a pipe delimiter to separate lines,\n" + " e.g. \"thrift:| git:| url: git@foo.com\"\n" +) diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc new file mode 100644 index 00000000..8b1a4452 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_delphi_generator.cc @@ -0,0 +1,3920 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 + +#include "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ofstream; +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_delphi_generator : public t_oop_generator { +public: + t_delphi_generator(t_program* program, + const std::map& parsed_options, + const std::string& option_string) + : t_oop_generator(program) { + (void)option_string; + indent_impl_ = 0; + has_forward = false; + has_enum = false; + has_const = false; + std::map::const_iterator iter; + + ansistr_binary_ = false; + register_types_ = false; + constprefix_ = false; + events_ = false; + xmldoc_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("ansistr_binary") == 0) { + ansistr_binary_ = true; + } else if( iter->first.compare("register_types") == 0) { + register_types_ = true; + } else if( iter->first.compare("constprefix") == 0) { + constprefix_ = true; + } else if( iter->first.compare("events") == 0) { + events_ = true; + } else if( iter->first.compare("xmldoc") == 0) { + xmldoc_ = true; + } else { + throw "unknown option delphi:" + iter->first; + } + } + + out_dir_base_ = "gen-delphi"; + escape_.clear(); + escape_['\''] = "''"; + } + + 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_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_property(ostream& out, t_field* tfield, bool isPublic, bool is_xception); + void generate_property_writer_(ostream& out, t_field* tfield, bool isPublic); + + void generate_delphi_property(ostream& out, + bool struct_is_exception, + 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_property_reader_definition(ostream& out, + t_field* tfield, + bool is_xception_class); + void generate_delphi_property_writer_definition(ostream& out, + t_field* tfield, + bool is_xception_class); + void generate_delphi_property_reader_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class); + void generate_delphi_property_writer_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class, + bool is_union, + bool is_xception_factory, + std::string xception_factroy_name); + void generate_delphi_clear_union_value(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class, + bool is_union, + bool is_xception_factory, + std::string xception_factroy_name); + void generate_delphi_isset_reader_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception); + void generate_delphi_struct_writer_impl(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception); + void generate_delphi_struct_result_writer_impl(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception); + + void generate_delphi_struct_tostring_impl(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_x_factory); + + void add_delphi_uses_list(string unitname); + + void generate_delphi_struct_reader_impl(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception); + void generate_delphi_create_exception_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception); + + bool const_needs_var(t_type* type); + void print_const_prop(std::ostream& out, string name, t_type* type, t_const_value* value); + void print_private_field(std::ostream& out, string name, t_type* type, t_const_value* value); + void print_const_value(std::ostream& vars, + std::ostream& out, + std::string name, + t_type* type, + t_const_value* value); + void initialize_field(std::ostream& vars, + std::ostream& out, + std::string name, + t_type* type, + t_const_value* value); + void finalize_field(std::ostream& out, + std::string name, + t_type* type, + t_const_value* value, + std::string cls_nm = ""); + std::string render_const_value(std::ostream& local_vars, + std::ostream& out, + std::string name, + t_type* type, + t_const_value* value); + void print_const_def_value(std::ostream& vars, + std::ostream& out, + std::string name, + t_type* type, + t_const_value* value, + std::string cls_nm = ""); + std::string make_constants_classname(); + + void generate_delphi_struct(t_struct* tstruct, bool is_exception); + void generate_delphi_struct_impl(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result = false, + bool is_x_factory = false); + void print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct); + void generate_delphi_struct_type_factory(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result = false, + bool is_x_factory = false); + void generate_delphi_struct_type_factory_registration(ostream& out, + std::string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result = false, + bool is_x_factory = false); + void generate_delphi_struct_definition(std::ostream& out, + t_struct* tstruct, + bool is_xception = false, + bool in_class = false, + bool is_result = false, + bool is_x_factory = false); + void generate_delphi_struct_reader(std::ostream& out, t_struct* tstruct); + void generate_delphi_struct_result_writer(std::ostream& out, t_struct* tstruct); + void generate_delphi_struct_writer(std::ostream& out, t_struct* tstruct); + void generate_delphi_struct_tostring(std::ostream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + void generate_service_interface(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_process_function(t_service* tservice, t_function* function); + + void generate_deserialize_field(std::ostream& out, + bool is_xception, + t_field* tfield, + std::string prefix, + std::ostream& local_vars); + void generate_deserialize_struct(std::ostream& out, + t_struct* tstruct, + std::string name, + std::string prefix); + void generate_deserialize_container(ostream& out, + bool is_xception, + t_type* ttype, + string name, + std::ostream& local_vars); + + void generate_deserialize_set_element(std::ostream& out, + bool is_xception, + t_set* tset, + std::string prefix, + std::ostream& local_vars); + void generate_deserialize_map_element(std::ostream& out, + bool is_xception, + t_map* tmap, + std::string prefix, + std::ostream& local_vars); + void generate_deserialize_list_element(std::ostream& out, + bool is_xception, + t_list* list, + std::string prefix, + std::ostream& local_vars); + + void generate_serialize_field(std::ostream& out, + bool is_xception, + t_field* tfield, + std::string prefix, + std::ostream& local_vars); + void generate_serialize_struct(std::ostream& out, + t_struct* tstruct, + std::string prefix, + std::ostream& local_vars); + void generate_serialize_container(std::ostream& out, + bool is_xception, + t_type* ttype, + std::string prefix, + std::ostream& local_vars); + void generate_serialize_map_element(std::ostream& out, + bool is_xception, + t_map* tmap, + std::string iter, + std::string map, + std::ostream& local_vars); + void generate_serialize_set_element(std::ostream& out, + bool is_xception, + t_set* tmap, + std::string iter, + std::ostream& local_vars); + void generate_serialize_list_element(std::ostream& out, + bool is_xception, + t_list* tlist, + std::string iter, + std::ostream& local_vars); + + void delphi_type_usings(std::ostream& out); + std::string delphi_thrift_usings(); + + std::string type_name(t_type* ttype, + bool b_cls = false, + bool b_no_postfix = false, + bool b_exception_factory = false, + bool b_full_exception_factory = false); + std::string normalize_clsnm(std::string name, + std::string prefix, + bool b_no_check_keyword = false); + std::string make_valid_delphi_identifier(std::string const& fromName); + std::string input_arg_prefix(t_type* ttype); + + std::string base_type_name(t_base_type* tbase); + std::string declare_field(t_field* tfield, + bool init = false, + std::string prefix = "", + bool is_xception_class = false); + std::string function_signature(t_function* tfunction, + std::string full_cls = "", + bool is_xception = false); + std::string argument_list(t_struct* tstruct); + std::string constructor_argument_list(t_struct* tstruct, std::string current_indent); + std::string type_to_enum(t_type* ttype); + std::string prop_name(t_field* tfield, bool is_xception = false); + std::string prop_name(std::string name, bool is_xception = false); + std::string constructor_param_name(string name); + + void write_enum(std::string line); + void write_forward_decr(std::string line); + void write_const(std::string line); + void write_struct(std::string line); + void write_service(std::string line); + + virtual std::string autogen_comment() { + 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"; + } + + string replace_all(string contents, string search, string replace); + string xml_encode(string contents); + string xmldoc_encode(string contents); + string xmlattrib_encode(string contents); + void generate_delphi_doc(std::ostream& out, t_field* field); + void generate_delphi_doc(std::ostream& out, t_doc* tdoc); + void generate_delphi_doc(std::ostream& out, t_function* tdoc); + void generate_delphi_docstring_comment(std::ostream& out, string contents); + + 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(); + } + +private: + std::string namespace_name_; + std::ostringstream s_forward_decr; + std::ostringstream s_enum; + std::ostringstream s_const; + std::ostringstream s_struct; + std::ostringstream s_service; + std::ostringstream s_const_impl; + std::ostringstream s_struct_impl; + std::ostringstream s_service_impl; + std::ostringstream s_type_factory_registration; + std::ostringstream s_type_factory_funcs; + bool has_forward; + bool has_enum; + bool has_const; + std::string namespace_dir_; + std::map delphi_keywords; + std::map delphi_reserved_method; + std::map delphi_reserved_method_exception; + std::map types_known; + std::list typedefs_pending; + std::vector uses_list; + void create_keywords(); + bool find_keyword(std::map& keyword_map, std::string name); + std::string normalize_name(std::string name, + bool b_method = false, + bool b_exception_method = false); + std::string empty_value(t_type* type); + bool is_fully_defined_type(t_type* ttype); + void add_defined_type(t_type* ttype); + void init_known_types_list(); + bool is_void(t_type* type); + int indent_impl_; + bool ansistr_binary_; + bool register_types_; + bool constprefix_; + bool events_; + bool xmldoc_; + void indent_up_impl() { ++indent_impl_; }; + void indent_down_impl() { --indent_impl_; }; + std::string indent_impl() { + std::string ind = ""; + int i; + for (i = 0; i < indent_impl_; ++i) { + ind += " "; + } + return ind; + }; + std::ostream& indent_impl(std::ostream& os) { return os << indent_impl(); }; +}; + +string t_delphi_generator::replace_all(string contents, string search, string repl) { + string str(contents); + + size_t slen = search.length(); + size_t rlen = repl.length(); + size_t incr = (rlen > 0) ? rlen : 1; + + if (slen > 0) { + size_t found = str.find(search); + while ((found != string::npos) && (found < str.length())) { + str.replace(found, slen, repl); + found = str.find(search, found + incr); + } + } + + return str; +} + +// XML encoding +string t_delphi_generator::xml_encode(string contents) { + string str(contents); + + // escape the escape + str = replace_all(str, "&", "&"); + + // other standard XML entities + str = replace_all(str, "<", "<"); + str = replace_all(str, ">", ">"); + + return str; +} + +// XML attribute encoding +string t_delphi_generator::xmlattrib_encode(string contents) { + string str(xml_encode(contents)); + + // our attribs are enclosed in " + str = replace_all(str, "\"", "\\\""); + + return str; +} + +// XML encoding for doc comments +string t_delphi_generator::xmldoc_encode(string contents) { + string str(xml_encode(contents)); + + // XMLDoc specific: convert linebreaks into graphs + str = replace_all(str, "\r\n", "\r"); + str = replace_all(str, "\n", "\r"); + str = replace_all(str, "\r", "\n"); + + return str; +} + +void t_delphi_generator::generate_delphi_docstring_comment(ostream& out, string contents) { + if (xmldoc_) { + generate_docstring_comment(out, + "{$REGION 'XMLDoc'}/// \n", + "/// ", + "" + contents + "", + "/// \n{$ENDREGION}\n"); + } +} + +void t_delphi_generator::generate_delphi_doc(ostream& out, t_field* field) { + if (xmldoc_) { + if (field->get_type()->is_enum()) { + string combined_message = xmldoc_encode(field->get_doc()) + "\nget_type())) + "\"/>"; + generate_delphi_docstring_comment(out, combined_message); + } else { + generate_delphi_doc(out, (t_doc*)field); + } + } +} + +void t_delphi_generator::generate_delphi_doc(ostream& out, t_doc* tdoc) { + if (tdoc->has_doc() && xmldoc_) { + generate_delphi_docstring_comment(out, xmldoc_encode(tdoc->get_doc())); + } +} + +void t_delphi_generator::generate_delphi_doc(ostream& out, t_function* tfunction) { + if (tfunction->has_doc() && xmldoc_) { + 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 << xmldoc_encode(str); + } + ps << ""; + } + generate_docstring_comment(out, + "{$REGION 'XMLDoc'}", + "/// ", + "" + xmldoc_encode(tfunction->get_doc()) + + "" + ps.str(), + "{$ENDREGION}\n"); + } +} + +bool t_delphi_generator::find_keyword(std::map& keyword_map, std::string name) { + std::string::size_type len = name.length(); + + if (len <= 0) { + return false; + } + + std::string::size_type nlast = name.find_last_of('_'); + + if (nlast >= 1) { + if (nlast == (len - 1)) { + string new_name(name, 0, nlast); + return find_keyword(keyword_map, new_name); + } + } + return (keyword_map[name] == 1); +} + +std::string t_delphi_generator::normalize_name(std::string name, + bool b_method, + bool b_exception_method) { + string tmp(name); + std::transform(tmp.begin(), tmp.end(), tmp.begin(), static_cast(std::tolower)); + + bool b_found = false; + + if (find_keyword(delphi_keywords, tmp)) { + b_found = true; + } else if (b_method && find_keyword(delphi_reserved_method, tmp)) { + b_found = true; + } else if (b_exception_method && find_keyword(delphi_reserved_method_exception, tmp)) { + b_found = true; + } + + if (b_found) { + return name + "_"; + } else { + return name; + } +} + +void t_delphi_generator::create_keywords() { + delphi_keywords["and"] = 1; + delphi_keywords["end"] = 1; + delphi_keywords["interface"] = 1; + delphi_keywords["raise"] = 1; + delphi_keywords["uses"] = 1; + delphi_keywords["array"] = 1; + delphi_keywords["except"] = 1; + delphi_keywords["is"] = 1; + delphi_keywords["record"] = 1; + delphi_keywords["var"] = 1; + delphi_keywords["as"] = 1; + delphi_keywords["exports"] = 1; + delphi_keywords["label"] = 1; + delphi_keywords["repeat"] = 1; + delphi_keywords["while"] = 1; + delphi_keywords["asm"] = 1; + delphi_keywords["file"] = 1; + delphi_keywords["library"] = 1; + delphi_keywords["resourcestring"] = 1; + delphi_keywords["with"] = 1; + delphi_keywords["begin"] = 1; + delphi_keywords["finalization"] = 1; + delphi_keywords["mod"] = 1; + delphi_keywords["set"] = 1; + delphi_keywords["xor"] = 1; + delphi_keywords["case"] = 1; + delphi_keywords["finally"] = 1; + delphi_keywords["nil"] = 1; + delphi_keywords["shl"] = 1; + delphi_keywords["class"] = 1; + delphi_keywords["for"] = 1; + delphi_keywords["not"] = 1; + delphi_keywords["shr"] = 1; + delphi_keywords["const"] = 1; + delphi_keywords["function"] = 1; + delphi_keywords["object"] = 1; + delphi_keywords["string"] = 1; + delphi_keywords["constructor"] = 1; + delphi_keywords["goto"] = 1; + delphi_keywords["of"] = 1; + delphi_keywords["then"] = 1; + delphi_keywords["destructor"] = 1; + delphi_keywords["if"] = 1; + delphi_keywords["or"] = 1; + delphi_keywords["threadvar"] = 1; + delphi_keywords["dispinterface"] = 1; + delphi_keywords["implementation"] = 1; + delphi_keywords["out"] = 1; + delphi_keywords["to"] = 1; + delphi_keywords["div"] = 1; + delphi_keywords["in"] = 1; + delphi_keywords["packed"] = 1; + delphi_keywords["try"] = 1; + delphi_keywords["do"] = 1; + delphi_keywords["inherited"] = 1; + delphi_keywords["procedure"] = 1; + delphi_keywords["type"] = 1; + delphi_keywords["downto"] = 1; + delphi_keywords["initialization"] = 1; + delphi_keywords["program"] = 1; + delphi_keywords["unit"] = 1; + delphi_keywords["else"] = 1; + delphi_keywords["inline"] = 1; + delphi_keywords["property"] = 1; + delphi_keywords["until"] = 1; + delphi_keywords["private"] = 1; + delphi_keywords["protected"] = 1; + delphi_keywords["public"] = 1; + delphi_keywords["published"] = 1; + delphi_keywords["automated"] = 1; + delphi_keywords["at"] = 1; + delphi_keywords["on"] = 1; + + // reserved/predefined variables and types (lowercase!) + delphi_keywords["result"] = 1; + delphi_keywords["tbytes"] = 1; + delphi_keywords["tobject"] = 1; + delphi_keywords["tclass"] = 1; + delphi_keywords["tinterfacedobject"] = 1; + + delphi_reserved_method["create"] = 1; + delphi_reserved_method["free"] = 1; + delphi_reserved_method["initinstance"] = 1; + delphi_reserved_method["cleanupinstance"] = 1; + delphi_reserved_method["classtype"] = 1; + delphi_reserved_method["classname"] = 1; + delphi_reserved_method["classnameis"] = 1; + delphi_reserved_method["classparent"] = 1; + delphi_reserved_method["classinfo"] = 1; + 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; + delphi_reserved_method["unitname"] = 1; + delphi_reserved_method["equals"] = 1; + delphi_reserved_method["gethashcode"] = 1; + delphi_reserved_method["tostring"] = 1; + delphi_reserved_method["safecallexception"] = 1; + delphi_reserved_method["afterconstruction"] = 1; + delphi_reserved_method["beforedestruction"] = 1; + delphi_reserved_method["dispatch"] = 1; + delphi_reserved_method["defaulthandler"] = 1; + delphi_reserved_method["newinstance"] = 1; + delphi_reserved_method["freeinstance"] = 1; + delphi_reserved_method["destroy"] = 1; + delphi_reserved_method["read"] = 1; + delphi_reserved_method["write"] = 1; + + delphi_reserved_method_exception["setinnerexception"] = 1; + delphi_reserved_method_exception["setstackinfo"] = 1; + delphi_reserved_method_exception["getstacktrace"] = 1; + delphi_reserved_method_exception["raisingexception"] = 1; + delphi_reserved_method_exception["createfmt"] = 1; + delphi_reserved_method_exception["createres"] = 1; + delphi_reserved_method_exception["createresfmt"] = 1; + delphi_reserved_method_exception["createhelp"] = 1; + delphi_reserved_method_exception["createfmthelp"] = 1; + delphi_reserved_method_exception["createreshelp"] = 1; + delphi_reserved_method_exception["createresfmthelp"] = 1; + delphi_reserved_method_exception["getbaseexception"] = 1; + delphi_reserved_method_exception["baseexception"] = 1; + delphi_reserved_method_exception["helpcontext"] = 1; + delphi_reserved_method_exception["innerexception"] = 1; + delphi_reserved_method_exception["message"] = 1; + delphi_reserved_method_exception["stacktrace"] = 1; + delphi_reserved_method_exception["stackinfo"] = 1; + delphi_reserved_method_exception["getexceptionstackinfoproc"] = 1; + delphi_reserved_method_exception["getstackinfostringproc"] = 1; + delphi_reserved_method_exception["cleanupstackinfoproc"] = 1; + delphi_reserved_method_exception["raiseouterexception"] = 1; + delphi_reserved_method_exception["throwouterexception"] = 1; +} + +void t_delphi_generator::add_delphi_uses_list(string unitname) { + vector::const_iterator s_iter; + bool found = false; + for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) { + if ((*s_iter) == unitname) { + found = true; + break; + } + } + if (!found) { + uses_list.push_back(unitname); + } +} + +void t_delphi_generator::init_generator() { + indent_impl_ = 0; + namespace_name_ = program_->get_namespace("delphi"); + has_forward = false; + has_enum = false; + has_const = false; + create_keywords(); + add_delphi_uses_list("Classes"); + add_delphi_uses_list("SysUtils"); + add_delphi_uses_list("Generics.Collections"); + add_delphi_uses_list("Thrift"); + add_delphi_uses_list("Thrift.Utils"); + add_delphi_uses_list("Thrift.Collections"); + add_delphi_uses_list("Thrift.Protocol"); + add_delphi_uses_list("Thrift.Transport"); + + if (register_types_) { + add_delphi_uses_list("Thrift.TypeRegistry"); + } + + init_known_types_list(); + + 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"); + if ("" != nsname) { + unitname = nsname; + } + add_delphi_uses_list(unitname); + } + + MKDIR(get_out_dir().c_str()); +} + +void t_delphi_generator::close_generator() { + std::string unitname = program_name_; + if ("" != namespace_name_) { + unitname = namespace_name_; + } + + for (int i = 0; i < (int)unitname.size(); i++) { + if (unitname[i] == ' ') { + unitname.replace(i, 1, "_"); + } + } + + std::string f_name = get_out_dir() + "/" + unitname + ".pas"; + std::ofstream f_all; + + f_all.open(f_name.c_str()); + + f_all << autogen_comment() << endl; + generate_delphi_doc(f_all, program_); + f_all << "unit " << unitname << ";" << endl << endl; + f_all << "interface" << endl << endl; + f_all << "uses" << endl; + + indent_up(); + + vector::const_iterator s_iter; + for (s_iter = uses_list.begin(); s_iter != uses_list.end(); ++s_iter) { + if (s_iter != uses_list.begin()) { + f_all << ","; + f_all << endl; + } + indent(f_all) << *s_iter; + } + + f_all << ";" << endl << endl; + + indent_down(); + + string tmp_unit(unitname); + for (int i = 0; i < (int)tmp_unit.size(); i++) { + if (tmp_unit[i] == '.') { + tmp_unit.replace(i, 1, "_"); + } + } + + f_all << "const" << endl; + indent_up(); + indent(f_all) << "c" << tmp_unit + << "_Option_AnsiStr_Binary = " << (ansistr_binary_ ? "True" : "False") << ";" + << endl; + indent(f_all) << "c" << tmp_unit + << "_Option_Register_Types = " << (register_types_ ? "True" : "False") << ";" + << endl; + indent(f_all) << "c" << tmp_unit + << "_Option_ConstPrefix = " << (constprefix_ ? "True" : "False") << ";" << endl; + indent(f_all) << "c" << tmp_unit << "_Option_Events = " << (events_ ? "True" : "False") + << ";" << endl; + indent(f_all) << "c" << tmp_unit << "_Option_XmlDoc = " << (xmldoc_ ? "True" : "False") + << ";" << endl; + indent_down(); + + f_all << endl; + f_all << "type" << endl; + if (has_forward) { + f_all << s_forward_decr.str() << endl; + } + if (has_enum) { + indent(f_all) << endl; + indent(f_all) << "{$SCOPEDENUMS ON}" << endl << endl; + f_all << s_enum.str(); + indent(f_all) << "{$SCOPEDENUMS OFF}" << endl << endl; + } + f_all << s_struct.str(); + f_all << s_service.str(); + f_all << s_const.str(); + f_all << "implementation" << endl << endl; + f_all << s_struct_impl.str(); + f_all << s_service_impl.str(); + f_all << s_const_impl.str(); + + if (register_types_) { + f_all << endl; + f_all << "// Type factory methods and registration" << endl; + f_all << s_type_factory_funcs.str(); + f_all << "procedure RegisterTypeFactories;" << endl; + f_all << "begin" << endl; + f_all << s_type_factory_registration.str(); + f_all << "end;" << endl; + } + f_all << endl; + + string constants_class = make_constants_classname(); + + f_all << "initialization" << endl; + if (has_const) { + f_all << "{$IF CompilerVersion < 21.0} // D2010" << endl; + f_all << " " << constants_class.c_str() << "_Initialize;" << endl; + f_all << "{$IFEND}" << endl; + } + if (register_types_) { + f_all << " RegisterTypeFactories;" << endl; + } + f_all << endl; + + f_all << "finalization" << endl; + if (has_const) { + f_all << "{$IF CompilerVersion < 21.0} // D2010" << endl; + f_all << " " << constants_class.c_str() << "_Finalize;" << endl; + f_all << "{$IFEND}" << endl; + } + f_all << endl << endl; + + f_all << "end." << endl; + f_all.close(); + + if (!typedefs_pending.empty()) { + pwarning(0, "%d typedefs with unresolved type references left:\n", typedefs_pending.size()); + for (std::list::iterator iter = typedefs_pending.begin(); + typedefs_pending.end() != iter; + ++iter) { + pwarning(0, "- %s\n", (*iter)->get_symbolic().c_str()); + } + } +} + +void t_delphi_generator::delphi_type_usings(ostream& out) { + indent_up(); + indent(out) << "Classes, SysUtils, Generics.Collections, Thrift.Collections, Thrift.Protocol," + << endl; + indent(out) << "Thrift.Transport;" << endl << endl; + indent_down(); +} + +void t_delphi_generator::generate_forward_declaration(t_struct* tstruct) { + // Forward declare struct def + has_forward = true; + pverbose("forward declaration of %s\n", type_name(tstruct).c_str()); + + string what = tstruct->is_xception() ? "class" : "interface"; + + indent_up(); + indent(s_forward_decr) << type_name(tstruct, tstruct->is_xception(), true) << " = " << what << ";" + << endl; + indent_down(); + + add_defined_type(tstruct); +} + +void t_delphi_generator::generate_typedef(t_typedef* ttypedef) { + t_type* type = ttypedef->get_type(); + + // write now or save for later? + if (!is_fully_defined_type(type)) { + pverbose("typedef %s: unresolved dependencies found\n", type_name(ttypedef).c_str()); + typedefs_pending.push_back(ttypedef); + return; + } + + indent_up(); + generate_delphi_doc(s_struct, ttypedef); + indent(s_struct) << type_name(ttypedef) << " = "; + + // commented out: the benefit is not big enough to risk breaking existing code + // bool container = type->is_list() || type->is_map() || type->is_set(); + // if( ! container) + // s_struct << "type "; //the "type A = type B" syntax leads to E2574 with generics + + s_struct << type_name(ttypedef->get_type()) << ";" << endl << endl; + indent_down(); + + add_defined_type(ttypedef); +} + +bool t_delphi_generator::is_fully_defined_type(t_type* ttype) { + if ((NULL != ttype->get_program()) && (ttype->get_program() != program_)) { + t_scope* scope = ttype->get_program()->scope(); + if (NULL != 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; + } + } + + if (ttype->is_typedef()) { + return (1 == types_known[type_name(ttype)]); + } + + if (ttype->is_base_type()) { + return (1 == types_known[base_type_name((t_base_type*)ttype)]); + } else if (ttype->is_enum()) { + return true; // enums are written first, before all other types + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + return is_fully_defined_type(tmap->get_key_type()) + && is_fully_defined_type(tmap->get_val_type()); + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + return is_fully_defined_type(tset->get_elem_type()); + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + return is_fully_defined_type(tlist->get_elem_type()); + } + + return (1 == types_known[type_name(ttype)]); +} + +void t_delphi_generator::add_defined_type(t_type* ttype) { + // mark as known type + types_known[type_name(ttype)] = 1; + + // check all pending typedefs + std::list::iterator iter; + bool more = true; + while (more && (!typedefs_pending.empty())) { + more = false; + + for (iter = typedefs_pending.begin(); typedefs_pending.end() != iter; ++iter) { + t_typedef* ttypedef = (*iter); + if (is_fully_defined_type(ttypedef->get_type())) { + pverbose("typedef %s: all pending references are now resolved\n", + type_name(ttypedef).c_str()); + typedefs_pending.erase(iter); + generate_typedef(ttypedef); + more = true; + break; + } + } + } +} + +void t_delphi_generator::init_known_types_list() { + // known base types + types_known[type_name(g_type_string)] = 1; + types_known[type_name(g_type_binary)] = 1; + types_known[type_name(g_type_bool)] = 1; + types_known[type_name(g_type_i8)] = 1; + types_known[type_name(g_type_i16)] = 1; + types_known[type_name(g_type_i32)] = 1; + types_known[type_name(g_type_i64)] = 1; + types_known[type_name(g_type_double)] = 1; +} + +void t_delphi_generator::generate_enum(t_enum* tenum) { + has_enum = true; + indent_up(); + generate_delphi_doc(s_enum, tenum); + indent(s_enum) << type_name(tenum, true, true) << " = " + << "(" << endl; + indent_up(); + vector constants = tenum->get_constants(); + if (constants.empty()) { + indent(s_enum) << "dummy = 0 // empty enums are not allowed"; + } else { + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + if (c_iter != constants.begin()) { + s_enum << ","; + s_enum << endl; + } + generate_delphi_doc(s_enum, *c_iter); + indent(s_enum) << normalize_name((*c_iter)->get_name()) << " = " << value; + } + } + s_enum << endl; + indent_down(); + indent(s_enum) << ");" << endl << endl; + indent_down(); +} + +std::string t_delphi_generator::make_valid_delphi_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; +} + +std::string t_delphi_generator::make_constants_classname() { + if (constprefix_) { + return make_valid_delphi_identifier("T" + program_name_ + "Constants"); + } else { + return "TConstants"; // compatibility + } +} + +void t_delphi_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + has_const = true; + string constants_class = make_constants_classname(); + + indent_up(); + indent(s_const) << constants_class.c_str() << " = class" << endl; + indent(s_const) << "private" << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if (const_needs_var((*c_iter)->get_type())) { + print_private_field(s_const, + normalize_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + } + indent_down(); + indent(s_const) << "public" << endl; + indent_up(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + generate_delphi_doc(s_const, *c_iter); + print_const_prop(s_const, + normalize_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + indent(s_const) << "{$IF CompilerVersion >= 21.0}" << endl; + indent(s_const) << "class constructor Create;" << endl; + indent(s_const) << "class destructor Destroy;" << endl; + indent(s_const) << "{$IFEND}" << endl; + indent_down(); + indent(s_const) << "end;" << endl << endl; + indent_down(); + + std::ostringstream vars, code; + + indent_up_impl(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + initialize_field(vars, + code, + "F" + prop_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + indent_down_impl(); + + indent_impl(s_const_impl) << "{$IF CompilerVersion >= 21.0}" << endl; + indent_impl(s_const_impl) << "class constructor " << constants_class.c_str() << ".Create;" + << endl; + + if (!vars.str().empty()) { + indent_impl(s_const_impl) << "var" << endl; + s_const_impl << vars.str(); + } + indent_impl(s_const_impl) << "begin" << endl; + if (!code.str().empty()) { + s_const_impl << code.str(); + } + indent_impl(s_const_impl) << "end;" << endl << endl; + indent_impl(s_const_impl) << "class destructor " << constants_class.c_str() << ".Destroy;" + << endl; + indent_impl(s_const_impl) << "begin" << endl; + indent_up_impl(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if (const_needs_var((*c_iter)->get_type())) { + finalize_field(s_const_impl, + normalize_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + } + indent_impl(s_const_impl) << "inherited;" << endl; + indent_down_impl(); + indent_impl(s_const_impl) << "end;" << endl; + indent_impl(s_const_impl) << "{$ELSE}" << endl; + + vars.str(""); + code.str(""); + + indent_up_impl(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + if (const_needs_var((*c_iter)->get_type())) { + initialize_field(vars, + code, + constants_class + ".F" + prop_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + } + indent_down_impl(); + + indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Initialize;" << endl; + if (!vars.str().empty()) { + indent_impl(s_const_impl) << "var" << endl; + s_const_impl << vars.str(); + } + indent_impl(s_const_impl) << "begin" << endl; + if (!code.str().empty()) { + s_const_impl << code.str(); + } + indent_impl(s_const_impl) << "end;" << endl << endl; + + indent_impl(s_const_impl) << "procedure " << constants_class.c_str() << "_Finalize;" << endl; + indent_impl(s_const_impl) << "begin" << endl; + indent_up_impl(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + finalize_field(s_const_impl, + normalize_name((*c_iter)->get_name()), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + constants_class); + } + indent_down_impl(); + indent_impl(s_const_impl) << "end;" << endl; + indent_impl(s_const_impl) << "{$IFEND}" << endl << endl; +} + +void t_delphi_generator::print_const_def_value(std::ostream& vars, + std::ostream& out, + string name, + t_type* type, + t_const_value* value, + string cls_nm) { + + string cls_prefix; + + if (cls_nm == "") { + cls_prefix = ""; + } else { + cls_prefix = cls_nm + "."; + } + + 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; + 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(vars, out, name, field_type, v_iter->second); + indent_impl(out) << cls_prefix << normalize_name(name) << "." + << prop_name(v_iter->first->get_string(), type->is_xception()) + << " := " << val << ";" << endl; + } + } 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(vars, out, name, ktype, v_iter->first); + string val = render_const_value(vars, out, name, vtype, v_iter->second); + indent_impl(out) << cls_prefix << normalize_name(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(vars, out, name, etype, *v_iter); + indent_impl(out) << cls_prefix << normalize_name(name) << ".Add(" << val << ");" << endl; + } + } +} + +void t_delphi_generator::print_private_field(std::ostream& out, + string name, + t_type* type, + t_const_value* value) { + (void)value; + indent(out) << "class var F" << name << ": " << type_name(type) << ";" << endl; +} + +bool t_delphi_generator::const_needs_var(t_type* type) { + t_type* truetype = type; + while (truetype->is_typedef()) { + truetype = ((t_typedef*)truetype)->get_type(); + } + return (!truetype->is_base_type()); +} + +void t_delphi_generator::print_const_prop(std::ostream& out, + string name, + t_type* type, + t_const_value* value) { + (void)value; + if (const_needs_var(type)) { + indent(out) << "class property " << name << ": " << type_name(type) << " read F" << name << ";" + << endl; + } else { + std::ostringstream vars; // dummy + string v2 = render_const_value(vars, out, name, type, value); + indent(out) << "const " << name << " = " << v2 << ";" << endl; + } +} + +void t_delphi_generator::print_const_value(std::ostream& vars, + std::ostream& out, + string name, + t_type* type, + t_const_value* value) { + t_type* truetype = type; + while (truetype->is_typedef()) { + truetype = ((t_typedef*)truetype)->get_type(); + } + + if (truetype->is_base_type()) { + // already done + // string v2 = render_const_value( vars, out, name, type, value); + // indent_impl(out) << name << " := " << v2 << ";" << endl; + } else if (truetype->is_enum()) { + indent_impl(out) << name << " := " << type_name(type) << "." << value->get_identifier_name() + << ";" << endl; + } else { + string typname; + typname = type_name(truetype, true, false, type->is_xception(), type->is_xception()); + indent_impl(out) << name << " := " << typname << ".Create;" << endl; + print_const_def_value(vars, out, name, truetype, value); + } +} + +void t_delphi_generator::initialize_field(std::ostream& vars, + std::ostream& out, + string name, + t_type* type, + t_const_value* value) { + print_const_value(vars, out, name, type, value); +} + +void t_delphi_generator::finalize_field(std::ostream& out, + string name, + t_type* type, + t_const_value* value, + string cls_nm) { + (void)out; + (void)name; + (void)type; + (void)value; + (void)cls_nm; +} + +string t_delphi_generator::render_const_value(ostream& vars, + ostream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + + t_type* truetype = type; + while (truetype->is_typedef()) { + truetype = ((t_typedef*)truetype)->get_type(); + } + + std::ostringstream render; + + if (truetype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)truetype)->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: + render << "ShortInt( " << value->get_integer() << ")"; + break; + case t_base_type::TYPE_I16: + render << "SmallInt( " << value->get_integer() << ")"; + break; + case t_base_type::TYPE_I32: + render << "LongInt( " << value->get_integer() << ")"; + break; + case t_base_type::TYPE_I64: + render << "Int64( " << value->get_integer() << ")"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << value->get_integer() << ".0"; // make it a double constant by adding ".0" + } else { + render << value->get_double(); + } + break; + default: + throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); + } + } else if (truetype->is_enum()) { + render << type_name(type, false) << "." << value->get_identifier_name(); + } else { + string t = tmp("tmp"); + vars << " " << t << " : " << type_name(type) << ";" << endl; + print_const_value(vars, out, t, type, value); + render << t; + } + + return render.str(); +} + +void t_delphi_generator::generate_struct(t_struct* tstruct) { + generate_delphi_struct(tstruct, false); +} + +void t_delphi_generator::generate_xception(t_struct* txception) { + generate_delphi_struct(txception, true); +} + +void t_delphi_generator::generate_delphi_struct(t_struct* tstruct, bool is_exception) { + indent_up(); + generate_delphi_struct_definition(s_struct, tstruct, is_exception); + indent_down(); + + add_defined_type(tstruct); + + generate_delphi_struct_impl(s_struct_impl, "", tstruct, is_exception); + if (register_types_) { + generate_delphi_struct_type_factory(s_type_factory_funcs, "", tstruct, is_exception); + generate_delphi_struct_type_factory_registration(s_type_factory_registration, + "", + tstruct, + is_exception); + } +} + +void t_delphi_generator::generate_delphi_struct_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result, + bool is_x_factory) { + + if (is_exception && (!is_x_factory)) { + generate_delphi_struct_impl(out, cls_prefix, tstruct, is_exception, is_result, true); + } + + string cls_nm; + + string exception_factory_name; + + if (is_exception) { + exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory"; + } + + if (is_exception) { + cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true); + } else { + cls_nm = type_name(tstruct, true, false); + } + + std::ostringstream vars, code; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent_up_impl(); + 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) { + initialize_field(vars, + code, + "F" + prop_name((*m_iter)->get_name(), is_exception), + t, + (*m_iter)->get_value()); + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + indent_impl(code) << "F__isset_" << prop_name((*m_iter), is_exception) << " := True;" + << endl; + } + } + } + indent_down_impl(); + + indent_impl(out) << "constructor " << cls_prefix << cls_nm << "." + << "Create;" << endl; + + if (!vars.str().empty()) { + out << "var" << endl; + out << vars.str(); + } + + indent_impl(out) << "begin" << endl; + indent_up_impl(); + if (is_exception && (!is_x_factory)) { + indent_impl(out) << "inherited Create('');" << endl; + indent_impl(out) << "F" << exception_factory_name << " := T" << exception_factory_name + << "Impl.Create;" << endl; + } else { + indent_impl(out) << "inherited;" << endl; + } + + if (!code.str().empty()) { + out << code.str(); + } + + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + + if ((members.size() > 0) && is_exception && (!is_x_factory)) { + indent_impl(out) << "constructor " << cls_prefix << cls_nm << "." + << "Create(" << constructor_argument_list(tstruct, indent_impl()) << ");" + << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "Create;" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string propname = prop_name((*m_iter)->get_name(), is_exception); + string param_name = constructor_param_name((*m_iter)->get_name()); + indent_impl(out) << propname << " := " << param_name << ";" << endl; + } + indent_impl(out) << "UpdateMessageProperty;" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + } + + indent_impl(out) << "destructor " << cls_prefix << cls_nm << "." + << "Destroy;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + + 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(); + } + finalize_field(out, prop_name(*m_iter, is_exception), t, (*m_iter)->get_value()); + } + + indent_impl(out) << "inherited;" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + + if (tstruct->is_union()) { + indent_impl(out) << "procedure " << cls_prefix << cls_nm << "." + << "ClearUnionValues;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + 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(); + } + + generate_delphi_clear_union_value(out, + cls_prefix, + cls_nm, + t, + *m_iter, + "F", + is_exception, + tstruct->is_union(), + is_x_factory, + exception_factory_name); + } + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; + } + + 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(); + } + generate_delphi_property_reader_impl(out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); + generate_delphi_property_writer_impl(out, + cls_prefix, + cls_nm, + t, + *m_iter, + "F", + is_exception, + tstruct->is_union(), + 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); + } + } + + 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_tostring_impl(out, cls_prefix, tstruct, is_exception, is_x_factory); + + if (is_exception && is_x_factory) { + generate_delphi_create_exception_impl(out, cls_prefix, tstruct, is_exception); + } +} + +void t_delphi_generator::print_delphi_struct_type_factory_func(ostream& out, t_struct* tstruct) { + string struct_intf_name = type_name(tstruct); + out << "Create_"; + out << struct_intf_name; + out << "_Impl"; +} + +void t_delphi_generator::generate_delphi_struct_type_factory(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result, + bool is_x_factory) { + (void)cls_prefix; + if (is_exception) + return; + if (is_result) + return; + if (is_x_factory) + return; + + string struct_intf_name = type_name(tstruct); + string cls_nm = type_name(tstruct, true, false); + + out << "function "; + print_delphi_struct_type_factory_func(out, tstruct); + out << ": "; + out << struct_intf_name; + out << ";" << endl; + out << "begin" << endl; + indent_up(); + indent(out) << "Result := " << cls_nm << ".Create;" << endl; + indent_down(); + out << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_struct_type_factory_registration(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_result, + bool is_x_factory) { + (void)cls_prefix; + if (is_exception) + return; + if (is_result) + return; + if (is_x_factory) + return; + + string struct_intf_name = type_name(tstruct); + + indent(out) << " TypeRegistry.RegisterTypeFactory<" << struct_intf_name << ">("; + print_delphi_struct_type_factory_func(out, tstruct); + out << ");"; + out << endl; +} + +void t_delphi_generator::generate_delphi_struct_definition(ostream& out, + t_struct* tstruct, + bool is_exception, + bool in_class, + bool is_result, + bool is_x_factory) { + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + string struct_intf_name; + string struct_name; + string isset_name; + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + string exception_factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory"; + + if (is_exception) { + struct_intf_name = type_name(tstruct, false, false, true); + } else { + struct_intf_name = type_name(tstruct); + } + + if (is_exception) { + struct_name = type_name(tstruct, true, (!is_x_factory), is_x_factory); + } else { + struct_name = type_name(tstruct, true); + } + + if ((!is_exception) || is_x_factory) { + + generate_delphi_doc(out, tstruct); + indent(out) << struct_intf_name << " = interface(IBase)" << endl; + indent_up(); + + 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); + } + + if (is_x_factory) { + out << endl; + indent(out) << "// Create Exception Object" << endl; + indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl; + } + + if (members.size() > 0) { + out << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_property(out, *m_iter, true, is_exception); + } + } + + if (members.size() > 0) { + 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); + } + } + } + + if (members.size() > 0) { + out << endl; + 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 << ": Boolean read Get" << isset_name << ";" + << endl; + } + } + } + + indent_down(); + indent(out) << "end;" << endl << endl; + } + + generate_delphi_doc(out, tstruct); + indent(out) << struct_name << " = "; + if (is_final) { + out << "sealed "; + } + out << "class("; + if (is_exception && (!is_x_factory)) { + out << "TException"; + } else { + out << "TInterfacedObject, IBase, " << struct_intf_name; + } + out << ")" << endl; + + if (is_exception && (!is_x_factory)) { + indent(out) << "public" << endl; + indent_up(); + indent(out) << "type" << endl; + indent_up(); + generate_delphi_struct_definition(out, tstruct, is_exception, in_class, is_result, true); + indent_down(); + indent_down(); + } + + indent(out) << "private" << endl; + indent_up(); + + if (is_exception && (!is_x_factory)) { + indent(out) << "F" << exception_factory_name << " :" << struct_intf_name << ";" << endl << endl; + } + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << declare_field(*m_iter, false, "F", is_exception) << endl; + } + + if (members.size() > 0) { + indent(out) << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + isset_name = "F__isset_" + prop_name(*m_iter, is_exception); + indent(out) << isset_name << ": Boolean;" << endl; + } + } + } + + indent(out) << endl; + + 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); + } + + if (tstruct->is_union()) { + out << endl; + indent(out) << "// Clear values(for union's property setter)" << endl; + indent(out) << "procedure ClearUnionValues;" << endl; + } + + if (members.size() > 0) { + out << endl; + 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) << "function Get" << isset_name << ": Boolean;" << endl; + } + } + } + + indent_down(); + + indent(out) << "public" << endl; + indent_up(); + + if ((members.size() > 0) && is_exception && (!is_x_factory)) { + indent(out) << "constructor Create; overload;" << endl; + indent(out) << "constructor Create(" << constructor_argument_list(tstruct, indent()) + << "); overload;" << endl; + } else { + indent(out) << "constructor Create;" << endl; + } + + indent(out) << "destructor Destroy; override;" << endl; + + out << endl; + indent(out) << "function ToString: string; override;" << endl; + + if (is_exception && (!is_x_factory)) { + out << endl; + indent(out) << "// Exception Factory" << endl; + indent(out) << "property " << exception_factory_name << ": " << struct_intf_name << " read F" + << exception_factory_name << " write F" << exception_factory_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; + } + + if (is_exception && is_x_factory) { + out << endl; + indent(out) << "// Create Exception Object" << endl; + indent(out) << "function CreateException: " << type_name(tstruct, true, true) << ";" << endl; + } + + if (members.size() > 0) { + out << endl; + indent(out) << "// Properties" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_property(out, *m_iter, true, is_exception); + } + } + + if (members.size() > 0) { + out << endl; + indent(out) << "// isset" << endl; + 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 << ": Boolean read Get" << isset_name << ";" + << endl; + } + } + } + + indent_down(); + indent(out) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_service(t_service* tservice) { + indent_up(); + generate_delphi_doc(s_service, tservice); + indent(s_service) << normalize_clsnm(service_name_, "T") << " = class" << endl; + indent(s_service) << "public" << endl; + indent_up(); + indent(s_service) << "type" << endl; + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + indent_down(); + indent_down(); + indent(s_service) << "end;" << endl; + indent(s_service) << endl; + indent_down(); +} + +void t_delphi_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_iface = ""; + + indent_up(); + + generate_delphi_doc(s_service, tservice); + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends(), true, true); + extends_iface = extends + ".Iface"; + generate_delphi_doc(s_service, tservice); + indent(s_service) << "Iface = interface(" << extends_iface << ")" << endl; + } else { + indent(s_service) << "Iface = interface" << endl; + } + + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_delphi_doc(s_service, *f_iter); + indent(s_service) << function_signature(*f_iter) << endl; + } + indent_down(); + indent(s_service) << "end;" << endl << endl; + + indent_down(); +} + +void t_delphi_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_delphi_struct_definition(s_service, ts, false, true); + generate_delphi_struct_impl(s_service_impl, + normalize_clsnm(service_name_, "T") + ".", + ts, + false); + generate_function_helpers(*f_iter); + } +} + +void t_delphi_generator::generate_service_client(t_service* tservice) { + indent_up(); + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_client = extends + ".Client, "; + } + + generate_delphi_doc(s_service, tservice); + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends(), true, true); + extends_client = extends + ".TClient"; + indent(s_service) << "TClient = class(" << extends_client << ", Iface)" << endl; + } else { + indent(s_service) << "TClient = class( TInterfacedObject, Iface)" << endl; + } + + indent(s_service) << "public" << endl; + indent_up(); + + indent(s_service) << "constructor Create( prot: IProtocol); overload;" << endl; + + indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T") + << ".TClient.Create( prot: IProtocol);" << endl; + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "Create( prot, prot );" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + indent(s_service) + << "constructor Create( const iprot: IProtocol; const oprot: IProtocol); overload;" << endl; + + indent_impl(s_service_impl) << "constructor " << normalize_clsnm(service_name_, "T") + << ".TClient.Create( const iprot: IProtocol; const oprot: IProtocol);" + << endl; + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "inherited Create;" << endl; + indent_impl(s_service_impl) << "iprot_ := iprot;" << endl; + indent_impl(s_service_impl) << "oprot_ := oprot;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + indent_down(); + + if (extends.empty()) { + indent(s_service) << "protected" << endl; + indent_up(); + indent(s_service) << "iprot_: IProtocol;" << endl; + indent(s_service) << "oprot_: IProtocol;" << endl; + indent(s_service) << "seqid_: Integer;" << endl; + indent_down(); + + indent(s_service) << "public" << endl; + indent_up(); + indent(s_service) << "property InputProtocol: IProtocol read iprot_;" << endl; + indent(s_service) << "property OutputProtocol: IProtocol read oprot_;" << endl; + indent_down(); + } + + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + + indent(s_service) << "protected" << endl; + indent_up(); + indent(s_service) << "// Iface" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funname = (*f_iter)->get_name(); + generate_delphi_doc(s_service, *f_iter); + indent(s_service) << function_signature(*f_iter) << endl; + } + indent_down(); + + indent(s_service) << "public" << endl; + indent_up(); + + string full_cls = normalize_clsnm(service_name_, "T") + ".TClient"; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funname = (*f_iter)->get_name(); + + indent_impl(s_service_impl) << function_signature(*f_iter, full_cls) << endl; + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "send_" << funname << "("; + + 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) { + if (first) { + first = false; + } else { + s_service_impl << ", "; + } + s_service_impl << normalize_name((*fld_iter)->get_name()); + } + s_service_impl << ");" << endl; + + if (!(*f_iter)->is_oneway()) { + s_service_impl << indent_impl(); + if (!(*f_iter)->get_returntype()->is_void()) { + s_service_impl << "Result := "; + } + s_service_impl << "recv_" << funname << "();" << endl; + } + + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + t_function send_function(g_type_void, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + + string argsname = (*f_iter)->get_name() + "_args"; + string args_clsnm = normalize_clsnm(argsname, "T"); + string args_intfnm = normalize_clsnm(argsname, "I"); + + string argsvar = tmp("_args"); + string msgvar = tmp("_msg"); + + indent(s_service) << function_signature(&send_function) << endl; + indent_impl(s_service_impl) << function_signature(&send_function, full_cls) << endl; + indent_impl(s_service_impl) << "var" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << argsvar << " : " << args_intfnm << ";" << endl; + indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.IMessage;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + + indent_impl(s_service_impl) << "seqid_ := seqid_ + 1;" << endl; + indent_impl(s_service_impl) << msgvar << " := Thrift.Protocol.TMessageImpl.Create('" << funname + << "', " << ((*f_iter)->is_oneway() ? "TMessageType.Oneway" + : "TMessageType.Call") + << ", seqid_);" << endl; + + indent_impl(s_service_impl) << "oprot_.WriteMessageBegin( " << msgvar << " );" << endl; + indent_impl(s_service_impl) << argsvar << " := " << args_clsnm << "Impl.Create();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter) + << " := " << normalize_name((*fld_iter)->get_name()) << ";" + << endl; + } + indent_impl(s_service_impl) << argsvar << ".Write(oprot_);" << endl; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + indent_impl(s_service_impl) << argsvar << "." << prop_name(*fld_iter) + << " := " << empty_value((*fld_iter)->get_type()) << ";" << endl; + } + + indent_impl(s_service_impl) << "oprot_.WriteMessageEnd();" << endl; + indent_impl(s_service_impl) << "oprot_.Transport.Flush();" << endl; + + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + if (!(*f_iter)->is_oneway()) { + string org_resultname = (*f_iter)->get_name() + "_result"; + string result_clsnm = normalize_clsnm(org_resultname, "T"); + string result_intfnm = normalize_clsnm(org_resultname, "I"); + + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs, + (*f_iter)->get_xceptions()); + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + + string exceptvar = tmp("_ex"); + string appexvar = tmp("_ax"); + string retvar = tmp("_ret"); + + indent(s_service) << function_signature(&recv_function) << endl; + indent_impl(s_service_impl) << function_signature(&recv_function, full_cls) << endl; + indent_impl(s_service_impl) << "var" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << msgvar << " : Thrift.Protocol.IMessage;" << endl; + if (xceptions.size() > 0) { + indent_impl(s_service_impl) << exceptvar << " : Exception;" << endl; + } + indent_impl(s_service_impl) << appexvar << " : TApplicationException;" << endl; + indent_impl(s_service_impl) << retvar << " : " << result_intfnm << ";" << endl; + + indent_down_impl(); + 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_up_impl(); + indent_impl(s_service_impl) << appexvar << " := TApplicationException.Read(iprot_);" << endl; + indent_impl(s_service_impl) << "iprot_.ReadMessageEnd();" << endl; + indent_impl(s_service_impl) << "raise " << appexvar << ";" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + + indent_impl(s_service_impl) << retvar << " := " << result_clsnm << "Impl.Create();" << endl; + indent_impl(s_service_impl) << retvar << ".Read(iprot_);" << endl; + 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_up_impl(); + indent_impl(s_service_impl) << "Result := " << retvar << ".Success;" << endl; + t_type* type = (*f_iter)->get_returntype(); + if (type->is_struct() || type->is_xception() || type->is_map() || type->is_list() + || type->is_set()) { + indent_impl(s_service_impl) << retvar << ".Success := nil;" << endl; + } + indent_impl(s_service_impl) << "Exit;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + } + + 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; + indent_up_impl(); + indent_impl(s_service_impl) << exceptvar << " := " << retvar << "." << prop_name(*x_iter) + << ".CreateException;" << endl; + indent_impl(s_service_impl) << "raise " << exceptvar << ";" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + } + + if (!(*f_iter)->get_returntype()->is_void()) { + indent_impl(s_service_impl) + << "raise TApplicationExceptionMissingResult.Create('" + << (*f_iter)->get_name() << " failed: unknown result');" << endl; + } + + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + } + } + + indent_down(); + indent(s_service) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_service_server(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + + string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl"; + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends(), true, true); + extends_processor = extends + ".TProcessorImpl"; + indent(s_service) << "TProcessorImpl = class(" << extends_processor << ", IProcessor)" << endl; + } else { + indent(s_service) << "TProcessorImpl = class( TInterfacedObject, IProcessor)" << endl; + } + + indent(s_service) << "public" << endl; + indent_up(); + indent(s_service) << "constructor Create( iface_: Iface );" << endl; + indent(s_service) << "destructor Destroy; override;" << endl; + indent_down(); + + 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) { + 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) { + indent_impl(s_service_impl) << "ASSERT( processMap_ <> nil); // inherited" << endl; + } else { + indent_impl(s_service_impl) + << "processMap_ := TThriftDictionaryImpl.Create;" << endl; + } + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent_impl(s_service_impl) << "processMap_.AddOrSetValue( '" << (*f_iter)->get_name() << "', " + << (*f_iter)->get_name() << "_Process);" << endl; + } + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + indent_impl(s_service_impl) << "destructor " << full_cls << ".Destroy;" << endl; + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "inherited;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + indent(s_service) << "private" << endl; + indent_up(); + indent(s_service) << "iface_: Iface;" << endl; + indent_down(); + + if (tservice->get_extends() == NULL) { + indent(s_service) << "protected" << endl; + indent_up(); + indent(s_service) << "type" << endl; + indent_up(); + indent(s_service) << "TProcessFunction = reference to procedure( seqid: Integer; const iprot: " + "IProtocol; const oprot: IProtocol" + << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl; + indent_down(); + indent_down(); + indent(s_service) << "protected" << endl; + indent_up(); + indent(s_service) << "processMap_: IThriftDictionary;" << endl; + indent_down(); + } + + indent(s_service) << "public" << endl; + indent_up(); + if (extends.empty()) { + indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const " + "events : IProcessorEvents): Boolean;" << endl; + } else { + indent(s_service) << "function Process( const iprot: IProtocol; const oprot: IProtocol; const " + "events : IProcessorEvents): Boolean; reintroduce;" << endl; + } + + indent_impl(s_service_impl) << "function " << full_cls << ".Process( const iprot: IProtocol; " + "const oprot: IProtocol; const events " + ": IProcessorEvents): Boolean;" << endl; + ; + indent_impl(s_service_impl) << "var" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "msg : Thrift.Protocol.IMessage;" << endl; + indent_impl(s_service_impl) << "fn : TProcessFunction;" << endl; + indent_impl(s_service_impl) << "x : TApplicationException;" << endl; + if (events_) { + indent_impl(s_service_impl) << "context : IRequestEvents;" << endl; + } + indent_down_impl(); + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "try" << endl; + indent_up_impl(); + 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_up_impl(); + indent_impl(s_service_impl) << "TProtocolUtil.Skip(iprot, TType.Struct);" << endl; + indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl; + indent_impl(s_service_impl) << "x := " + "TApplicationExceptionUnknownMethod.Create(" + "'Invalid method name: ''' + msg.Name + '''');" << endl; + indent_impl(s_service_impl) + << "msg := Thrift.Protocol.TMessageImpl.Create(msg.Name, TMessageType.Exception, msg.SeqID);" + << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl; + indent_impl(s_service_impl) << "x.Write(oprot);" << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl; + indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl; + indent_impl(s_service_impl) << "Result := True;" << endl; + indent_impl(s_service_impl) << "Exit;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + if (events_) { + indent_impl(s_service_impl) << "if events <> nil" << endl; + indent_impl(s_service_impl) << "then context := events.CreateRequestContext(msg.Name)" << endl; + indent_impl(s_service_impl) << "else context := nil;" << endl; + indent_impl(s_service_impl) << "try" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot, context);" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "finally" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "if context <> nil then begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "context.CleanupContext;" << endl; + indent_impl(s_service_impl) << "context := nil;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + } else { + indent_impl(s_service_impl) << "fn(msg.SeqID, iprot, oprot);" << endl; + } + indent_down_impl(); + indent_impl(s_service_impl) << "except" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "on TTransportExceptionTimedOut do begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "Result := True;" << endl; + indent_impl(s_service_impl) << "Exit;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + indent_impl(s_service_impl) << "else begin" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "Result := False;" << endl; + indent_impl(s_service_impl) << "Exit;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + indent_impl(s_service_impl) << "Result := True;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(s_service) << "end;" << endl << endl; +} + +void t_delphi_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_delphi_struct_definition(s_service, &result, false, true, true); + generate_delphi_struct_impl(s_service_impl, + normalize_clsnm(service_name_, "T") + ".", + &result, + false); +} + +void t_delphi_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + string funcname = tfunction->get_name(); + string full_cls = normalize_clsnm(service_name_, "T") + ".TProcessorImpl"; + + string org_argsname = funcname + "_args"; + string args_clsnm = normalize_clsnm(org_argsname, "T"); + string args_intfnm = normalize_clsnm(org_argsname, "I"); + + string org_resultname = funcname + "_result"; + string result_clsnm = normalize_clsnm(org_resultname, "T"); + string result_intfnm = normalize_clsnm(org_resultname, "I"); + + indent(s_service) << "procedure " << funcname + << "_Process( seqid: Integer; const iprot: IProtocol; const oprot: IProtocol" + << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl; + + if (tfunction->is_oneway()) { + indent_impl(s_service_impl) << "// one way processor" << endl; + } else { + indent_impl(s_service_impl) << "// both way processor" << endl; + } + + indent_impl(s_service_impl) + << "procedure " << full_cls << "." << funcname + << "_Process( seqid: Integer; const iprot: IProtocol; const oprot: IProtocol" + << (events_ ? "; const events : IRequestEvents" : "") << ");" << endl; + indent_impl(s_service_impl) << "var" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "args: " << args_intfnm << ";" << endl; + if (!tfunction->is_oneway()) { + indent_impl(s_service_impl) << "msg: Thrift.Protocol.IMessage;" << endl; + indent_impl(s_service_impl) << "ret: " << result_intfnm << ";" << endl; + indent_impl(s_service_impl) << "appx : TApplicationException;" << endl; + } + + indent_down_impl(); + indent_impl(s_service_impl) << "begin" << endl; + indent_up_impl(); + + if (events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PreRead;" << endl; + } + indent_impl(s_service_impl) << "args := " << args_clsnm << "Impl.Create;" << endl; + indent_impl(s_service_impl) << "args.Read(iprot);" << endl; + indent_impl(s_service_impl) << "iprot.ReadMessageEnd();" << endl; + if (events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PostRead;" << endl; + } + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + if (!tfunction->is_oneway()) { + indent_impl(s_service_impl) << "ret := " << result_clsnm << "Impl.Create;" << endl; + } + + indent_impl(s_service_impl) << "try" << endl; + indent_up_impl(); + + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + s_service_impl << indent_impl(); + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + s_service_impl << "ret.Success := "; + } + s_service_impl << "iface_." << normalize_name(tfunction->get_name(), true) << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + s_service_impl << ", "; + } + s_service_impl << "args." << prop_name(*f_iter); + } + s_service_impl << ");" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent_impl(s_service_impl) << "args." << prop_name(*f_iter) + << " := " << empty_value((*f_iter)->get_type()) << ";" << endl; + } + + indent_down_impl(); + indent_impl(s_service_impl) << "except" << endl; + indent_up_impl(); + + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + indent_impl(s_service_impl) << "on E: " << type_name((*x_iter)->get_type(), true, true) + << " do begin" << endl; + indent_up_impl(); + if (!tfunction->is_oneway()) { + string factory_name = normalize_clsnm((*x_iter)->get_type()->get_name(), "", true) + + "Factory"; + indent_impl(s_service_impl) << "ret." << prop_name(*x_iter) << " := E." << factory_name << ";" + << endl; + } + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + } + + indent_impl(s_service_impl) << "on E: Exception do begin" << endl; + indent_up_impl(); + if(events_) { + indent_impl(s_service_impl) << "if events <> nil then events.UnhandledError(E);" << endl; + } + if (!tfunction->is_oneway()) { + indent_impl(s_service_impl) << "appx := TApplicationExceptionInternalError.Create(E.Message);" + << endl; + indent_impl(s_service_impl) << "try" << endl; + indent_up_impl(); + if(events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl; + } + indent_impl(s_service_impl) << "msg := Thrift.Protocol.TMessageImpl.Create('" + << tfunction->get_name() << "', TMessageType.Exception, seqid);" + << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg);" << endl; + indent_impl(s_service_impl) << "appx.Write(oprot);" << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl; + indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl; + if(events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl; + } + indent_impl(s_service_impl) << "Exit;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "finally" << endl; + indent_up_impl(); + indent_impl(s_service_impl) << "appx.Free;" << endl; + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + } + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl; + + if (!tfunction->is_oneway()) { + if (events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PreWrite;" << endl; + } + indent_impl(s_service_impl) << "msg := Thrift.Protocol.TMessageImpl.Create('" + << tfunction->get_name() << "', TMessageType.Reply, seqid); " + << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageBegin( msg); " << endl; + indent_impl(s_service_impl) << "ret.Write(oprot);" << endl; + indent_impl(s_service_impl) << "oprot.WriteMessageEnd();" << endl; + indent_impl(s_service_impl) << "oprot.Transport.Flush();" << endl; + if (events_) { + indent_impl(s_service_impl) << "if events <> nil then events.PostWrite;" << endl; + } + } else if (events_) { + indent_impl(s_service_impl) << "if events <> nil then events.OnewayComplete;" << endl; + } + + indent_down_impl(); + indent_impl(s_service_impl) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_deserialize_field(ostream& out, + bool is_xception, + t_field* tfield, + string prefix, + ostream& local_vars) { + 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 + prop_name(tfield, is_xception); + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, name, ""); + } else if (type->is_container()) { + generate_deserialize_container(out, is_xception, type, name, local_vars); + } else if (type->is_base_type() || type->is_enum()) { + indent_impl(out) << name << " := "; + + if (type->is_enum()) { + out << type_name(type, false) << "("; + } + + 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 (((t_base_type*)type)->is_binary()) { + if (ansistr_binary_) { + out << "ReadAnsiString();"; + } else { + 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 Delphi name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "ReadI32()"; + out << ");"; + } + 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_delphi_generator::generate_deserialize_struct(ostream& out, + t_struct* tstruct, + string name, + string prefix) { + string typ_name; + + if (tstruct->is_xception()) { + typ_name = type_name(tstruct, true, false, true, true); + } else { + typ_name = type_name(tstruct, true, false); + } + + indent_impl(out) << prefix << name << " := " << typ_name << ".Create;" << endl; + indent_impl(out) << prefix << name << ".Read(iprot);" << endl; +} + +void t_delphi_generator::generate_deserialize_container(ostream& out, + bool is_xception, + t_type* ttype, + string name, + std::ostream& local_vars) { + + string obj; + string counter; + string local_var; + + if (ttype->is_map()) { + obj = tmp("_map"); + } else if (ttype->is_set()) { + obj = tmp("_set"); + } else if (ttype->is_list()) { + obj = tmp("_list"); + } + + if (ttype->is_map()) { + local_var = obj + ": IMap;"; + } else if (ttype->is_set()) { + local_var = obj + ": ISet;"; + } else if (ttype->is_list()) { + local_var = obj + ": IList;"; + } + local_vars << " " << local_var << endl; + counter = tmp("_i"); + local_var = counter + ": Integer;"; + local_vars << " " << local_var << endl; + + indent_impl(out) << name << " := " << type_name(ttype, true) << ".Create;" << endl; + + if (ttype->is_map()) { + indent_impl(out) << obj << " := iprot.ReadMapBegin();" << endl; + } else if (ttype->is_set()) { + indent_impl(out) << obj << " := iprot.ReadSetBegin();" << endl; + } else if (ttype->is_list()) { + 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_up_impl(); + if (ttype->is_map()) { + generate_deserialize_map_element(out, is_xception, (t_map*)ttype, name, local_vars); + } else if (ttype->is_set()) { + generate_deserialize_set_element(out, is_xception, (t_set*)ttype, name, local_vars); + } else if (ttype->is_list()) { + generate_deserialize_list_element(out, is_xception, (t_list*)ttype, name, local_vars); + } + indent_down_impl(); + indent_impl(out) << "end;" << endl; + + if (ttype->is_map()) { + indent_impl(out) << "iprot.ReadMapEnd();" << endl; + } else if (ttype->is_set()) { + indent_impl(out) << "iprot.ReadSetEnd();" << endl; + } else if (ttype->is_list()) { + indent_impl(out) << "iprot.ReadListEnd();" << endl; + } +} + +void t_delphi_generator::generate_deserialize_map_element(ostream& out, + bool is_xception, + t_map* tmap, + string prefix, + ostream& local_vars) { + + string key = tmp("_key"); + string val = tmp("_val"); + string local_var; + + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + + local_vars << " " << declare_field(&fkey) << endl; + local_vars << " " << declare_field(&fval) << endl; + + generate_deserialize_field(out, is_xception, &fkey, "", local_vars); + generate_deserialize_field(out, is_xception, &fval, "", local_vars); + + indent_impl(out) << prefix << ".AddOrSetValue( " << key << ", " << val << ");" << endl; +} + +void t_delphi_generator::generate_deserialize_set_element(ostream& out, + bool is_xception, + t_set* tset, + string prefix, + ostream& local_vars) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + local_vars << " " << declare_field(&felem) << endl; + generate_deserialize_field(out, is_xception, &felem, "", local_vars); + indent_impl(out) << prefix << ".Add(" << elem << ");" << endl; +} + +void t_delphi_generator::generate_deserialize_list_element(ostream& out, + bool is_xception, + t_list* tlist, + string prefix, + ostream& local_vars) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + local_vars << " " << declare_field(&felem) << endl; + generate_deserialize_field(out, is_xception, &felem, "", local_vars); + indent_impl(out) << prefix << ".Add(" << elem << ");" << endl; +} + +void t_delphi_generator::generate_serialize_field(ostream& out, + bool is_xception, + t_field* tfield, + string prefix, + ostream& local_vars) { + (void)local_vars; + + t_type* type = tfield->get_type(); + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + + string name = prefix + prop_name(tfield, is_xception); + + 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, local_vars); + } else if (type->is_container()) { + generate_serialize_container(out, is_xception, type, name, local_vars); + } else if (type->is_base_type() || type->is_enum()) { + + indent_impl(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + if (ansistr_binary_) { + out << "WriteAnsiString("; + } else { + out << "WriteBinary("; + } + } else { + out << "WriteString("; + } + out << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "WriteBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "WriteByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "WriteI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "WriteI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "WriteI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "WriteDouble(" << name << ");"; + break; + default: + throw "compiler error: no Delphi name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "WriteI32(Integer(" << 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_delphi_generator::generate_serialize_struct(ostream& out, + t_struct* tstruct, + string prefix, + ostream& local_vars) { + (void)local_vars; + (void)tstruct; + out << indent_impl() << prefix << ".Write(oprot);" << endl; +} + +void t_delphi_generator::generate_serialize_container(ostream& out, + bool is_xception, + t_type* ttype, + string prefix, + ostream& local_vars) { + string obj; + if (ttype->is_map()) { + obj = tmp("map"); + local_vars << " " << obj << " : IMap;" << endl; + indent_impl(out) << obj << " := TMapImpl.Create( " + << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix + << ".Count);" << endl; + indent_impl(out) << "oprot.WriteMapBegin( " << obj << ");" << endl; + } else if (ttype->is_set()) { + obj = tmp("set_"); + local_vars << " " << obj << " : ISet;" << endl; + indent_impl(out) << obj << " := TSetImpl.Create(" + << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix + << ".Count);" << endl; + indent_impl(out) << "oprot.WriteSetBegin( " << obj << ");" << endl; + } else if (ttype->is_list()) { + obj = tmp("list_"); + local_vars << " " << obj << " : IList;" << endl; + indent_impl(out) << obj << " := TListImpl.Create(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix + << ".Count);" << endl; + indent_impl(out) << "oprot.WriteListBegin( " << obj << ");" << endl; + } + + 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_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_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_up_impl(); + } + + if (ttype->is_map()) { + generate_serialize_map_element(out, is_xception, (t_map*)ttype, iter, prefix, local_vars); + } else if (ttype->is_set()) { + generate_serialize_set_element(out, is_xception, (t_set*)ttype, iter, local_vars); + } else if (ttype->is_list()) { + generate_serialize_list_element(out, is_xception, (t_list*)ttype, iter, local_vars); + } + + indent_down_impl(); + indent_impl(out) << "end;" << endl; + + if (ttype->is_map()) { + indent_impl(out) << "oprot.WriteMapEnd();" << endl; + } else if (ttype->is_set()) { + indent_impl(out) << "oprot.WriteSetEnd();" << endl; + } else if (ttype->is_list()) { + indent_impl(out) << "oprot.WriteListEnd();" << endl; + } +} + +void t_delphi_generator::generate_serialize_map_element(ostream& out, + bool is_xception, + t_map* tmap, + string iter, + string map, + ostream& local_vars) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, is_xception, &kfield, "", local_vars); + t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); + generate_serialize_field(out, is_xception, &vfield, "", local_vars); +} + +void t_delphi_generator::generate_serialize_set_element(ostream& out, + bool is_xception, + t_set* tset, + string iter, + ostream& local_vars) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, is_xception, &efield, "", local_vars); +} + +void t_delphi_generator::generate_serialize_list_element(ostream& out, + bool is_xception, + t_list* tlist, + string iter, + ostream& local_vars) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, is_xception, &efield, "", local_vars); +} + +void t_delphi_generator::generate_property(ostream& out, + t_field* tfield, + bool isPublic, + bool is_xception) { + generate_delphi_property(out, is_xception, tfield, isPublic, "Get"); +} + +void t_delphi_generator::generate_delphi_property(ostream& out, + bool struct_is_xception, + t_field* tfield, + bool isPublic, + std::string fieldPrefix) { + (void)isPublic; + + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + generate_delphi_doc(out, tfield); + indent(out) << "property " << prop_name(tfield, struct_is_xception) << ": " + << type_name(ftype, false, true, is_xception, true) << " read " + << fieldPrefix + prop_name(tfield, struct_is_xception) << " write Set" + << prop_name(tfield, struct_is_xception) << ";" << endl; +} + +std::string t_delphi_generator::prop_name(t_field* tfield, bool is_xception) { + return prop_name(tfield->get_name(), is_xception); +} + +std::string t_delphi_generator::prop_name(string name, bool is_xception) { + string ret = name; + ret[0] = toupper(ret[0]); + return normalize_name(ret, true, is_xception); +} + +std::string t_delphi_generator::constructor_param_name(string name) { + string ret = name; + ret[0] = toupper(ret[0]); + ret = "A" + ret; + return normalize_name(ret, false, false); +} + +string t_delphi_generator::normalize_clsnm(string clsnm, string prefix, bool b_no_check_keyword) { + if (clsnm.size() > 0) { + clsnm[0] = toupper(clsnm[0]); + } + if (b_no_check_keyword) { + return prefix + clsnm; + } else { + return normalize_name(prefix + clsnm); + } +} + +string t_delphi_generator::type_name(t_type* ttype, + bool b_cls, + bool b_no_postfix, + bool b_exception_factory, + bool b_full_exception_factory) { + + 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) { + return type_name(tdef->get_type(), + b_cls, + b_no_postfix, + b_exception_factory, + b_full_exception_factory); + } else { + throw "unresolved forward declaration: " + tdef->get_symbolic(); + } + } else { + return normalize_name("T" + tdef->get_symbolic()); + } + } + + string typ_nm; + + string s_factory; + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype); + } else if (ttype->is_enum()) { + b_cls = true; + b_no_postfix = true; + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + if (b_cls) { + typ_nm = "TThriftDictionaryImpl"; + } else { + typ_nm = "IThriftDictionary"; + } + return typ_nm + "<" + type_name(tmap->get_key_type()) + ", " + type_name(tmap->get_val_type()) + + ">"; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + if (b_cls) { + typ_nm = "THashSetImpl"; + } else { + typ_nm = "IHashSet"; + } + return typ_nm + "<" + type_name(tset->get_elem_type()) + ">"; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + if (b_cls) { + typ_nm = "TThriftListImpl"; + } else { + typ_nm = "IThriftList"; + } + return typ_nm + "<" + type_name(tlist->get_elem_type()) + ">"; + } + + string type_prefix; + + if (b_cls) { + type_prefix = "T"; + } else { + type_prefix = "I"; + } + + string nm = normalize_clsnm(ttype->get_name(), type_prefix); + + if (b_exception_factory) { + nm = nm + "Factory"; + } + + if (b_cls) { + if (!b_no_postfix) { + nm = nm + "Impl"; + } + } + + if (b_exception_factory && b_full_exception_factory) { + return type_name(ttype, true, true, false, false) + "." + nm; + } + + return nm; +} + +// returns "const " for some argument types +string t_delphi_generator::input_arg_prefix(t_type* ttype) { + + // base types + if (ttype->is_base_type()) { + switch (((t_base_type*)ttype)->get_base()) { + + // these should be const'ed for optimal performamce + case t_base_type::TYPE_STRING: // refcounted pointer + case t_base_type::TYPE_I64: // larger than 32 bit + case t_base_type::TYPE_DOUBLE: // larger than 32 bit + return "const "; + + // all others don't need to be + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_VOID: + return ""; + + // we better always report any unknown types + default: + throw "compiler error: no input_arg_prefix() for base type " + + t_base_type::t_base_name(((t_base_type*)ttype)->get_base()); + } + + // enums + } else if (ttype->is_enum()) { + return ""; // usually <= 32 bit + + // containers + } else if (ttype->is_map()) { + return "const "; // refcounted pointer + + } else if (ttype->is_set()) { + return "const "; // refcounted pointer + + } else if (ttype->is_list()) { + return "const "; // refcounted pointer + } + + // any other type, either TSomething or ISomething + return "const "; // possibly refcounted pointer +} + +string t_delphi_generator::base_type_name(t_base_type* tbase) { + switch (tbase->get_base()) { + case t_base_type::TYPE_VOID: + // no "void" in Delphi language + return ""; + case t_base_type::TYPE_STRING: + if (tbase->is_binary()) { + if (ansistr_binary_) { + return "AnsiString"; + } else { + return "TBytes"; + } + } else { + return "string"; + } + case t_base_type::TYPE_BOOL: + return "Boolean"; + case t_base_type::TYPE_I8: + return "ShortInt"; + case t_base_type::TYPE_I16: + return "SmallInt"; + case t_base_type::TYPE_I32: + return "Integer"; + case t_base_type::TYPE_I64: + return "Int64"; + case t_base_type::TYPE_DOUBLE: + return "Double"; + default: + throw "compiler error: no Delphi name for base type " + + t_base_type::t_base_name(tbase->get_base()); + } +} + +string t_delphi_generator::declare_field(t_field* tfield, + bool init, + std::string prefix, + bool is_xception_class) { + (void)init; + + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + string result = prefix + prop_name(tfield, is_xception_class) + ": " + + type_name(ftype, false, true, is_xception, true) + ";"; + return result; +} + +string t_delphi_generator::function_signature(t_function* tfunction, + std::string full_cls, + bool is_xception) { + t_type* ttype = tfunction->get_returntype(); + string prefix; + if (full_cls == "") { + prefix = ""; + } else { + prefix = full_cls + "."; + } + if (is_void(ttype)) { + return "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) + ";"; + } +} + +string t_delphi_generator::argument_list(t_struct* tstruct) { + string result = ""; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool first = true; + t_type* tt; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += "; "; + } + + tt = (*f_iter)->get_type(); + result += input_arg_prefix(tt); // const? + result += normalize_name((*f_iter)->get_name()) + ": " + + type_name(tt, false, true, tt->is_xception(), true); + } + return result; +} + +string t_delphi_generator::constructor_argument_list(t_struct* tstruct, string current_indent) { + ostringstream result; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool first = true; + t_type* tt; + string line = ""; + string newline_indent = current_indent + " "; + + bool firstline = true; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + line += ";"; + } + + if (line.size() > 80) { + if (firstline) { + result << endl << newline_indent; + firstline = false; + } + result << line << endl; + line = newline_indent; + } else if (line.size() > 0) { + line += " "; + } + + tt = (*f_iter)->get_type(); + line += input_arg_prefix(tt); // const? + line += constructor_param_name((*f_iter)->get_name()) + ": " + + type_name(tt, false, true, tt->is_xception(), true); + } + + if (line.size() > 0) { + result << line; + } + + string result_str; + + if (firstline) { + result_str = " " + result.str(); + } else { + result_str = result.str(); + } + + return result_str; +} + +string t_delphi_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(); +} + +string t_delphi_generator::empty_value(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: + return "0"; + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + if (ansistr_binary_) { + return "''"; + } else { + return "nil"; + } + } else { + return "''"; + } + case t_base_type::TYPE_BOOL: + return "False"; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + return "0"; + case t_base_type::TYPE_DOUBLE: + return "0.0"; + } + } else if (type->is_enum()) { + return "T" + type->get_name() + "(0)"; + } else if (type->is_struct() || type->is_xception()) { + return "nil"; + } else if (type->is_map()) { + return "nil"; + } else if (type->is_set()) { + return "nil"; + } else if (type->is_list()) { + return "nil"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +void t_delphi_generator::generate_delphi_property_writer_definition(ostream& out, + t_field* tfield, + bool is_xception_class) { + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + indent(out) << "procedure Set" << prop_name(tfield, is_xception_class) + << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");" + << endl; +} + +void t_delphi_generator::generate_delphi_property_reader_definition(ostream& out, + t_field* tfield, + bool is_xception_class) { + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + indent(out) << "function Get" << prop_name(tfield, is_xception_class) << ": " + << type_name(ftype, false, true, is_xception, true) << ";" << endl; +} + +void t_delphi_generator::generate_delphi_isset_reader_definition(ostream& out, + t_field* tfield, + bool is_xception) { + indent(out) << "function Get__isset_" << prop_name(tfield, is_xception) << ": Boolean;" << endl; +} + +void t_delphi_generator::generate_delphi_clear_union_value(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class, + bool is_union, + bool is_xception_factory, + std::string xception_factory_name) { + (void)cls_prefix; + (void)name; + (void)type; + (void)is_union; + (void)is_xception_factory; + (void)xception_factory_name; + + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + indent_impl(out) << "if F__isset_" << prop_name(tfield, is_xception_class) << " then begin" + << endl; + indent_up_impl(); + indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := False;" << endl; + indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := " + << "Default( " << type_name(ftype, false, true, is_xception, true) << ");" + << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl; +} + +void t_delphi_generator::generate_delphi_property_writer_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class, + bool is_union, + bool is_xception_factory, + std::string xception_factroy_name) { + (void)type; + + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + indent_impl(out) << "procedure " << cls_prefix << name << "." + << "Set" << prop_name(tfield, is_xception_class) + << "( const Value: " << type_name(ftype, false, true, is_xception, true) << ");" + << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + if (is_union) { + indent_impl(out) << "ClearUnionValues;" << endl; + } + if (tfield->get_req() != t_field::T_REQUIRED) { + indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := True;" << endl; + } + indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := Value;" << endl; + + if (is_xception_class && (!is_xception_factory)) { + indent_impl(out) << "F" << xception_factroy_name << "." << prop_name(tfield, is_xception_class) + << " := Value;" << endl; + } + + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_property_reader_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception_class) { + (void)type; + + t_type* ftype = tfield->get_type(); + bool is_xception = ftype->is_xception(); + + indent_impl(out) << "function " << cls_prefix << name << "." + << "Get" << prop_name(tfield, is_xception_class) << ": " + << type_name(ftype, false, true, is_xception, true) << ";" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "Result := " << fieldPrefix << prop_name(tfield, is_xception_class) << ";" + << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_isset_reader_impl(ostream& out, + std::string cls_prefix, + std::string name, + t_type* type, + t_field* tfield, + std::string fieldPrefix, + bool is_xception) { + (void)type; + + string isset_name = "__isset_" + prop_name(tfield, is_xception); + indent_impl(out) << "function " << cls_prefix << name << "." + << "Get" << isset_name << ": Boolean;" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + indent_impl(out) << "Result := " << fieldPrefix << isset_name << ";" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception) { + (void)cls_prefix; + + string exception_cls_nm = type_name(tstruct, true, true); + string cls_nm = type_name(tstruct, true, false, is_exception, is_exception); + + indent_impl(out) << "function " << cls_nm << ".CreateException: " << exception_cls_nm << ";" + << endl; + + indent_impl(out) << "begin" << endl; + indent_up_impl(); + + indent_impl(out) << "Result := " << exception_cls_nm << ".Create;" << endl; + string factory_name = normalize_clsnm(tstruct->get_name(), "", true) + "Factory"; + indent_impl(out) << "Result." << factory_name << " := Self;" << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + string propname; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + propname = prop_name(*f_iter, is_exception); + if ((*f_iter)->get_req() != t_field::T_REQUIRED) { + indent_impl(out) << "if __isset_" << propname << " then" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + } + indent_impl(out) << "Result." << propname << " := " << propname << ";" << endl; + if ((*f_iter)->get_req() != t_field::T_REQUIRED) { + indent_down_impl(); + indent_impl(out) << "end;" << endl; + } + } + + indent_impl(out) << "Result.UpdateMessageProperty;" << endl; + + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception) { + + ostringstream local_vars; + ostringstream code_block; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent_impl(code_block) << "begin" << endl; + indent_up_impl(); + + indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl; + indent_impl(code_block) << "tracker := iprot.NextRecursionLevel;" << endl; + + // local bools for required fields + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(local_vars) << "_req_isset_" << prop_name(*f_iter, is_exception) << " : Boolean;" + << endl; + indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := FALSE;" + << endl; + } + } + + indent_impl(code_block) << "struc := iprot.ReadStructBegin;" << endl; + + indent_impl(code_block) << "try" << endl; + indent_up_impl(); + + indent_impl(code_block) << "while (true) do" << endl; + indent_impl(code_block) << "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; + + bool first = true; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + + if (first) { + indent_impl(code_block) << "case field_.ID of" << endl; + indent_up_impl(); + } + + first = false; + if (f_iter != fields.begin()) { + 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()) + << ") then begin" << endl; + indent_up_impl(); + + generate_deserialize_field(code_block, is_exception, *f_iter, "", local_vars); + + // required field? + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(code_block) << "_req_isset_" << prop_name(*f_iter, is_exception) << " := TRUE;" + << endl; + } + + indent_down_impl(); + + indent_impl(code_block) << "end else begin" << endl; + indent_up_impl(); + indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl; + indent_down_impl(); + indent_impl(code_block) << "end;" << endl; + indent_down_impl(); + indent_impl(code_block) << "end"; + } + + if (!first) { + code_block << endl; + indent_impl(code_block) << "else begin" << endl; + indent_up_impl(); + } + + indent_impl(code_block) << "TProtocolUtil.Skip(iprot, field_.Type_);" << endl; + + 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; + + indent_down_impl(); + + indent_impl(code_block) << "end;" << endl; + indent_down_impl(); + + indent_impl(code_block) << "finally" << endl; + indent_up_impl(); + indent_impl(code_block) << "iprot.ReadStructEnd;" << endl; + indent_down_impl(); + indent_impl(code_block) << "end;" << endl; + + // all required fields have been read? + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(code_block) << "if not _req_isset_" << prop_name(*f_iter, is_exception) << endl; + indent_impl(code_block) + << "then raise TProtocolExceptionInvalidData.Create(" + << "'required field " << prop_name(*f_iter, is_exception) << " not set');" + << 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); + + indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Read( const iprot: IProtocol);" + << endl; + indent_impl(out) << "var" << endl; + indent_up_impl(); + indent_impl(out) << "field_ : IField;" << endl; + indent_impl(out) << "struc : IStruct;" << endl; + indent_down_impl(); + out << local_vars.str() << endl; + out << code_block.str(); +} + +void t_delphi_generator::generate_delphi_struct_result_writer_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception) { + + ostringstream local_vars; + ostringstream code_block; + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent_impl(code_block) << "begin" << endl; + indent_up_impl(); + + indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl; + indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl; + + indent_impl(code_block) << "struc := TStructImpl.Create('" << name << "');" << endl; + indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl; + + if (fields.size() > 0) { + indent_impl(code_block) << "field_ := TFieldImpl.Create;" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then" + << endl; + indent_impl(code_block) << "begin" << endl; + indent_up_impl(); + indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl; + indent_impl(code_block) << "field_.Type_ := " << type_to_enum((*f_iter)->get_type()) << ";" + << endl; + indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl; + indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl; + generate_serialize_field(code_block, is_exception, *f_iter, "", local_vars); + indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl; + indent_down_impl(); + } + } + + indent_impl(code_block) << "oprot.WriteFieldStop();" << endl; + indent_impl(code_block) << "oprot.WriteStructEnd();" << 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); + + indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);" + << endl; + indent_impl(out) << "var" << endl; + indent_up_impl(); + indent_impl(out) << "struc : IStruct;" << endl; + + if (fields.size() > 0) { + indent_impl(out) << "field_ : IField;" << endl; + } + + out << local_vars.str(); + indent_down_impl(); + out << code_block.str(); +} + +void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception) { + + ostringstream local_vars; + ostringstream code_block; + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent_impl(code_block) << "begin" << endl; + indent_up_impl(); + + indent_impl(local_vars) << "tracker : IProtocolRecursionTracker;" << endl; + indent_impl(code_block) << "tracker := oprot.NextRecursionLevel;" << endl; + + indent_impl(code_block) << "struc := TStructImpl.Create('" << name << "');" << endl; + indent_impl(code_block) << "oprot.WriteStructBegin(struc);" << endl; + + if (fields.size() > 0) { + indent_impl(code_block) << "field_ := TFieldImpl.Create;" << endl; + } + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string fieldname = prop_name((*f_iter), is_exception); + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + bool is_required = ((*f_iter)->get_req() == t_field::T_REQUIRED); + bool has_isset = (!is_required); + if (is_required && null_allowed) { + null_allowed = false; + indent_impl(code_block) << "if (" << fieldname << " = nil)" << endl; + indent_impl(code_block) << "then raise TProtocolExceptionInvalidData.Create(" + << "'required field " << fieldname << " not set');" + << endl; + } + if (null_allowed) { + indent_impl(code_block) << "if (" << fieldname << " <> nil)"; + if (has_isset) { + code_block << " and __isset_" << fieldname; + } + code_block << " then begin" << endl; + indent_up_impl(); + } else { + if (has_isset) { + indent_impl(code_block) << "if (__isset_" << fieldname << ") then begin" << endl; + indent_up_impl(); + } + } + indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl; + indent_impl(code_block) << "field_.Type_ := " << type_to_enum((*f_iter)->get_type()) << ";" + << endl; + indent_impl(code_block) << "field_.ID := " << (*f_iter)->get_key() << ";" << endl; + indent_impl(code_block) << "oprot.WriteFieldBegin(field_);" << endl; + generate_serialize_field(code_block, is_exception, *f_iter, "", local_vars); + indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl; + if (null_allowed || has_isset) { + indent_down_impl(); + indent_impl(code_block) << "end;" << endl; + } + } + + indent_impl(code_block) << "oprot.WriteFieldStop();" << endl; + indent_impl(code_block) << "oprot.WriteStructEnd();" << 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); + + indent_impl(out) << "procedure " << cls_prefix << cls_nm << ".Write( const oprot: IProtocol);" + << endl; + indent_impl(out) << "var" << endl; + indent_up_impl(); + indent_impl(out) << "struc : IStruct;" << endl; + if (fields.size() > 0) { + indent_impl(out) << "field_ : IField;" << endl; + } + out << local_vars.str(); + indent_down_impl(); + out << code_block.str(); +} + +void t_delphi_generator::generate_delphi_struct_tostring_impl(ostream& out, + string cls_prefix, + t_struct* tstruct, + bool is_exception, + bool is_x_factory) { + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + string cls_nm; + + if (is_exception) { + cls_nm = type_name(tstruct, true, (!is_x_factory), is_x_factory, true); + } else { + cls_nm = type_name(tstruct, true, false); + } + + string tmp_sb = tmp("_sb"); + string tmp_first = tmp("_first"); + bool useFirstFlag = false; + + indent_impl(out) << "function " << cls_prefix << cls_nm << ".ToString: string;" << endl; + indent_impl(out) << "var" << endl; + indent_up_impl(); + indent_impl(out) << tmp_sb << " : TThriftStringBuilder;" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED); + if (is_optional) { + indent_impl(out) << tmp_first << " : Boolean;" << endl; + useFirstFlag = true; + } + break; + } + indent_down_impl(); + indent_impl(out) << "begin" << endl; + indent_up_impl(); + + indent_impl(out) << tmp_sb << " := TThriftStringBuilder.Create('(');" << endl; + indent_impl(out) << "try" << endl; + indent_up_impl(); + + if (useFirstFlag) { + indent_impl(out) << tmp_first << " := TRUE;" << endl; + } + + 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 null_allowed = type_can_be_null((*f_iter)->get_type()); + bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED); + if (null_allowed) { + indent_impl(out) << "if (" << prop_name((*f_iter), is_exception) << " <> nil)"; + if (is_optional) { + out << " and __isset_" << prop_name(*f_iter, is_exception); + } + out << " then begin" << endl; + indent_up_impl(); + } else { + if (is_optional) { + indent_impl(out) << "if (__isset_" << prop_name(*f_iter, is_exception) << ") then begin" + << endl; + indent_up_impl(); + } + } + + if (useFirstFlag && (!had_required)) { + indent_impl(out) << "if not " << tmp_first << " then " << tmp_sb << ".Append(',');" << endl; + if (is_optional) { + indent_impl(out) << tmp_first << " := FALSE;" << endl; + } + indent_impl(out) << tmp_sb << ".Append('" << prop_name((*f_iter), is_exception) << ": ');" + << endl; + } else { + indent_impl(out) << tmp_sb << ".Append(', " << prop_name((*f_iter), is_exception) << ": ');" + << endl; + } + + t_type* ttype = (*f_iter)->get_type(); + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_xception() || ttype->is_struct()) { + indent_impl(out) << "if (" << prop_name((*f_iter), is_exception) << " = nil) then " << tmp_sb + << ".Append('') else " << tmp_sb << ".Append(" + << prop_name((*f_iter), is_exception) << ".ToString());" << endl; + } else if (ttype->is_enum()) { + indent_impl(out) << tmp_sb << ".Append(Integer(" << prop_name((*f_iter), is_exception) + << "));" << endl; + } else { + indent_impl(out) << tmp_sb << ".Append(" << prop_name((*f_iter), is_exception) << ");" + << endl; + } + + if (null_allowed || is_optional) { + indent_down_impl(); + indent_impl(out) << "end;" << endl; + } + + if (!is_optional) { + had_required = true; // now __first must be false, so we don't need to check it anymore + } + } + + indent_impl(out) << tmp_sb << ".Append(')');" << endl; + indent_impl(out) << "Result := " << tmp_sb << ".ToString;" << endl; + if (useFirstFlag) { + indent_impl(out) << "if " << tmp_first << " then {prevent warning};" << endl; + } + + indent_down_impl(); + indent_impl(out) << "finally" << endl; + indent_up_impl(); + indent_impl(out) << tmp_sb << ".Free;" << endl; + indent_down_impl(); + indent_impl(out) << "end;" << endl; + + indent_down_impl(); + indent_impl(out) << "end;" << endl << endl; +} + +bool t_delphi_generator::is_void(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(); + if (tbase == t_base_type::TYPE_VOID) { + return true; + } + } + return false; +} + +THRIFT_REGISTER_GENERATOR( + delphi, + "delphi", + " ansistr_binary: Use AnsiString for binary datatype (default is TBytes).\n" + " register_types: Enable TypeRegistry, allows for creation of struct, union\n" + " and container instances by interface or TypeInfo()\n" + " constprefix: Name TConstants classes after IDL to reduce ambiguities\n" + " events: Enable and use processing events in the generated code.\n" + " xmldoc: Enable XMLDoc comments for Help Insight etc.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc new file mode 100644 index 00000000..48694144 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_erl_generator.cc @@ -0,0 +1,1169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/version.h" +#include "thrift/generate/t_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; + +static const std::string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * Erlang code generator. + * + */ +class t_erl_generator : public t_generator { +public: + t_erl_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; + + legacy_names_ = false; + maps_ = false; + otp16_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("legacynames") == 0) { + legacy_names_ = true; + } else if( iter->first.compare("maps") == 0) { + maps_ = true; + } else if( iter->first.compare("otp16") == 0) { + otp16_ = true; + } else { + throw "unknown option erl:" + iter->first; + } + } + + if (maps_ && otp16_) { + throw "argument error: Cannot specify both maps and otp16; maps are not available for Erlang/OTP R16 or older"; + } + + out_dir_base_ = "gen-erl"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + void generate_member_type(std::ostream& out, t_type* type); + void generate_member_value(std::ostream& out, t_type* type, t_const_value* value); + + std::string render_member_type(t_field* field); + std::string render_member_value(t_field* field); + std::string render_member_requiredness(t_field* field); + + // std::string render_default_value(t_type* type); + std::string render_default_value(t_field* field); + std::string render_const_value(t_type* type, t_const_value* value); + std::string render_type_term(t_type* ttype, bool expand_structs, bool extended_info = false); + + /** + * Struct generation code + */ + + void generate_erl_struct(t_struct* tstruct, bool is_exception); + void generate_erl_struct_definition(std::ostream& out, t_struct* tstruct); + void generate_erl_struct_member(std::ostream& out, t_field* tmember); + void generate_erl_struct_info(std::ostream& out, t_struct* tstruct); + void generate_erl_extended_struct_info(std::ostream& out, t_struct* tstruct); + void generate_erl_function_helpers(t_function* tfunction); + void generate_type_metadata(std::string function_name, vector names); + void generate_enum_info(t_enum* tenum); + void generate_enum_metadata(); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_metadata(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_function_info(t_service* tservice, t_function* tfunction); + + /** + * Helper rendering functions + */ + + std::string erl_autogen_comment(); + std::string erl_imports(); + std::string render_includes(); + std::string type_name(t_type* ttype); + + 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 type_module(t_type* ttype); + + std::string make_safe_for_module_name(std::string in) { + if (legacy_names_) { + return decapitalize(in); + } else { + return underscore(in); + } + } + + std::string atomify(std::string in) { + if (legacy_names_) { + return "'" + decapitalize(in) + "'"; + } else { + return "'" + in + "'"; + } + } + + std::string constify(std::string in) { + if (legacy_names_) { + return capitalize(in); + } else { + return uppercase(in); + } + } + + static std::string comment(string in); + +private: + bool has_default_value(t_field*); + + /* if true retain pre 0.9.2 naming scheme for functions, atoms and consts */ + bool legacy_names_; + + /* if true use maps instead of dicts in generated code */ + bool maps_; + + /* if true use non-namespaced dict and set instead of dict:dict and sets:set */ + bool otp16_; + + /** + * add function to export list + */ + + void export_function(t_function* tfunction, std::string prefix = ""); + void export_string(std::string name, int num); + + void export_types_function(t_function* tfunction, std::string prefix = ""); + void export_types_string(std::string name, int num); + + /** + * write out headers and footers for hrl files + */ + + void hrl_header(std::ostream& out, std::string name); + void hrl_footer(std::ostream& out, std::string name); + + /** + * stuff to spit out at the top of generated files + */ + + bool export_lines_first_; + std::ostringstream export_lines_; + + bool export_types_lines_first_; + std::ostringstream export_types_lines_; + + /** + * File streams + */ + + std::ostringstream f_info_; + std::ostringstream f_info_ext_; + + std::ofstream f_types_file_; + std::ofstream f_types_hrl_file_; + + std::ofstream f_consts_; + std::ostringstream f_service_; + std::ofstream f_service_file_; + std::ofstream f_service_hrl_; + + /** + * Metadata containers + */ + std::vector v_struct_names_; + std::vector v_enum_names_; + std::vector v_exception_names_; + std::vector v_enums_; +}; + +/** + * UI for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_erl_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // setup export lines + export_lines_first_ = true; + export_types_lines_first_ = true; + + // types files + string f_types_name = get_out_dir() + make_safe_for_module_name(program_name_) + "_types.erl"; + string f_types_hrl_name = get_out_dir() + make_safe_for_module_name(program_name_) + "_types.hrl"; + + f_types_file_.open(f_types_name.c_str()); + f_types_hrl_file_.open(f_types_hrl_name.c_str()); + + hrl_header(f_types_hrl_file_, make_safe_for_module_name(program_name_) + "_types"); + + f_types_file_ << erl_autogen_comment() << endl << "-module(" + << make_safe_for_module_name(program_name_) << "_types)." << endl << erl_imports() + << endl; + + f_types_file_ << "-include(\"" << make_safe_for_module_name(program_name_) << "_types.hrl\")." + << endl << endl; + + f_types_hrl_file_ << render_includes() << endl; + + // consts file + string f_consts_name = get_out_dir() + make_safe_for_module_name(program_name_) + + "_constants.hrl"; + f_consts_.open(f_consts_name.c_str()); + + f_consts_ << erl_autogen_comment() << endl << erl_imports() << endl << "-include(\"" + << make_safe_for_module_name(program_name_) << "_types.hrl\")." << endl << endl; +} + +/** + * 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)." + << endl; +} + +void t_erl_generator::hrl_footer(ostream& out, string name) { + (void)name; + out << "-endif." << endl; +} + +/** + * Renders all the imports necessary for including another Thrift program + */ +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()) + + "_types.hrl\").\n"; + } + if (includes.size() > 0) { + result += "\n"; + } + return result; +} + +/** + * Autogen'd comment + */ +string t_erl_generator::erl_autogen_comment() { + 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"; +} + +/** + * Comment out text + */ + +string t_erl_generator::comment(string in) { + size_t pos = 0; + in.insert(pos, "%% "); + while ((pos = in.find_first_of('\n', pos)) != string::npos) { + in.insert(++pos, "%% "); + } + return in; +} + +/** + * Prints standard thrift imports + */ +string t_erl_generator::erl_imports() { + return ""; +} + +/** + * Closes the type files + */ +void t_erl_generator::close_generator() { + + export_types_string("struct_info", 1); + export_types_string("struct_info_ext", 1); + export_types_string("enum_info", 1); + export_types_string("enum_names", 0); + export_types_string("struct_names", 0); + export_types_string("exception_names", 0); + + f_types_file_ << "-export([" << export_types_lines_.str() << "])." << endl << endl; + + f_types_file_ << f_info_.str(); + f_types_file_ << "struct_info(_) -> erlang:error(function_clause)." << endl << endl; + + f_types_file_ << f_info_ext_.str(); + f_types_file_ << "struct_info_ext(_) -> erlang:error(function_clause)." << endl << endl; + + generate_type_metadata("struct_names", v_struct_names_); + generate_enum_metadata(); + generate_type_metadata("enum_names", v_enum_names_); + generate_type_metadata("exception_names", v_exception_names_); + + hrl_footer(f_types_hrl_file_, string("BOGUS")); + + f_types_file_.close(); + f_types_hrl_file_.close(); + f_consts_.close(); +} + +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"; + indent_up(); + indent(f_types_file_) << "["; + + + for(size_t i=0; i < num_structs; i++) { + f_types_file_ << names.at(i); + + if (i < num_structs - 1) { + f_types_file_ << ", "; + } + } + + f_types_file_ << "].\n\n"; + indent_down(); +} + +/** + * Generates a typedef. no op + * + * @param ttypedef The type definition + */ +void t_erl_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_erl_generator::generate_enum(t_enum* tenum) { + vector constants = tenum->get_constants(); + vector::iterator c_iter; + + v_enums_.push_back(tenum); + v_enum_names_.push_back(atomify(tenum->get_name())); + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + string name = (*c_iter)->get_name(); + indent(f_types_hrl_file_) << "-define(" << constify(make_safe_for_module_name(program_name_)) + << "_" << constify(tenum->get_name()) << "_" << constify(name) << ", " + << value << ")." << endl; + } + + f_types_hrl_file_ << endl; +} + +void t_erl_generator::generate_enum_info(t_enum* tenum){ + vector constants = tenum->get_constants(); + size_t num_constants = constants.size(); + + indent(f_types_file_) << "enum_info(" << atomify(tenum->get_name()) << ") ->\n"; + indent_up(); + indent(f_types_file_) << "[\n"; + + for(size_t i=0; i < num_constants; i++) { + indent_up(); + t_enum_value* value = constants.at(i); + indent(f_types_file_) << "{" << atomify(value->get_name()) << ", " << value->get_value() << "}"; + + if (i < num_constants - 1) { + f_types_file_ << ",\n"; + } + indent_down(); + } + f_types_file_ << "\n"; + indent(f_types_file_) << "];\n\n"; + indent_down(); +} + +void t_erl_generator::generate_enum_metadata() { + size_t enum_count = v_enums_.size(); + + for(size_t i=0; i < enum_count; i++) { + t_enum* tenum = v_enums_.at(i); + generate_enum_info(tenum); + } + + indent(f_types_file_) << "enum_info(_) -> erlang:error(function_clause).\n\n"; +} + +/** + * Generate a constant value + */ +void t_erl_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + f_consts_ << "-define(" << constify(make_safe_for_module_name(program_name_)) << "_" + << constify(name) << ", " << render_const_value(type, value) << ")." << endl << endl; +} + +/** + * 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 + */ +string t_erl_generator::render_const_value(t_type* type, t_const_value* value) { + type = get_true_type(type); + std::ostringstream out; + + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + indent(out) << value->get_integer(); + + } else if (type->is_struct() || type->is_xception()) { + out << "#" << type_name(type) << "{"; + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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) { + 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) { + first = false; + } else { + out << ","; + } + out << v_iter->first->get_string(); + out << " = "; + out << render_const_value(field_type, v_iter->second); + } + indent_down(); + indent(out) << "}"; + + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + + if (maps_) { + out << "maps:from_list(["; + } else { + out << "dict:from_list(["; + } + map::const_iterator i, end = value->get_map().end(); + for (i = value->get_map().begin(); i != end;) { + out << "{" << render_const_value(ktype, i->first) << "," + << render_const_value(vtype, i->second) << "}"; + if (++i != end) { + out << ","; + } + } + out << "])"; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + out << "sets:from_list(["; + vector::const_iterator i, end = value->get_list().end(); + for (i = value->get_list().begin(); i != end;) { + out << render_const_value(etype, *i); + if (++i != end) { + out << ","; + } + } + out << "])"; + } else if (type->is_list()) { + t_type* etype; + etype = ((t_list*)type)->get_elem_type(); + out << "["; + + bool first = true; + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + if (first) { + first = false; + } else { + out << ","; + } + out << render_const_value(etype, *v_iter); + } + out << "]"; + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + return out.str(); +} + +string t_erl_generator::render_default_value(t_field* field) { + t_type* type = field->get_type(); + if (type->is_struct() || type->is_xception()) { + return "#" + type_name(type) + "{}"; + } else if (type->is_map()) { + if (maps_) { + return "#{}"; + } else { + return "dict:new()"; + } + } else if (type->is_set()) { + return "sets:new()"; + } else if (type->is_list()) { + return "[]"; + } else { + return "undefined"; + } +} + +string t_erl_generator::render_member_type(t_field* field) { + t_type* type = get_true_type(field->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_STRING: + return "string() | binary()"; + case t_base_type::TYPE_BOOL: + return "boolean()"; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + return "integer()"; + case t_base_type::TYPE_DOUBLE: + return "float()"; + default: + throw "compiler error: unsupported base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + return "integer()"; + } else if (type->is_struct() || type->is_xception()) { + return type_name(type) + "()"; + } else if (type->is_map()) { + if (maps_) { + return "#{}"; + } else if (otp16_) { + return "dict()"; + } else { + return "dict:dict()"; + } + } else if (type->is_set()) { + if (otp16_) { + return "set()"; + } else { + return "sets:set()"; + } + } else if (type->is_list()) { + return "list()"; + } else { + throw "compiler error: unsupported type " + type->get_name(); + } +} + +string t_erl_generator::render_member_requiredness(t_field* field) { + switch (field->get_req()) { + case t_field::T_REQUIRED: + return "required"; + case t_field::T_OPTIONAL: + return "optional"; + default: + return "undefined"; + } +} + +/** + * Generates a struct + */ +void t_erl_generator::generate_struct(t_struct* tstruct) { + v_struct_names_.push_back(type_name(tstruct)); + generate_erl_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_erl_generator::generate_xception(t_struct* txception) { + v_exception_names_.push_back(type_name(txception)); + generate_erl_struct(txception, true); +} + +/** + * Generates a struct + */ +void t_erl_generator::generate_erl_struct(t_struct* tstruct, bool is_exception) { + (void)is_exception; + generate_erl_struct_definition(f_types_hrl_file_, tstruct); + generate_erl_struct_info(f_info_, tstruct); + generate_erl_extended_struct_info(f_info_ext_, tstruct); +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_erl_generator::generate_erl_struct_definition(ostream& out, t_struct* tstruct) { + indent(out) << "%% struct " << type_name(tstruct) << endl << endl; + + std::stringstream buf; + buf << indent() << "-record(" << type_name(tstruct) << ", {"; + string field_indent(buf.str().size(), ' '); + + const vector& members = tstruct->get_members(); + for (vector::const_iterator m_iter = members.begin(); m_iter != members.end();) { + generate_erl_struct_member(buf, *m_iter); + if (++m_iter != members.end()) { + buf << "," << endl << field_indent; + } + } + buf << "})."; + + out << buf.str() << endl; + out << "-type " + type_name(tstruct) << "() :: #" + type_name(tstruct) + "{}." << endl << endl; +} + +/** + * Generates the record field definition + */ + +void t_erl_generator::generate_erl_struct_member(ostream& out, t_field* tmember) { + out << atomify(tmember->get_name()); + if (has_default_value(tmember)) + out << " = " << render_member_value(tmember); + out << " :: " << render_member_type(tmember); +} + +bool t_erl_generator::has_default_value(t_field* field) { + t_type* type = field->get_type(); + if (!field->get_value()) { + if (field->get_req() == t_field::T_REQUIRED) { + if (type->is_struct() || type->is_xception() || type->is_map() || type->is_set() + || type->is_list()) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return true; + } +} + +string t_erl_generator::render_member_value(t_field* field) { + if (!field->get_value()) { + return render_default_value(field); + } else { + return render_const_value(field->get_type(), field->get_value()); + } +} + +/** + * Generates the read method for a struct + */ +void t_erl_generator::generate_erl_struct_info(ostream& out, t_struct* tstruct) { + indent(out) << "struct_info(" << type_name(tstruct) << ") ->" << endl; + indent_up(); + out << indent() << render_type_term(tstruct, true) << ";" << endl; + indent_down(); + out << endl; +} + +void t_erl_generator::generate_erl_extended_struct_info(ostream& out, t_struct* tstruct) { + indent(out) << "struct_info_ext(" << type_name(tstruct) << ") ->" << endl; + indent_up(); + out << indent() << render_type_term(tstruct, true, true) << ";" << endl; + indent_down(); + out << endl; +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_erl_generator::generate_service(t_service* tservice) { + service_name_ = make_safe_for_module_name(service_name_); + + string f_service_hrl_name = get_out_dir() + service_name_ + "_thrift.hrl"; + string f_service_name = get_out_dir() + service_name_ + "_thrift.erl"; + f_service_file_.open(f_service_name.c_str()); + f_service_hrl_.open(f_service_hrl_name.c_str()); + + // Reset service text aggregating stream streams + f_service_.str(""); + export_lines_.str(""); + export_lines_first_ = true; + + hrl_header(f_service_hrl_, service_name_); + + if (tservice->get_extends() != NULL) { + f_service_hrl_ << "-include(\"" + << make_safe_for_module_name(tservice->get_extends()->get_name()) + << "_thrift.hrl\"). % inherit " << endl; + } + + f_service_hrl_ << "-include(\"" << make_safe_for_module_name(program_name_) << "_types.hrl\")." + << endl << endl; + + // Generate the three main parts of the service (well, two for now in PHP) + generate_service_helpers(tservice); // cpiro: New Erlang Order + + generate_service_interface(tservice); + + generate_service_metadata(tservice); + + // indent_down(); + + f_service_file_ << erl_autogen_comment() << endl << "-module(" << service_name_ << "_thrift)." + << endl << "-behaviour(thrift_service)." << endl << endl << erl_imports() << endl; + + f_service_file_ << "-include(\"" << make_safe_for_module_name(tservice->get_name()) + << "_thrift.hrl\")." << endl << endl; + + f_service_file_ << "-export([" << export_lines_.str() << "])." << endl << endl; + + f_service_file_ << f_service_.str(); + + hrl_footer(f_service_hrl_, f_service_name); + + // Close service file + f_service_file_.close(); + f_service_hrl_.close(); +} + +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; + indent_up(); + indent(f_service_) << "["; + + for (size_t i=0; i < num_functions; i++) { + t_function* current = functions.at(i); + f_service_ << atomify(current->get_name()); + if (i < num_functions - 1) { + f_service_ << ", "; + } + } + + f_service_ << "].\n\n"; + indent_down(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_erl_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // indent(f_service_) << + // "% HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + export_string("struct_info", 1); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_erl_function_helpers(*f_iter); + } + f_service_ << "struct_info(_) -> erlang:error(function_clause)." << endl; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_erl_generator::generate_erl_function_helpers(t_function* tfunction) { + (void)tfunction; +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_erl_generator::generate_service_interface(t_service* tservice) { + + export_string("function_info", 2); + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + f_service_ << "%%% interface" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "% " << function_signature(*f_iter) << endl; + + generate_function_info(tservice, *f_iter); + } + + // Inheritance - pass unknown functions to base class + if (tservice->get_extends() != NULL) { + indent(f_service_) << "function_info(Function, InfoType) ->" << endl; + indent_up(); + indent(f_service_) << make_safe_for_module_name(tservice->get_extends()->get_name()) + << "_thrift:function_info(Function, InfoType)." << endl; + indent_down(); + } else { + // return function_clause error for non-existent functions + indent(f_service_) << "function_info(_Func, _Info) -> erlang:error(function_clause)." << endl; + } + + indent(f_service_) << endl; +} + +/** + * Generates a function_info(FunctionName, params_type) and + * function_info(FunctionName, reply_type) + */ +void t_erl_generator::generate_function_info(t_service* tservice, t_function* tfunction) { + (void)tservice; + string name_atom = atomify(tfunction->get_name()); + + t_struct* xs = tfunction->get_xceptions(); + t_struct* arg_struct = tfunction->get_arglist(); + + // function_info(Function, params_type): + indent(f_service_) << "function_info(" << name_atom << ", params_type) ->" << endl; + indent_up(); + + indent(f_service_) << render_type_term(arg_struct, true) << ";" << endl; + + indent_down(); + + // function_info(Function, reply_type): + indent(f_service_) << "function_info(" << name_atom << ", reply_type) ->" << endl; + indent_up(); + + if (!tfunction->get_returntype()->is_void()) + indent(f_service_) << render_type_term(tfunction->get_returntype(), false) << ";" << endl; + else if (tfunction->is_oneway()) + indent(f_service_) << "oneway_void;" << endl; + else + indent(f_service_) << "{struct, []}" + << ";" << endl; + indent_down(); + + // function_info(Function, exceptions): + indent(f_service_) << "function_info(" << name_atom << ", exceptions) ->" << endl; + indent_up(); + indent(f_service_) << render_type_term(xs, true) << ";" << endl; + indent_down(); +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_erl_generator::function_signature(t_function* tfunction, string prefix) { + return prefix + tfunction->get_name() + "(This" + + capitalize(argument_list(tfunction->get_arglist())) + ")"; +} + +/** + * Add a function to the exports list + */ +void t_erl_generator::export_string(string name, int num) { + if (export_lines_first_) { + export_lines_first_ = false; + } else { + export_lines_ << ", "; + } + export_lines_ << name << "/" << num; +} + +void t_erl_generator::export_types_function(t_function* tfunction, string prefix) { + + export_types_string(prefix + tfunction->get_name(), + 1 // This + + ((tfunction->get_arglist())->get_members()).size()); +} + +void t_erl_generator::export_types_string(string name, int num) { + if (export_types_lines_first_) { + export_types_lines_first_ = false; + } else { + export_types_lines_ << ", "; + } + export_types_lines_ << name << "/" << num; +} + +void t_erl_generator::export_function(t_function* tfunction, string prefix) { + + export_string(prefix + tfunction->get_name(), + 1 // This + + ((tfunction->get_arglist())->get_members()).size()); +} + +/** + * Renders a field list + */ +string t_erl_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; + result += ", "; // initial comma to compensate for initial This + } else { + result += ", "; + } + result += capitalize((*f_iter)->get_name()); + } + return result; +} + +string t_erl_generator::type_name(t_type* ttype) { + string prefix = ""; + string erl_namespace = ttype->get_program()->get_namespace("erl"); + + if (erl_namespace.length() > 0) { + prefix = erl_namespace + "."; + } + + string name = ttype->get_name(); + + if (ttype->is_struct() || ttype->is_xception() || ttype->is_service()) { + name = ttype->get_name(); + } + + return atomify(prefix + name); +} + +/** + * Converts the parse type to a Erlang "type" (macro for int constants) + */ +string t_erl_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 "?tType_STRING"; + case t_base_type::TYPE_BOOL: + return "?tType_BOOL"; + case t_base_type::TYPE_I8: + return "?tType_I8"; + 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(); +} + +/** + * Generate an Erlang term which represents a thrift type + */ +std::string t_erl_generator::render_type_term(t_type* type, + bool expand_structs, + bool extended_info) { + 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 "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_I8: + return "byte"; + case t_base_type::TYPE_I16: + return "i16"; + case t_base_type::TYPE_I32: + return "i32"; + case t_base_type::TYPE_I64: + return "i64"; + case t_base_type::TYPE_DOUBLE: + return "double"; + } + } else if (type->is_enum()) { + return "i32"; + } else if (type->is_struct() || type->is_xception()) { + if (expand_structs) { + + std::stringstream buf; + buf << "{struct, ["; + string field_indent(buf.str().size(), ' '); + + t_struct::members_type const& fields = static_cast(type)->get_members(); + t_struct::members_type::const_iterator i, end = fields.end(); + for (i = fields.begin(); i != end;) { + t_struct::members_type::value_type member = *i; + int32_t key = member->get_key(); + string type = render_type_term(member->get_type(), false, false); // recursive call + + if (!extended_info) { + // Convert to format: {struct, [{Fid, Type}|...]} + buf << "{" << key << ", " << type << "}"; + } else { + // Convert to format: {struct, [{Fid, Req, Type, Name, Def}|...]} + string name = member->get_name(); + string value = render_member_value(member); + string requiredness = render_member_requiredness(member); + buf << "{" << key << ", " << requiredness << ", " << type << ", " << atomify(name) << ", " + << value << "}"; + } + + if (++i != end) { + buf << "," << endl << field_indent; + } + } + + buf << "]}" << endl; + return buf.str(); + } else { + return "{struct, {" + atomify(type_module(type)) + ", " + type_name(type) + "}}"; + } + } else if (type->is_map()) { + // {map, KeyType, ValType} + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + + return "{map, " + render_type_term(key_type, false) + ", " + render_type_term(val_type, false) + + "}"; + + } else if (type->is_set()) { + t_type* elem_type = ((t_set*)type)->get_elem_type(); + + return "{set, " + render_type_term(elem_type, false) + "}"; + + } else if (type->is_list()) { + t_type* elem_type = ((t_list*)type)->get_elem_type(); + + return "{list, " + render_type_term(elem_type, false) + "}"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +std::string t_erl_generator::type_module(t_type* ttype) { + return make_safe_for_module_name(ttype->get_program()->get_name()) + "_types"; +} + +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") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.cc new file mode 100644 index 00000000..0c1f49da --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.cc @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "thrift/generate/t_generator.h" +using namespace std; + +/** + * Top level program generation function. Calls the generator subclass methods + * for preparing file streams etc. then iterates over all the parts of the + * program to perform the correct actions. + * + * @param program The thrift program to compile into C++ source + */ +void t_generator::generate_program() { + // Initialize the generator + init_generator(); + + // 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); + } + + // 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); + } + + // Generate structs, exceptions, and unions in declared order + vector objects = program_->get_objects(); + + vector::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + generate_forward_declaration(*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); + } + } + + // Generate constants + vector consts = program_->get_consts(); + generate_consts(consts); + + // 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); + } + + // Close the generator + close_generator(); +} + +string t_generator::escape_string(const string& in) const { + string result = ""; + for (string::const_iterator it = in.begin(); it < in.end(); it++) { + std::map::const_iterator res = escape_.find(*it); + if (res != escape_.end()) { + result.append(res->second); + } else { + result.push_back(*it); + } + } + return result; +} + +void t_generator::generate_consts(vector consts) { + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + generate_const(*c_iter); + } +} + +void t_generator::generate_docstring_comment(ostream& out, + const string& comment_start, + const string& line_prefix, + const string& contents, + const string& comment_end) { + if (comment_start != "") + indent(out) << comment_start; + stringstream docs(contents, ios_base::in); + while (!(docs.eof() || docs.fail())) { + char line[1024]; + docs.getline(line, 1024); + + // Just prnt a newline when the line & prefix are empty. + if (strlen(line) == 0 && line_prefix == "" && !docs.eof()) { + out << std::endl; + } else if (strlen(line) > 0 || !docs.eof()) { // skip the empty last line + indent(out) << line_prefix << line << std::endl; + } + } + if (comment_end != "") + indent(out) << comment_end; +} + +void t_generator_registry::register_generator(t_generator_factory* factory) { + gen_map_t& the_map = get_generator_map(); + if (the_map.find(factory->get_short_name()) != the_map.end()) { + failure("Duplicate generators for language \"%s\"!\n", factory->get_short_name().c_str()); + } + the_map[factory->get_short_name()] = factory; +} + +void t_generator::parse_options(const string& options, + string& language, + map& parsed_options) { + string::size_type colon = options.find(':'); + language = options.substr(0, colon); + + if (colon != string::npos) { + string::size_type pos = colon + 1; + while (pos != string::npos && pos < options.size()) { + string::size_type next_pos = options.find(',', pos); + string option = options.substr(pos, next_pos - pos); + pos = ((next_pos == string::npos) ? next_pos : next_pos + 1); + + string::size_type separator = option.find('='); + string key, value; + if (separator == string::npos) { + key = option; + value = ""; + } else { + key = option.substr(0, separator); + value = option.substr(separator + 1); + } + + parsed_options[key] = value; + } + } +} + +t_generator* t_generator_registry::get_generator(t_program* program, + const string& language, + const map& parsed_options, + const std::string& options) { + gen_map_t& the_map = get_generator_map(); + gen_map_t::iterator iter = the_map.find(language); + + if (iter == the_map.end()) { + return NULL; + } + + return iter->second->get_generator(program, parsed_options, options); +} + +t_generator* t_generator_registry::get_generator(t_program* program, const string& options) { + string language; + map parsed_options; + t_generator::parse_options(options, language, parsed_options); + return get_generator(program, language, parsed_options, options); +} + +t_generator_registry::gen_map_t& t_generator_registry::get_generator_map() { + // http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 + static gen_map_t* the_map = new gen_map_t(); + return *the_map; +} + +t_generator_factory::t_generator_factory(const std::string& short_name, + const std::string& long_name, + const std::string& documentation) + : short_name_(short_name), long_name_(long_name), documentation_(documentation) { + t_generator_registry::register_generator(this); +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.h new file mode 100644 index 00000000..051bbc42 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator.h @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_GENERATOR_H +#define T_GENERATOR_H + +#include +#include +#include +#include +#include "thrift/common.h" +#include "thrift/version.h" +#include "thrift/generate/t_generator_registry.h" +#include "thrift/parse/t_program.h" + +/** + * Base class for a thrift code generator. This class defines the basic + * routines for code generation and contains the top level method that + * dispatches code generation across various components. + * + */ +class t_generator { +public: + t_generator(t_program* program) { + tmp_ = 0; + indent_ = 0; + program_ = program; + program_name_ = get_program_name(program); + escape_['\n'] = "\\n"; + escape_['\r'] = "\\r"; + escape_['\t'] = "\\t"; + escape_['"'] = "\\\""; + escape_['\\'] = "\\\\"; + } + + virtual ~t_generator() {} + + /** + * Framework generator method that iterates over all the parts of a program + * and performs general actions. This is implemented by the base class and + * should not normally be overwritten in the subclasses. + */ + virtual void generate_program(); + + const t_program* get_program() const { return program_; } + + void generate_docstring_comment(std::ostream& out, + const std::string& comment_start, + const std::string& line_prefix, + const std::string& contents, + const std::string& comment_end); + + static void parse_options(const std::string& options, std::string& language, + std::map& parsed_options); + + /** + * check whether sub-namespace declaraction is used by generator. + * e.g. allow + * namespace py.twisted bar + * to specify namespace to use when -gen py:twisted is specified. + * Will be called with subnamespace, i.e. is_valid_namespace("twisted") + * will be called for the above example. + */ + static bool is_valid_namespace(const std::string& sub_namespace) { + (void)sub_namespace; + return false; + } + + /** + * Escape string to use one in generated sources. + */ + virtual std::string escape_string(const std::string& in) const; + + std::string get_escaped_string(t_const_value* constval) { + return escape_string(constval->get_string()); + } + +protected: + /** + * Optional methods that may be imlemented by subclasses to take necessary + * steps at the beginning or end of code generation. + */ + + virtual void init_generator() {} + virtual void close_generator() {} + + virtual void generate_consts(std::vector consts); + + /** + * Pure virtual methods implemented by the generator subclasses. + */ + + virtual void generate_typedef(t_typedef* ttypedef) = 0; + virtual void generate_enum(t_enum* tenum) = 0; + virtual void generate_const(t_const* tconst) { (void)tconst; } + virtual void generate_struct(t_struct* tstruct) = 0; + virtual void generate_service(t_service* tservice) = 0; + virtual void generate_forward_declaration(t_struct*) {} + virtual void generate_xception(t_struct* txception) { + // By default exceptions are the same as structs + generate_struct(txception); + } + + /** + * Method to get the program name, may be overridden + */ + virtual std::string get_program_name(t_program* tprogram) { return tprogram->get_name(); } + + /** + * Method to get the service name, may be overridden + */ + virtual std::string get_service_name(t_service* tservice) { return tservice->get_name(); } + + /** + * Get the current output directory + */ + virtual std::string get_out_dir() const { + if (program_->is_out_path_absolute()) { + return program_->get_out_path() + "/"; + } + + return program_->get_out_path() + out_dir_base_ + "/"; + } + + /** + * Creates a unique temporary variable name, which is just "name" with a + * number appended to it (i.e. name35) + */ + std::string tmp(std::string name) { + std::ostringstream out; + out << name << tmp_++; + return out.str(); + } + + /** + * Generates a comment about this code being autogenerated, using C++ style + * comments, which are also fair game in Java / PHP, yay! + * + * @return C-style comment mentioning that this file is autogenerated. + */ + virtual std::string autogen_comment() { + return std::string("/**\n") + " * " + autogen_summary() + "\n" + " *\n" + + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + + " * @generated\n" + " */\n"; + } + + virtual std::string autogen_summary() { + return std::string("Autogenerated by Thrift Compiler (") + THRIFT_VERSION + ")"; + } + + /** + * Indentation level modifiers + */ + + void indent_up() { ++indent_; } + + void indent_down() { --indent_; } + + /** + * Indentation print function + */ + std::string indent() { + std::string ind = ""; + int i; + for (i = 0; i < indent_; ++i) { + ind += indent_str(); + } + return ind; + } + + /** + * Indentation utility wrapper + */ + std::ostream& indent(std::ostream& os) { return os << indent(); } + + /** + * Capitalization helpers + */ + std::string capitalize(std::string in) { + in[0] = toupper(in[0]); + return in; + } + std::string decapitalize(std::string in) { + in[0] = tolower(in[0]); + return in; + } + static std::string lowercase(std::string in) { + for (size_t i = 0; i < in.size(); ++i) { + in[i] = tolower(in[i]); + } + return in; + } + static std::string uppercase(std::string in) { + for (size_t i = 0; i < in.size(); ++i) { + in[i] = toupper(in[i]); + } + return in; + } + /** + * Transforms a camel case string to an equivalent one separated by underscores + * e.g. aMultiWord -> a_multi_word + * someName -> some_name + * CamelCase -> camel_case + * name -> name + * Name -> name + */ + std::string underscore(std::string in) { + in[0] = tolower(in[0]); + for (size_t i = 1; i < in.size(); ++i) { + if (isupper(in[i])) { + in[i] = tolower(in[i]); + in.insert(i, "_"); + } + } + return in; + } + /** + * Transforms a string with words separated by underscores to a camel case equivalent + * e.g. a_multi_word -> aMultiWord + * some_name -> someName + * name -> name + */ + std::string camelcase(std::string in) { + std::ostringstream out; + bool underscore = false; + + for (size_t i = 0; i < in.size(); i++) { + if (in[i] == '_') { + underscore = true; + continue; + } + if (underscore) { + out << (char)toupper(in[i]); + underscore = false; + continue; + } + out << in[i]; + } + + return out.str(); + } + +public: + /** + * Get the true type behind a series of typedefs. + */ + static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); } + static t_type* get_true_type(t_type* type) { return type->get_true_type(); } + +protected: + /** + * The program being generated + */ + t_program* program_; + + /** + * Quick accessor for formatted program name that is currently being + * generated. + */ + std::string program_name_; + + /** + * Quick accessor for formatted service name that is currently being + * generated. + */ + std::string service_name_; + + /** + * Output type-specifc directory name ("gen-*") + */ + std::string out_dir_base_; + + /** + * Map of characters to escape in string literals. + */ + std::map escape_; + + virtual std::string indent_str() const { + return " "; + } + +private: + /** + * Current code indentation level + */ + int indent_; + + /** + * Temporary variable counter, for making unique variable names + */ + int tmp_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h new file mode 100644 index 00000000..1f02167b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_generator_registry.h @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_GENERATOR_REGISTRY_H +#define T_GENERATOR_REGISTRY_H + +class t_generator; + +/** + * A factory for producing generator classes of a particular language. + * + * This class is also responsible for: + * - Registering itself with the generator registry. + * - Providing documentation for the generators it produces. + */ +class t_generator_factory { +public: + t_generator_factory(const std::string& short_name, + const std::string& long_name, + const std::string& documentation); + + virtual ~t_generator_factory() {} + + virtual t_generator* get_generator( + // The program to generate. + t_program* program, + // Note: parsed_options will not exist beyond the call to get_generator. + const std::map& parsed_options, + // Note: option_string might not exist beyond the call to get_generator. + const std::string& option_string) = 0; + + virtual bool is_valid_namespace(const std::string& sub_namespace) = 0; + + std::string get_short_name() { return short_name_; } + std::string get_long_name() { return long_name_; } + std::string get_documentation() { return documentation_; } + +private: + std::string short_name_; + std::string long_name_; + std::string documentation_; +}; + +template +class t_generator_factory_impl : public t_generator_factory { +public: + t_generator_factory_impl(const std::string& short_name, + const std::string& long_name, + const std::string& documentation) + : t_generator_factory(short_name, long_name, documentation) {} + + virtual t_generator* get_generator(t_program* program, + const std::map& parsed_options, + const std::string& option_string) { + return new generator(program, parsed_options, option_string); + } + + virtual bool is_valid_namespace(const std::string& sub_namespace) { + return generator::is_valid_namespace(sub_namespace); + } +}; + +class t_generator_registry { +public: + static void register_generator(t_generator_factory* factory); + + static t_generator* get_generator(t_program* program, const std::string& options); + static t_generator* get_generator(t_program* program, + const std::string& laugnage, + const std::map& parsed_options, + const std::string& options); + + typedef std::map gen_map_t; + static gen_map_t& get_generator_map(); + +private: + t_generator_registry(); + t_generator_registry(const t_generator_registry&); +}; + +#define THRIFT_REGISTER_GENERATOR(language, long_name, doc) \ + class t_##language##_generator_factory_impl \ + : public t_generator_factory_impl { \ + public: \ + t_##language##_generator_factory_impl() \ + : t_generator_factory_impl(#language, long_name, doc) {} \ + }; \ + static t_##language##_generator_factory_impl _registerer; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc new file mode 100644 index 00000000..9919cb1e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_go_generator.cc @@ -0,0 +1,3685 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is programmatically sanitized for style: + * astyle --style=1tbs -f -p -H -j -U t_go_generator.cc + * + * The output of astyle should not be taken unquestioningly, but it is a good + * guide for ensuring uniformity and readability. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "thrift/platform.h" +#include "thrift/version.h" +#include "thrift/generate/t_generator.h" + +using std::map; +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 + +/** + * A helper for automatically formatting the emitted Go code from the Thrift + * IDL per the Go style guide. + * + * Returns: + * - true, if the formatting process succeeded. + * - false, if the formatting process failed, which means the basic output was + * still generated. + */ +bool format_go_output(const string& file_path); + +const string DEFAULT_THRIFT_IMPORT = "git.apache.org/thrift.git/lib/go/thrift"; +static std::string package_flag; + +/** + * Go code generator. + */ +class t_go_generator : public t_generator { +public: + t_go_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; + + + gen_thrift_import_ = DEFAULT_THRIFT_IMPORT; + gen_package_prefix_ = ""; + package_flag = ""; + read_write_private_ = false; + ignore_initialisms_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("package_prefix") == 0) { + gen_package_prefix_ = (iter->second); + } else if( iter->first.compare("thrift_import") == 0) { + gen_thrift_import_ = (iter->second); + } else if( iter->first.compare("package") == 0) { + package_flag = (iter->second); + } else if( iter->first.compare("read_write_private") == 0) { + read_write_private_ = true; + } else if( iter->first.compare("ignore_initialisms") == 0) { + ignore_initialisms_ = true; + } else { + throw "unknown option go:" + iter->first; + } + } + + out_dir_base_ = "gen-go"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + std::string render_const_value(t_type* type, t_const_value* value, const string& name); + + /** + * Struct generation code + */ + + void generate_go_struct(t_struct* tstruct, bool is_exception); + void generate_go_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_result = false, + bool is_args = false); + void generate_go_struct_initializer(std::ofstream& out, + t_struct* tstruct, + bool is_args_or_result = false); + void generate_isset_helpers(std::ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result = false); + void generate_countsetfields_helper(std::ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result = false); + void generate_go_struct_reader(std::ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result = false); + void generate_go_struct_writer(std::ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result = false, + bool uses_countsetfields = false); + void generate_go_function_helpers(t_function* tfunction); + void get_publicized_name_and_def_value(t_field* tfield, + string* OUT_pub_name, + t_const_value** OUT_def_value) const; + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_remote(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::ofstream& out, + t_field* tfield, + bool declare, + std::string prefix = "", + bool inclass = false, + bool coerceData = false, + bool inkey = false, + bool in_container = false, + bool use_true_type = false); + + void generate_deserialize_struct(std::ofstream& out, + t_struct* tstruct, + bool is_pointer_field, + bool declare, + std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, + t_type* ttype, + bool pointer_field, + bool declare, + std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, + t_set* tset, + bool declare, + std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, + t_map* tmap, + bool declare, + std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + bool declare, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool inkey = false); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, + t_type* ttype, + bool pointer_field, + std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_go_docstring(std::ofstream& out, t_struct* tstruct); + + void generate_go_docstring(std::ofstream& out, t_function* tfunction); + + void generate_go_docstring(std::ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader); + + void generate_go_docstring(std::ofstream& out, t_doc* tdoc); + + /** + * Helper rendering functions + */ + + std::string go_autogen_comment(); + std::string go_package(); + std::string go_imports_begin(bool consts); + std::string go_imports_end(); + std::string render_includes(bool consts); + std::string render_included_programs(string& unused_protection); + std::string render_import_protection(); + std::string render_fastbinary_includes(); + std::string declare_argument(t_field* tfield); + std::string render_field_initial_value(t_field* tfield, const string& name, bool optional_field); + std::string type_name(t_type* ttype); + std::string module_name(t_type* ttype); + std::string function_signature(t_function* tfunction, std::string prefix = ""); + std::string function_signature_if(t_function* tfunction, + std::string prefix = "", + bool addError = false); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + std::string type_to_go_type(t_type* ttype); + std::string type_to_go_type_with_opt(t_type* ttype, + bool optional_field); + std::string type_to_go_key_type(t_type* ttype); + std::string type_to_spec_args(t_type* ttype); + + static std::string get_real_go_module(const t_program* program) { + + if (!package_flag.empty()) { + return package_flag; + } + std::string real_module = program->get_namespace("go"); + if (!real_module.empty()) { + return real_module; + } + + return lowercase(program->get_name()); + } + +private: + std::string gen_package_prefix_; + std::string gen_thrift_import_; + bool read_write_private_; + bool ignore_initialisms_; + + /** + * File streams + */ + + std::ofstream f_types_; + std::string f_types_name_; + std::ofstream f_consts_; + std::string f_consts_name_; + std::stringstream f_const_values_; + + std::string package_name_; + std::string package_dir_; + std::string read_method_name_; + std::string write_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 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); + static bool is_pointer_field(t_field* tfield, bool in_container = false); + static bool omit_initialization(t_field* tfield); +}; + +// returns true if field initialization can be omitted since it has corresponding go type zero value +// or default value is not set +bool t_go_generator::omit_initialization(t_field* tfield) { + t_const_value* value = tfield->get_value(); + if (!value) { + return true; + } + t_type* type = tfield->get_type()->get_true_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 ""; + + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + //[]byte are always inline + return false; + } + // strings are pointers if has no default + return value->get_string().empty(); + + 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: + return value->get_integer() == 0; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + return value->get_integer() == 0; + } else { + return value->get_double() == 0.; + } + } + } + return false; +} + +// Returns true if the type need a reference if used as optional without default +static bool type_need_reference(t_type* type) { + type = type->get_true_type(); + if (type->is_map() || type->is_set() || type->is_list() || type->is_struct() + || type->is_xception() || (type->is_string() && ((t_base_type*)type)->is_binary())) { + return false; + } + return true; +} + +// returns false if field could not use comparison to default value as !IsSet* +bool t_go_generator::is_pointer_field(t_field* tfield, bool in_container_value) { + (void)in_container_value; + if (tfield->annotations_.count("cpp.ref") != 0) { + return true; + } + t_type* type = tfield->get_type()->get_true_type(); + // Structs in containers are pointers + if (type->is_struct() || type->is_xception()) { + return true; + } + if (!(tfield->get_req() == t_field::T_OPTIONAL)) { + return false; + } + + bool has_default = tfield->get_value(); + 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 ""; + + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + //[]byte are always inline + return false; + } + // strings are pointers if has no default + return !has_default; + + 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 !has_default; + } + } else if (type->is_enum()) { + return !has_default; + } else if (type->is_struct() || type->is_xception()) { + return true; + } else if (type->is_map()) { + return has_default; + } else if (type->is_set()) { + return has_default; + } else if (type->is_list()) { + return has_default; + } else if (type->is_typedef()) { + return has_default; + } + + throw "INVALID TYPE IN type_to_go_type: " + type->get_name(); +} + +std::string t_go_generator::camelcase(const std::string& value) const { + std::string value2(value); + std::setlocale(LC_ALL, "C"); // set locale to classic + + // Fix common initialism in first word + fix_common_initialism(value2, 0); + + // as long as we are changing things, let's change _ followed by lowercase to + // capital and fix common initialisms + for (std::string::size_type i = 1; i < value2.size() - 1; ++i) { + if (value2[i] == '_') { + if (islower(value2[i + 1])) { + value2.replace(i, 2, 1, toupper(value2[i + 1])); + } + fix_common_initialism(value2, i); + } + } + + return value2; +} + +// Checks to see if the word starting at i in value contains a common initialism +// and if so replaces it with the upper case version of the word. +void t_go_generator::fix_common_initialism(std::string& value, int i) const { + if (!ignore_initialisms_) { + size_t wordLen = value.find('_', i); + if (wordLen != std::string::npos) { + wordLen -= i; + } + std::string word = value.substr(i, wordLen); + std::transform(word.begin(), word.end(), word.begin(), ::toupper); + if (commonInitialisms.find(word) != commonInitialisms.end()) { + value.replace(i, word.length(), word); + } + } +} + +std::string t_go_generator::publicize(const std::string& value, bool is_args_or_result) const { + if (value.size() <= 0) { + return value; + } + + std::string value2(value), prefix; + + string::size_type dot_pos = value.rfind('.'); + if (dot_pos != string::npos) { + prefix = value.substr(0, dot_pos + 1) + prefix; + value2 = value.substr(dot_pos + 1); + } + + if (!isupper(value2[0])) { + value2[0] = toupper(value2[0]); + } + + value2 = camelcase(value2); + + // final length before further checks, the string may become longer + size_t len_before = value2.length(); + + // IDL identifiers may start with "New" which interferes with the CTOR pattern + // Adding an extra underscore to all those identifiers solves this + if ((len_before >= 3) && (value2.substr(0, 3) == "New")) { + value2 += '_'; + } + + // IDL identifiers may end with "Args"/"Result" which interferes with the implicit service + // function structs + // Adding another extra underscore to all those identifiers solves this + // Suppress this check for the actual helper struct names + if (!is_args_or_result) { + bool ends_with_args = (len_before >= 4) && (value2.substr(len_before - 4, 4) == "Args"); + bool ends_with_rslt = (len_before >= 6) && (value2.substr(len_before - 6, 6) == "Result"); + if (ends_with_args || ends_with_rslt) { + value2 += '_'; + } + } + + // Avoid naming collisions with other services + if (is_args_or_result) { + prefix += publicize(service_name_); + } + + return prefix + value2; +} + +std::string t_go_generator::new_prefix(const std::string& value) const { + if (value.size() <= 0) { + return value; + } + + string::size_type dot_pos = value.rfind('.'); + if (dot_pos != string::npos) { + return value.substr(0, dot_pos + 1) + "New" + publicize(value.substr(dot_pos + 1)); + } + return "New" + publicize(value); +} + +std::string t_go_generator::privatize(const std::string& value) const { + if (value.size() <= 0) { + return value; + } + + std::string value2(value); + + if (!islower(value2[0])) { + value2[0] = tolower(value2[0]); + } + + value2 = camelcase(value2); + + return value2; +} + +std::string t_go_generator::variable_name_to_go_name(const std::string& value) { + if (value.size() <= 0) { + return value; + } + + std::string value2(value); + std::transform(value2.begin(), value2.end(), value2.begin(), ::tolower); + + switch (value[0]) { + case 'b': + case 'B': + if (value2 != "break") { + return value; + } + + break; + + case 'c': + case 'C': + if (value2 != "case" && value2 != "chan" && value2 != "const" && value2 != "continue") { + return value; + } + + break; + + case 'd': + case 'D': + if (value2 != "default" && value2 != "defer") { + return value; + } + + break; + + case 'e': + case 'E': + if (value2 != "else" && value2 != "error") { + return value; + } + + break; + + case 'f': + case 'F': + if (value2 != "fallthrough" && value2 != "for" && value2 != "func") { + return value; + } + + break; + + case 'g': + case 'G': + if (value2 != "go" && value2 != "goto") { + return value; + } + + break; + + case 'i': + case 'I': + if (value2 != "if" && value2 != "import" && value2 != "interface") { + return value; + } + + break; + + case 'm': + case 'M': + if (value2 != "map") { + return value; + } + + break; + + case 'p': + case 'P': + if (value2 != "package") { + return value; + } + + break; + + case 'r': + case 'R': + if (value2 != "range" && value2 != "return") { + return value; + } + + break; + + case 's': + case 'S': + if (value2 != "select" && value2 != "struct" && value2 != "switch") { + return value; + } + + break; + + case 't': + case 'T': + if (value2 != "type") { + return value; + } + + break; + + case 'v': + case 'V': + if (value2 != "var") { + return value; + } + + break; + + default: + return value; + } + + return value2 + "_a1"; +} + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_go_generator::init_generator() { + // Make output directory + string module = get_real_go_module(program_); + string target = module; + package_dir_ = get_out_dir(); + + // This set is taken from https://github.com/golang/lint/blob/master/lint.go#L692 + commonInitialisms.insert("API"); + commonInitialisms.insert("ASCII"); + commonInitialisms.insert("CPU"); + commonInitialisms.insert("CSS"); + commonInitialisms.insert("DNS"); + commonInitialisms.insert("EOF"); + commonInitialisms.insert("GUID"); + commonInitialisms.insert("HTML"); + commonInitialisms.insert("HTTP"); + commonInitialisms.insert("HTTPS"); + commonInitialisms.insert("ID"); + commonInitialisms.insert("IP"); + commonInitialisms.insert("JSON"); + commonInitialisms.insert("LHS"); + commonInitialisms.insert("QPS"); + commonInitialisms.insert("RAM"); + commonInitialisms.insert("RHS"); + commonInitialisms.insert("RPC"); + commonInitialisms.insert("SLA"); + commonInitialisms.insert("SMTP"); + commonInitialisms.insert("SSH"); + commonInitialisms.insert("TCP"); + commonInitialisms.insert("TLS"); + commonInitialisms.insert("TTL"); + commonInitialisms.insert("UDP"); + commonInitialisms.insert("UI"); + commonInitialisms.insert("UID"); + commonInitialisms.insert("UUID"); + commonInitialisms.insert("URI"); + commonInitialisms.insert("URL"); + commonInitialisms.insert("UTF8"); + commonInitialisms.insert("VM"); + commonInitialisms.insert("XML"); + commonInitialisms.insert("XSRF"); + commonInitialisms.insert("XSS"); + + // names of read and write methods + if (read_write_private_) { + read_method_name_ = "read"; + write_method_name_ = "write"; + } else { + read_method_name_ = "Read"; + write_method_name_ = "Write"; + } + + while (true) { + // TODO: Do better error checking here. + MKDIR(package_dir_.c_str()); + + if (module.empty()) { + break; + } + + string::size_type pos = module.find('.'); + + if (pos == string::npos) { + package_dir_ += "/"; + package_dir_ += module; + package_name_ = module; + module.clear(); + } else { + package_dir_ += "/"; + package_dir_ += module.substr(0, pos); + module.erase(0, pos + 1); + } + } + + string::size_type loc; + + while ((loc = target.find(".")) != string::npos) { + target.replace(loc, 1, 1, '/'); + } + + // Make output files + f_types_name_ = package_dir_ + "/" + program_name_ + ".go"; + f_types_.open(f_types_name_.c_str()); + + f_consts_name_ = package_dir_ + "/" + program_name_ + "-consts.go"; + f_consts_.open(f_consts_name_.c_str()); + + vector services = program_->get_services(); + vector::iterator sv_iter; + + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + string service_dir = package_dir_ + "/" + underscore((*sv_iter)->get_name()) + "-remote"; + MKDIR(service_dir.c_str()); + } + + // Print header + f_types_ << go_autogen_comment() << go_package() << render_includes(false); + + f_consts_ << go_autogen_comment() << go_package() << render_includes(true); + + f_const_values_ << endl << "func init() {" << endl; + + // Create file for the GoUnusedProtection__ variable + string f_unused_prot_name_ = package_dir_ + "/" + "GoUnusedProtection__.go"; + ofstream f_unused_prot_; + f_unused_prot_.open(f_unused_prot_name_.c_str()); + f_unused_prot_ << go_autogen_comment() << go_package() << render_import_protection(); + f_unused_prot_.close(); +} + + +string t_go_generator::render_included_programs(string& unused_protection) { + 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")) { + 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_protection += "var _ = " + go_module.substr(found) + ".GoUnusedProtection__\n"; + } + + return result; +} + +/** + * Renders all the imports necessary for including another Thrift program. + * If consts include the additional imports. + */ +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"; + } + + if (includes.size() > 0) { + result += "\n"; + } + + return go_imports_begin(consts) + result + go_imports_end() + unused_prot; +} + +string t_go_generator::render_import_protection() { + return string("var GoUnusedProtection__ int;\n\n"); +} + +/** + * Renders all the imports necessary to use the accelerated TBinaryProtocol + */ +string t_go_generator::render_fastbinary_includes() { + return ""; +} + +/** + * Autogen'd comment + */ +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"; +} + +/** + * Prints standard thrift package + */ +string t_go_generator::go_package() { + return string("package ") + package_name_ + "\n\n"; +} + +/** + * Render the beginning of the import statement. + * If consts include the additional imports. + */ +string t_go_generator::go_imports_begin(bool consts) { + string extra; + // 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"; + } + return string( + "import (\n" + "\t\"bytes\"\n" + + extra + + "\t\"fmt\"\n" + "\t\"" + gen_thrift_import_ + "\"\n"); +} + +/** + * End the import statement, include undscore-assignments + * + * These "_ =" prevent the go compiler complaining about used imports. + * This will have to do in lieu of more intelligent import statement construction + */ +string t_go_generator::go_imports_end() { + return string( + ")\n\n" + "// (needed to ensure safety because of naive import list construction.)\n" + "var _ = thrift.ZERO\n" + "var _ = fmt.Printf\n" + "var _ = bytes.Equal\n\n"); +} + +/** + * Closes the type files + */ +void t_go_generator::close_generator() { + f_const_values_ << "}" << endl << endl; + f_consts_ << f_const_values_.str(); + + // Close types and constants files + f_consts_.close(); + f_types_.close(); + format_go_output(f_types_name_); + format_go_output(f_consts_name_); +} + +/** + * Generates a typedef. + * + * @param ttypedef The type definition + */ +void t_go_generator::generate_typedef(t_typedef* ttypedef) { + generate_go_docstring(f_types_, ttypedef); + string new_type_name(publicize(ttypedef->get_symbolic())); + string base_type(type_to_go_type(ttypedef->get_type())); + + if (base_type == new_type_name) { + return; + } + + f_types_ << "type " << new_type_name << " " << base_type << endl << endl; + // Generate a convenience function that converts an instance of a type + // (which may be a constant) into a pointer to an instance of a type. + f_types_ << "func " << new_type_name << "Ptr(v " << new_type_name << ") *" << new_type_name + << " { return &v }" << endl << endl; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_go_generator::generate_enum(t_enum* tenum) { + std::ostringstream to_string_mapping, from_string_mapping; + std::string tenum_name(publicize(tenum->get_name())); + generate_go_docstring(f_types_, tenum); + f_types_ << "type " << tenum_name << " int64" << endl << "const (" << endl; + + to_string_mapping << indent() << "func (p " << tenum_name << ") String() string {" << endl; + to_string_mapping << indent() << " switch p {" << endl; + + from_string_mapping << indent() << "func " << tenum_name << "FromString(s string) (" << tenum_name + << ", error) {" << endl; + from_string_mapping << indent() << " switch s {" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + int value = -1; + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + value = (*c_iter)->get_value(); + + string iter_std_name(escape_string((*c_iter)->get_name())); + string iter_name((*c_iter)->get_name()); + f_types_ << indent() << " " << tenum_name << "_" << iter_name << ' ' << tenum_name << " = " + << value << endl; + // Dictionaries to/from string names of enums + to_string_mapping << indent() << " case " << tenum_name << "_" << iter_name << ": return \"" + << iter_std_name << "\"" << endl; + + if (iter_std_name != escape_string(iter_name)) { + from_string_mapping << indent() << " case \"" << iter_std_name << "\", \"" + << escape_string(iter_name) << "\": return " << tenum_name << "_" + << iter_name << ", nil " << endl; + } else { + from_string_mapping << indent() << " case \"" << iter_std_name << "\": return " << tenum_name + << "_" << iter_name << ", nil " << endl; + } + } + + to_string_mapping << indent() << " }" << endl; + to_string_mapping << indent() << " return \"\"" << endl; + to_string_mapping << indent() << "}" << endl; + from_string_mapping << indent() << " }" << endl; + from_string_mapping << indent() << " return " << tenum_name << "(0)," + << " fmt.Errorf(\"not a valid " << tenum_name << " string\")" << endl; + from_string_mapping << indent() << "}" << endl; + + f_types_ << ")" << endl << endl << to_string_mapping.str() << endl << from_string_mapping.str() + << endl << endl; + + // Generate a convenience function that converts an instance of an enum + // (which may be a constant) into a pointer to an instance of that enum + // type. + f_types_ << "func " << tenum_name << "Ptr(v " << tenum_name << ") *" << tenum_name + << " { return &v }" << endl << endl; + + // Generate MarshalText + f_types_ << "func (p " << tenum_name << ") MarshalText() ([]byte, error) {" << endl; + f_types_ << "return []byte(p.String()), nil" << endl; + f_types_ << "}" << endl << endl; + + // Generate UnmarshalText + f_types_ << "func (p *" << tenum_name << ") UnmarshalText(text []byte) error {" << endl; + f_types_ << "q, err := " << tenum_name << "FromString(string(text))" << endl; + f_types_ << "if (err != nil) {" << endl << "return err" << endl << "}" << endl; + f_types_ << "*p = q" << endl; + f_types_ << "return nil" << endl; + f_types_ << "}" << endl << endl; + + // Generate Scan for sql.Scanner interface + f_types_ << "func (p *" << tenum_name << ") Scan(value interface{}) error {" <get_type(); + string name = publicize(tconst->get_name()); + t_const_value* value = tconst->get_value(); + + if (type->is_base_type() || type->is_enum()) { + indent(f_consts_) << "const " << name << " = " << render_const_value(type, value, name) << endl; + } else { + f_const_values_ << indent() << name << " = " << render_const_value(type, value, name) << endl + << endl; + + f_consts_ << indent() << "var " << name << " " << type_to_go_type(type) << endl; + } +} + +/** + * 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 + */ +string t_go_generator::render_const_value(t_type* type, t_const_value* value, const string& name) { + type = get_true_type(type); + std::ostringstream out; + + 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: + if (((t_base_type*)type)->is_binary()) { + out << "[]byte(\"" << get_escaped_string(value) << "\")"; + } else { + out << '"' << get_escaped_string(value) << '"'; + } + + break; + + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + indent(out) << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << "&" << publicize(type_name(type)) << "{"; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + + 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(); + } + + out << endl << indent() << publicize(v_iter->first->get_string()) << ": " + << render_const_value(field_type, v_iter->second, name) << "," << endl; + } + + indent_down(); + out << "}"; + + } 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(); + out << "map[" << type_to_go_type(ktype) << "]" << type_to_go_type(vtype) << "{" << endl; + indent_up(); + map::const_iterator v_iter; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent() << render_const_value(ktype, v_iter->first, name) << ": " + << render_const_value(vtype, v_iter->second, name) << "," << endl; + } + + indent_down(); + out << indent() << "}"; + } else if (type->is_list()) { + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector& val = value->get_list(); + out << "[]" << type_to_go_type(etype) << "{" << endl; + indent_up(); + vector::const_iterator v_iter; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent() << render_const_value(etype, *v_iter, name) << ", "; + } + + indent_down(); + out << indent() << "}"; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector& val = value->get_list(); + out << "map[" << type_to_go_key_type(etype) << "]struct{}{" << endl; + indent_up(); + vector::const_iterator v_iter; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent() << render_const_value(etype, *v_iter, name) << ": struct{}{}," << endl; + } + + indent_down(); + out << indent() << "}"; + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + + return out.str(); +} + +/** + * Generates a go struct + */ +void t_go_generator::generate_struct(t_struct* tstruct) { + generate_go_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_go_generator::generate_xception(t_struct* txception) { + generate_go_struct(txception, true); +} + +/** + * Generates a go struct + */ +void t_go_generator::generate_go_struct(t_struct* tstruct, bool is_exception) { + generate_go_struct_definition(f_types_, tstruct, is_exception); +} + +void t_go_generator::get_publicized_name_and_def_value(t_field* tfield, + string* OUT_pub_name, + t_const_value** OUT_def_value) const { + const string base_field_name = tfield->get_name(); + const string escaped_field_name = escape_string(base_field_name); + *OUT_pub_name = publicize(escaped_field_name); + *OUT_def_value = tfield->get_value(); +} + +void t_go_generator::generate_go_struct_initializer(ofstream& out, + t_struct* tstruct, + 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); + 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)) { + out << endl << indent() << publicized_name << ": " + << render_field_initial_value(*m_iter, (*m_iter)->get_name(), pointer_field) << "," + << endl; + } + } + + out << "}" << endl; +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_go_generator::generate_go_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_result, + bool is_args) { + const vector& members = tstruct->get_members(); + const vector& sorted_members = tstruct->get_sorted_members(); + vector::const_iterator m_iter; + + std::string tstruct_name(publicize(tstruct->get_name(), is_args || is_result)); + generate_go_docstring(out, tstruct); + out << indent() << "type " << tstruct_name << " struct {" << endl; + /* + Here we generate the structure specification for the fastbinary codec. + These specifications have the following structure: + thrift_spec -> tuple of item_spec + item_spec -> nil | (tag, type_enum, name, spec_args, default) + tag -> integer + type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ... + name -> string_literal + default -> nil # Handled by __init__ + spec_args -> nil # For simple types + | (type_enum, spec_args) # Value type for list/set + | (type_enum, spec_args, type_enum, spec_args) + # Key and value for map + | (class_name, spec_args_ptr) # For struct/exception + class_name -> identifier # Basically a pointer to the class + spec_args_ptr -> expression # just class_name.spec_args + + TODO(dreiss): Consider making this work for structs with negative tags. + */ + // TODO(dreiss): Look into generating an empty tuple instead of nil + // for structures with no members. + // TODO(dreiss): Test encoding of structs where some inner structs + // don't have thrift_spec. + indent_up(); + + int num_setable = 0; + if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) { + int sorted_keys_pos = 0; + + for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) { + // Set field to optional if field is union, this is so we can get a + // pointer to the field. + if (tstruct->is_union()) + (*m_iter)->set_req(t_field::T_OPTIONAL); + if (sorted_keys_pos != (*m_iter)->get_key()) { + int first_unused = std::max(1, sorted_keys_pos++); + while (sorted_keys_pos != (*m_iter)->get_key()) { + ++sorted_keys_pos; + } + int last_unused = sorted_keys_pos - 1; + if (first_unused < last_unused) { + indent(out) << "// unused fields # " << first_unused << " to " << last_unused << endl; + } else if (first_unused == last_unused) { + indent(out) << "// unused field # " << first_unused << endl; + } + } + + 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\""; + } else { + gotag += "json:\"" + escape_string((*m_iter)->get_name()) + "\""; + } + + // Check for user override of db and json tags using "go.tag" + std::map::iterator it = (*m_iter)->annotations_.find("go.tag"); + if (it != (*m_iter)->annotations_.end()) { + gotag = it->second; + } + 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) { + out << ",required"; + } + + out << "\" " << gotag << "`" << endl; + sorted_keys_pos++; + } + } else { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + // This fills in default values, as opposed to nulls + out << indent() << publicize((*m_iter)->get_name()) << " " + << type_to_go_type((*m_iter)->get_type()) << endl; + } + } + + indent_down(); + out << indent() << "}" << endl << endl; + out << indent() << "func New" << tstruct_name << "() *" << tstruct_name << " {" << endl; + out << indent() << " return &"; + generate_go_struct_initializer(out, tstruct, is_result || is_args); + out << indent() << "}" << endl << endl; + // Default values for optional fields + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string publicized_name; + t_const_value* def_value; + get_publicized_name_and_def_value(*m_iter, &publicized_name, &def_value); + t_type* fieldType = (*m_iter)->get_type(); + string goType = type_to_go_type_with_opt(fieldType, false); + 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) { + out << " = " << render_const_value(fieldType, def_value, (*m_iter)->get_name()); + } + out << endl; + } + if (is_pointer_field(*m_iter)) { + string goOptType = type_to_go_type_with_opt(fieldType, true); + string maybepointer = goOptType != goType ? "*" : ""; + out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() " + << goType << " {" << endl; + out << indent() << " if !p.IsSet" << publicized_name << "() {" << endl; + out << indent() << " return " << def_var_name << endl; + out << indent() << " }" << endl; + out << indent() << "return " << maybepointer << "p." << publicized_name << endl; + out << indent() << "}" << endl; + num_setable += 1; + } else { + out << endl; + out << indent() << "func (p *" << tstruct_name << ") Get" << publicized_name << "() " + << goType << " {" << endl; + out << indent() << " return p." << publicized_name << endl; + out << indent() << "}" << endl; + } + } + + if (tstruct->is_union() && num_setable > 0) { + generate_countsetfields_helper(out, tstruct, tstruct_name, is_result); + } + + 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); + + out << indent() << "func (p *" << tstruct_name << ") String() string {" << endl; + out << indent() << " if p == nil {" << endl; + out << indent() << " return \"\"" << endl; + out << indent() << " }" << endl; + out << indent() << " return fmt.Sprintf(\"" << escape_string(tstruct_name) << "(%+v)\", *p)" + << endl; + out << indent() << "}" << endl << endl; + + if (is_exception) { + out << indent() << "func (p *" << tstruct_name << ") Error() string {" << endl; + out << indent() << " return p.String()" << endl; + out << indent() << "}" << endl << endl; + } +} + +/** + * Generates the IsSet helper methods for a struct + */ +void t_go_generator::generate_isset_helpers(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result) { + (void)is_result; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + const string escaped_tstruct_name(escape_string(tstruct->get_name())); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + const string field_name(publicize(escape_string((*f_iter)->get_name()))); + if ((*f_iter)->get_req() == t_field::T_OPTIONAL || is_pointer_field(*f_iter)) { + out << indent() << "func (p *" << tstruct_name << ") IsSet" << field_name << "() bool {" + << endl; + indent_up(); + t_type* ttype = (*f_iter)->get_type()->get_true_type(); + bool is_byteslice = ttype->is_base_type() && ((t_base_type*)ttype)->is_binary(); + bool compare_to_nil_only = ttype->is_set() || ttype->is_list() || ttype->is_map() + || (is_byteslice && !(*f_iter)->get_value()); + if (is_pointer_field(*f_iter) || compare_to_nil_only) { + out << indent() << "return p." << field_name << " != nil" << endl; + } else { + string def_var_name = tstruct_name + "_" + field_name + "_DEFAULT"; + if (is_byteslice) { + out << indent() << "return !bytes.Equal(p." << field_name << ", " << def_var_name << ")" + << endl; + } else { + out << indent() << "return p." << field_name << " != " << def_var_name << endl; + } + } + indent_down(); + out << indent() << "}" << endl << endl; + } + } +} + +/** + * Generates the CountSetFields helper method for a struct + */ +void t_go_generator::generate_countsetfields_helper(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result) { + (void)is_result; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + const string escaped_tstruct_name(escape_string(tstruct->get_name())); + + out << indent() << "func (p *" << tstruct_name << ") CountSetFields" << tstruct_name << "() int {" + << endl; + indent_up(); + out << indent() << "count := 0" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) + continue; + + if (!is_pointer_field(*f_iter)) + continue; + + const string field_name(publicize(escape_string((*f_iter)->get_name()))); + + out << indent() << "if (p.IsSet" << field_name << "()) {" << endl; + indent_up(); + out << indent() << "count++" << endl; + indent_down(); + out << indent() << "}" << endl; + } + + out << indent() << "return count" << endl << endl; + indent_down(); + out << indent() << "}" << endl << endl; +} + +/** + * Generates the read method for a struct + */ +void t_go_generator::generate_go_struct_reader(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result) { + (void)is_result; + 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 {" + << endl; + indent_up(); + out << indent() << "if _, err := iprot.ReadStructBegin(); err != nil {" << endl; + out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T read error: \", p), err)" + << endl; + out << indent() << "}" << endl << endl; + + // Required variables does not have IsSet functions, so we need tmp vars to check them. + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + const string field_name(publicize(escape_string((*f_iter)->get_name()))); + indent(out) << "var isset" << field_name << " bool = false;" << endl; + } + } + out << endl; + + // Loop over reading in fields + indent(out) << "for {" << endl; + indent_up(); + // Read beginning field marker + out << indent() << "_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin()" << endl; + out << indent() << "if err != nil {" << endl; + out << indent() << " return thrift.PrependError(fmt.Sprintf(" + "\"%T field %d read error: \", p, fieldId), err)" << endl; + out << indent() << "}" << endl; + // Check for field STOP marker and break + out << indent() << "if fieldTypeId == thrift.STOP { break; }" << endl; + + string thriftFieldTypeId; + // Generate deserialization code for known cases + int32_t field_id = -1; + + // Switch statement on the field we are reading, false if no fields present + bool have_switch = !fields.empty(); + if (have_switch) { + indent(out) << "switch fieldId {" << endl; + } + + // All the fields we know + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + field_id = (*f_iter)->get_key(); + + // if negative id, ensure we generate a valid method name + string field_method_prefix("ReadField"); + int32_t field_method_suffix = field_id; + + if (field_method_suffix < 0) { + field_method_prefix += "_"; + field_method_suffix *= -1; + } + + out << indent() << "case " << field_id << ":" << endl; + indent_up(); + thriftFieldTypeId = type_to_enum((*f_iter)->get_type()); + + if (thriftFieldTypeId == "thrift.BINARY") { + thriftFieldTypeId = "thrift.STRING"; + } + + out << indent() << "if err := p." << field_method_prefix << field_method_suffix << "(iprot); err != nil {" + << endl; + out << indent() << " return err" << endl; + out << indent() << "}" << endl; + + // Mark required field as read + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + const string field_name(publicize(escape_string((*f_iter)->get_name()))); + out << indent() << "isset" << field_name << " = true" << endl; + } + + indent_down(); + } + + // Begin switch default case + if (have_switch) { + out << indent() << "default:" << endl; + indent_up(); + } + + // Skip unknown fields in either case + out << indent() << "if err := iprot.Skip(fieldTypeId); err != nil {" << endl; + out << indent() << " return err" << endl; + out << indent() << "}" << endl; + + // End switch default case + if (have_switch) { + indent_down(); + out << indent() << "}" << endl; + } + + // Read field end marker + out << indent() << "if err := iprot.ReadFieldEnd(); 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() << " return thrift.PrependError(fmt.Sprintf(" + "\"%T read struct end error: \", p), err)" << endl; + out << indent() << "}" << endl; + + // Return error if any required fields are missing. + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + const string field_name(publicize(escape_string((*f_iter)->get_name()))); + out << indent() << "if !isset" << field_name << "{" << endl; + out << indent() << " return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, " + "fmt.Errorf(\"Required field " << field_name << " is not set\"));" << endl; + out << indent() << "}" << endl; + } + } + + out << indent() << "return nil" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string field_type_name(publicize((*f_iter)->get_type()->get_name())); + string field_name(publicize((*f_iter)->get_name())); + string field_method_prefix("ReadField"); + int32_t field_id = (*f_iter)->get_key(); + int32_t field_method_suffix = field_id; + + if (field_method_suffix < 0) { + field_method_prefix += "_"; + field_method_suffix *= -1; + } + + out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix + << "(iprot thrift.TProtocol) error {" << endl; + indent_up(); + generate_deserialize_field(out, *f_iter, false, "p."); + indent_down(); + out << indent() << " return nil" << endl; + out << indent() << "}" << endl << endl; + } +} + +void t_go_generator::generate_go_struct_writer(ofstream& out, + t_struct* tstruct, + const string& tstruct_name, + bool is_result, + bool uses_countsetfields) { + (void)is_result; + 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_up(); + if (tstruct->is_union() && uses_countsetfields) { + std::string tstruct_name(publicize(tstruct->get_name())); + out << indent() << "if c := p.CountSetFields" << tstruct_name << "(); c != 1 {" << endl + << indent() + << " 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() << " return thrift.PrependError(fmt.Sprintf(" + "\"%T write struct begin error: \", p), err) }" << endl; + + string field_name; + string escape_field_name; + // t_const_value* field_default_value; + t_field::e_req field_required; + int32_t field_id = -1; + + out << indent() << "if p != nil {" << endl; + indent_up(); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string field_method_prefix("writeField"); + field_name = (*f_iter)->get_name(); + escape_field_name = escape_string(field_name); + field_id = (*f_iter)->get_key(); + int32_t field_method_suffix = field_id; + + if (field_method_suffix < 0) { + field_method_prefix += "_"; + field_method_suffix *= -1; + } + + out << indent() << "if err := p." << field_method_prefix << field_method_suffix + << "(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() << " return thrift.PrependError(\"write field stop error: \", err) }" << endl; + out << indent() << "if err := oprot.WriteStructEnd(); err != nil {" << endl; + out << indent() << " return thrift.PrependError(\"write struct stop error: \", err) }" << endl; + out << indent() << "return nil" << endl; + indent_down(); + out << indent() << "}" << endl << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + string field_method_prefix("writeField"); + field_id = (*f_iter)->get_key(); + field_name = (*f_iter)->get_name(); + escape_field_name = escape_string(field_name); + // field_default_value = (*f_iter)->get_value(); + field_required = (*f_iter)->get_req(); + int32_t field_method_suffix = field_id; + + if (field_method_suffix < 0) { + field_method_prefix += "_"; + field_method_suffix *= -1; + } + + out << indent() << "func (p *" << tstruct_name << ") " << field_method_prefix << field_method_suffix + << "(oprot thrift.TProtocol) (err error) {" << endl; + indent_up(); + + if (field_required == t_field::T_OPTIONAL) { + out << indent() << "if p.IsSet" << publicize(field_name) << "() {" << endl; + indent_up(); + } + + out << indent() << "if err := oprot.WriteFieldBegin(\"" << 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; + + // Write field contents + generate_serialize_field(out, *f_iter, "p."); + + // Write field closer + out << indent() << "if err := oprot.WriteFieldEnd(); err != nil {" << endl; + out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T write field end error " + << field_id << ":" << escape_field_name << ": \", p), err) }" << endl; + + if (field_required == t_field::T_OPTIONAL) { + indent_down(); + out << indent() << "}" << endl; + } + + indent_down(); + out << indent() << " return err" << endl; + out << indent() << "}" << endl << endl; + } +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_go_generator::generate_service(t_service* tservice) { + string test_suffix("_test"); + string filename = lowercase(service_name_); + string f_service_name; + + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + generate_service_remote(tservice); + f_types_ << endl; +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_go_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + f_types_ << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_go_struct_definition(f_types_, ts, false, false, true); + generate_go_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_go_generator::generate_go_function_helpers(t_function* tfunction) { + if (!tfunction->is_oneway()) { + t_struct result(program_, tfunction->get_name() + "_result"); + t_field success(tfunction->get_returntype(), "success", 0); + success.set_req(t_field::T_OPTIONAL); + + 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) { + t_field* f = *f_iter; + f->set_req(t_field::T_OPTIONAL); + result.append(f); + } + + generate_go_struct_definition(f_types_, &result, false, true); + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_go_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_if = ""; + string serviceName(publicize(tservice->get_name())); + string interfaceName = serviceName; + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + + if (index != string::npos) { + extends_if = "\n" + indent() + " " + extends.substr(0, index + 1) + + publicize(extends.substr(index + 1)) + "\n"; + } else { + extends_if = "\n" + indent() + publicize(extends) + "\n"; + } + } + + f_types_ << indent() << "type " << interfaceName << " interface {" << extends_if; + indent_up(); + generate_go_docstring(f_types_, tservice); + vector functions = tservice->get_functions(); + + if (!functions.empty()) { + f_types_ << endl; + vector::iterator f_iter; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_go_docstring(f_types_, (*f_iter)); + f_types_ << indent() << function_signature_if(*f_iter, "", true) << endl; + } + } + + indent_down(); + f_types_ << indent() << "}" << endl << endl; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_go_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_field = ""; + string extends_client = ""; + string extends_client_new = ""; + string serviceName(publicize(tservice->get_name())); + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + + if (index != string::npos) { + extends_client = extends.substr(0, index + 1) + publicize(extends.substr(index + 1)) + + "Client"; + extends_client_new = extends.substr(0, index + 1) + "New" + + publicize(extends.substr(index + 1)) + "Client"; + } else { + extends_client = publicize(extends) + "Client"; + extends_client_new = "New" + extends_client; + } + } + + extends_field = extends_client.substr(extends_client.find(".") + 1); + + generate_go_docstring(f_types_, tservice); + f_types_ << indent() << "type " << serviceName << "Client struct {" << endl; + indent_up(); + + if (!extends_client.empty()) { + f_types_ << indent() << "*" << extends_client << endl; + } else { + f_types_ << indent() << "Transport thrift.TTransport" << endl; + f_types_ << indent() << "ProtocolFactory thrift.TProtocolFactory" << endl; + f_types_ << indent() << "InputProtocol thrift.TProtocol" << endl; + f_types_ << indent() << "OutputProtocol thrift.TProtocol" << endl; + f_types_ << indent() << "SeqId int32" << endl; + /*f_types_ << indent() << "reqs map[int32]Deferred" << endl*/; + } + + indent_down(); + f_types_ << indent() << "}" << endl << endl; + // Constructor function + f_types_ << indent() << "func New" << serviceName + << "ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *" << serviceName + << "Client {" << endl; + indent_up(); + f_types_ << indent() << "return &" << serviceName << "Client"; + + if (!extends.empty()) { + f_types_ << "{" << extends_field << ": " << extends_client_new << "Factory(t, f)}"; + } else { + indent_up(); + f_types_ << "{Transport: t," << endl; + f_types_ << indent() << "ProtocolFactory: f," << endl; + f_types_ << indent() << "InputProtocol: f.GetProtocol(t)," << endl; + f_types_ << indent() << "OutputProtocol: f.GetProtocol(t)," << endl; + f_types_ << indent() << "SeqId: 0," << endl; + /*f_types_ << indent() << "Reqs: make(map[int32]Deferred)" << endl*/; + indent_down(); + f_types_ << indent() << "}" << endl; + } + + indent_down(); + f_types_ << indent() << "}" << endl << endl; + // Constructor function + f_types_ + << indent() << "func New" << serviceName + << "ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *" + << serviceName << "Client {" << endl; + indent_up(); + f_types_ << indent() << "return &" << serviceName << "Client"; + + if (!extends.empty()) { + f_types_ << "{" << extends_field << ": " << extends_client_new << "Protocol(t, iprot, oprot)}" + << endl; + } else { + indent_up(); + f_types_ << "{Transport: t," << endl; + f_types_ << indent() << "ProtocolFactory: nil," << endl; + f_types_ << indent() << "InputProtocol: iprot," << endl; + f_types_ << indent() << "OutputProtocol: oprot," << endl; + f_types_ << indent() << "SeqId: 0," << endl; + /*f_types_ << indent() << "Reqs: make(map[int32]interface{})" << endl*/; + indent_down(); + f_types_ << indent() << "}" << endl; + } + + indent_down(); + f_types_ << indent() << "}" << endl << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = publicize((*f_iter)->get_name()); + // Open function + generate_go_docstring(f_types_, (*f_iter)); + f_types_ << indent() << "func (p *" << serviceName << "Client) " + << function_signature_if(*f_iter, "", true) << " {" << endl; + indent_up(); + /* + f_types_ << + indent() << "p.SeqId += 1" << endl; + if (!(*f_iter)->is_oneway()) { + f_types_ << + indent() << "d := defer.Deferred()" << endl << + indent() << "p.Reqs[p.SeqId] = d" << endl; + } + */ + f_types_ << indent() << "if err = p.send" << funname << "("; + bool first = true; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + if (first) { + first = false; + } else { + f_types_ << ", "; + } + + f_types_ << variable_name_to_go_name((*fld_iter)->get_name()); + } + + f_types_ << "); err != nil { return }" << endl; + + if (!(*f_iter)->is_oneway()) { + f_types_ << indent() << "return p.recv" << funname << "()" << endl; + } else { + f_types_ << indent() << "return" << endl; + } + + indent_down(); + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func (p *" << serviceName << "Client) send" + << function_signature(*f_iter) << "(err error) {" << endl; + indent_up(); + std::string argsname = publicize((*f_iter)->get_name() + "_args", true); + // Serialize the request header + f_types_ << indent() << "oprot := p.OutputProtocol" << endl; + f_types_ << indent() << "if oprot == nil {" << endl; + f_types_ << indent() << " oprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl; + f_types_ << indent() << " p.OutputProtocol = oprot" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "p.SeqId++" << endl; + f_types_ << indent() << "if err = oprot.WriteMessageBegin(\"" << (*f_iter)->get_name() + << "\", " << ((*f_iter)->is_oneway() ? "thrift.ONEWAY" : "thrift.CALL") + << ", p.SeqId); err != nil {" << endl; + indent_up(); + f_types_ << indent() << " return" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "args := " << argsname << "{" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_types_ << indent() << publicize((*fld_iter)->get_name()) << " : " + << variable_name_to_go_name((*fld_iter)->get_name()) << "," << endl; + } + f_types_ << indent() << "}" << endl; + + // Write to the stream + f_types_ << indent() << "if err = args." << write_method_name_ << "(oprot); err != nil {" << endl; + indent_up(); + f_types_ << indent() << " return" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if err = oprot.WriteMessageEnd(); err != nil {" << endl; + indent_up(); + f_types_ << indent() << " return" << endl; + indent_down(); + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "return oprot.Flush()" << endl; + indent_down(); + f_types_ << indent() << "}" << endl << endl; + + if (!(*f_iter)->is_oneway()) { + std::string resultname = publicize((*f_iter)->get_name() + "_result", true); + // Open function + f_types_ << endl << indent() << "func (p *" << serviceName << "Client) recv" + << publicize((*f_iter)->get_name()) << "() ("; + + if (!(*f_iter)->get_returntype()->is_void()) { + f_types_ << "value " << type_to_go_type((*f_iter)->get_returntype()) << ", "; + } + + f_types_ << "err error) {" << endl; + indent_up(); + // TODO(mcslee): Validate message reply here, seq ids etc. + string error(tmp("error")); + string error2(tmp("error")); + f_types_ << indent() << "iprot := p.InputProtocol" << endl; + f_types_ << indent() << "if iprot == nil {" << endl; + f_types_ << indent() << " iprot = p.ProtocolFactory.GetProtocol(p.Transport)" << endl; + f_types_ << indent() << " p.InputProtocol = iprot" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "method, mTypeId, seqId, err := iprot.ReadMessageBegin()" << endl; + f_types_ << indent() << "if err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if method != \"" << (*f_iter)->get_name() << "\" {" << endl; + f_types_ << indent() << " err = thrift.NewTApplicationException(" + << "thrift.WRONG_METHOD_NAME, \"" << (*f_iter)->get_name() + << " failed: wrong method name\")" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if p.SeqId != seqId {" << endl; + f_types_ << indent() << " err = thrift.NewTApplicationException(" + << "thrift.BAD_SEQUENCE_ID, \"" << (*f_iter)->get_name() + << " failed: out of sequence response\")" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if mTypeId == thrift.EXCEPTION {" << endl; + f_types_ << indent() << " " << error + << " := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, " + "\"Unknown Exception\")" << endl; + f_types_ << indent() << " var " << error2 << " error" << endl; + f_types_ << indent() << " " << error2 << ", err = " << error << ".Read(iprot)" << endl; + f_types_ << indent() << " if err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << " }" << endl; + f_types_ << indent() << " if err = iprot.ReadMessageEnd(); err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << " }" << endl; + f_types_ << indent() << " err = " << error2 << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if mTypeId != thrift.REPLY {" << endl; + f_types_ << indent() << " err = thrift.NewTApplicationException(" + << "thrift.INVALID_MESSAGE_TYPE_EXCEPTION, \"" << (*f_iter)->get_name() + << " failed: invalid message type\")" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "result := " << resultname << "{}" << endl; + f_types_ << indent() << "if err = result." << read_method_name_ << "(iprot); err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if err = iprot.ReadMessageEnd(); err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + + 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) { + const std::string pubname = publicize((*x_iter)->get_name()); + + f_types_ << indent() << "if result." << pubname << " != nil {" << endl; + f_types_ << indent() << " err = result." << pubname << endl; + f_types_ << indent() << " return " << endl; + f_types_ << indent() << "}"; + + if ((x_iter + 1) != xceptions.end()) { + f_types_ << " else "; + } else { + f_types_ << endl; + } + } + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_types_ << indent() << "value = result.GetSuccess()" << endl; + } + + f_types_ << indent() << "return" << endl; + // Close function + indent_down(); + f_types_ << indent() << "}" << endl << endl; + } + } + + // indent_down(); + f_types_ << endl; +} + +/** + * Generates a command line tool for making remote requests + * + * @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(); + + // collect inherited functions + while (parent != NULL) { + vector p_functions = parent->get_functions(); + functions.insert(functions.end(), p_functions.begin(), p_functions.end()); + parent = parent->get_extends(); + } + + vector::iterator f_iter; + string f_remote_name = package_dir_ + "/" + underscore(service_name_) + "-remote/" + + underscore(service_name_) + "-remote.go"; + ofstream 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; + + f_remote << go_autogen_comment(); + f_remote << indent() << "package main" << endl << endl; + f_remote << indent() << "import (" << 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 << indent() << render_included_programs(unused_protection); + f_remote << indent() << " \"" << service_module << "\"" << endl; + f_remote << indent() << ")" << endl; + f_remote << indent() << endl; + f_remote << indent() << unused_protection; // filled in render_included_programs() + f_remote << indent() << endl; + f_remote << indent() << "func Usage() {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Usage of \", os.Args[0], \" " + "[-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:\")" + << endl; + f_remote << indent() << " flag.PrintDefaults()" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"\\nFunctions:\")" << endl; + + 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; + + for (std::vector::size_type i = 0; i < num_args; ++i) { + if (first) { + first = false; + } else { + f_remote << ", "; + } + + f_remote << args[i]->get_type()->get_name() << " " << args[i]->get_name(); + } + + f_remote << ")\")" << endl; + } + + f_remote << indent() << " fmt.Fprintln(os.Stderr)" << endl; + f_remote << indent() << " os.Exit(0)" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << endl; + f_remote << indent() << "func main() {" << endl; + indent_up(); + f_remote << indent() << "flag.Usage = Usage" << endl; + f_remote << indent() << "var host string" << endl; + f_remote << indent() << "var port int" << endl; + f_remote << indent() << "var protocol string" << endl; + f_remote << indent() << "var urlString string" << endl; + f_remote << indent() << "var framed bool" << endl; + f_remote << indent() << "var useHttp bool" << endl; + f_remote << indent() << "var parsedUrl url.URL" << endl; + f_remote << indent() << "var trans thrift.TTransport" << endl; + f_remote << indent() << "_ = strconv.Atoi" << endl; + f_remote << indent() << "_ = math.Abs" << endl; + f_remote << indent() << "flag.Usage = Usage" << endl; + f_remote << indent() << "flag.StringVar(&host, \"h\", \"localhost\", \"Specify host and port\")" + << endl; + f_remote << indent() << "flag.IntVar(&port, \"p\", 9090, \"Specify port\")" << endl; + f_remote << indent() << "flag.StringVar(&protocol, \"P\", \"binary\", \"" + "Specify the protocol (binary, compact, simplejson, json)\")" << endl; + f_remote << indent() << "flag.StringVar(&urlString, \"u\", \"\", \"Specify the url\")" << endl; + f_remote << indent() << "flag.BoolVar(&framed, \"framed\", false, \"Use framed transport\")" + << endl; + f_remote << indent() << "flag.BoolVar(&useHttp, \"http\", false, \"Use http\")" << endl; + f_remote << indent() << "flag.Parse()" << endl; + f_remote << indent() << endl; + f_remote << indent() << "if len(urlString) > 0 {" << endl; + f_remote << indent() << " parsedUrl, err := url.Parse(urlString)" << endl; + f_remote << indent() << " if err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl; + f_remote << indent() << " flag.Usage()" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << " host = parsedUrl.Host" << endl; + f_remote << indent() << " useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == \"http\"" + << endl; + f_remote << indent() << "} else if useHttp {" << endl; + f_remote << indent() << " _, err := url.Parse(fmt.Sprint(\"http://\", host, \":\", port))" + << endl; + f_remote << indent() << " if err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error parsing URL: \", err)" << endl; + f_remote << indent() << " flag.Usage()" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << endl; + f_remote << indent() << "cmd := flag.Arg(0)" << endl; + f_remote << indent() << "var err error" << endl; + f_remote << indent() << "if useHttp {" << endl; + f_remote << indent() << " trans, err = thrift.NewTHttpClient(parsedUrl.String())" << endl; + f_remote << indent() << "} else {" << endl; + f_remote << indent() << " portStr := fmt.Sprint(port)" << endl; + f_remote << indent() << " if strings.Contains(host, \":\") {" << endl; + f_remote << indent() << " host, portStr, err = net.SplitHostPort(host)" << endl; + f_remote << indent() << " if err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"error with host:\", err)" + << endl; + f_remote << indent() << " os.Exit(1)" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << " trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr))" + << endl; + f_remote << indent() << " if err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"error resolving address:\", err)" << endl; + f_remote << indent() << " os.Exit(1)" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << " if framed {" << endl; + f_remote << indent() << " trans = thrift.NewTFramedTransport(trans)" << endl; + f_remote << indent() << " }" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "if err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error creating transport\", err)" << endl; + f_remote << indent() << " os.Exit(1)" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "defer trans.Close()" << endl; + f_remote << indent() << "var protocolFactory thrift.TProtocolFactory" << endl; + f_remote << indent() << "switch protocol {" << endl; + f_remote << indent() << "case \"compact\":" << endl; + f_remote << indent() << " protocolFactory = thrift.NewTCompactProtocolFactory()" << endl; + f_remote << indent() << " break" << endl; + f_remote << indent() << "case \"simplejson\":" << endl; + f_remote << indent() << " protocolFactory = thrift.NewTSimpleJSONProtocolFactory()" << endl; + f_remote << indent() << " break" << endl; + f_remote << indent() << "case \"json\":" << endl; + f_remote << indent() << " protocolFactory = thrift.NewTJSONProtocolFactory()" << endl; + f_remote << indent() << " break" << endl; + f_remote << indent() << "case \"binary\", \"\":" << endl; + f_remote << indent() << " protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()" << endl; + f_remote << indent() << " break" << endl; + f_remote << indent() << "default:" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Invalid protocol specified: \", protocol)" + << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " os.Exit(1)" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "client := " << package_name_ << ".New" << publicize(service_name_) + << "ClientFactory(trans, protocolFactory)" << endl; + f_remote << indent() << "if err := trans.Open(); err != nil {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Error opening socket to \", " + "host, \":\", port, \" \", err)" << endl; + f_remote << indent() << " os.Exit(1)" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << endl; + f_remote << indent() << "switch cmd {" << endl; + + 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)); + f_remote << indent() << "case \"" << escape_string(funcName) << "\":" << endl; + indent_up(); + f_remote << indent() << "if flag.NArg() - 1 != " << num_args << " {" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"" << escape_string(pubName) << " requires " + << num_args << " args\")" << endl; + f_remote << indent() << " flag.Usage()" << endl; + f_remote << indent() << "}" << endl; + + for (std::vector::size_type i = 0; i < num_args; ++i) { + int flagArg = i + 1; + t_type* the_type(args[i]->get_type()); + t_type* the_type2(get_true_type(the_type)); + + if (the_type2->is_enum()) { + f_remote << indent() << "tmp" << i << ", err := (strconv.Atoi(flag.Arg(" << flagArg << ")))" + << endl; + f_remote << indent() << "if err != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "argvalue" << i << " := " << package_name_ << "." + << 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(); + string err(tmp("err")); + + switch (e) { + case t_base_type::TYPE_VOID: + break; + + case t_base_type::TYPE_STRING: + if (((t_base_type*)the_type2)->is_binary()) { + f_remote << indent() << "argvalue" << i << " := []byte(flag.Arg(" << flagArg << "))" + << endl; + } else { + f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ")" << endl; + } + break; + + case t_base_type::TYPE_BOOL: + f_remote << indent() << "argvalue" << i << " := flag.Arg(" << flagArg << ") == \"true\"" + << endl; + break; + + case t_base_type::TYPE_I8: + f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" + << flagArg << ")))" << endl; + f_remote << indent() << "if " << err << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "argvalue" << i << " := int8(tmp" << i << ")" << endl; + break; + + case t_base_type::TYPE_I16: + f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" + << flagArg << ")))" << endl; + f_remote << indent() << "if " << err << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "argvalue" << i << " := int16(tmp" << i << ")" << endl; + break; + + case t_base_type::TYPE_I32: + f_remote << indent() << "tmp" << i << ", " << err << " := (strconv.Atoi(flag.Arg(" + << flagArg << ")))" << endl; + f_remote << indent() << "if " << err << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "argvalue" << i << " := int32(tmp" << i << ")" << endl; + break; + + case t_base_type::TYPE_I64: + f_remote << indent() << "argvalue" << i << ", " << err + << " := (strconv.ParseInt(flag.Arg(" << flagArg << "), 10, 64))" << endl; + f_remote << indent() << "if " << err << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + break; + + case t_base_type::TYPE_DOUBLE: + f_remote << indent() << "argvalue" << i << ", " << err + << " := (strconv.ParseFloat(flag.Arg(" << flagArg << "), 64))" << endl; + f_remote << indent() << "if " << err << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + break; + + default: + throw("Invalid base type in generate_service_remote"); + } + + // f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg << + // ")))"; + } else if (the_type2->is_struct()) { + string arg(tmp("arg")); + string mbTrans(tmp("mbTrans")); + string err1(tmp("err")); + string factory(tmp("factory")); + string jsProt(tmp("jsProt")); + string err2(tmp("err")); + 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_; + } + + f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl; + f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" + << endl; + f_remote << indent() << "defer " << mbTrans << ".Close()" << endl; + f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" + << endl; + f_remote << indent() << "if " << err1 << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl; + f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" + << 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() << "if " << err2 << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + } else if (the_type2->is_container() || the_type2->is_xception()) { + string arg(tmp("arg")); + string mbTrans(tmp("mbTrans")); + string err1(tmp("err")); + string factory(tmp("factory")); + string jsProt(tmp("jsProt")); + string err2(tmp("err")); + std::string argName(publicize(args[i]->get_name())); + f_remote << indent() << arg << " := flag.Arg(" << flagArg << ")" << endl; + f_remote << indent() << mbTrans << " := thrift.NewTMemoryBufferLen(len(" << arg << "))" + << endl; + f_remote << indent() << "defer " << mbTrans << ".Close()" << endl; + f_remote << indent() << "_, " << err1 << " := " << mbTrans << ".WriteString(" << arg << ")" + << endl; + f_remote << indent() << "if " << err1 << " != nil { " << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << factory << " := thrift.NewTSimpleJSONProtocolFactory()" << endl; + f_remote << indent() << jsProt << " := " << factory << ".GetProtocol(" << mbTrans << ")" + << endl; + f_remote << indent() << "containerStruct" << i << " := " << package_name_ << ".New" + << argumentsName << "()" << endl; + f_remote << indent() << err2 << " := containerStruct" << i << ".ReadField" << (i + 1) << "(" + << jsProt << ")" << endl; + f_remote << indent() << "if " << err2 << " != nil {" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " return" << endl; + f_remote << indent() << "}" << endl; + f_remote << indent() << "argvalue" << i << " := containerStruct" << i << "." << argName + << endl; + } else { + throw("Invalid argument type in generate_service_remote"); + } + + if (the_type->is_typedef()) { + std::string typedef_module( module_name(the_type)); + if(typedef_module.empty()) { + typedef_module = package_name_; + } + f_remote << indent() << "value" << i << " := " << typedef_module << "." + << publicize(the_type->get_name()) << "(argvalue" << i << ")" << endl; + } else { + f_remote << indent() << "value" << i << " := argvalue" << i << endl; + } + } + + f_remote << indent() << "fmt.Print(client." << pubName << "("; + bool argFirst = true; + + for (std::vector::size_type i = 0; i < num_args; ++i) { + if (argFirst) { + argFirst = false; + } else { + f_remote << ", "; + } + + if (args[i]->get_type()->is_enum()) { + f_remote << "value" << i; + } else if (args[i]->get_type()->is_base_type()) { + t_base_type::t_base e = ((t_base_type*)(args[i]->get_type()))->get_base(); + + switch (e) { + case t_base_type::TYPE_VOID: + break; + + case t_base_type::TYPE_STRING: + 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: + f_remote << "value" << i; + break; + + default: + throw("Invalid base type in generate_service_remote"); + } + + // f_remote << publicize(args[i]->get_name()) << "(strconv.Atoi(flag.Arg(" << flagArg << + // ")))"; + } else { + f_remote << "value" << i; + } + } + + f_remote << "))" << endl; + f_remote << indent() << "fmt.Print(\"\\n\")" << endl; + f_remote << indent() << "break" << endl; + indent_down(); + } + + f_remote << indent() << "case \"\":" << endl; + f_remote << indent() << " Usage()" << endl; + f_remote << indent() << " break" << endl; + f_remote << indent() << "default:" << endl; + f_remote << indent() << " fmt.Fprintln(os.Stderr, \"Invalid function \", cmd)" << endl; + f_remote << indent() << "}" << endl; + indent_down(); + f_remote << indent() << "}" << endl; + // Close service file + f_remote.close(); + format_go_output(f_remote_name); +#ifndef _MSC_VER + // Make file executable, love that bitwise OR action + chmod(f_remote_name.c_str(), + S_IRUSR | S_IWUSR | S_IXUSR +#ifndef _WIN32 + | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH +#endif + ); +#endif +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_go_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + string extends = ""; + string extends_processor = ""; + string extends_processor_new = ""; + string serviceName(publicize(tservice->get_name())); + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + size_t index = extends.rfind("."); + + if (index != string::npos) { + extends_processor = extends.substr(0, index + 1) + publicize(extends.substr(index + 1)) + + "Processor"; + extends_processor_new = extends.substr(0, index + 1) + "New" + + publicize(extends.substr(index + 1)) + "Processor"; + } else { + extends_processor = publicize(extends) + "Processor"; + extends_processor_new = "New" + extends_processor; + } + } + + string pServiceName(privatize(tservice->get_name())); + // Generate the header portion + string self(tmp("self")); + + if (extends_processor.empty()) { + f_types_ << indent() << "type " << serviceName << "Processor struct {" << endl; + f_types_ << indent() << " processorMap map[string]thrift.TProcessorFunction" << endl; + f_types_ << indent() << " handler " << serviceName << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func (p *" << serviceName + << "Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {" + << endl; + f_types_ << indent() << " p.processorMap[key] = processor" << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func (p *" << serviceName + << "Processor) GetProcessorFunction(key string) " + "(processor thrift.TProcessorFunction, ok bool) {" << endl; + f_types_ << indent() << " processor, ok = p.processorMap[key]" << endl; + f_types_ << indent() << " return processor, ok" << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func (p *" << serviceName + << "Processor) ProcessorMap() map[string]thrift.TProcessorFunction {" << endl; + f_types_ << indent() << " return p.processorMap" << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func New" << serviceName << "Processor(handler " << serviceName + << ") *" << serviceName << "Processor {" << endl << endl; + f_types_ + << indent() << " " << self << " := &" << serviceName + << "Processor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)}" + << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string escapedFuncName(escape_string((*f_iter)->get_name())); + f_types_ << indent() << " " << self << ".processorMap[\"" << escapedFuncName << "\"] = &" + << pServiceName << "Processor" << publicize((*f_iter)->get_name()) + << "{handler:handler}" << endl; + } + + string x(tmp("x")); + f_types_ << indent() << "return " << self << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func (p *" << serviceName + << "Processor) Process(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() << " if processor, ok := p.GetProcessorFunction(name); ok {" << endl; + f_types_ << indent() << " return processor.Process(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() << " " << 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.Flush()" << endl; + f_types_ << indent() << " return false, " << x << endl; + f_types_ << indent() << "" << endl; + f_types_ << indent() << "}" << endl << endl; + } else { + f_types_ << indent() << "type " << serviceName << "Processor struct {" << endl; + f_types_ << indent() << " *" << extends_processor << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "func New" << serviceName << "Processor(handler " << serviceName + << ") *" << serviceName << "Processor {" << endl; + f_types_ << indent() << " " << self << " := &" << serviceName << "Processor{" + << extends_processor_new << "(handler)}" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string escapedFuncName(escape_string((*f_iter)->get_name())); + f_types_ << indent() << " " << self << ".AddToProcessorMap(\"" << escapedFuncName + << "\", &" << pServiceName << "Processor" << publicize((*f_iter)->get_name()) + << "{handler:handler})" << endl; + } + + f_types_ << indent() << " return " << self << endl; + f_types_ << indent() << "}" << endl << endl; + } + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + f_types_ << endl; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_go_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + // Open function + string processorName = privatize(tservice->get_name()) + "Processor" + + publicize(tfunction->get_name()); + string argsname = publicize(tfunction->get_name() + "_args", true); + string resultname = publicize(tfunction->get_name() + "_result", true); + // 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; + f_types_ << indent() << "func (p *" << processorName + << ") Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err " + "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; + if (!tfunction->is_oneway()) { + f_types_ << indent() + << " x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error())" + << endl; + f_types_ << indent() << " oprot.WriteMessageBegin(\"" << 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() << " oprot.Flush()" << endl; + } + f_types_ << indent() << " return false, err" << endl; + f_types_ << indent() << "}" << endl << endl; + f_types_ << indent() << "iprot.ReadMessageEnd()" << 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 err2 error" << endl; + f_types_ << indent() << "if "; + + if (!tfunction->is_oneway()) { + if (!tfunction->get_returntype()->is_void()) { + f_types_ << "retval, "; + } + } + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + f_types_ << "err2 = p.handler." << publicize(tfunction->get_name()) << "("; + bool first = true; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_types_ << ", "; + } + + f_types_ << "args." << publicize((*f_iter)->get_name()); + } + + f_types_ << "); err2 != nil {" << endl; + + t_struct* exceptions = tfunction->get_xceptions(); + const vector& x_fields = exceptions->get_members(); + if (!x_fields.empty()) { + f_types_ << indent() << "switch v := err2.(type) {" << endl; + + vector::const_iterator xf_iter; + + for (xf_iter = x_fields.begin(); xf_iter != x_fields.end(); ++xf_iter) { + f_types_ << indent() << " case " << type_to_go_type(((*xf_iter)->get_type())) << ":" + << endl; + f_types_ << indent() << "result." << publicize((*xf_iter)->get_name()) << " = v" << endl; + } + + f_types_ << indent() << " default:" << endl; + } + + if (!tfunction->is_oneway()) { + 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()) + << "\", thrift.EXCEPTION, seqId)" << endl; + f_types_ << indent() << " x.Write(oprot)" << endl; + f_types_ << indent() << " oprot.WriteMessageEnd()" << endl; + f_types_ << indent() << " oprot.Flush()" << endl; + } + + f_types_ << indent() << " return true, err2" << endl; + + if (!x_fields.empty()) { + f_types_ << indent() << "}" << endl; + } + + f_types_ << indent() << "}"; // closes err2 != nil + + if (!tfunction->is_oneway()) { + if (!tfunction->get_returntype()->is_void()) { + f_types_ << " else {" << endl; // make sure we set Success retval only on success + indent_up(); + f_types_ << indent() << "result.Success = "; + if (need_reference) { + f_types_ << "&"; + } + f_types_ << "retval" << endl; + indent_down(); + f_types_ << "}" << endl; + } else { + f_types_ << endl; + } + f_types_ << indent() << "if err2 = oprot.WriteMessageBegin(\"" + << escape_string(tfunction->get_name()) << "\", thrift.REPLY, seqId); err2 != nil {" + << endl; + f_types_ << indent() << " err = 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() << "}" << endl; + f_types_ << indent() << "if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil {" + << endl; + f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if err2 = oprot.Flush(); err == nil && err2 != nil {" << endl; + f_types_ << indent() << " err = err2" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "if err != nil {" << endl; + f_types_ << indent() << " return" << endl; + f_types_ << indent() << "}" << endl; + f_types_ << indent() << "return true, err" << endl; + } else { + f_types_ << endl; + f_types_ << indent() << "return true, nil" << endl; + } + indent_down(); + f_types_ << indent() << "}" << endl << endl; +} + +/** + * Deserializes a field of any type. + */ +void t_go_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + bool declare, + string prefix, + bool inclass, + bool coerceData, + bool inkey, + bool in_container_value, + bool use_true_type) { + (void)inclass; + (void)coerceData; + t_type* orig_type = tfield->get_type(); + t_type* type = get_true_type(orig_type); + string name(prefix + publicize(tfield->get_name())); + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + name; + } + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, + (t_struct*)type, + is_pointer_field(tfield, in_container_value), + declare, + name); + } else if (type->is_container()) { + generate_deserialize_container(out, orig_type, is_pointer_field(tfield), declare, name); + } else if (type->is_base_type() || type->is_enum()) { + + if (declare) { + t_type* actual_type = use_true_type ? tfield->get_type()->get_true_type() + : tfield->get_type(); + + string type_name = inkey ? type_to_go_key_type(actual_type) : type_to_go_type(actual_type); + + out << "var " << tfield->get_name() << " " << type_name << endl; + } + + indent(out) << "if v, err := 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 (((t_base_type*)type)->is_binary() && !inkey) { + 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 Go name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "ReadI32()"; + } + + out << "; err != nil {" << endl; + out << indent() << "return thrift.PrependError(\"error reading field " << tfield->get_key() + << ": \", err)" << endl; + + out << "} else {" << endl; + string wrap; + + if (type->is_enum() || (orig_type->is_typedef() && !use_true_type)) { + wrap = publicize(type_name(orig_type)); + } else if (((t_base_type*)type)->get_base() == t_base_type::TYPE_I8) { + wrap = "int8"; + } + + string maybe_address = (is_pointer_field(tfield) ? "&" : ""); + if (wrap == "") { + indent(out) << name << " = " << maybe_address << "v" << endl; + } else { + indent(out) << "temp := " << wrap << "(v)" << endl; + indent(out) << name << " = " << maybe_address << "temp" << endl; + } + + out << "}" << endl; + } else { + throw "INVALID TYPE IN generate_deserialize_field '" + type->get_name() + "' for field '" + + tfield->get_name() + "'"; + } +} + +/** + * Generates an unserializer for a struct, calling read() + */ +void t_go_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + bool pointer_field, + bool declare, + string prefix) { + string eq(declare ? " := " : " = "); + + 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() << " return thrift.PrependError(fmt.Sprintf(\"%T error reading struct: \", " + << prefix << "), err)" << endl; + out << indent() << "}" << endl; +} + +/** + * Serialize a container by writing out the header followed by + * data and then a footer. + */ +void t_go_generator::generate_deserialize_container(ofstream& out, + t_type* orig_type, + bool pointer_field, + bool declare, + string prefix) { + t_type* ttype = get_true_type(orig_type); + string eq(" = "); + + if (declare) { + eq = " := "; + } + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << "_, _, size, err := iprot.ReadMapBegin()" << 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()) { + t_set* t = (t_set*)ttype; + out << indent() << "_, size, err := iprot.ReadSetBegin()" << 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(map[" + << type_to_go_key_type(t->get_elem_type()->get_true_type()) << "]struct{}, size)" << endl; + out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tSet" << endl; + } else if (ttype->is_list()) { + out << indent() << "_, size, err := iprot.ReadListBegin()" << endl; + out << indent() << "if err != nil {" << endl; + out << indent() << " return thrift.PrependError(\"error reading list begin: \", err)" << endl; + out << indent() << "}" << endl; + out << indent() << "tSlice := make(" << type_to_go_type(orig_type) << ", 0, size)" << endl; + out << indent() << prefix << eq << " " << (pointer_field ? "&" : "") << "tSlice" << endl; + } else { + throw "INVALID TYPE IN generate_deserialize_container '" + ttype->get_name() + "' for prefix '" + + prefix + "'"; + } + + // For loop iterates over elements + out << indent() << "for i := 0; i < size; i ++ {" << endl; + indent_up(); + + if (pointer_field) { + prefix = "(*" + prefix + ")"; + } + if (ttype->is_map()) { + generate_deserialize_map_element(out, (t_map*)ttype, declare, prefix); + } else if (ttype->is_set()) { + generate_deserialize_set_element(out, (t_set*)ttype, declare, prefix); + } else if (ttype->is_list()) { + generate_deserialize_list_element(out, (t_list*)ttype, declare, prefix); + } + + indent_down(); + out << indent() << "}" << endl; + + // Read container end + if (ttype->is_map()) { + out << indent() << "if err := iprot.ReadMapEnd(); 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() << " 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() << " return thrift.PrependError(\"error reading list end: \", err)" << endl; + out << indent() << "}" << endl; + } +} + +/** + * Generates code to deserialize a map + */ +void t_go_generator::generate_deserialize_map_element(ofstream& out, + t_map* tmap, + bool declare, + string prefix) { + (void)declare; + string key = tmp("_key"); + string val = tmp("_val"); + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + fkey.set_req(t_field::T_OPT_IN_REQ_OUT); + fval.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_deserialize_field(out, &fkey, true, "", false, false, true); + generate_deserialize_field(out, &fval, true, "", false, false, false, true); + indent(out) << prefix << "[" << key << "] = " << val << endl; +} + +/** + * Write a set element + */ +void t_go_generator::generate_deserialize_set_element(ofstream& out, + t_set* tset, + bool declare, + string prefix) { + (void)declare; + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + felem.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_deserialize_field(out, &felem, true, "", false, false, false, true, true); + indent(out) << prefix << "[" << elem << "] = struct{}{}" << endl; +} + +/** + * Write a list element + */ +void t_go_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + bool declare, + string prefix) { + (void)declare; + string elem = tmp("_elem"); + t_field felem(((t_list*)tlist)->get_elem_type(), elem); + felem.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_deserialize_field(out, &felem, true, "", false, false, false, true, true); + indent(out) << prefix << " = append(" << prefix << ", " << elem << ")" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_go_generator::generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool inkey) { + t_type* type = get_true_type(tfield->get_type()); + string name(prefix + publicize(tfield->get_name())); + + // Do nothing for void types + if (type->is_void()) { + throw "compiler error: cannot generate serialize 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, is_pointer_field(tfield), name); + } else if (type->is_base_type() || type->is_enum()) { + indent(out) << "if err := oprot."; + + if (is_pointer_field(tfield)) { + name = "*" + 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 (((t_base_type*)type)->is_binary() && !inkey) { + out << "WriteBinary(" << name << ")"; + } else { + out << "WriteString(string(" << name << "))"; + } + + break; + + case t_base_type::TYPE_BOOL: + out << "WriteBool(bool(" << name << "))"; + break; + + case t_base_type::TYPE_I8: + out << "WriteByte(int8(" << name << "))"; + break; + + case t_base_type::TYPE_I16: + out << "WriteI16(int16(" << name << "))"; + break; + + case t_base_type::TYPE_I32: + out << "WriteI32(int32(" << name << "))"; + break; + + case t_base_type::TYPE_I64: + out << "WriteI64(int64(" << name << "))"; + break; + + case t_base_type::TYPE_DOUBLE: + out << "WriteDouble(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 << "; err != nil {" << endl; + out << indent() << "return thrift.PrependError(fmt.Sprintf(\"%T." + << escape_string(tfield->get_name()) << " (" << tfield->get_key() + << ") field write error: \", p), err) }" << endl; + } else { + throw "compiler error: Invalid type in generate_serialize_field '" + type->get_name() + + "' for field '" + name + "'"; + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_go_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + out << indent() << "if err := " << prefix << "." << write_method_name_ << "(oprot); err != nil {" << endl; + out << indent() << " return thrift.PrependError(fmt.Sprintf(\"%T error writing struct: \", " + << prefix << "), err)" << endl; + out << indent() << "}" << endl; +} + +void t_go_generator::generate_serialize_container(ofstream& out, + t_type* ttype, + bool pointer_field, + string prefix) { + if (pointer_field) { + prefix = "*" + prefix; + } + if (ttype->is_map()) { + out << indent() << "if err := oprot.WriteMapBegin(" + << 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(" + << 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(" + << 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; + out << indent() << "}" << endl; + } else { + throw "compiler error: Invalid type in generate_serialize_container '" + ttype->get_name() + + "' for prefix '" + prefix + "'"; + } + + if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + out << indent() << "for k, v := range " << prefix << " {" << endl; + indent_up(); + generate_serialize_map_element(out, tmap, "k", "v"); + indent_down(); + indent(out) << "}" << endl; + } else if (ttype->is_set()) { + t_set* tset = (t_set*)ttype; + out << indent() << "for v, _ := range " << prefix << " {" << endl; + indent_up(); + generate_serialize_set_element(out, tset, "v"); + indent_down(); + indent(out) << "}" << endl; + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + out << indent() << "for _, v := range " << prefix << " {" << endl; + + indent_up(); + generate_serialize_list_element(out, tlist, "v"); + indent_down(); + indent(out) << "}" << endl; + } + + if (ttype->is_map()) { + out << indent() << "if err := oprot.WriteMapEnd(); 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() << " 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() << " return thrift.PrependError(\"error writing list end: \", err)" << endl; + out << indent() << "}" << endl; + } +} + +/** + * Serializes the members of a map. + * + */ +void t_go_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), ""); + t_field vfield(tmap->get_val_type(), ""); + kfield.set_req(t_field::T_OPT_IN_REQ_OUT); + vfield.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_serialize_field(out, &kfield, kiter, true); + generate_serialize_field(out, &vfield, viter); +} + +/** + * Serializes the members of a set. + */ +void t_go_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string prefix) { + t_field efield(tset->get_elem_type(), ""); + efield.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_serialize_field(out, &efield, prefix); +} + +/** + * Serializes the members of a list. + */ +void t_go_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string prefix) { + t_field efield(tlist->get_elem_type(), ""); + efield.set_req(t_field::T_OPT_IN_REQ_OUT); + generate_serialize_field(out, &efield, prefix); +} + +/** + * Generates the docstring for a given struct. + */ +void t_go_generator::generate_go_docstring(ofstream& out, t_struct* tstruct) { + generate_go_docstring(out, tstruct, tstruct, "Attributes"); +} + +/** + * Generates the docstring for a given function. + */ +void t_go_generator::generate_go_docstring(ofstream& out, t_function* tfunction) { + generate_go_docstring(out, tfunction, tfunction->get_arglist(), "Parameters"); +} + +/** + * Generates the docstring for a struct or function. + */ +void t_go_generator::generate_go_docstring(ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader) { + bool has_doc = false; + stringstream ss; + + if (tdoc->has_doc()) { + has_doc = true; + ss << tdoc->get_doc(); + } + + const vector& fields = tstruct->get_members(); + + if (fields.size() > 0) { + if (has_doc) { + ss << endl; + } + + has_doc = true; + ss << subheader << ":\n"; + vector::const_iterator p_iter; + + for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { + t_field* p = *p_iter; + ss << " - " << publicize(p->get_name()); + + if (p->has_doc()) { + ss << ": " << p->get_doc(); + } else { + ss << endl; + } + } + } + + if (has_doc) { + generate_docstring_comment(out, "", "// ", ss.str(), ""); + } +} + +/** + * Generates the docstring for a generic object. + */ +void t_go_generator::generate_go_docstring(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, "", "//", tdoc->get_doc(), ""); + } +} + +/** + * Declares an argument, which may include initialization as necessary. + * + * @param tfield The field + */ +string t_go_generator::declare_argument(t_field* tfield) { + std::ostringstream result; + result << publicize(tfield->get_name()) << "="; + + if (tfield->get_value() != NULL) { + result << "thrift_spec[" << tfield->get_key() << "][4]"; + } else { + result << "nil"; + } + + return result.str(); +} + +/** + * Renders a struct field initial value. + * + * @param tfield The field, which must have `tfield->get_value() != NULL` + */ +string t_go_generator::render_field_initial_value(t_field* tfield, + const string& name, + bool optional_field) { + t_type* type = get_true_type(tfield->get_type()); + + if (optional_field) { + // The caller will make a second pass for optional fields, + // assigning the result of render_const_value to "*field_name". It + // is maddening that Go syntax does not allow for a type-agnostic + // way to initialize a pointer to a const value, but so it goes. + // The alternative would be to write type specific functions that + // convert from const values to pointer types, but given the lack + // of overloading it would be messy. + return "new(" + type_to_go_type(tfield->get_type()) + ")"; + } else { + return render_const_value(type, tfield->get_value(), name); + } +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_go_generator::function_signature(t_function* tfunction, string prefix) { + // TODO(mcslee): Nitpicky, no ',' if argument_list is empty + return publicize(prefix + tfunction->get_name()) + "(" + argument_list(tfunction->get_arglist()) + + ")"; +} + +/** + * Renders an interface function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_go_generator::function_signature_if(t_function* tfunction, string prefix, bool addError) { + // TODO(mcslee): Nitpicky, no ',' if argument_list is empty + string signature = publicize(prefix + tfunction->get_name()) + "("; + signature += argument_list(tfunction->get_arglist()) + ") ("; + t_type* ret = tfunction->get_returntype(); + t_struct* exceptions = tfunction->get_xceptions(); + string errs = argument_list(exceptions); + + if (!ret->is_void()) { + signature += "r " + type_to_go_type(ret); + + if (addError || errs.size() == 0) { + signature += ", "; + } + } + + if (addError) { + signature += "err error"; + } + + signature += ")"; + return signature; +} + +/** + * Renders a field list + */ +string t_go_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 += variable_name_to_go_name((*f_iter)->get_name()) + " " + + type_to_go_type((*f_iter)->get_type()); + } + + return result; +} + +string t_go_generator::type_name(t_type* ttype) { + string module( module_name(ttype)); + if( ! module.empty()) { + return module + "." + ttype->get_name(); + } + + return ttype->get_name(); +} + +string t_go_generator::module_name(t_type* ttype) { + t_program* program = ttype->get_program(); + + if (program != NULL && 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 ""; +} + +/** + * Converts the parse type to a go tyoe + */ +string t_go_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: + /* this is wrong, binary is still a string type internally + if (((t_base_type*)type)->is_binary()) { + return "thrift.BINARY"; + } + */ + return "thrift.STRING"; + + case t_base_type::TYPE_BOOL: + return "thrift.BOOL"; + + case t_base_type::TYPE_I8: + return "thrift.BYTE"; + + case t_base_type::TYPE_I16: + return "thrift.I16"; + + case t_base_type::TYPE_I32: + return "thrift.I32"; + + case t_base_type::TYPE_I64: + return "thrift.I64"; + + case t_base_type::TYPE_DOUBLE: + return "thrift.DOUBLE"; + } + } else if (type->is_enum()) { + return "thrift.I32"; + } else if (type->is_struct() || type->is_xception()) { + return "thrift.STRUCT"; + } else if (type->is_map()) { + return "thrift.MAP"; + } else if (type->is_set()) { + return "thrift.SET"; + } else if (type->is_list()) { + return "thrift.LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts the parse type to a go map type, will throw an exception if it will + * not produce a valid go map type. + */ +string t_go_generator::type_to_go_key_type(t_type* type) { + t_type* resolved_type = type; + + while (resolved_type->is_typedef()) { + resolved_type = ((t_typedef*)resolved_type)->get_type()->get_true_type(); + } + + if (resolved_type->is_map() || resolved_type->is_list() || resolved_type->is_set()) { + throw "Cannot produce a valid type for a Go map key: " + type_to_go_type(type) + " - aborting."; + } + + if (resolved_type->is_string() && ((t_base_type*)resolved_type)->is_binary()) + return "string"; + + return type_to_go_type(type); +} + +/** + * Converts the parse type to a go type + */ +string t_go_generator::type_to_go_type(t_type* type) { + return type_to_go_type_with_opt(type, false); +} + +/** + * Converts the parse type to a go type, taking into account whether the field + * associated with the type is T_OPTIONAL. + */ +string t_go_generator::type_to_go_type_with_opt(t_type* type, + bool optional_field) { + string maybe_pointer(optional_field ? "*" : ""); + + if (type->is_typedef() && ((t_typedef*)type)->is_forward_typedef()) { + type = ((t_typedef*)type)->get_true_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 ""; + + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + return maybe_pointer + "[]byte"; + } + + return maybe_pointer + "string"; + + case t_base_type::TYPE_BOOL: + return maybe_pointer + "bool"; + + case t_base_type::TYPE_I8: + return maybe_pointer + "int8"; + + case t_base_type::TYPE_I16: + return maybe_pointer + "int16"; + + case t_base_type::TYPE_I32: + return maybe_pointer + "int32"; + + case t_base_type::TYPE_I64: + return maybe_pointer + "int64"; + + case t_base_type::TYPE_DOUBLE: + return maybe_pointer + "float64"; + } + } else if (type->is_enum()) { + return maybe_pointer + publicize(type_name(type)); + } else if (type->is_struct() || type->is_xception()) { + return "*" + publicize(type_name(type)); + } else if (type->is_map()) { + t_map* t = (t_map*)type; + string keyType = type_to_go_key_type(t->get_key_type()); + string valueType = type_to_go_type(t->get_val_type()); + return maybe_pointer + string("map[") + keyType + "]" + valueType; + } else if (type->is_set()) { + t_set* t = (t_set*)type; + string elemType = type_to_go_key_type(t->get_elem_type()); + return maybe_pointer + string("map[") + elemType + string("]struct{}"); + } else if (type->is_list()) { + t_list* t = (t_list*)type; + string elemType = type_to_go_type(t->get_elem_type()); + return maybe_pointer + string("[]") + elemType; + } else if (type->is_typedef()) { + return maybe_pointer + publicize(type_name(type)); + } + + throw "INVALID TYPE IN type_to_go_type: " + type->get_name(); +} + +/** See the comment inside generate_go_struct_definition for what this is. */ +string t_go_generator::type_to_spec_args(t_type* ttype) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_base_type() || ttype->is_enum()) { + return "nil"; + } else if (ttype->is_struct() || ttype->is_xception()) { + return "(" + type_name(ttype) + ", " + type_name(ttype) + ".thrift_spec)"; + } else if (ttype->is_map()) { + return "(" + type_to_enum(((t_map*)ttype)->get_key_type()) + "," + + type_to_spec_args(((t_map*)ttype)->get_key_type()) + "," + + type_to_enum(((t_map*)ttype)->get_val_type()) + "," + + type_to_spec_args(((t_map*)ttype)->get_val_type()) + ")"; + } else if (ttype->is_set()) { + return "(" + type_to_enum(((t_set*)ttype)->get_elem_type()) + "," + + type_to_spec_args(((t_set*)ttype)->get_elem_type()) + ")"; + } else if (ttype->is_list()) { + return "(" + type_to_enum(((t_list*)ttype)->get_elem_type()) + "," + + type_to_spec_args(((t_list*)ttype)->get_elem_type()) + ")"; + } + + throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name(); +} + +bool format_go_output(const string& file_path) { + + // formatting via gofmt deactivated due to THRIFT-3893 + // Please look at the ticket and make sure you fully understand all the implications + // before submitting a patch that enables this feature again. Thank you. + (void) file_path; + return false; + + /* + const string command = "gofmt -w " + file_path; + + if (system(command.c_str()) == 0) { + return true; + } + + fprintf(stderr, "WARNING - Running '%s' failed.\n", command.c_str()); + return false; + */ + } + +THRIFT_REGISTER_GENERATOR(go, "Go", + " package_prefix= Package prefix for generated files.\n" \ + " thrift_import= Override thrift package import path (default:" + DEFAULT_THRIFT_IMPORT + ")\n" \ + " package= Package name (default: inferred from thrift file name)\n" \ + " ignore_initialisms\n" + " Disable automatic spelling correction of initialisms (e.g. \"URL\")\n" \ + " read_write_private\n" + " Make read/write methods private, default is public Read/Write\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc new file mode 100644 index 00000000..72b7c822 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc @@ -0,0 +1,345 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * Graphviz code generator + */ +class t_gv_generator : public t_generator { +public: + t_gv_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; + + exception_arrows = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("exceptions") == 0) { + exception_arrows = true; + } else { + throw "unknown option gv:" + iter->first; + } + } + + out_dir_base_ = "gen-gv"; + } + + /** + * Init and end of generator + */ + void init_generator(); + void close_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); + +protected: + /** + * Helpers + */ + void print_type(t_type* ttype, string struct_field_ref); + void print_const_value(t_type* type, t_const_value* tvalue); + +private: + std::ofstream f_out_; + std::list edges; + bool exception_arrows; +}; + +/** + * Init generator: + * - Adds some escaping for the Graphviz domain. + * - Create output directory and open file for writting. + * - Write the file header. + */ +void t_gv_generator::init_generator() { + escape_['{'] = "\\{"; + escape_['}'] = "\\}"; + + // Make output directory + MKDIR(get_out_dir().c_str()); + string fname = get_out_dir() + program_->get_name() + ".gv"; + f_out_.open(fname.c_str()); + f_out_ << "digraph \"" << escape_string(program_name_) << "\" {" << endl; + f_out_ << "node [style=filled, shape=record];" << endl; + f_out_ << "edge [arrowsize=0.5];" << endl; + f_out_ << "rankdir=LR" << endl; +} + +/** + * Closes generator: + * - Print accumulated nodes connections. + * - Print footnote. + * - Closes file. + */ +void t_gv_generator::close_generator() { + // Print edges + std::list::iterator iter = edges.begin(); + for (; iter != edges.end(); iter++) { + f_out_ << (*iter) << endl; + } + + // Print graph end } and close file + f_out_ << "}" << endl; + f_out_.close(); +} + +void t_gv_generator::generate_typedef(t_typedef* ttypedef) { + string name = ttypedef->get_name(); + f_out_ << "node [fillcolor=azure];" << endl; + f_out_ << name << " [label=\""; + + f_out_ << escape_string(name); + f_out_ << " :: "; + print_type(ttypedef->get_type(), name); + + f_out_ << "\"];" << endl; +} + +void t_gv_generator::generate_enum(t_enum* tenum) { + string name = tenum->get_name(); + f_out_ << "node [fillcolor=white];" << endl; + f_out_ << name << " [label=\"enum " << escape_string(name); + + vector values = tenum->get_constants(); + vector::iterator val_iter; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + f_out_ << '|' << (*val_iter)->get_name(); + f_out_ << " = "; + f_out_ << (*val_iter)->get_value(); + } + + f_out_ << "\"];" << endl; +} + +void t_gv_generator::generate_const(t_const* tconst) { + string name = tconst->get_name(); + + f_out_ << "node [fillcolor=aliceblue];" << endl; + f_out_ << "const_" << name << " [label=\""; + + f_out_ << escape_string(name); + f_out_ << " = "; + print_const_value(tconst->get_type(), tconst->get_value()); + f_out_ << " :: "; + print_type(tconst->get_type(), "const_" + name); + + f_out_ << "\"];" << endl; +} + +void t_gv_generator::generate_struct(t_struct* tstruct) { + string name = tstruct->get_name(); + + if (tstruct->is_xception()) { + f_out_ << "node [fillcolor=lightpink];" << endl; + f_out_ << name << " [label=\""; + f_out_ << "exception " << escape_string(name); + } else if (tstruct->is_union()) { + f_out_ << "node [fillcolor=lightcyan];" << endl; + f_out_ << name << " [label=\""; + f_out_ << "union " << escape_string(name); + } else { + f_out_ << "node [fillcolor=beige];" << endl; + f_out_ << name << " [label=\""; + f_out_ << "struct " << escape_string(name); + } + + vector members = tstruct->get_members(); + vector::iterator mem_iter = members.begin(); + for (; mem_iter != members.end(); mem_iter++) { + string field_name = (*mem_iter)->get_name(); + + // print port (anchor reference) + f_out_ << "|'; + + // field name :: field type + f_out_ << (*mem_iter)->get_name(); + f_out_ << " :: "; + print_type((*mem_iter)->get_type(), name + ":field_" + field_name); + } + + f_out_ << "\"];" << endl; +} + +void t_gv_generator::print_type(t_type* ttype, string struct_field_ref) { + if (ttype->is_container()) { + if (ttype->is_list()) { + f_out_ << "list\\<"; + print_type(((t_list*)ttype)->get_elem_type(), struct_field_ref); + f_out_ << "\\>"; + } else if (ttype->is_set()) { + f_out_ << "set\\<"; + print_type(((t_set*)ttype)->get_elem_type(), struct_field_ref); + f_out_ << "\\>"; + } else if (ttype->is_map()) { + f_out_ << "map\\<"; + print_type(((t_map*)ttype)->get_key_type(), struct_field_ref); + f_out_ << ", "; + print_type(((t_map*)ttype)->get_val_type(), struct_field_ref); + f_out_ << "\\>"; + } + } else if (ttype->is_base_type()) { + f_out_ << (((t_base_type*)ttype)->is_binary() ? "binary" : ttype->get_name()); + } else { + f_out_ << ttype->get_name(); + edges.push_back(struct_field_ref + " -> " + ttype->get_name()); + } +} + +/** + * Prints out an string representation of the provided constant value + */ +void t_gv_generator::print_const_value(t_type* type, t_const_value* tvalue) { + bool first = true; + switch (tvalue->get_type()) { + case t_const_value::CV_INTEGER: + f_out_ << tvalue->get_integer(); + break; + case t_const_value::CV_DOUBLE: + f_out_ << tvalue->get_double(); + break; + case t_const_value::CV_STRING: + f_out_ << "\\\"" << get_escaped_string(tvalue) << "\\\""; + break; + case t_const_value::CV_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*)type)->get_key_type(), map_iter->first); + f_out_ << " = "; + print_const_value(((t_map*)type)->get_val_type(), map_iter->second); + } + f_out_ << " \\}"; + } break; + case t_const_value::CV_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; + if (type->is_list()) { + print_const_value(((t_list*)type)->get_elem_type(), *list_iter); + } else { + print_const_value(((t_set*)type)->get_elem_type(), *list_iter); + } + } + f_out_ << " \\}"; + } break; + case t_const_value::CV_IDENTIFIER: + f_out_ << escape_string(type->get_name()) << "." + << escape_string(tvalue->get_identifier_name()); + break; + default: + f_out_ << "UNKNOWN"; + break; + } +} + +void t_gv_generator::generate_service(t_service* tservice) { + string service_name = get_service_name(tservice); + f_out_ << "subgraph cluster_" << service_name << " {" << endl; + f_out_ << "node [fillcolor=bisque];" << endl; + f_out_ << "style=dashed;" << endl; + f_out_ << "label = \"" << escape_string(service_name) << " service\";" << endl; + + // TODO: service extends + + 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; + f_out_ << "[label=\"function " << escape_string(fn_name); + f_out_ << " :: "; + print_type((*fn_iter)->get_returntype(), "function_" + service_name + fn_name + ":return_type"); + + vector args = (*fn_iter)->get_arglist()->get_members(); + vector::iterator arg_iter = args.begin(); + for (; arg_iter != args.end(); arg_iter++) { + f_out_ << "|get_name() << ">"; + f_out_ << (*arg_iter)->get_name(); + if ((*arg_iter)->get_value() != NULL) { + f_out_ << " = "; + print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value()); + } + f_out_ << " :: "; + print_type((*arg_iter)->get_type(), + "function_" + service_name + fn_name + ":param_" + (*arg_iter)->get_name()); + } + // end of node + f_out_ << "\"];" << endl; + + // Exception edges + if (exception_arrows) { + vector excepts = (*fn_iter)->get_xceptions()->get_members(); + vector::iterator ex_iter = excepts.begin(); + for (; ex_iter != excepts.end(); ex_iter++) { + edges.push_back("function_" + service_name + fn_name + " -> " + + (*ex_iter)->get_type()->get_name() + " [color=red]"); + } + } + } + + f_out_ << " }" << endl; +} + +THRIFT_REGISTER_GENERATOR( + gv, + "Graphviz", + " exceptions: Whether to draw arrows from functions to exception.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc new file mode 100644 index 00000000..e7be1a5b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_haxe_generator.cc @@ -0,0 +1,2981 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * Haxe code generator. + * + */ +class t_haxe_generator : public t_oop_generator { +public: + t_haxe_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; + + callbacks_ = false; + rtti_ = false; + buildmacro_ = ""; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("callbacks") == 0) { + callbacks_ = true; + } else if( iter->first.compare("rtti") == 0) { + rtti_ = true; + } else if( iter->first.compare("buildmacro") == 0) { + buildmacro_ = (iter->second); + } else { + throw "unknown option haxe:" + iter->first; + } + } + + out_dir_base_ = "gen-haxe"; + } + + /** + * 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(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval = false); + std::string render_const_value(ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + /** + * Service-level generation functions + */ + + void generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result = false); + + void generate_haxe_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_result = false); + // removed -- equality,compare_to + void generate_haxe_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_haxe_validator(std::ofstream& out, t_struct* tstruct); + void generate_haxe_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_haxe_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_haxe_struct_tostring(std::ofstream& out, t_struct* tstruct); + void generate_haxe_meta_data_map(std::ofstream& out, t_struct* tstruct); + void generate_field_value_meta_data(std::ofstream& out, t_type* type); + std::string get_haxe_type_string(t_type* type); + void generate_reflection_setters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_reflection_getters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_generic_field_getters_setters(std::ofstream& out, t_struct* tstruct); + void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); + void generate_property_getters_setters(std::ofstream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + std::string get_cap_name(std::string name); + std::string generate_isset_check(t_field* field); + std::string generate_isset_check(std::string field); + void generate_isset_set(ofstream& out, t_field* field); + // removed std::string isset_field_id(t_field* field); + + void generate_service_interface(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_process_function(t_service* tservice, t_function* tfunction); + void generate_service_method_signature(t_function* tfunction, bool is_interface); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + + void generate_haxe_doc(std::ofstream& out, t_doc* tdoc); + void generate_haxe_doc(std::ofstream& out, t_function* tdoc); + + void generate_rtti_decoration(std::ofstream& out); + void generate_macro_decoration(std::ofstream& out); + + /** + * Helper rendering functions + */ + + std::string haxe_package(); + std::string haxe_type_imports(); + std::string haxe_thrift_imports(); + std::string haxe_thrift_gen_imports(t_struct* tstruct, string& imports); + std::string haxe_thrift_gen_imports(t_service* tservice); + std::string type_name(t_type* ttype, bool in_container = false, bool in_init = false); + std::string base_type_name(t_base_type* tbase, bool in_container = false); + std::string declare_field(t_field* tfield, bool init = false); + std::string function_signature_callback(t_function* tfunction); + 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); + 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); + + bool type_can_be_null(t_type* ttype) { + ttype = get_true_type(ttype); + + if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) { + return true; + } + + if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + // case t_base_type::TYPE_I64: - Int64 is not really nullable, even though it behaved that + // way before Haxe 3.2.0 + return true; + default: + return false; + } + } + + return false; + } + + std::string constant_name(std::string name); + +private: + bool callbacks_; + bool rtti_; + string buildmacro_; + + /** + * File streams + */ + + std::string package_name_; + std::ofstream f_service_; + std::string package_dir_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_haxe_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + package_name_ = program_->get_namespace("haxe"); + + // Haxe package names are lowercase + if (package_name_.length() > 0) { + package_name_[0] = tolower(package_name_[0]); + size_t index = package_name_.find('.'); + while (index != std::string::npos) { + if (++index < package_name_.length()) { + package_name_[index] = tolower(package_name_[index]); + } + index = package_name_.find('.', index); + } + } + + string dir = package_name_; + string subdir = get_out_dir(); + 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()); + } + + package_dir_ = subdir; +} + +/** + * Packages the generated file + * + * @return String of the package, i.e. "package org.apache.thriftdemo;" + */ +string t_haxe_generator::haxe_package() { + if (!package_name_.empty()) { + return string("package ") + package_name_; + } + return "package"; +} + +/** + * Prints standard haxe imports + * + * @return List of imports for haxe types that are used in here + */ +string t_haxe_generator::haxe_type_imports() { + return string() + "import org.apache.thrift.helper.*;\n" + "import haxe.io.Bytes;\n" + + "import haxe.ds.IntMap;\n" + "import haxe.ds.StringMap;\n" + + "import haxe.ds.ObjectMap;\n" + "\n" + "#if flash\n" + + "import flash.errors.ArgumentError;\n" + "#end\n" + "\n"; +} + +/** + * Prints standard haxe imports + * + * @return List of imports necessary for thrift + */ +string t_haxe_generator::haxe_thrift_imports() { + return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n" + + "import org.apache.thrift.protocol.*;\n" + "\n"; +} + +/** + * Prints imports needed for a given type + * + * @return List of imports necessary for a given t_struct + */ +string t_haxe_generator::haxe_thrift_gen_imports(t_struct* tstruct, string& imports) { + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + // 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_) { + string package = program->get_namespace("haxe"); + if (!package.empty()) { + if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) { + imports.append("import " + package + "." + (*m_iter)->get_type()->get_name() + ";\n"); + } + } + } + } + return imports; +} + +/** + * Prints imports needed for a given type + * + * @return List of imports necessary for a given t_service + */ +string t_haxe_generator::haxe_thrift_gen_imports(t_service* tservice) { + string imports; + const vector& functions = tservice->get_functions(); + vector::const_iterator f_iter; + + // 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_) { + string package = program->get_namespace("haxe"); + if (!package.empty()) { + if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) { + imports.append("import " + package + "." + (*f_iter)->get_returntype()->get_name() + + ";\n"); + } + } + } + + haxe_thrift_gen_imports((*f_iter)->get_arglist(), imports); + haxe_thrift_gen_imports((*f_iter)->get_xceptions(), imports); + } + + return imports; +} + +/** + * Nothing in haxe + */ +void t_haxe_generator::close_generator() { +} + +/** + * Generates a typedef. This is not done in haxe, since it does + * not support arbitrary name replacements, and it'd be a wacky waste + * of overhead to make wrapper classes. + * + * @param ttypedef The type definition + */ +void t_haxe_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Enums are a class with a set of static constants. + * + * @param tenum The enumeration + */ +void t_haxe_generator::generate_enum(t_enum* tenum) { + // Make output file + string f_enum_name = package_dir_ + "/" + get_cap_name(tenum->get_name()) + ".hx"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + // Comment and package it + f_enum << autogen_comment() << haxe_package() << ";" << endl << endl; + + // Add haxe imports + f_enum << string() + "import org.apache.thrift.helper.*;" << endl << endl; + + generate_rtti_decoration(f_enum); + generate_macro_decoration(f_enum); + indent(f_enum) << "class " << get_cap_name(tenum->get_name()) << " "; + scope_up(f_enum); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << "public static inline var " << (*c_iter)->get_name() << " : Int = " << value + << ";" << endl; + } + + // Create a static Set with all valid values for this enum + f_enum << endl; + + indent(f_enum) << "public static var VALID_VALUES = { new IntSet( ["; + indent_up(); + bool firstValue = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + // populate set + f_enum << (firstValue ? "" : ", ") << (*c_iter)->get_name(); + firstValue = false; + } + indent_down(); + f_enum << "]); };" << endl; + + indent(f_enum) << "public static var VALUES_TO_NAMES = { ["; + indent_up(); + firstValue = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + f_enum << (firstValue ? "" : ",") << endl; + indent(f_enum) << (*c_iter)->get_name() << " => \"" << (*c_iter)->get_name() << "\""; + firstValue = false; + } + f_enum << endl; + indent_down(); + indent(f_enum) << "]; };" << endl; + + scope_down(f_enum); // end class + + f_enum.close(); +} + +/** + * Generates a class that holds all the constants. + */ +void t_haxe_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + string f_consts_name = package_dir_ + "/" + get_cap_name(program_name_) + "Constants.hx"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + // Print header + f_consts << autogen_comment() << haxe_package() << ";" << endl << endl; + + f_consts << endl; + + f_consts << haxe_type_imports(); + + generate_rtti_decoration(f_consts); + generate_macro_decoration(f_consts); + indent(f_consts) << "class " << get_cap_name(program_name_) << "Constants {" << endl << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false); + } + indent_down(); + indent(f_consts) << "}" << endl; + f_consts.close(); +} + +void t_haxe_generator::print_const_value(std::ofstream& out, + string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval) { + type = get_true_type(type); + + indent(out); + if (!defval) { + out << (in_static ? "var " : "public static inline var "); + } + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = " << value->get_integer() << ";" << endl << endl; + } 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; + out << name << ":" << type_name(type) << " = new " << type_name(type, false, true) << "();" + << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function() : Void {" << endl; + indent_up(); + } + 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, name, field_type, v_iter->second); + indent(out) << name << "."; + out << v_iter->first->get_string() << " = " << val << ";" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_map()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function() : Void {" << endl; + indent_up(); + } + 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; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_list() || type->is_set()) { + out << name; + if (!defval) { + out << ":" << type_name(type); + } + out << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "{" << endl; + indent_up(); + indent(out) << "new function() : Void {" << endl; + indent_up(); + } + 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 << "." << (type->is_list() ? "push" : "add") << "(" << val << ");" + << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}();" << endl; + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else { + throw "compiler error: no const of type " + type->get_name(); + } +} + +string t_haxe_generator::render_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + 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) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + render << "(byte)" << value->get_integer(); + break; + case t_base_type::TYPE_I16: + render << "(short)" << value->get_integer(); + break; + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << "(double)" << 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); + render << t; + } + + return render.str(); +} + +/** + * Generates a struct definition for a thrift data type. This is a class + * with data members, read(), write(), and an inner Isset class. + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_struct(t_struct* tstruct) { + generate_haxe_struct(tstruct, false); +} + +/** + * Exceptions are structs, but they inherit from Exception + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_xception(t_struct* txception) { + generate_haxe_struct(txception, true); +} + +/** + * Haxe struct definition. + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result) { + // Make output file + string f_struct_name = package_dir_ + "/" + get_cap_name(tstruct->get_name()) + ".hx"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << haxe_package() << ";" << endl; + + f_struct << endl; + + string imports; + + f_struct << haxe_type_imports() << haxe_thrift_imports() + << haxe_thrift_gen_imports(tstruct, imports) << endl; + + generate_haxe_struct_definition(f_struct, tstruct, is_exception, is_result); + + f_struct.close(); +} + +/** + * haxe struct definition. This has various parameters, as it could be + * generated standalone or inside another class as a helper. If it + * is a helper than it is a static class. + * + * @param tstruct The struct definition + * @param is_exception Is this an exception? + * @param in_class If inside a class, needs to be static class + * @param is_result If this is a result it needs a different writer + */ +void t_haxe_generator::generate_haxe_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_result) { + generate_haxe_doc(out, tstruct); + + string clsname = get_cap_name(tstruct->get_name()); + + generate_rtti_decoration(out); + generate_macro_decoration(out); + indent(out) << "class " << clsname << " "; + + if (is_exception) { + out << "extends TException "; + } + out << "implements TBase "; + + scope_up(out); + indent(out) << endl; + + indent(out) << "static var STRUCT_DESC = { new TStruct(\"" << tstruct->get_name() << "\"); };" + << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "static var " << constant_name((*m_iter)->get_name()) + << "_FIELD_DESC = { new TField(\"" << (*m_iter)->get_name() << "\", " + << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << "); };" + << endl; + } + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_haxe_doc(out, *m_iter); + // indent(out) << "private var _" << (*m_iter)->get_name() + " : " + + // type_name((*m_iter)->get_type()) << ";" << endl; + indent(out) << "@:isVar" << endl; + indent(out) << "public var " + << (*m_iter)->get_name() + "(get,set) : " + + get_cap_name(type_name((*m_iter)->get_type())) << ";" << endl; + } + + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "inline static var " << upcase_string((*m_iter)->get_name()) + << "_FIELD_ID : Int = " << (*m_iter)->get_key() << ";" << endl; + } + + out << endl; + + // Inner Isset class + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null((*m_iter)->get_type())) { + indent(out) << "private var __isset_" << (*m_iter)->get_name() << " : Bool = false;" + << endl; + } + } + } + + out << endl; + + // Static initializer to populate global class to struct metadata map + if (false) { + // TODO: reactivate when needed + generate_haxe_meta_data_map(out, tstruct); + indent(out) << "{" << endl; + indent_up(); + indent(out) << "FieldMetaData.addStructMetaDataMap(" << type_name(tstruct) << ", metaDataMap);" + << endl; + indent_down(); + indent(out) << "}" << endl; + indent(out) << "}" << endl; + } + + // Default constructor + indent(out) << "public function new() {" << endl; + indent_up(); + if (is_exception) { + indent(out) << "super();" << endl; + } + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_value() != NULL) { + indent(out) << "this." << (*m_iter)->get_name() << " = " + << (*m_iter)->get_value()->get_integer() << ";" << endl; + } + } + indent_down(); + indent(out) << "}" << endl << endl; + + generate_property_getters_setters(out, tstruct); + generate_generic_field_getters_setters(out, tstruct); + generate_generic_isset_method(out, tstruct); + + generate_haxe_struct_reader(out, tstruct); + if (is_result) { + generate_haxe_struct_result_writer(out, tstruct); + } else { + generate_haxe_struct_writer(out, tstruct); + } + generate_haxe_struct_tostring(out, tstruct); + generate_haxe_validator(out, tstruct); + scope_down(out); + out << endl; +} + +/** + * Generates a function to read all the fields of the struct. + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_struct_reader(ofstream& out, t_struct* tstruct) { + out << indent() << "public function read( iprot : TProtocol) : Void {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "iprot.IncrementRecursionDepth();" << endl; + indent(out) << "try" << endl; + scope_up(out); + + // Declare stack tmp variables and read struct header + out << indent() << "var field : TField;" << endl << indent() << "iprot.readStructBegin();" + << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "field = iprot.readFieldBegin();" << endl; + + // Check for field STOP marker and break + indent(out) << "if (field.type == TType.STOP) { " << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << "switch (field.id)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << upcase_string((*f_iter)->get_name()) << "_FIELD_ID:" << endl; + indent_up(); + indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + generate_deserialize_field(out, *f_iter, "this."); + generate_isset_set(out, *f_iter); + indent_down(); + out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" + << endl << indent() << "}" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "default:" << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" + << endl; + + scope_down(out); + + // Read field end marker + indent(out) << "iprot.readFieldEnd();" << endl; + + scope_down(out); + + out << indent() << "iprot.readStructEnd();" << endl << endl; + + indent(out) << "iprot.DecrementRecursionDepth();" << endl; + scope_down(out); + indent(out) << "catch(e:Dynamic)" << endl; + scope_up(out); + indent(out) << "iprot.DecrementRecursionDepth();" << endl; + indent(out) << "throw e;" << endl; + scope_down(out); + + // check for required fields of primitive type + // (which can be checked here but not in the general validate method) + out << endl << indent() << "// check for required fields of primitive type, which can't be " + "checked in the validate method" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) { + out << indent() << "if (!__isset_" << (*f_iter)->get_name() << ") {" << endl << indent() + << " throw new TProtocolException(TProtocolException.UNKNOWN, \"Required field '" + << (*f_iter)->get_name() + << "' was not found in serialized data! Struct: \" + toString());" << endl << indent() + << "}" << endl; + } + } + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +// generates haxe method to perform various checks +// (e.g. check that all required fields are set) +void t_haxe_generator::generate_haxe_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "public function validate() : Void {" << 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) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + if (type_can_be_null((*f_iter)->get_type())) { + indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) + << " throw new TProtocolException(TProtocolException.UNKNOWN, \"Required field '" + << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name() + << "' because it's a primitive." << endl; + } + } + } + + // check that fields of type enum have valid values + out << indent() << "// check that fields of type enum have valid values" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + t_type* type = field->get_type(); + // if field is an enum, check that its value is valid + if (type->is_enum()) { + indent(out) << "if (" << generate_isset_check(field) << " && !" + << get_cap_name(get_enum_class_name(type)) << ".VALID_VALUES.contains(" + << field->get_name() << ")){" << endl; + indent_up(); + indent(out) << "throw new TProtocolException(TProtocolException.UNKNOWN, \"The field '" + << field->get_name() << "' has been assigned the invalid value \" + " + << field->get_name() << ");" << endl; + indent_down(); + indent(out) << "}" << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to write all the fields of the struct + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_struct_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public function write(oprot:TProtocol) : Void {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl; + indent(out) << "oprot.IncrementRecursionDepth();" << endl; + indent(out) << "try" << endl; + scope_up(out); + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + } + + indent(out) << "oprot.writeFieldStop();" << endl; + indent(out) << "oprot.writeStructEnd();" << endl; + + indent(out) << "oprot.DecrementRecursionDepth();" << endl; + scope_down(out); + indent(out) << "catch(e:Dynamic)" << endl; + scope_up(out); + indent(out) << "oprot.DecrementRecursionDepth();" << endl; + indent(out) << "throw e;" << endl; + scope_down(out); + + 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 in the Isset array, and only one of them + * can be set at a time. + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_struct_result_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public function write(oprot:TProtocol) : Void {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << "oprot.IncrementRecursionDepth();" << endl; + indent(out) << "try" << endl; + scope_up(out); + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << 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 << "(this." << generate_isset_check(*f_iter) << ") {" << endl; + + indent_up(); + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}"; + } + + indent(out) << endl; + indent(out) << "oprot.writeFieldStop();" << endl; + indent(out) << "oprot.writeStructEnd();" << endl; + + indent(out) << "oprot.DecrementRecursionDepth();" << endl; + scope_down(out); + indent(out) << "catch(e:Dynamic)" << endl; + scope_up(out); + indent(out) << "oprot.DecrementRecursionDepth();" << endl; + indent(out) << "throw e;" << endl; + scope_down(out); + + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_haxe_generator::generate_reflection_getters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + (void)type; + (void)cap_name; + indent(out) << "case " << upcase_string(field_name) << "_FIELD_ID:" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); +} + +void t_haxe_generator::generate_reflection_setters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + (void)type; + (void)cap_name; + indent(out) << "case " << upcase_string(field_name) << "_FIELD_ID:" << endl; + indent_up(); + indent(out) << "if (value == null) {" << endl; + indent(out) << " unset" << get_cap_name(field_name) << "();" << endl; + indent(out) << "} else {" << endl; + indent(out) << " this." << field_name << " = value;" << endl; + indent(out) << "}" << endl << endl; + + indent_down(); +} + +void t_haxe_generator::generate_generic_field_getters_setters(std::ofstream& 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; + t_type* type = get_true_type(field->get_type()); + std::string field_name = field->get_name(); + std::string cap_name = get_cap_name(field_name); + + indent_up(); + generate_reflection_setters(setter_stream, type, field_name, cap_name); + generate_reflection_getters(getter_stream, type, field_name, cap_name); + indent_down(); + } + + // create the setter + indent(out) << "public function setFieldValue(fieldID : Int, value : Dynamic) : Void {" << endl; + indent_up(); + + if (fields.size() > 0) { + indent(out) << "switch (fieldID) {" << endl; + out << setter_stream.str(); + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + } + + indent_down(); + indent(out) << "}" << endl << endl; + + // create the getter + indent(out) << "public function getFieldValue(fieldID : Int) : Dynamic {" << endl; + indent_up(); + + if (fields.size() > 0) { + indent(out) << "switch (fieldID) {" << endl; + out << getter_stream.str(); + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + } + + indent_down(); + + indent(out) << "}" << endl << endl; +} + +// Creates a generic isSet method that takes the field number as argument +void t_haxe_generator::generate_generic_isset_method(std::ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // create the isSet method + indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a " + "value) and false otherwise" << endl; + indent(out) << "public function isSet(fieldID : Int) : Bool {" << endl; + indent_up(); + if (fields.size() > 0) { + indent(out) << "switch (fieldID) {" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + indent(out) << "case " << upcase_string(field->get_name()) << "_FIELD_ID:" << endl; + indent_up(); + indent(out) << "return " << generate_isset_check(field) << ";" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a set of property setters/getters for the given struct. + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_property_getters_setters(ofstream& out, t_struct* tstruct) { + 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 = get_cap_name(field_name); + + // Simple getter + generate_haxe_doc(out, field); + indent(out) << "public function get_" << field_name << "() : " << get_cap_name(type_name(type)) + << " {" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + // Simple setter + generate_haxe_doc(out, field); + indent(out) << "public function set_" << field_name << "(" << field_name << ":" + << get_cap_name(type_name(type)) << ") : " << get_cap_name(type_name(type)) << " {" + << endl; + indent_up(); + indent(out) << "this." << field_name << " = " << field_name << ";" << endl; + generate_isset_set(out, field); + indent(out) << "return this." << field_name << ";" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; + + // Unsetter + indent(out) << "public function unset" << cap_name << "() : Void {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "this." << field_name << " = null;" << endl; + } else { + indent(out) << "this.__isset_" << field_name << " = false;" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + // isSet method + indent(out) << "// Returns true if field " << field_name + << " is set (has been assigned a value) and false otherwise" << endl; + indent(out) << "public function is" << get_cap_name("set") << cap_name << "() : Bool {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "return this." << field_name << " != null;" << endl; + } else { + indent(out) << "return this.__isset_" << field_name << ";" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + } +} + +/** + * Generates a toString() method for the given struct + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_struct_tostring(ofstream& out, t_struct* tstruct) { + out << indent() << "public " + << "function toString() : String {" << endl; + indent_up(); + + out << indent() << "var ret : String = \"" << tstruct->get_name() << "(\";" << endl; + out << indent() << "var first : Bool = true;" << endl << 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) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + + t_field* field = (*f_iter); + + if (!first) { + indent(out) << "if (!first) ret += \", \";" << endl; + } + indent(out) << "ret += \"" << (*f_iter)->get_name() << ":\";" << endl; + bool can_be_null = type_can_be_null(field->get_type()); + if (can_be_null) { + indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) << " ret += \"null\";" << endl; + indent(out) << "} else {" << endl; + indent_up(); + } + + if (field->get_type()->is_base_type() && ((t_base_type*)(field->get_type()))->is_binary()) { + indent(out) << " ret += \"BINARY\";" << endl; + } else if (field->get_type()->is_enum()) { + indent(out) << "var " << field->get_name() + << "_name : String = " << get_cap_name(get_enum_class_name(field->get_type())) + << ".VALUES_TO_NAMES[this." << (*f_iter)->get_name() << "];" << endl; + indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; + indent(out) << " ret += " << field->get_name() << "_name;" << endl; + indent(out) << " ret += \" (\";" << endl; + indent(out) << "}" << endl; + indent(out) << "ret += this." << field->get_name() << ";" << endl; + indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; + indent(out) << " ret += \")\";" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "ret += this." << (*f_iter)->get_name() << ";" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + indent(out) << "first = false;" << endl; + + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + first = false; + } + out << indent() << "ret += \")\";" << endl << indent() << "return ret;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a static map with meta data to store information such as fieldID to + * fieldName mapping + * + * @param tstruct The struct definition + */ +void t_haxe_generator::generate_haxe_meta_data_map(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Static Map with fieldID -> FieldMetaData mappings + indent(out) << "inline static var metaDataMap : IntMap = new IntMap();" << endl; + + if (fields.size() > 0) { + // Populate map + scope_up(out); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + std::string field_name = field->get_name(); + indent(out) << "metaDataMap[" << upcase_string(field_name) + << "_FIELD_ID] = new FieldMetaData(\"" << field_name << "\", "; + + // Set field requirement type (required, optional, etc.) + if (field->get_req() == t_field::T_REQUIRED) { + out << "TFieldRequirementType.REQUIRED, "; + } else if (field->get_req() == t_field::T_OPTIONAL) { + out << "TFieldRequirementType.OPTIONAL, "; + } else { + out << "TFieldRequirementType.DEFAULT, "; + } + + // Create value meta data + generate_field_value_meta_data(out, field->get_type()); + out << ");" << endl; + } + scope_down(out); + } +} + +/** + * Returns a string with the haxe representation of the given thrift type + * (e.g. for the type struct it returns "TType.STRUCT") + */ +std::string t_haxe_generator::get_haxe_type_string(t_type* type) { + if (type->is_list()) { + return "TType.LIST"; + } else if (type->is_map()) { + return "TType.MAP"; + } else if (type->is_set()) { + return "TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.STRUCT"; + } else if (type->is_enum()) { + return "TType.I32"; + } else if (type->is_typedef()) { + return get_haxe_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID: + return "TType.VOID"; + break; + case t_base_type::TYPE_STRING: + return "TType.STRING"; + break; + case t_base_type::TYPE_BOOL: + return "TType.BOOL"; + break; + case t_base_type::TYPE_I8: + return "TType.BYTE"; + break; + case t_base_type::TYPE_I16: + return "TType.I16"; + break; + case t_base_type::TYPE_I32: + return "TType.I32"; + break; + case t_base_type::TYPE_I64: + return "TType.I64"; + break; + case t_base_type::TYPE_DOUBLE: + return "TType.DOUBLE"; + break; + default: + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_haxe_generator::get_haxe_type_string!"); + break; // This should never happen! + } + } else { + throw std::runtime_error( + "Unknown thrift type \"" + type->get_name() + + "\" passed to t_haxe_generator::get_haxe_type_string!"); // This should never happen! + } +} + +void t_haxe_generator::generate_field_value_meta_data(std::ofstream& out, t_type* type) { + out << endl; + indent_up(); + indent_up(); + if (type->is_struct()) { + indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type); + } else if (type->is_container()) { + if (type->is_list()) { + indent(out) << "new ListMetaData(TType.LIST, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else if (type->is_set()) { + indent(out) << "new SetMetaData(TType.SET, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else { // map + indent(out) << "new MapMetaData(TType.MAP, "; + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + generate_field_value_meta_data(out, key_type); + out << ", "; + generate_field_value_meta_data(out, val_type); + } + } else { + indent(out) << "new FieldValueMetaData(" << get_haxe_type_string(type); + } + out << ")"; + indent_down(); + indent_down(); +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_haxe_generator::generate_service(t_service* tservice) { + // Make interface file + string f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + ".hx"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << haxe_package() << ";" << endl; + + f_service_ << endl << haxe_type_imports() << haxe_thrift_imports() + << haxe_thrift_gen_imports(tservice); + + if (tservice->get_extends() != NULL) { + t_type* parent = tservice->get_extends(); + string parent_namespace = parent->get_program()->get_namespace("haxe"); + if (!parent_namespace.empty() && parent_namespace != package_name_) { + f_service_ << "import " << type_name(parent) << ";" << endl; + } + } + + f_service_ << endl; + + generate_service_interface(tservice); + + f_service_.close(); + + // Now make the implementation/client file + f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + "Impl.hx"; + f_service_.open(f_service_name.c_str()); + + 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) { + t_type* parent = tservice->get_extends(); + string parent_namespace = parent->get_program()->get_namespace("haxe"); + if (!parent_namespace.empty() && parent_namespace != package_name_) { + f_service_ << "import " << type_name(parent) << "Impl;" << endl; + } + } + + f_service_ << endl; + + generate_service_client(tservice); + + f_service_.close(); + + // Now make the helper class files + generate_service_helpers(tservice); + + // Now make the processor/server file + f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + "Processor.hx"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << haxe_package() << ";" << endl << endl << haxe_type_imports() + << haxe_thrift_imports() << haxe_thrift_gen_imports(tservice) << endl; + + if (!package_name_.empty()) { + f_service_ << "import " << package_name_ << ".*;" << endl; + f_service_ << "import " << package_name_ << "." << get_cap_name(service_name_).c_str() + << "Impl;" << endl; + f_service_ << endl; + } + + generate_service_server(tservice); + + f_service_.close(); +} + +/** + * Generates the code snippet for the onSuccess callbacks + * + * @param tfunction The service function to generate code for. + */ +string t_haxe_generator::generate_service_method_onsuccess(t_function* tfunction, + bool as_type, + bool omit_name) { + if (tfunction->is_oneway()) { + return ""; + } + + string name = ""; + if (!omit_name) { + name = "onSuccess"; + if (as_type) { + name += " : "; + } + } + + if (tfunction->get_returntype()->is_void()) { + if (as_type) { + return name + "Void->Void = null"; + } else { + return name + "() : Void"; + } + } + + if (as_type) { + return name + type_name(tfunction->get_returntype()) + "->Void = null"; + } else { + return name + "( retval : " + type_name(tfunction->get_returntype()) + ")"; + } +} + +/** + * Generates a service method header + * + * @param tfunction The service function to generate code for. + */ +void t_haxe_generator::generate_service_method_signature(t_function* tfunction, bool is_interface) { + if (callbacks_) { + generate_service_method_signature_callback(tfunction, is_interface); + } else { + generate_service_method_signature_normal(tfunction, is_interface); + } +} + +/** + * Generates a service method header in "normal" style + * + * @param tfunction The service function to generate code for. + */ +void t_haxe_generator::generate_service_method_signature_normal(t_function* tfunction, + bool is_interface) { + if (is_interface) { + indent(f_service_) << function_signature_normal(tfunction) << ";" << endl << endl; + } else { + indent(f_service_) << "public " << function_signature_normal(tfunction) << " {" << endl; + } +} + +/** + * Generates a service method header in "callback" style + * + * @param tfunction The service function to generate code for. + */ +void t_haxe_generator::generate_service_method_signature_callback(t_function* tfunction, + bool is_interface) { + if (!tfunction->is_oneway()) { + std::string on_success_impl = generate_service_method_onsuccess(tfunction, false, false); + indent(f_service_) << "// function onError(Dynamic) : Void;" << endl; + indent(f_service_) << "// function " << on_success_impl.c_str() << ";" << endl; + } + + if (is_interface) { + indent(f_service_) << function_signature_callback(tfunction) << ";" << endl << endl; + } else { + indent(f_service_) << "public " << function_signature_callback(tfunction) << " {" << endl; + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_haxe_generator::generate_service_interface(t_service* tservice) { + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends_iface = " extends " + tservice->get_extends()->get_name(); + } + + generate_haxe_doc(f_service_, tservice); + // generate_rtti_decoration(f_service_); - not yet, because of + // https://github.com/HaxeFoundation/haxe/issues/3626 + generate_macro_decoration(f_service_); + f_service_ << indent() << "interface " << get_cap_name(service_name_) << extends_iface << " {" + << endl << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_haxe_doc(f_service_, *f_iter); + generate_service_method_signature(*f_iter, true); + } + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Generates structs for all the service args and return types + * + * @param tservice The service + */ +void t_haxe_generator::generate_service_helpers(t_service* tservice) { + f_service_ << endl << endl; + 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_haxe_struct(ts, false); + generate_function_helpers(*f_iter); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_haxe_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = get_cap_name(tservice->get_extends()->get_name()); + extends_client = " extends " + extends + "Impl"; + } + + generate_rtti_decoration(f_service_); + // build macro is inherited from interface + indent(f_service_) << "class " << get_cap_name(service_name_) << "Impl" << extends_client + << " implements " << get_cap_name(service_name_) << " {" << endl << endl; + indent_up(); + + indent(f_service_) << "public function new( iprot : TProtocol, oprot : TProtocol = null)" << endl; + scope_up(f_service_); + if (extends.empty()) { + f_service_ << indent() << "iprot_ = iprot;" << endl; + f_service_ << indent() << "if (oprot == null) {" << endl; + indent_up(); + f_service_ << indent() << "oprot_ = iprot;" << endl; + indent_down(); + f_service_ << indent() << "} else {" << endl; + indent_up(); + f_service_ << indent() << "oprot_ = oprot;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + } else { + f_service_ << indent() << "super(iprot, oprot);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + if (extends.empty()) { + f_service_ << indent() << "private var iprot_ : TProtocol;" << endl << indent() + << "private var oprot_ : TProtocol;" << endl << indent() + << "private var seqid_ : Int;" << endl << endl; + + indent(f_service_) << "public function getInputProtocol() : TProtocol" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.iprot_;" << endl; + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << "public function getOutputProtocol() : TProtocol" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.oprot_;" << endl; + scope_down(f_service_); + f_service_ << 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) { + string funname = (*f_iter)->get_name(); + + // Open function + generate_service_method_signature(*f_iter, false); + + indent_up(); + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + string argsname = get_cap_name((*f_iter)->get_name() + "_args"); + vector::const_iterator fld_iter; + const vector& fields = arg_struct->get_members(); + + // Serialize the request + string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL"; + f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname + << "\", TMessageType." << calltype << ", seqid_));" << endl << indent() + << "var args : " << argsname << " = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = " + << (*fld_iter)->get_name() << ";" << endl; + } + + f_service_ << indent() << "args.write(oprot_);" << endl << indent() + << "oprot_.writeMessageEnd();" << endl; + + if (!((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void())) { + f_service_ << indent() << "var retval : " << type_name((*f_iter)->get_returntype()) << ";" + << endl; + } + + if ((*f_iter)->is_oneway()) { + f_service_ << indent() << "oprot_.getTransport().flush();" << endl; + } else { + indent(f_service_) << "oprot_.getTransport().flush(function(error:Dynamic) : Void {" << endl; + indent_up(); + if (callbacks_) { + indent(f_service_) << "try {" << endl; + indent_up(); + } + string resultname = get_cap_name((*f_iter)->get_name() + "_result"); + indent(f_service_) << "if (error != null) {" << endl; + indent_up(); + if (callbacks_) { + indent(f_service_) << "if (onError != null) onError(error);" << endl; + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "throw error;" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + indent(f_service_) << "var msg : TMessage = iprot_.readMessageBegin();" << endl; + indent(f_service_) << "if (msg.type == TMessageType.EXCEPTION) {" << endl; + indent_up(); + indent(f_service_) << "var x = TApplicationException.read(iprot_);" << endl; + indent(f_service_) << "iprot_.readMessageEnd();" << endl; + if (callbacks_) { + indent(f_service_) << "if (onError != null) onError(x);" << endl; + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "throw x;" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + indent(f_service_) << "var result : " << resultname << " = new " << resultname << "();" + << endl; + indent(f_service_) << "result.read(iprot_);" << endl; + indent(f_service_) << "iprot_.readMessageEnd();" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "if (result." << generate_isset_check("success") << ") {" << endl; + indent_up(); + if (callbacks_) { + indent(f_service_) << "if (onSuccess != null) onSuccess(result.success);" << endl; + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "retval = result.success;" << endl; + indent(f_service_) << "return;" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + } + + 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) { + indent(f_service_) << "if (result." << (*x_iter)->get_name() << " != null) {" << endl; + indent_up(); + if (callbacks_) { + indent(f_service_) << "if (onError != null) onError(result." << (*x_iter)->get_name() + << ");" << endl; + indent(f_service_) << "return;" << endl; + } else { + indent(f_service_) << "throw result." << (*x_iter)->get_name() << ";" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + } + + // If you get here it's an exception, unless a void function + if ((*f_iter)->get_returntype()->is_void()) { + if (callbacks_) { + indent(f_service_) << "if (onSuccess != null) onSuccess();" << endl; + } + indent(f_service_) << "return;" << endl; + } else { + if (callbacks_) { + indent(f_service_) << "if (onError != null)" << endl; + indent_up(); + indent(f_service_) + << "onError( new TApplicationException(TApplicationException.MISSING_RESULT," << endl; + indent(f_service_) << " \"" << (*f_iter)->get_name() + << " failed: unknown result\"));" << endl; + indent_down(); + } else { + indent(f_service_) + << "throw new TApplicationException(TApplicationException.MISSING_RESULT," << endl; + indent(f_service_) << " \"" << (*f_iter)->get_name() + << " failed: unknown result\");" << endl; + } + } + + if (callbacks_) { + indent_down(); + indent(f_service_) << "} catch( e : TException) {" << endl; + indent_up(); + indent(f_service_) << "if (onError != null) onError(e);" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + } + + indent_down(); + indent(f_service_) << "});" << endl; + } + + if (!((*f_iter)->is_oneway() || (*f_iter)->get_returntype()->is_void())) { + f_service_ << indent() << "return retval;" << endl; + } + + // Close function + scope_down(f_service_); + f_service_ << endl; + } + + indent_down(); + indent(f_service_) << "}" << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_haxe_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = get_cap_name(type_name(tservice->get_extends())); + extends_processor = " extends " + extends + "Processor"; + } + + // Generate the header portion + generate_rtti_decoration(f_service_); + generate_macro_decoration(f_service_); + indent(f_service_) << "class " << get_cap_name(service_name_) << "Processor" << extends_processor + << " implements TProcessor {" << endl << endl; + indent_up(); + + f_service_ << indent() << "private var " << get_cap_name(service_name_) + << "_iface_ : " << get_cap_name(service_name_) << ";" << endl; + + if (extends.empty()) { + f_service_ << indent() + << "private var PROCESS_MAP = new StringMap< Int->TProtocol->TProtocol->Void >();" + << endl; + } + + f_service_ << endl; + + indent(f_service_) << "public function new( iface : " << get_cap_name(service_name_) << ")" + << endl; + scope_up(f_service_); + if (!extends.empty()) { + f_service_ << indent() << "super(iface);" << endl; + } + f_service_ << indent() << get_cap_name(service_name_) << "_iface_ = iface;" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "PROCESS_MAP.set(\"" << (*f_iter)->get_name() << "\", " + << (*f_iter)->get_name() << "());" << endl; + } + + scope_down(f_service_); + f_service_ << endl; + + // Generate the server implementation + string override = ""; + if (tservice->get_extends() != NULL) { + override = "override "; + } + indent(f_service_) << override + << "public function process( iprot : TProtocol, oprot : TProtocol) : Bool" + << endl; + scope_up(f_service_); + + f_service_ << indent() << "var msg : TMessage = iprot.readMessageBegin();" << endl; + + // TODO(mcslee): validate message, was the seqid etc. legit? + // AS- If all method is oneway: + // do you have an oprot? + // do you you need nullcheck? + f_service_ + << indent() << "var fn = PROCESS_MAP.get(msg.name);" << endl << indent() + << "if (fn == null) {" << endl << indent() << " TProtocolUtil.skip(iprot, TType.STRUCT);" + << endl << indent() << " iprot.readMessageEnd();" << endl << indent() + << " var x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, \"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.getTransport().flush();" << endl << indent() + << " return true;" << endl << indent() << "}" << endl << indent() + << "fn( msg.seqid, iprot, oprot);" << endl; + + f_service_ << indent() << "return true;" << endl; + + scope_down(f_service_); + f_service_ << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << "}" << endl << endl; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_haxe_generator::generate_function_helpers(t_function* tfunction) { + if (tfunction->is_oneway()) { + return; + } + + string resultname = get_cap_name(tfunction->get_name() + "_result"); + t_struct result(program_, resultname); + 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_haxe_struct(&result, false, true); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_haxe_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open class + indent(f_service_) << "private function " << tfunction->get_name() + << "() : Int->TProtocol->TProtocol->Void {" << endl; + indent_up(); + + // Open function + indent(f_service_) << "return function( seqid : Int, iprot : TProtocol, oprot : TProtocol) : Void" + << endl; + scope_up(f_service_); + + string argsname = get_cap_name(tfunction->get_name() + "_args"); + string resultname = get_cap_name(tfunction->get_name() + "_result"); + + f_service_ << indent() << "var args : " << argsname << " = 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; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << "var result : " << resultname << " = new " << resultname << "();" + << endl; + } + + // Try block for any function to catch (defined or undefined) exceptions + f_service_ << indent() << "try {" << endl; + indent_up(); + + if (callbacks_) { + // callback function style onError/onSuccess + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + f_service_ << indent(); + f_service_ << get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + + if (tfunction->is_oneway()) { + f_service_ << ");" << endl; + } else { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + string on_success = generate_service_method_onsuccess(tfunction, false, true); + indent_up(); + f_service_ << endl; + indent(f_service_) << "null, // errors are thrown by the handler" << endl; + if (tfunction->get_returntype()->is_void()) { + indent(f_service_) << "null); // no retval" << endl; + } else { + indent(f_service_) << "function" << on_success.c_str() << " {" << endl; + if (!tfunction->get_returntype()->is_void()) { + indent_up(); + indent(f_service_) << "result.success = retval;" << endl; + indent_down(); + } + indent(f_service_) << "});" << endl; + } + indent_down(); + } + + } else { + // normal function():result style + + // Generate the function call + 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_ << get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + } + + indent_down(); + f_service_ << indent() << "}"; + if (!tfunction->is_oneway()) { + // catch exceptions defined in the IDL + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << (*x_iter)->get_name() << ":" + << get_cap_name(type_name((*x_iter)->get_type(), false, false)) << ") {" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << ";" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << "}"; + } + } + } + + // always catch all exceptions to prevent from service denial + f_service_ << " catch (th : Dynamic) {" << endl; + indent_up(); + indent(f_service_) << "trace(\"Internal error processing " << tfunction->get_name() << "\", th);" + << endl; + if (!tfunction->is_oneway()) { + indent(f_service_) << "var x = new TApplicationException(TApplicationException.INTERNAL_ERROR, " + "\"Internal error processing " << tfunction->get_name() << "\");" << endl; + indent(f_service_) << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.EXCEPTION, seqid));" << endl; + indent(f_service_) << "x.write(oprot);" << endl; + indent(f_service_) << "oprot.writeMessageEnd();" << endl; + indent(f_service_) << "oprot.getTransport().flush();" << endl; + } + indent(f_service_) << "return;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "return;" << endl; + scope_down(f_service_); + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; + return; + } + + f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);" + << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() + << "oprot.getTransport().flush();" << endl; + + // Close function + scope_down(f_service_); + f_service_ << endl; + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Deserializes a field of any type. + * + * @param tfield The field + * @param prefix The variable name or container for this field + */ +void t_haxe_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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 << " = 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 (((t_base_type*)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 Haxe 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()); + } +} + +/** + * Generates an unserializer for a struct, invokes read() + */ +void t_haxe_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix) { + out << indent() << prefix << " = new " << get_cap_name(type_name(tstruct)) << "();" << endl + << indent() << prefix << ".read(iprot);" << endl; +} + +/** + * Deserializes a container by reading its size and then iterating + */ +void t_haxe_generator::generate_deserialize_container(ofstream& 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"); + } + + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "var " << obj << " = iprot.readMapBegin();" << endl; + } else if (ttype->is_set()) { + indent(out) << "var " << obj << " = iprot.readSetBegin();" << endl; + } else if (ttype->is_list()) { + indent(out) << "var " << obj << " = iprot.readListBegin();" << endl; + } + + indent(out) << prefix << " = new " << type_name(ttype, false, true) + // size the collection correctly + << "(" + << ");" << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for( " << i << " in 0 ... " << obj << ".size)" << 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); + + // Read container end + 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); +} + +/** + * Generates code to deserialize a map + */ +void t_haxe_generator::generate_deserialize_map_element(ofstream& 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 << ".set( " << key << ", " << val << ");" << endl; +} + +/** + * Deserializes a set element + */ +void t_haxe_generator::generate_deserialize_set_element(ofstream& 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; +} + +/** + * Deserializes a list element + */ +void t_haxe_generator::generate_deserialize_list_element(ofstream& 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; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_haxe_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + tfield->get_name(); + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no Haxe name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_haxe_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + out << indent() << prefix << ".write(oprot);" << endl; +} + +/** + * Serializes a container by writing its size then the elements. + * + * @param ttype The type of container + * @param prefix String prefix for fields + */ +void t_haxe_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + string iter = tmp("_key"); + string counter = tmp("_sizeCounter"); + indent(out) << "var " << counter << " : Int = 0;" << endl; + indent(out) << "for( " << iter << " in " << prefix << ") {" << endl; + indent(out) << " " << counter << +"++;" << endl; + indent(out) << "}" << endl; + + indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) + << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << counter << "));" + << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " << prefix << ".size));" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(new TList(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));" + << endl; + } + + string iter = tmp("elem"); + if (ttype->is_map()) { + indent(out) << "for( " << iter << " in " << prefix << ".keys())" << endl; + } else if (ttype->is_set()) { + indent(out) << "for( " << iter << " in " << prefix << ".toArray())" << endl; + } else if (ttype->is_list()) { + indent(out) << "for( " << iter << " in " << prefix << ")" << 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); +} + +/** + * Serializes the members of a map. + */ +void t_haxe_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string iter, + string map) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, &kfield, ""); + t_field vfield(tmap->get_val_type(), map + ".get(" + iter + ")"); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_haxe_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_haxe_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Returns a haxe type name + * + * @param ttype The type + * @param container Is the type going inside a container? + * @return haxe type name, i.e. HashMap + */ +string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_init) { + (void)in_init; + + // typedefs are just resolved to their real type + ttype = get_true_type(ttype); + string prefix; + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype, in_container); + } + + if (ttype->is_enum()) { + return "Int"; + } + + if (ttype->is_map()) { + t_type* tkey = get_true_type(((t_map*)ttype)->get_key_type()); + t_type* tval = get_true_type(((t_map*)ttype)->get_val_type()); + if (tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + if (!(((t_base_type*)tkey)->is_binary())) { + return "StringMap< " + type_name(tval) + ">"; + } + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + return "IntMap< " + type_name(tval) + ">"; + case t_base_type::TYPE_I64: + return "Int64Map< " + type_name(tval) + ">"; + default: + break; // default to ObjectMap<> + } + } + if (tkey->is_enum()) { + return "IntMap< " + type_name(tval) + ">"; + } + return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">"; + } + + if (ttype->is_set()) { + t_type* tkey = get_true_type(((t_list*)ttype)->get_elem_type()); + if (tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + if (!(((t_base_type*)tkey)->is_binary())) { + return "StringSet"; + } + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + return "IntSet"; + case t_base_type::TYPE_I64: + return "Int64Set"; + default: + break; // default to ObjectSet + } + } + if (tkey->is_enum()) { + return "IntSet"; + } + return "ObjectSet< " + type_name(tkey) + ">"; + } + + if (ttype->is_list()) { + t_type* telm = ((t_list*)ttype)->get_elem_type(); + return "List< " + type_name(telm) + ">"; + } + + // Check for namespacing + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + string package = program->get_namespace("haxe"); + if (!package.empty()) { + return package + "." + ttype->get_name(); + } + } + + return ttype->get_name(); +} + +/** + * Returns the haxe type that corresponds to the thrift type. + * + * @param tbase The base type + * @param container Is it going in a haxe container? + */ +string t_haxe_generator::base_type_name(t_base_type* type, bool in_container) { + (void)in_container; + 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 "haxe.io.Bytes"; + } else { + return "String"; + } + case t_base_type::TYPE_BOOL: + return "Bool"; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + return "haxe.Int32"; + case t_base_type::TYPE_I64: + return "haxe.Int64"; + case t_base_type::TYPE_DOUBLE: + return "Float"; + default: + throw "compiler error: no Haxe name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_haxe_generator::declare_field(t_field* tfield, bool init) { + // TODO(mcslee): do we ever need to initialize the field? + 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) { + 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 += " = 0"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype, false, true) + "()"; + } else { + result += " = new " + type_name(ttype, false, true) + "()"; + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_haxe_generator::function_signature_callback(t_function* tfunction) { + std::string on_error_success = "onError : Dynamic->Void = null, " + + generate_service_method_onsuccess(tfunction, true, false); + + std::string arguments = argument_list(tfunction->get_arglist()); + if (!tfunction->is_oneway()) { + if (arguments != "") { + arguments += ", "; + } + arguments += on_error_success; //"onError : Function, onSuccess : Function"; + } + + std::string result = "function " + tfunction->get_name() + "(" + arguments + ") : Void"; + return result; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_haxe_generator::function_signature_normal(t_function* tfunction) { + std::string arguments = argument_list(tfunction->get_arglist()); + + std::string resulttype; + if (tfunction->is_oneway() || tfunction->get_returntype()->is_void()) { + resulttype = "Void"; + } else { + resulttype = type_name(tfunction->get_returntype()); + } + + std::string result = "function " + tfunction->get_name() + "(" + arguments + ") : " + resulttype; + return result; +} + +/** + * Renders a comma separated field list, with type names + */ +string t_haxe_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 += (*f_iter)->get_name() + " : " + type_name((*f_iter)->get_type()); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_haxe_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 "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(); +} + +/** + * Haxe class names must start with uppercase letter, but Haxe namespaces must not. + */ +std::string t_haxe_generator::get_cap_name(std::string name) { + if (name.length() == 0) { + return name; + } + + // test.for.Generic< data.Type, or.the.Like> and handle it recursively + size_t generic_first = name.find('<'); + size_t generic_last = name.rfind('>'); + if ((generic_first != std::string::npos) && (generic_last != std::string::npos)) { + string outer_type = name.substr(0, generic_first); + string inner_types = name.substr(generic_first + 1, generic_last - generic_first - 1); + + string new_inner = ""; + size_t comma_start = 0; + while (comma_start < inner_types.length()) { + size_t comma_pos = comma_start; + int nested = 0; + + while (comma_pos < inner_types.length()) { + bool found = false; + switch (inner_types[comma_pos]) { + case '<': + ++nested; + break; + case '>': + --nested; + break; + case ',': + found = (nested == 0); + break; + } + if (found) { + break; + } + ++comma_pos; + } + + if (new_inner.length() > 0) { + new_inner += ","; + } + + string inner = inner_types.substr(comma_start, comma_pos - comma_start); + new_inner += get_cap_name(inner); + comma_start = ++comma_pos; + } + + return get_cap_name(outer_type) + "<" + new_inner + ">"; + } + + // package name + size_t index = name.find_first_not_of(" \n\r\t"); + if (index < name.length()) { + name[index] = tolower(name[index]); + index = name.find('.'); + while (index != std::string::npos) { + if (++index < name.length()) { + name[index] = tolower(name[index]); + } + index = name.find('.', index); + } + } + + // class name + index = name.rfind('.'); + if (index != std::string::npos) { + ++index; + } else { + index = name.find_first_not_of(" \n\r\t"); + } + + if (index < name.length()) { + name[index] = toupper(name[index]); + } + + return name; +} + +string t_haxe_generator::constant_name(string name) { + string constant_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); + + bool is_upper = isupper(character); + + if (is_upper && !is_first && !was_previous_char_upper) { + constant_name += '_'; + } + constant_name += toupper(character); + + is_first = false; + was_previous_char_upper = is_upper; + } + + return constant_name; +} + +/** + * Enables RTTI for a class or interface + */ +void t_haxe_generator::generate_rtti_decoration(ofstream& out) { + if (rtti_) { + out << "@:rtti" << endl; + } +} + +/** + * Adds build macros to a class or interface + */ +void t_haxe_generator::generate_macro_decoration(ofstream& out) { + if (!buildmacro_.empty()) { + out << "#if ! macro" << endl; + out << "@:build( " << buildmacro_ << ")" << endl; // current class/interface + out << "@:autoBuild( " << buildmacro_ << ")" << endl; // inherited classes/interfaces + out << "#end" << endl; + } +} + +/** + * Emits a haxeDoc comment if the provided object has a doc in Thrift + */ +void t_haxe_generator::generate_haxe_doc(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, "/**\n", " * ", tdoc->get_doc(), " */\n"); + } +} + +/** + * Emits a haxeDoc comment if the provided function object has a doc in Thrift + */ +void t_haxe_generator::generate_haxe_doc(ofstream& out, t_function* tfunction) { + if (tfunction->has_doc()) { + stringstream ss; + ss << tfunction->get_doc(); + 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; + ss << "\n@param " << p->get_name(); + if (p->has_doc()) { + ss << " " << p->get_doc(); + } + } + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); + } +} + +std::string t_haxe_generator::generate_isset_check(t_field* field) { + return generate_isset_check(field->get_name()); +} + +std::string t_haxe_generator::generate_isset_check(std::string field_name) { + return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; +} + +void t_haxe_generator::generate_isset_set(ofstream& out, t_field* field) { + if (!type_can_be_null(field->get_type())) { + indent(out) << "this.__isset_" << field->get_name() << " = true;" << endl; + } +} + +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_*/) { + package = program->get_namespace("haxe") + "."; + } + return package + type->get_name(); +} + +THRIFT_REGISTER_GENERATOR( + haxe, + "Haxe", + " callbacks Use onError()/onSuccess() callbacks for service methods (like AS3)\n" + " rtti Enable @:rtti for generated classes and interfaces\n" + " buildmacro=my.macros.Class.method(args)\n" + " Add @:build macro calls to generated classes and interfaces\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc new file mode 100644 index 00000000..a3ccd8d4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_hs_generator.cc @@ -0,0 +1,1734 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/version.h" + +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * Haskell code generator. + * + */ +class t_hs_generator : public t_oop_generator { +public: + t_hs_generator(t_program* program, + const map& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)option_string; + std::map::const_iterator iter; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option hs:" + iter->first; + } + + out_dir_base_ = "gen-hs"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + string render_const_value(t_type* type, t_const_value* value); + + /** + * Struct generation code + */ + + void generate_hs_struct(t_struct* tstruct, bool is_exception); + + void generate_hs_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool helper = false); + + void generate_hs_struct_reader(ofstream& out, t_struct* tstruct); + + void generate_hs_struct_writer(ofstream& out, t_struct* tstruct); + + void generate_hs_struct_arbitrary(ofstream& out, t_struct* tstruct); + + void generate_hs_function_helpers(t_function* tfunction); + + void generate_hs_typemap(ofstream& out, t_struct* tstruct); + + void generate_hs_default(ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(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(ofstream& out, t_field* tfield, string prefix); + + void generate_deserialize_struct(ofstream& out, t_struct* tstruct, string name = ""); + + void generate_deserialize_container(ofstream& out, t_type* ttype, string arg = ""); + + void generate_deserialize_set_element(ofstream& out, t_set* tset); + + void generate_deserialize_list_element(ofstream& out, t_list* tlist, string prefix = ""); + + void generate_deserialize_type(ofstream& out, t_type* type, string arg = ""); + + void generate_serialize_type(ofstream& out, t_type* type, string name = ""); + + void generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix = ""); + + void generate_serialize_container(ofstream& out, t_type* ttype, string prefix = ""); + + void generate_serialize_map_element(ofstream& out, t_map* tmap, string kiter, string viter); + + void generate_serialize_set_element(ofstream& out, t_set* tmap, string iter); + + void generate_serialize_list_element(ofstream& out, t_list* tlist, string iter); + + /** + * Helper rendering functions + */ + + string hs_autogen_comment(); + string hs_language_pragma(); + string hs_imports(); + + string type_name(t_type* ttype, string function_prefix = ""); + + string field_name(string tname, string fname); + + string function_type(t_function* tfunc, + bool options = false, + bool io = false, + bool method = false); + + string type_to_enum(t_type* ttype); + + string type_to_default(t_type* ttype); + + string render_hs_type(t_type* type, bool needs_parens); + + string type_to_constructor(t_type* ttype); + + string render_hs_type_for_function_name(t_type* type); + +private: + ofstream f_types_; + ofstream f_consts_; + ofstream f_service_; + ofstream f_iface_; + ofstream f_client_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_hs_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Make output file + string pname = capitalize(program_name_); + string f_types_name = get_out_dir() + pname + "_Types.hs"; + f_types_.open(f_types_name.c_str()); + + string f_consts_name = get_out_dir() + pname + "_Consts.hs"; + f_consts_.open(f_consts_name.c_str()); + + // Print header + f_types_ << hs_language_pragma() << endl; + f_types_ << hs_autogen_comment() << endl; + f_types_ << "module " << pname << "_Types where" << endl; + f_types_ << hs_imports() << endl; + + f_consts_ << hs_language_pragma() << endl; + f_consts_ << hs_autogen_comment() << endl; + f_consts_ << "module " << pname << "_Consts where" << endl; + f_consts_ << hs_imports() << endl; + f_consts_ << "import " << pname << "_Types" << endl; +} + +string t_hs_generator::hs_language_pragma() { + return string( + "{-# LANGUAGE DeriveDataTypeable #-}\n" + "{-# LANGUAGE DeriveGeneric #-}\n" + "{-# LANGUAGE OverloadedStrings #-}\n" + "{-# OPTIONS_GHC -fno-warn-missing-fields #-}\n" + "{-# OPTIONS_GHC -fno-warn-missing-signatures #-}\n" + "{-# OPTIONS_GHC -fno-warn-name-shadowing #-}\n" + "{-# OPTIONS_GHC -fno-warn-unused-imports #-}\n" + "{-# OPTIONS_GHC -fno-warn-unused-matches #-}\n"); +} + +/** + * Autogen'd comment + */ +string t_hs_generator::hs_autogen_comment() { + return string("-----------------------------------------------------------------\n") + + "-- Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ") --\n" + + "-- --\n" + + "-- DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING --\n" + + "-----------------------------------------------------------------\n"; +} + +/** + * Prints standard thrift imports + */ +string t_hs_generator::hs_imports() { + const vector& includes = program_->get_includes(); + string result = string( + "import Prelude (($), (.), (>>=), (==), (++))\n" + "import qualified Prelude as P\n" + "import qualified Control.Exception as X\n" + "import qualified Control.Monad as M ( liftM, ap, when )\n" + "import Data.Functor ( (<$>) )\n" + "import qualified Data.ByteString.Lazy as LBS\n" + "import qualified Data.Hashable as H\n" + "import qualified Data.Int as I\n" + "import qualified Data.Maybe as M (catMaybes)\n" + "import qualified Data.Text.Lazy.Encoding as E ( decodeUtf8, encodeUtf8 )\n" + "import qualified Data.Text.Lazy as LT\n" + "import qualified GHC.Generics as G (Generic)\n" + "import qualified Data.Typeable as TY ( Typeable )\n" + "import qualified Data.HashMap.Strict as Map\n" + "import qualified Data.HashSet as Set\n" + "import qualified Data.Vector as Vector\n" + "import qualified Test.QuickCheck.Arbitrary as QC ( Arbitrary(..) )\n" + "import qualified Test.QuickCheck as QC ( elements )\n" + "\n" + "import qualified Thrift as T\n" + "import qualified Thrift.Types as T\n" + "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"; + + if (includes.size() > 0) + result += "\n"; + + return result; +} + +/** + * Closes the type files + */ +void t_hs_generator::close_generator() { + // Close types file + f_types_.close(); + f_consts_.close(); +} + +/** + * Generates a typedef. Ez. + * + * @param ttypedef The type definition + */ +void t_hs_generator::generate_typedef(t_typedef* ttypedef) { + string tname = capitalize(ttypedef->get_symbolic()); + string tdef = render_hs_type(ttypedef->get_type(), false); + indent(f_types_) << "type " << tname << " = " << tdef << endl; + f_types_ << endl; +} + +/** + * Generates code for an enumerated type. + * the values. + * + * @param tenum The enumeration + */ +void t_hs_generator::generate_enum(t_enum* tenum) { + indent(f_types_) << "data " << capitalize(tenum->get_name()) << " = "; + 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) { + string name = capitalize((*c_iter)->get_name()); + f_types_ << (first ? "" : "|"); + f_types_ << name; + first = false; + } + indent(f_types_) << "deriving (P.Show, P.Eq, G.Generic, TY.Typeable, P.Ord, P.Bounded)" << endl; + indent_down(); + + string ename = capitalize(tenum->get_name()); + + indent(f_types_) << "instance P.Enum " << ename << " where" << endl; + indent_up(); + indent(f_types_) << "fromEnum t = case t of" << endl; + 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()); + indent(f_types_) << name << " -> " << value << endl; + } + indent_down(); + indent(f_types_) << "toEnum t = case t of" << endl; + 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()); + indent(f_types_) << value << " -> " << name << endl; + } + indent(f_types_) << "_ -> X.throw T.ThriftException" << endl; + indent_down(); + indent_down(); + + indent(f_types_) << "instance H.Hashable " << ename << " where" << endl; + indent_up(); + indent(f_types_) << "hashWithSalt salt = H.hashWithSalt salt P.. P.fromEnum" << endl; + indent_down(); + + indent(f_types_) << "instance QC.Arbitrary " << ename << " where" << endl; + indent_up(); + indent(f_types_) << "arbitrary = QC.elements (P.enumFromTo P.minBound P.maxBound)" << endl; + indent_down(); +} + +/** + * Generate a constant value + */ +void t_hs_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = decapitalize(tconst->get_name()); + + t_const_value* value = tconst->get_value(); + + indent(f_consts_) << name << " :: " << render_hs_type(type, false) << endl; + indent(f_consts_) << name << " = " << render_const_value(type, value) << endl; + f_consts_ << endl; +} + +/** + * 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 + */ +string t_hs_generator::render_const_value(t_type* type, t_const_value* value) { + if (value == NULL) + return type_to_default(type); + + type = get_true_type(type); + ostringstream out; + + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + + case t_base_type::TYPE_BOOL: + out << (value->get_integer() > 0 ? "P.True" : "P.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: + out << "(" << value->get_integer() << ")"; + break; + + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << "(" << value->get_integer() << ")"; + } else { + out << "(" << 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()) { + 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(); + if (val == value->get_integer()) { + t_program* prog = type->get_program(); + if (prog != NULL && prog != program_) + out << capitalize(prog->get_name()) << "_Types."; + out << capitalize((*c_iter)->get_name()); + break; + } + } + + } else if (type->is_struct() || type->is_xception()) { + string cname = type_name(type); + out << "default_" << cname << "{"; + + const vector& fields = ((t_struct*)type)->get_members(); + 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 (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); + + if (field == NULL) + 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); + + out << (first ? "" : ", "); + out << field_name(cname, fname) << " = "; + if (field->get_req() == t_field::T_OPTIONAL || ((t_type*)field->get_type())->is_xception()) { + out << "P.Just "; + } + out << const_value; + first = false; + } + + out << "}"; + + } 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; + + out << "(Map.fromList ["; + + bool first = true; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string key = render_const_value(ktype, v_iter->first); + string val = render_const_value(vtype, v_iter->second); + out << (first ? "" : ","); + out << "(" << key << "," << val << ")"; + first = false; + } + out << "])"; + + } else if (type->is_list() || type->is_set()) { + t_type* etype = type->is_list() ? ((t_list*)type)->get_elem_type() + : ((t_set*)type)->get_elem_type(); + + const vector& val = value->get_list(); + vector::const_iterator v_iter; + + if (type->is_set()) + out << "(Set.fromList ["; + else + out << "(Vector.fromList ["; + + bool first = true; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << (first ? "" : ","); + out << render_const_value(etype, *v_iter); + first = false; + } + + out << "])"; + + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + + return out.str(); +} + +/** + * Generates a "struct" + */ +void t_hs_generator::generate_struct(t_struct* tstruct) { + generate_hs_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct, but also has an exception declaration. + * + * @param txception The struct definition + */ +void t_hs_generator::generate_xception(t_struct* txception) { + generate_hs_struct(txception, true); +} + +/** + * Generates a Haskell struct + */ +void t_hs_generator::generate_hs_struct(t_struct* tstruct, bool is_exception) { + generate_hs_struct_definition(f_types_, tstruct, is_exception, false); +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_hs_generator::generate_hs_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool helper) { + (void)helper; + string tname = type_name(tstruct); + string name = tstruct->get_name(); + const vector& members = tstruct->get_members(); + + indent(out) << "data " << tname << " = " << tname; + if (members.size() > 0) { + indent_up(); + bool first = true; + for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); + ++m_iter) { + if (first) { + indent(out) << "{ "; + first = false; + } else { + indent(out) << ", "; + } + string mname = (*m_iter)->get_name(); + out << field_name(tname, mname) << " :: "; + if ((*m_iter)->get_req() == t_field::T_OPTIONAL + || ((t_type*)(*m_iter)->get_type())->is_xception()) { + out << "P.Maybe "; + } + out << render_hs_type((*m_iter)->get_type(), true) << endl; + } + indent(out) << "}"; + indent_down(); + } + + out << " deriving (P.Show,P.Eq,G.Generic,TY.Typeable)" << endl; + + if (is_exception) + out << "instance X.Exception " << tname << endl; + + 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(); + indent(out) << " `H.hashWithSalt` " << field_name(tname, mname) << " record"; + } + indent(out) << endl; + indent_down(); + + generate_hs_struct_arbitrary(out, tstruct); + generate_hs_struct_writer(out, tstruct); + generate_hs_struct_reader(out, tstruct); + generate_hs_typemap(out, tstruct); + generate_hs_default(out, tstruct); +} + +void t_hs_generator::generate_hs_struct_arbitrary(ofstream& out, t_struct* tstruct) { + 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(); + if (members.size() > 0) { + indent(out) << "arbitrary = M.liftM " << tname; + indent_up(); + indent_up(); + indent_up(); + indent_up(); + bool first = true; + for (vector::const_iterator m_iter = members.begin(); m_iter != members.end(); + ++m_iter) { + if (first) { + first = false; + out << " "; + } else { + indent(out) << "`M.ap`"; + } + out << "("; + if ((*m_iter)->get_req() == t_field::T_OPTIONAL + || ((t_type*)(*m_iter)->get_type())->is_xception()) { + out << "M.liftM P.Just "; + } + out << "QC.arbitrary)" << endl; + } + indent_down(); + indent_down(); + indent_down(); + indent_down(); + + // Shrink + indent(out) << "shrink obj | obj == default_" << tname << " = []" << endl; + 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) { + if (first) { + first = false; + indent(out) << "[ "; + } else { + indent(out) << ", "; + } + string fname = field_name(tname, (*m_iter)->get_name()); + out << "if obj == default_" << tname; + out << "{" << fname << " = " << fname << " obj} "; + out << "then P.Nothing "; + out << "else P.Just $ default_" << tname; + out << "{" << fname << " = " << fname << " obj}" << endl; + } + indent(out) << "]" << endl; + indent_down(); + } else { /* 0 == members.size() */ + indent(out) << "arbitrary = QC.elements [" << tname << "]" << endl; + } + indent_down(); +} + +/** + * Generates the read method for a struct + */ +void t_hs_generator::generate_hs_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + string sname = type_name(tstruct); + string id = tmp("_id"); + string val = tmp("_val"); + + indent(out) << "to_" << sname << " :: T.ThriftVal -> " << sname << endl; + indent(out) << "to_" << sname << " (T.TStruct fields) = " << sname << "{" << endl; + indent_up(); + + 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(); + + if (first) { + first = false; + } else { + out << "," << endl; + } + + // Fill in Field + indent(out) << field_name(sname, fname) << " = "; + + out << "P.maybe ("; + if ((*f_iter)->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) { + out << "P.Nothing"; + } else { + out << field_name(sname, fname) << " default_" << sname; + } + } + out << ") "; + + out << "(\\(_," << val << ") -> "; + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || ((t_type*)(*f_iter)->get_type())->is_xception()) + out << "P.Just "; + generate_deserialize_field(out, *f_iter, val); + out << ")"; + out << " (Map.lookup (" << key << ") fields)"; + } + + out << endl; + indent(out) << "}" << endl; + indent_down(); + + // read + string tmap = type_name(tstruct, "typemap_"); + indent(out) << "to_" << sname << " _ = P.error \"not a struct\"" << endl; + + indent(out) << "read_" << sname << " :: (T.Transport t, T.Protocol p) => p t -> P.IO " << sname + << endl; + indent(out) << "read_" << sname << " iprot = to_" << sname; + out << " <$> T.readVal iprot (T.T_STRUCT " << tmap << ")" << endl; + + indent(out) << "decode_" << sname + << " :: (T.Protocol p, T.Transport t) => p t -> LBS.ByteString -> " << sname << endl; + indent(out) << "decode_" << sname << " iprot bs = to_" << sname << " $ "; + out << "T.deserializeVal iprot (T.T_STRUCT " << tmap << ") bs" << endl; +} + +void t_hs_generator::generate_hs_struct_writer(ofstream& 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"); + + indent(out) << "from_" << name << " :: " << name << " -> T.ThriftVal" << endl; + indent(out) << "from_" << name << " record = T.TStruct $ Map.fromList "; + indent_up(); + + // 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()) { + hasExn = true; + break; + } + } + + bool isfirst = true; + if (hasExn) { + 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()) { + if (isfirst) { + out << "[ "; + isfirst = false; + } else { + out << ", "; + } + string mname = (*f_iter)->get_name(); + int32_t key = (*f_iter)->get_key(); + out << "(\\" << v << " -> (" << key << ", (\"" << mname << "\","; + generate_serialize_type(out, (*f_iter)->get_type(), v); + out << "))) <$> " << field_name(name, mname) << " record"; + } + } + if (!isfirst) { + out << "]" << endl; + } + indent_down(); + indent(out) << "in if P.not (P.null exns) then exns else "; + indent_up(); + } else { + out << "$ "; + } + + out << "M.catMaybes" << endl; + // Get the Rest + isfirst = true; + for (vector::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Write field header + if (isfirst) { + indent(out) << "[ "; + isfirst = false; + } else { + indent(out) << ", "; + } + string mname = (*f_iter)->get_name(); + int32_t key = (*f_iter)->get_key(); + out << "(\\"; + out << v << " -> "; + if ((*f_iter)->get_req() != t_field::T_OPTIONAL + && !((t_type*)(*f_iter)->get_type())->is_xception()) { + out << "P.Just "; + } + out << "(" << key << ", (\"" << mname << "\","; + generate_serialize_type(out, (*f_iter)->get_type(), v); + out << "))) "; + if ((*f_iter)->get_req() != t_field::T_OPTIONAL + && !((t_type*)(*f_iter)->get_type())->is_xception()) { + out << "$"; + } else { + out << "<$>"; + } + out << " " << field_name(name, mname) << " record" << endl; + } + + // Write the struct map + if (isfirst) { + indent(out) << "[]" << endl; + } else { + indent(out) << "]" << endl; + } + if (hasExn) { + indent(out) << ")" << endl; + indent_down(); + } + indent_down(); + + // write + indent(out) << "write_" << name << " :: (T.Protocol p, T.Transport t) => p t -> " << name + << " -> P.IO ()" << endl; + indent(out) << "write_" << name << " oprot record = T.writeVal oprot $ from_"; + out << name << " record" << endl; + + // encode + indent(out) << "encode_" << name << " :: (T.Protocol p, T.Transport t) => p t -> " << name + << " -> LBS.ByteString" << endl; + indent(out) << "encode_" << name << " oprot record = T.serializeVal oprot $ "; + out << "from_" << name << " record" << endl; +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_hs_generator::generate_service(t_service* tservice) { + string f_service_name = get_out_dir() + capitalize(service_name_) + ".hs"; + f_service_.open(f_service_name.c_str()); + + f_service_ << hs_language_pragma() << endl; + f_service_ << hs_autogen_comment() << endl; + f_service_ << "module " << capitalize(service_name_) << " where" << endl; + f_service_ << hs_imports() << endl; + + if (tservice->get_extends()) { + f_service_ << "import qualified " << capitalize(tservice->get_extends()->get_name()) << endl; + } + + f_service_ << "import " << capitalize(program_name_) << "_Types" << endl; + f_service_ << "import qualified " << capitalize(service_name_) << "_Iface as Iface" << endl; + + // Generate the three main parts of the service + generate_service_helpers(tservice); + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + + // Close service file + f_service_.close(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_hs_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + indent(f_service_) << "-- HELPER FUNCTIONS AND STRUCTURES --" << endl; + indent(f_service_) << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_hs_struct_definition(f_service_, ts, false); + generate_hs_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_hs_generator::generate_hs_function_helpers(t_function* tfunction) { + t_struct result(program_, field_name(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_hs_struct_definition(f_service_, &result, false); +} + +/** + * Generate the map from field names to (type, id) + * @param tstruct the Struct + */ +void t_hs_generator::generate_hs_typemap(ofstream& 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(); + if (!first) { + out << ","; + } + + t_type* type = get_true_type((*f_iter)->get_type()); + int32_t key = (*f_iter)->get_key(); + out << "(" << key << ",(\"" << mname << "\"," << type_to_enum(type) << "))"; + first = false; + } + out << "]" << endl; +} + +/** + * generate the struct with default values filled in + * @param tstruct the Struct + */ +void t_hs_generator::generate_hs_default(ofstream& out, t_struct* tstruct) { + string name = type_name(tstruct); + string fname = type_name(tstruct, "default_"); + const vector& fields = tstruct->get_sorted_members(); + + indent(out) << fname << " :: " << name << endl; + 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(); + 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(); + 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) { + out << "P.Nothing"; + } else { + out << "P.Just " << render_const_value(type, value); + } + } else { + out << render_const_value(type, value); + } + } + out << "}" << endl; + indent_down(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_hs_generator::generate_service_interface(t_service* tservice) { + string f_iface_name = get_out_dir() + capitalize(service_name_) + "_Iface.hs"; + f_iface_.open(f_iface_name.c_str()); + + f_iface_ << hs_language_pragma() << endl; + f_iface_ << hs_autogen_comment() << endl; + + f_iface_ << "module " << capitalize(service_name_) << "_Iface where" << endl; + + f_iface_ << hs_imports() << endl; + f_iface_ << "import " << capitalize(program_name_) << "_Types" << endl; + f_iface_ << endl; + + string sname = capitalize(service_name_); + if (tservice->get_extends() != NULL) { + string extends = type_name(tservice->get_extends()); + + indent(f_iface_) << "import " << extends << "_Iface" << endl; + indent(f_iface_) << "class " << extends << "_Iface a => " << sname << "_Iface a where" << endl; + + } else { + indent(f_iface_) << "class " << sname << "_Iface a where" << endl; + } + + indent_up(); + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string ft = function_type(*f_iter, true, true, true); + indent(f_iface_) << decapitalize((*f_iter)->get_name()) << " :: a -> " << ft << endl; + } + + indent_down(); + f_iface_.close(); +} + +/** + * Generates a service client definition. Note that in Haskell, the client doesn't implement iface. + *This is because + * The client does not (and should not have to) deal with arguments being Nothing. + * + * @param tservice The service to generate a server for. + */ +void t_hs_generator::generate_service_client(t_service* tservice) { + string f_client_name = get_out_dir() + capitalize(service_name_) + "_Client.hs"; + f_client_.open(f_client_name.c_str()); + f_client_ << hs_language_pragma() << endl; + f_client_ << hs_autogen_comment() << endl; + + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + + string extends = ""; + string exports = ""; + + bool first = true; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + exports += (first ? "" : ","); + string funname = (*f_iter)->get_name(); + exports += decapitalize(funname); + first = false; + } + + string sname = capitalize(service_name_); + indent(f_client_) << "module " << sname << "_Client(" << exports << ") where" << endl; + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + indent(f_client_) << "import " << extends << "_Client" << endl; + } + + indent(f_client_) << "import qualified Data.IORef as R" << endl; + indent(f_client_) << hs_imports() << endl; + indent(f_client_) << "import " << capitalize(program_name_) << "_Types" << endl; + indent(f_client_) << "import " << capitalize(service_name_) << endl; + + // DATS RITE A GLOBAL VAR + indent(f_client_) << "seqid = R.newIORef 0" << endl; + + // Generate client method implementations + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + string fargs = ""; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) + fargs += " arg_" + (*fld_iter)->get_name(); + + // Open function + indent(f_client_) << decapitalize(funname) << " (ip,op)" << fargs << " = do" << endl; + indent_up(); + indent(f_client_) << "send_" << funname << " op" << fargs; + + f_client_ << endl; + + if (!(*f_iter)->is_oneway()) + indent(f_client_) << "recv_" << funname << " ip" << endl; + + indent_down(); + + indent(f_client_) << "send_" << funname << " op" << fargs << " = do" << endl; + indent_up(); + + indent(f_client_) << "seq <- seqid" << endl; + indent(f_client_) << "seqn <- R.readIORef seq" << endl; + string argsname = capitalize((*f_iter)->get_name() + "_args"); + + // Serialize the request header + string fname = (*f_iter)->get_name(); + string msgType = (*f_iter)->is_oneway() ? "T.M_ONEWAY" : "T.M_CALL"; + indent(f_client_) << "T.writeMessageBegin op (\"" << fname << "\", " << msgType << ", seqn)" + << endl; + 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(); + 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()) + f_client_ << "P.Just "; + f_client_ << "arg_" << fieldname; + first = false; + } + f_client_ << "})" << endl; + indent(f_client_) << "T.writeMessageEnd op" << endl; + + // Write to the stream + indent(f_client_) << "T.tFlush (T.getTransport op)" << endl; + indent_down(); + + if (!(*f_iter)->is_oneway()) { + string resultname = capitalize((*f_iter)->get_name() + "_result"); + t_struct noargs(program_); + + string funname = string("recv_") + (*f_iter)->get_name(); + t_function recv_function((*f_iter)->get_returntype(), funname, &noargs); + + // Open function + indent(f_client_) << funname << " ip = do" << endl; + indent_up(); + + indent(f_client_) << "(fname, mtype, rseqid) <- T.readMessageBegin ip" << endl; + indent(f_client_) << "M.when (mtype == T.M_EXCEPTION) $ do { exn <- T.readAppExn ip ; " + "T.readMessageEnd ip ; X.throw exn }" << endl; + + indent(f_client_) << "res <- read_" << resultname << " ip" << endl; + indent(f_client_) << "T.readMessageEnd ip" << endl; + + 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) { + indent(f_client_) << "P.maybe (P.return ()) X.throw (" + << field_name(resultname, (*x_iter)->get_name()) << " res)" << endl; + } + + if (!(*f_iter)->get_returntype()->is_void()) + indent(f_client_) << "P.return $ " << field_name(resultname, "success") << " res" << endl; + else + indent(f_client_) << "P.return ()" << endl; + + // Close function + indent_down(); + } + } + + f_client_.close(); +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_hs_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) + generate_process_function(tservice, *f_iter); + + indent(f_service_) << "proc_ handler (iprot,oprot) (name,typ,seqid) = case name of" << endl; + indent_up(); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string fname = (*f_iter)->get_name(); + indent(f_service_) << "\"" << fname << "\" -> process_" << decapitalize(fname) + << " (seqid,iprot,oprot,handler)" << endl; + } + + indent(f_service_) << "_ -> "; + if (tservice->get_extends() != NULL) { + f_service_ << type_name(tservice->get_extends()) + << ".proc_ handler (iprot,oprot) (name,typ,seqid)" << endl; + + } else { + f_service_ << "do" << endl; + indent_up(); + indent(f_service_) << "_ <- T.readVal iprot (T.T_STRUCT Map.empty)" << endl; + indent(f_service_) << "T.writeMessageBegin oprot (name,T.M_EXCEPTION,seqid)" << endl; + indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN_METHOD (\"Unknown function " + "\" ++ LT.unpack name))" << endl; + indent(f_service_) << "T.writeMessageEnd oprot" << endl; + indent(f_service_) << "T.tFlush (T.getTransport oprot)" << endl; + indent_down(); + } + + indent_down(); + + // Generate the server implementation + indent(f_service_) << "process handler (iprot, oprot) = do" << endl; + indent_up(); + + indent(f_service_) << "(name, typ, seqid) <- T.readMessageBegin iprot" << endl; + indent(f_service_) << "proc_ handler (iprot,oprot) (name,typ,seqid)" << endl; + indent(f_service_) << "T.readMessageEnd iprot" << endl; + indent(f_service_) << "P.return P.True" << endl; + indent_down(); +} + +bool hasNoArguments(t_function* func) { + return (func->get_arglist()->get_members().empty()); +} + +string t_hs_generator::render_hs_type_for_function_name(t_type* type) { + string type_str = render_hs_type(type, false); + std::string::size_type found = -1; + + while (true) { + found = type_str.find_first_of("[]. ", found + 1); + if (string::npos == size_t(found)) { + break; + } + + if (type_str[found] == '.') + type_str[found] = '_'; + else + type_str[found] = 'Z'; + } + return type_str; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_hs_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open function + string funname = decapitalize(tfunction->get_name()); + indent(f_service_) << "process_" << funname << " (seqid, iprot, oprot, handler) = do" << endl; + indent_up(); + + string argsname = capitalize(tfunction->get_name()) + "_args"; + string resultname = capitalize(tfunction->get_name()) + "_result"; + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + indent(f_service_) << "args <- read_" << argsname << " iprot" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + size_t n = xceptions.size() + 1; + // Try block for a function with exceptions + if (n > 0) { + for (size_t i = 0; i < n; i++) { + indent(f_service_) << "(X.catch" << endl; + indent_up(); + } + } + + if (n > 0) { + indent(f_service_) << "(do" << endl; + indent_up(); + } + indent(f_service_); + + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) + f_service_ << "val <- "; + + f_service_ << "Iface." << decapitalize(tfunction->get_name()) << " handler"; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) + f_service_ << " (" << field_name(argsname, (*f_iter)->get_name()) << " args)"; + + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + f_service_ << endl; + indent(f_service_) << "let res = default_" << resultname << "{" + << field_name(resultname, "success") << " = val}"; + + } else if (!tfunction->is_oneway()) { + f_service_ << endl; + indent(f_service_) << "let res = default_" << resultname; + } + f_service_ << endl; + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + indent(f_service_) << "P.return ()"; + } else { + indent(f_service_) << "T.writeMessageBegin oprot (\"" << tfunction->get_name() + << "\", T.M_REPLY, seqid)" << endl; + indent(f_service_) << "write_" << resultname << " oprot res" << endl; + indent(f_service_) << "T.writeMessageEnd oprot" << endl; + indent(f_service_) << "T.tFlush (T.getTransport oprot)"; + } + if (n > 0) { + f_service_ << ")"; + indent_down(); + } + f_service_ << endl; + + if (n > 0) { + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + indent(f_service_) << "(\\e -> do" << endl; + indent_up(); + + if (!tfunction->is_oneway()) { + indent(f_service_) << "let res = default_" << resultname << "{" + << field_name(resultname, (*x_iter)->get_name()) << " = P.Just e}" + << endl; + indent(f_service_) << "T.writeMessageBegin oprot (\"" << tfunction->get_name() + << "\", T.M_REPLY, seqid)" << endl; + indent(f_service_) << "write_" << resultname << " oprot res" << endl; + indent(f_service_) << "T.writeMessageEnd oprot" << endl; + indent(f_service_) << "T.tFlush (T.getTransport oprot)"; + } else { + indent(f_service_) << "P.return ()"; + } + + f_service_ << "))" << endl; + indent_down(); + indent_down(); + } + indent(f_service_) << "((\\_ -> do" << endl; + indent_up(); + + if (!tfunction->is_oneway()) { + indent(f_service_) << "T.writeMessageBegin oprot (\"" << tfunction->get_name() + << "\", T.M_EXCEPTION, seqid)" << endl; + indent(f_service_) << "T.writeAppExn oprot (T.AppExn T.AE_UNKNOWN \"\")" << endl; + indent(f_service_) << "T.writeMessageEnd oprot" << endl; + indent(f_service_) << "T.tFlush (T.getTransport oprot)"; + } else { + indent(f_service_) << "P.return ()"; + } + + f_service_ << ") :: X.SomeException -> P.IO ()))" << endl; + indent_down(); + indent_down(); + } + // Close function + indent_down(); +} + +/** + * Deserializes a field of any type. + */ +void t_hs_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + (void)prefix; + t_type* type = tfield->get_type(); + generate_deserialize_type(out, type, prefix); +} + +/** + * Deserializes a field of any type. + */ +void t_hs_generator::generate_deserialize_type(ofstream& out, t_type* type, string arg) { + type = get_true_type(type); + string val = tmp("_val"); + out << "(case " << arg << " of {" << type_to_constructor(type) << " " << val << " -> "; + + if (type->is_void()) + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE"; + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, val); + + } else if (type->is_container()) { + generate_deserialize_container(out, type, val); + + } else if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + if (tbase == t_base_type::TYPE_STRING && !((t_base_type*)type)->is_binary()) { + out << "E.decodeUtf8 "; + } + out << val; + if (((t_base_type*)type)->is_binary()) { + // Since wire type of binary is the same as string, we actually receive T.TString not + // T.TBinary + out << "; T.TString " << val << " -> " << val; + } + } else if (type->is_enum()) { + out << "P.toEnum $ P.fromIntegral " << val; + + } else { + throw "DO NOT KNOW HOW TO DESERIALIZE TYPE " + type->get_name(); + } + out << "; _ -> P.error \"wrong type\"})"; +} + +/** + * Generates an unserializer for a struct, calling read() + */ +void t_hs_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string name) { + + out << "(" << type_name(tstruct, "to_") << " (T.TStruct " << name << "))"; +} + +/** + * Serialize a container by writing out the header followed by + * data and then a footer. + */ +void t_hs_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string arg) { + + string val = tmp("_v"); + // Declare variables, read header + if (ttype->is_map()) { + string key = tmp("_k"); + out << "(Map.fromList $ P.map (\\(" << key << "," << val << ") -> ("; + generate_deserialize_type(out, ((t_map*)ttype)->get_key_type(), key); + + out << ","; + generate_deserialize_type(out, ((t_map*)ttype)->get_val_type(), val); + + out << ")) " << arg << ")"; + + } else if (ttype->is_set()) { + out << "(Set.fromList $ P.map (\\" << val << " -> "; + generate_deserialize_type(out, ((t_map*)ttype)->get_key_type(), val); + out << ") " << arg << ")"; + + } else if (ttype->is_list()) { + out << "(Vector.fromList $ P.map (\\" << val << " -> "; + generate_deserialize_type(out, ((t_map*)ttype)->get_key_type(), val); + out << ") " << arg << ")"; + } +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_hs_generator::generate_serialize_type(ofstream& out, t_type* type, string name) { + + type = get_true_type(type); + // Do nothing for void types + if (type->is_void()) + throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE"; + + 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()) { + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + out << type_to_constructor(type) << " "; + if (tbase == t_base_type::TYPE_STRING && !((t_base_type*)type)->is_binary()) { + out << "$ E.encodeUtf8 "; + } + out << name; + + } else if (type->is_enum()) { + string ename = capitalize(type->get_name()); + out << "T.TI32 $ P.fromIntegral $ P.fromEnum " << name; + } + + } else { + throw "DO NOT KNOW HOW TO SERIALIZE FIELD OF TYPE " + type->get_name(); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_hs_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << type_name(tstruct, "from_") << " " << prefix; +} + +void t_hs_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + string k = tmp("_k"); + string v = tmp("_v"); + + if (ttype->is_map()) { + t_type* ktype = ((t_map*)ttype)->get_key_type(); + t_type* vtype = ((t_map*)ttype)->get_val_type(); + out << "T.TMap " << type_to_enum(ktype) << " " << type_to_enum(vtype); + out << " $ P.map (\\(" << k << "," << v << ") -> ("; + generate_serialize_type(out, ktype, k); + out << ", "; + generate_serialize_type(out, vtype, v); + out << ")) $ Map.toList " << prefix; + + } else if (ttype->is_set()) { + out << "T.TSet " << type_to_enum(((t_list*)ttype)->get_elem_type()); + out << " $ P.map (\\" << v << " -> "; + generate_serialize_type(out, ((t_list*)ttype)->get_elem_type(), v); + out << ") $ Set.toList " << prefix; + + } else if (ttype->is_list()) { + out << "T.TList " << type_to_enum(((t_list*)ttype)->get_elem_type()); + out << " $ P.map (\\" << v << " -> "; + generate_serialize_type(out, ((t_list*)ttype)->get_elem_type(), v); + out << ") $ Vector.toList " << prefix; + } +} + +string t_hs_generator::function_type(t_function* tfunc, bool options, bool io, bool method) { + 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()) + result += "P.Maybe "; + result += render_hs_type((*f_iter)->get_type(), options); + result += " -> "; + } + + if (fields.empty() && !method) + result += "() -> "; + + if (io) + result += "P.IO "; + + result += render_hs_type(tfunc->get_returntype(), io); + return result; +} + +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 (!ttype->is_service()) + prefix = capitalize(program->get_name()) + "_Types."; + + return prefix + function_prefix + capitalize(ttype->get_name()); +} + +string t_hs_generator::field_name(string tname, string fname) { + return decapitalize(tname) + "_" + fname; +} + +/** + * Converts the parse type to a Protocol.t_type enum + */ +string t_hs_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: + return "T.T_VOID"; + case t_base_type::TYPE_STRING: + return ((t_base_type*)type)->is_binary() ? "T.T_BINARY" : "T.T_STRING"; + case t_base_type::TYPE_BOOL: + return "T.T_BOOL"; + case t_base_type::TYPE_I8: + return "T.T_BYTE"; + case t_base_type::TYPE_I16: + return "T.T_I16"; + case t_base_type::TYPE_I32: + return "T.T_I32"; + case t_base_type::TYPE_I64: + return "T.T_I64"; + case t_base_type::TYPE_DOUBLE: + return "T.T_DOUBLE"; + } + + } else if (type->is_enum()) { + return "T.T_I32"; + + } else if (type->is_struct() || type->is_xception()) { + return "(T.T_STRUCT " + type_name((t_struct*)type, "typemap_") + ")"; + + } else if (type->is_map()) { + string ktype = type_to_enum(((t_map*)type)->get_key_type()); + string vtype = type_to_enum(((t_map*)type)->get_val_type()); + return "(T.T_MAP " + ktype + " " + vtype + ")"; + + } else if (type->is_set()) { + return "(T.T_SET " + type_to_enum(((t_list*)type)->get_elem_type()) + ")"; + + } else if (type->is_list()) { + return "(T.T_LIST " + type_to_enum(((t_list*)type)->get_elem_type()) + ")"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts the parse type to a default value + */ +string t_hs_generator::type_to_default(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: + return "P.error \"No default value for type T_VOID\""; + case t_base_type::TYPE_STRING: + return "\"\""; + case t_base_type::TYPE_BOOL: + return "P.False"; + case t_base_type::TYPE_I8: + return "0"; + case t_base_type::TYPE_I16: + return "0"; + case t_base_type::TYPE_I32: + return "0"; + case t_base_type::TYPE_I64: + return "0"; + case t_base_type::TYPE_DOUBLE: + return "0"; + } + + } else if (type->is_enum()) { + return "(P.toEnum 0)"; + + } else if (type->is_struct() || type->is_xception()) { + return type_name((t_struct*)type, "default_"); + + } else if (type->is_map()) { + return "Map.empty"; + + } else if (type->is_set()) { + return "Set.empty"; + + } else if (type->is_list()) { + return "Vector.empty"; + } + + throw "INVALID TYPE IN type_to_default: " + type->get_name(); +} + +/** + * Converts the parse type to an haskell type + */ +string t_hs_generator::render_hs_type(t_type* type, bool needs_parens) { + type = get_true_type(type); + string type_repr; + + 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: + return "()"; + case t_base_type::TYPE_STRING: + return (((t_base_type*)type)->is_binary() ? "LBS.ByteString" : "LT.Text"); + case t_base_type::TYPE_BOOL: + return "P.Bool"; + case t_base_type::TYPE_I8: + return "I.Int8"; + case t_base_type::TYPE_I16: + return "I.Int16"; + case t_base_type::TYPE_I32: + return "I.Int32"; + case t_base_type::TYPE_I64: + return "I.Int64"; + case t_base_type::TYPE_DOUBLE: + return "P.Double"; + } + + } else if (type->is_enum()) { + return type_name((t_enum*)type); + + } else if (type->is_struct() || type->is_xception()) { + return type_name((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(); + type_repr = "Map.HashMap " + render_hs_type(ktype, true) + " " + render_hs_type(vtype, true); + + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + type_repr = "Set.HashSet " + render_hs_type(etype, true); + + } else if (type->is_list()) { + t_type* etype = ((t_list*)type)->get_elem_type(); + type_repr = "Vector.Vector " + render_hs_type(etype, true); + + } else { + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); + } + + return needs_parens ? "(" + type_repr + ")" : type_repr; +} + +/** + * Converts the parse type to a haskell constructor + */ +string t_hs_generator::type_to_constructor(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 "invalid type: T_VOID"; + case t_base_type::TYPE_STRING: + return ((t_base_type*)type)->is_binary() ? "T.TBinary" : "T.TString"; + case t_base_type::TYPE_BOOL: + return "T.TBool"; + case t_base_type::TYPE_I8: + return "T.TByte"; + case t_base_type::TYPE_I16: + return "T.TI16"; + case t_base_type::TYPE_I32: + return "T.TI32"; + case t_base_type::TYPE_I64: + return "T.TI64"; + case t_base_type::TYPE_DOUBLE: + return "T.TDouble"; + } + + } else if (type->is_enum()) { + return "T.TI32"; + + } else if (type->is_struct() || type->is_xception()) { + return "T.TStruct"; + + } else if (type->is_map()) { + return "T.TMap _ _"; + + } else if (type->is_set()) { + return "T.TSet _"; + + } else if (type->is_list()) { + return "T.TList _"; + } + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +THRIFT_REGISTER_GENERATOR(hs, "Haskell", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc new file mode 100644 index 00000000..ec78e102 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc @@ -0,0 +1,1088 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_generator.h" +#include "thrift/generate/t_html_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 string endl = "\n"; // avoid ostream << std::endl flushes + +enum input_type { INPUT_UNKNOWN, INPUT_UTF8, INPUT_PLAIN }; + +/** + * HTML code generator + * + * mostly copy/pasting/tweaking from mcslee's work. + */ +class t_html_generator : public t_generator { +public: + t_html_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; + + standalone_ = false; + unsafe_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("standalone") == 0) { + standalone_ = true; + } else if( iter->first.compare("noescape") == 0) { + unsafe_ = true; + } else { + throw "unknown option html:" + iter->first; + } + } + + + out_dir_base_ = "gen-html"; + input_type_ = INPUT_UNKNOWN; + + escape_.clear(); + escape_['&'] = "&"; + escape_['<'] = "<"; + escape_['>'] = ">"; + escape_['"'] = """; + escape_['\''] = "'"; + + init_allowed__markup(); + } + + void generate_program(); + 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); + void generate_css(); + void generate_css_content(std::ofstream& f_target); + void generate_style_tag(); + std::string make_file_link(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); + 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 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); + +private: + std::ofstream f_out_; + std::string current_file_; + input_type input_type_; + std::map allowed_markup; + bool standalone_; + bool unsafe_; +}; + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_html_generator::generate_program_toc() { + f_out_ << "" + << "" << endl; + generate_program_toc_row(program_); + f_out_ << "
ModuleServicesData typesConstants
" << 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_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()) { + 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); + } +} + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_html_generator::generate_program_toc_row(t_program* tprog) { + string fname = tprog->get_name() + ".html"; + f_out_ << "" << endl << "" << 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) { + string name = get_service_name(*sv_iter); + f_out_ << "" << name + << "
" << endl; + f_out_ << "
    " << endl; + map fn_html; + 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(); + string html = "
  • " + 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; + } + f_out_ << "
" << endl; + } + } + f_out_ << "" << endl << ""; + map data_types; + 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) { + string name = (*en_iter)->get_name(); + // f_out_ << "" << name + // << "
" << endl; + string html = "" + name + ""; + data_types.insert(pair(name, html)); + } + } + 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) { + string name = (*td_iter)->get_symbolic(); + // f_out_ << "" << name + // << "
" << endl; + string html = "" + name + + ""; + data_types.insert(pair(name, html)); + } + } + 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) { + string name = (*o_iter)->get_name(); + // f_out_ << "" << name + //<< "
" << endl; + string html = "" + name + + ""; + 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; + } + f_out_ << "" << endl << ""; + if (!tprog->get_consts().empty()) { + map const_html; + vector consts = tprog->get_consts(); + vector::iterator con_iter; + for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) { + string name = (*con_iter)->get_name(); + string html = "" + name + + ""; + 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; + } + } + f_out_ << "" << endl << ""; +} + +/** + * Prepares for file generation by opening up the necessary file output + * stream. + */ +void t_html_generator::generate_program() { + // Make output directory + MKDIR(get_out_dir().c_str()); + 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; + generate_style_tag(); + f_out_ << "Thrift module: " << program_->get_name() << "" << endl + << "
" << endl + << "

Thrift module: " << program_->get_name() << "

" << endl; + + print_doc(program_); + + generate_program_toc(); + + if (!program_->get_consts().empty()) { + f_out_ << "

Constants

" << endl; + vector consts = program_->get_consts(); + f_out_ << ""; + f_out_ << "" << endl; + generate_consts(consts); + f_out_ << "
ConstantTypeValue
"; + } + + if (!program_->get_enums().empty()) { + f_out_ << "

Enumerations

" << 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_ << "

Type declarations

" << 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_ << "

Data structures

" << 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_ << "

Services

" << 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(); + generate_css(); +} + +/** + * Emits the index.html file for the recursive set of Thrift programs + */ +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; + generate_style_tag(); + f_out_ << "All Thrift declarations" << endl + << "
" << endl << "

All Thrift declarations

" << endl; + f_out_ << "" + << "" << endl; + vector programs; + generate_program_toc_rows(program_, programs); + f_out_ << "
ModuleServicesData typesConstants
" << endl; + f_out_ << "
" << endl; + f_out_.close(); +} + +void t_html_generator::generate_css() { + if (!standalone_) { + current_file_ = "style.css"; + string css_fname = get_out_dir() + current_file_; + f_out_.open(css_fname.c_str()); + generate_css_content(f_out_); + f_out_.close(); + } +} + +void t_html_generator::generate_css_content(std::ofstream& f_target) { + f_target << BOOTSTRAP_CSS() << endl; + f_target << "/* Auto-generated CSS for generated Thrift docs */" << endl; + f_target << "h3, h4 { margin-bottom: 6px; }" << endl; + f_target << "div.definition { border: 1px solid #CCC; margin-bottom: 10px; padding: 10px; }" + << endl; + f_target << "div.extends { margin: -0.5em 0 1em 5em }" << endl; + f_target << "td { vertical-align: top; }" << endl; + f_target << "table { empty-cells: show; }" << endl; + f_target << "code { line-height: 20px; }" << endl; + f_target << ".table-bordered th, .table-bordered td { border-bottom: 1px solid #DDDDDD; }" + << endl; +} + +/** + * Generates the CSS tag. + * Depending on "standalone", either a CSS file link (default), or the entire CSS is embedded + * inline. + */ +void t_html_generator::generate_style_tag() { + if (!standalone_) { + f_out_ << "" << endl; + } else { + f_out_ << "" << endl; + } +} + +/** + * Returns the target file for a link + * The returned string is empty, whenever filename refers to the current file. + */ +std::string t_html_generator::make_file_link(std::string filename) { + return (current_file_.compare(filename) != 0) ? filename : ""; +} + +/** + * If the provided documentable object has documentation attached, this + * will emit it to the output stream in HTML format. + */ +void t_html_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_html_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_html_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_html_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_html_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 (std::string::size_type i = 0; i < tag_key.length(); ++i) { + tag_key[i] = tolower(tag_key[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_html_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 << c; + 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 HTML + */ +int t_html_generator::print_type(t_type* ttype) { + std::string::size_type len = 0; + f_out_ << ""; + 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_ << (((t_base_type*)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_ << "
is_typedef()) { + f_out_ << "Typedef_"; + } else if (ttype->is_struct() || ttype->is_xception()) { + f_out_ << "Struct_"; + } else if (ttype->is_enum()) { + f_out_ << "Enum_"; + } else if (ttype->is_service()) { + f_out_ << "Svc_"; + } + f_out_ << type_name << "\">"; + len = type_name.size(); + if (ttype->get_program() != program_) { + f_out_ << prog_name << "."; + len += prog_name.size() + 1; + } + f_out_ << type_name << ""; + } + f_out_ << ""; + return (int)len; +} + +/** + * Prints out an HTML representation of the provided constant value + */ +void t_html_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 = program_->get_name() + ".html"; + string name = escape_html(tvalue->get_identifier()); + f_out_ << "" + 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(); + 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; + } + } 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 = 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: " + 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_html_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; + } + if (has_docs) { + arg_iter = args.begin(); + f_out_ << "

get_name() + << "\">Parameters

" << endl; + f_out_ << ""; + f_out_ << ""; + for (; arg_iter != args.end(); arg_iter++) { + f_out_ << "" << endl; + } + f_out_ << "
NameDescription
" << (*arg_iter)->get_name(); + f_out_ << ""; + f_out_ << escape_html((*arg_iter)->get_doc()); + f_out_ << "
"; + } + } + + 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; + } + if (has_docs) { + ex_iter = excepts.begin(); + f_out_ << "

get_name() + << "\">Exceptions

" << endl; + f_out_ << ""; + f_out_ << ""; + for (; ex_iter != excepts.end(); ex_iter++) { + f_out_ << "" << endl; + } + f_out_ << "
TypeDescription
" << (*ex_iter)->get_type()->get_name(); + f_out_ << ""; + f_out_ << escape_html((*ex_iter)->get_doc()); + f_out_ << "
"; + } + } +} + +/** + * Generates a typedef. + * + * @param ttypedef The type definition + */ +void t_html_generator::generate_typedef(t_typedef* ttypedef) { + string name = ttypedef->get_name(); + f_out_ << "
"; + f_out_ << "

Typedef: " << name << "

" << endl; + f_out_ << "

Base type: "; + print_type(ttypedef->get_type()); + f_out_ << "

" << endl; + print_doc(ttypedef); + f_out_ << "
" << endl; +} + +/** + * Generates code for an enumerated type. + * + * @param tenum The enumeration + */ +void t_html_generator::generate_enum(t_enum* tenum) { + string name = tenum->get_name(); + f_out_ << "
"; + f_out_ << "

Enumeration: " << name << "

" << endl; + print_doc(tenum); + vector values = tenum->get_constants(); + vector::iterator val_iter; + f_out_ << "
" << endl; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + f_out_ << "" << endl; + } + f_out_ << "
"; + f_out_ << (*val_iter)->get_name(); + f_out_ << ""; + f_out_ << (*val_iter)->get_value(); + f_out_ << "" << endl; + print_doc((*val_iter)); + f_out_ << "
" << endl; +} + +/** + * Generates a constant value + */ +void t_html_generator::generate_const(t_const* tconst) { + 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()) { + f_out_ << "
"; + print_doc(tconst); + f_out_ << "
"; + } +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_html_generator::generate_struct(t_struct* tstruct) { + string name = tstruct->get_name(); + f_out_ << "
"; + 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; + vector members = tstruct->get_members(); + vector::iterator mem_iter = members.begin(); + f_out_ << ""; + f_out_ << "" << endl; + for (; mem_iter != members.end(); mem_iter++) { + f_out_ << "" << endl; + } + f_out_ << "
KeyFieldTypeDescriptionRequirednessDefault value
" << (*mem_iter)->get_key() << ""; + f_out_ << (*mem_iter)->get_name(); + f_out_ << ""; + print_type((*mem_iter)->get_type()); + f_out_ << ""; + f_out_ << escape_html((*mem_iter)->get_doc()); + f_out_ << ""; + 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 != NULL) { + f_out_ << ""; + print_const_value((*mem_iter)->get_type(), default_val); + f_out_ << ""; + } + f_out_ << "

"; + print_doc(tstruct); + f_out_ << "
"; +} + +/** + * Exceptions are special structs + * + * @param tstruct The struct definition + */ +void t_html_generator::generate_xception(t_struct* txception) { + generate_struct(txception); +} + +/** + * Generates the HTML block for a Thrift service. + * + * @param tservice The service definition + */ +void t_html_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_ << "
\n"; + } + print_doc(tservice); + 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_ << "
"; + f_out_ << "

Function: " << service_name_ + << "." << fn_name << "

" << endl; + f_out_ << "
";
+    std::string::size_type offset = print_type((*fn_iter)->get_returntype());
+    bool first = true;
+    f_out_ << " " << fn_name << "(";
+    offset += fn_name.size() + 2;
+    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;
+        for (std::string::size_type i = 0; i < offset; ++i) {
+          f_out_ << " ";
+        }
+      }
+      first = false;
+      print_type((*arg_iter)->get_type());
+      f_out_ << " " << (*arg_iter)->get_name();
+      if ((*arg_iter)->get_value() != NULL) {
+        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;
+    }
+    f_out_ << "
"; + print_doc(*fn_iter); + print_fn_args_doc(*fn_iter); + f_out_ << "
"; + } +} + +THRIFT_REGISTER_GENERATOR( + html, + "HTML", + " standalone: Self-contained mode, includes all CSS in the HTML files.\n" + " Generates no style.css file, but HTML files will be larger.\n" + " noescape: Do not escape html in doc text.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h new file mode 100644 index 00000000..600b17f1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_html_generator.h @@ -0,0 +1,240 @@ +#define BOOTSTRAP_CSS() \ + "/*!\n" \ + " * Bootstrap v2.0.3\n" \ + " *\n" \ + " * Copyright 2012 Twitter, Inc\n" \ + " * Licensed under the Apache License v2.0\n" \ + " * http://www.apache.org/licenses/LICENSE-2.0\n" \ + " *\n" \ + " * Designed and built with all the love in the world @twitter by @mdo and @fat.\n" \ + " */\n" \ + ".clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:\"\";}\n" \ + ".clearfix:after{clear:both;}\n" \ + ".hide-text{font:0/0 " \ + "a;color:transparent;text-shadow:none;background-color:transparent;border:0;}\n" \ + ".input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-" \ + "moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}\n" \ + "article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}\n" \ + "audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}\n" \ + "audio:not([controls]){display:none;}\n" \ + "html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}\n" \ + "a:focus{outline:thin dotted #333;outline:5px auto " \ + "-webkit-focus-ring-color;outline-offset:-2px;}\n" \ + "a:hover,a:active{outline:0;}\n" \ + "sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}\n" \ + "sup{top:-0.5em;}\n" \ + "sub{bottom:-0.25em;}\n" \ + "img{max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic;}\n" \ + "button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}\n" \ + "button,input{*overflow:visible;line-height:normal;}\n" \ + "button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}\n" \ + "button,input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{cursor:pointer;-" \ + "webkit-appearance:button;}\n" \ + "input[type=\"search\"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:" \ + "content-box;-webkit-appearance:textfield;}\n" \ + "input[type=\"search\"]::-webkit-search-decoration,input[type=\"search\"]::-webkit-search-" \ + "cancel-button{-webkit-appearance:none;}\n" \ + "textarea{overflow:auto;vertical-align:top;}\n" \ + "body{margin:0;font-family:\"Helvetica " \ + "Neue\",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-" \ + "color:#ffffff;}\n" \ + "a{color:#0088cc;text-decoration:none;}\n" \ + "a:hover{color:#005580;text-decoration:underline;}\n" \ + ".row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:\"\";}\n" \ + ".row:after{clear:both;}\n" \ + "[class*=\"span\"]{float:left;margin-left:20px;}\n" \ + ".container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}\n" \ + ".span12{width:940px;}\n" \ + ".span11{width:860px;}\n" \ + ".span10{width:780px;}\n" \ + ".span9{width:700px;}\n" \ + ".span8{width:620px;}\n" \ + ".span7{width:540px;}\n" \ + ".span6{width:460px;}\n" \ + ".span5{width:380px;}\n" \ + ".span4{width:300px;}\n" \ + ".span3{width:220px;}\n" \ + ".span2{width:140px;}\n" \ + ".span1{width:60px;}\n" \ + ".offset12{margin-left:980px;}\n" \ + ".offset11{margin-left:900px;}\n" \ + ".offset10{margin-left:820px;}\n" \ + ".offset9{margin-left:740px;}\n" \ + ".offset8{margin-left:660px;}\n" \ + ".offset7{margin-left:580px;}\n" \ + ".offset6{margin-left:500px;}\n" \ + ".offset5{margin-left:420px;}\n" \ + ".offset4{margin-left:340px;}\n" \ + ".offset3{margin-left:260px;}\n" \ + ".offset2{margin-left:180px;}\n" \ + ".offset1{margin-left:100px;}\n" \ + ".row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:\"\";}" \ + "\n" \ + ".row-fluid:after{clear:both;}\n" \ + ".row-fluid " \ + "[class*=\"span\"]{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-" \ + "box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:" \ + "2.127659574%;*margin-left:2.0744680846382977%;}\n" \ + ".row-fluid [class*=\"span\"]:first-child{margin-left:0;}\n" \ + ".row-fluid .span12{width:99.99999998999999%;*width:99.94680850063828%;}\n" \ + ".row-fluid .span11{width:91.489361693%;*width:91.4361702036383%;}\n" \ + ".row-fluid .span10{width:82.97872339599999%;*width:82.92553190663828%;}\n" \ + ".row-fluid .span9{width:74.468085099%;*width:74.4148936096383%;}\n" \ + ".row-fluid .span8{width:65.95744680199999%;*width:65.90425531263828%;}\n" \ + ".row-fluid .span7{width:57.446808505%;*width:57.3936170156383%;}\n" \ + ".row-fluid .span6{width:48.93617020799999%;*width:48.88297871863829%;}\n" \ + ".row-fluid .span5{width:40.425531911%;*width:40.3723404216383%;}\n" \ + ".row-fluid .span4{width:31.914893614%;*width:31.8617021246383%;}\n" \ + ".row-fluid .span3{width:23.404255317%;*width:23.3510638276383%;}\n" \ + ".row-fluid .span2{width:14.89361702%;*width:14.8404255306383%;}\n" \ + ".row-fluid .span1{width:6.382978723%;*width:6.329787233638298%;}\n" \ + ".container{margin-right:auto;margin-left:auto;*zoom:1;}.container:before,.container:after{" \ + "display:table;content:\"\";}\n" \ + ".container:after{clear:both;}\n" \ + ".container-fluid{padding-right:20px;padding-left:20px;*zoom:1;}.container-fluid:before,." \ + "container-fluid:after{display:table;content:\"\";}\n" \ + ".container-fluid:after{clear:both;}\n" \ + "p{margin:0 0 9px;font-family:\"Helvetica " \ + "Neue\",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p " \ + "small{font-size:11px;color:#999999;}\n" \ + ".lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}\n" \ + "h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:" \ + "optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 " \ + "small{font-weight:normal;color:#999999;}\n" \ + "h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}\n" \ + "h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}\n" \ + "h3{font-size:18px;line-height:27px;}h3 small{font-size:14px;}\n" \ + "h4,h5,h6{line-height:18px;}\n" \ + "h4{font-size:14px;}h4 small{font-size:12px;}\n" \ + "h5{font-size:12px;}\n" \ + "h6{font-size:11px;color:#999999;text-transform:uppercase;}\n" \ + ".page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}\n" \ + ".page-header h1{line-height:1;}\n" \ + "ul,ol{padding:0;margin:0 0 9px 25px;}\n" \ + "ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}\n" \ + "ul{list-style:disc;}\n" \ + "ol{list-style:decimal;}\n" \ + "li{line-height:18px;}\n" \ + "ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}\n" \ + "dl{margin-bottom:18px;}\n" \ + "dt,dd{line-height:18px;}\n" \ + "dt{font-weight:bold;line-height:17px;}\n" \ + "dd{margin-left:9px;}\n" \ + ".dl-horizontal " \ + "dt{float:left;width:120px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;" \ + "white-space:nowrap;}\n" \ + ".dl-horizontal dd{margin-left:130px;}\n" \ + "hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}\n" \ + "strong{font-weight:bold;}\n" \ + "em{font-style:italic;}\n" \ + ".muted{color:#999999;}\n" \ + "abbr[title]{cursor:help;border-bottom:1px dotted #ddd;}\n" \ + "abbr.initialism{font-size:90%;text-transform:uppercase;}\n" \ + "blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote " \ + "p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}\n" \ + "blockquote small{display:block;line-height:18px;color:#999999;}blockquote " \ + "small:before{content:'\\2014 \\00A0';}\n" \ + "blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid " \ + "#eeeeee;border-left:0;}blockquote.pull-right p,blockquote.pull-right " \ + "small{text-align:right;}\n" \ + "q:before,q:after,blockquote:before,blockquote:after{content:\"\";}\n" \ + "address{display:block;margin-bottom:18px;font-style:normal;line-height:18px;}\n" \ + "small{font-size:100%;}\n" \ + "cite{font-style:normal;}\n" \ + "code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,\"Courier " \ + "New\",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;" \ + "border-radius:3px;}\n" \ + "code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}\n" \ + "pre{display:block;padding:8.5px;margin:0 0 " \ + "9px;font-size:12.025px;line-height:18px;word-break:break-all;word-wrap:break-word;white-space:" \ + "pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid " \ + "rgba(0, 0, 0, " \ + "0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}pre.prettyprint{" \ + "margin-bottom:18px;}\n" \ + "pre code{padding:0;color:inherit;background-color:transparent;border:0;}\n" \ + ".pre-scrollable{max-height:340px;overflow-y:scroll;}\n" \ + ".label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#ffffff;vertical-" \ + "align:baseline;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, " \ + "0.25);background-color:#999999;}\n" \ + ".label{padding:1px 4px " \ + "2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}\n" \ + ".badge{padding:1px 9px " \ + "2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}\n" \ + "a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}\n" \ + ".label-important,.badge-important{background-color:#b94a48;}\n" \ + ".label-important[href],.badge-important[href]{background-color:#953b39;}\n" \ + ".label-warning,.badge-warning{background-color:#f89406;}\n" \ + ".label-warning[href],.badge-warning[href]{background-color:#c67605;}\n" \ + ".label-success,.badge-success{background-color:#468847;}\n" \ + ".label-success[href],.badge-success[href]{background-color:#356635;}\n" \ + ".label-info,.badge-info{background-color:#3a87ad;}\n" \ + ".label-info[href],.badge-info[href]{background-color:#2d6987;}\n" \ + ".label-inverse,.badge-inverse{background-color:#333333;}\n" \ + ".label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a;}\n" \ + "table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0;}" \ + "\n" \ + ".table{width:100%;margin-bottom:18px;}.table th,.table " \ + "td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid " \ + "#dddddd;}\n" \ + ".table th{font-weight:bold;}\n" \ + ".table thead th{vertical-align:bottom;}\n" \ + ".table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table " \ + "colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table " \ + "thead:first-child tr:first-child th,.table thead:first-child tr:first-child " \ + "td{border-top:0;}\n" \ + ".table tbody+tbody{border-top:2px solid #dddddd;}\n" \ + ".table-condensed th,.table-condensed td{padding:4px 5px;}\n" \ + ".table-bordered{border:1px solid " \ + "#dddddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-" \ + "radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered " \ + "td{border-left:1px solid #dddddd;}\n" \ + ".table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child " \ + "th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead " \ + "tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered " \ + "colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child " \ + "th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child " \ + "tr:first-child td{border-top:0;}\n" \ + ".table-bordered thead:first-child tr:first-child th:first-child,.table-bordered " \ + "tbody:first-child tr:first-child " \ + "td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-" \ + "radius-topleft:4px;}\n" \ + ".table-bordered thead:first-child tr:first-child th:last-child,.table-bordered " \ + "tbody:first-child tr:first-child " \ + "td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-" \ + "radius-topright:4px;}\n" \ + ".table-bordered thead:last-child tr:last-child th:first-child,.table-bordered " \ + "tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 " \ + "4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 " \ + "4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-" \ + "bottomleft:4px;}\n" \ + ".table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child " \ + "tr:last-child " \ + "td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-" \ + "border-radius-bottomright:4px;}\n" \ + ".table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) " \ + "th{background-color:#f9f9f9;}\n" \ + ".table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}\n" \ + "table .span1{float:none;width:44px;margin-left:0;}\n" \ + "table .span2{float:none;width:124px;margin-left:0;}\n" \ + "table .span3{float:none;width:204px;margin-left:0;}\n" \ + "table .span4{float:none;width:284px;margin-left:0;}\n" \ + "table .span5{float:none;width:364px;margin-left:0;}\n" \ + "table .span6{float:none;width:444px;margin-left:0;}\n" \ + "table .span7{float:none;width:524px;margin-left:0;}\n" \ + "table .span8{float:none;width:604px;margin-left:0;}\n" \ + "table .span9{float:none;width:684px;margin-left:0;}\n" \ + "table .span10{float:none;width:764px;margin-left:0;}\n" \ + "table .span11{float:none;width:844px;margin-left:0;}\n" \ + "table .span12{float:none;width:924px;margin-left:0;}\n" \ + "table .span13{float:none;width:1004px;margin-left:0;}\n" \ + "table .span14{float:none;width:1084px;margin-left:0;}\n" \ + "table .span15{float:none;width:1164px;margin-left:0;}\n" \ + "table .span16{float:none;width:1244px;margin-left:0;}\n" \ + "table .span17{float:none;width:1324px;margin-left:0;}\n" \ + "table .span18{float:none;width:1404px;margin-left:0;}\n" \ + "table .span19{float:none;width:1484px;margin-left:0;}\n" \ + "table .span20{float:none;width:1564px;margin-left:0;}\n" \ + "table .span21{float:none;width:1644px;margin-left:0;}\n" \ + "table .span22{float:none;width:1724px;margin-left:0;}\n" \ + "table .span23{float:none;width:1804px;margin-left:0;}\n" \ + "table .span24{float:none;width:1884px;margin-left:0;}" diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc new file mode 100644 index 00000000..fb581e4f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_java_generator.cc @@ -0,0 +1,5280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include + +#include "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ofstream; +using std::ostringstream; +using std::setfill; +using std::setw; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * Java code generator. + * + */ +class t_java_generator : public t_oop_generator { +public: + t_java_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; + + bean_style_ = false; + android_style_ = false; + private_members_ = false; + nocamel_style_ = false; + fullcamel_style_ = false; + android_legacy_ = false; + sorted_containers_ = false; + java5_ = false; + reuse_objects_ = false; + use_option_type_ = false; + undated_generated_annotations_ = false; + suppress_generated_annotations_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("beans") == 0) { + bean_style_ = true; + } else if( iter->first.compare("android") == 0) { + android_style_ = true; + } else if( iter->first.compare("private-members") == 0) { + private_members_ = true; + } else if( iter->first.compare("nocamel") == 0) { + nocamel_style_ = true; + } else if( iter->first.compare("fullcamel") == 0) { + fullcamel_style_ = true; + } else if( iter->first.compare("android_legacy") == 0) { + android_legacy_ = true; + } else if( iter->first.compare("sorted_containers") == 0) { + sorted_containers_ = true; + } else if( iter->first.compare("java5") == 0) { + java5_ = true; + } else if( iter->first.compare("reuse-objects") == 0) { + reuse_objects_ = true; + } else if( iter->first.compare("option_type") == 0) { + use_option_type_ = true; + } else if( iter->first.compare("generated_annotations") == 0) { + if( iter->second.compare("undated") == 0) { + undated_generated_annotations_ = true; + } else if(iter->second.compare("suppress") == 0) { + suppress_generated_annotations_ = true; + } else { + throw "unknown option java:" + iter->first + "=" + iter->second; + } + } else { + throw "unknown option java:" + iter->first; + } + } + + if (java5_) { + android_legacy_ = true; + } + + out_dir_base_ = (bean_style_ ? "gen-javabean" : "gen-java"); + } + + /** + * 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_union(t_struct* tunion); + void generate_xception(t_struct* txception); + void generate_service(t_service* tservice); + + void print_const_value(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval = false); + std::string render_const_value(std::ofstream& out, t_type* type, t_const_value* value); + + /** + * Service-level generation functions + */ + + void generate_java_struct(t_struct* tstruct, bool is_exception); + + void generate_java_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool in_class = false, + bool is_result = false); + void generate_java_struct_parcelable(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_equality(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_compare_to(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_java_validator(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_tostring(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_clear(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_write_object(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_read_object(std::ofstream& out, t_struct* tstruct); + void generate_java_meta_data_map(std::ofstream& out, t_struct* tstruct); + void generate_field_value_meta_data(std::ofstream& out, t_type* type); + std::string get_java_type_string(t_type* type); + void generate_java_struct_field_by_id(ofstream& out, t_struct* tstruct); + void generate_reflection_setters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_reflection_getters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_generic_field_getters_setters(std::ofstream& out, t_struct* tstruct); + void generate_generic_isset_method(std::ofstream& out, t_struct* tstruct); + void generate_java_bean_boilerplate(std::ofstream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + std::string as_camel_case(std::string name, bool ucfirst = true); + std::string get_rpc_method_name(std::string name); + std::string get_cap_name(std::string name); + std::string generate_isset_check(t_field* field); + std::string generate_isset_check(std::string field); + void generate_isset_set(ofstream& out, t_field* field, std::string prefix); + std::string isset_field_id(t_field* field); + + void generate_service_interface(t_service* tservice); + void generate_service_async_interface(t_service* tservice); + void generate_service_helpers(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_async_client(t_service* tservice); + void generate_service_server(t_service* tservice); + void generate_service_async_server(t_service* tservice); + void generate_process_function(t_service* tservice, t_function* tfunction); + void generate_process_async_function(t_service* tservice, t_function* tfunction); + + void generate_java_union(t_struct* tstruct); + void generate_union_constructor(ofstream& out, t_struct* tstruct); + void generate_union_getters_and_setters(ofstream& out, t_struct* tstruct); + void generate_union_is_set_methods(ofstream& out, t_struct* tstruct); + void generate_union_abstract_methods(ofstream& out, t_struct* tstruct); + void generate_check_type(ofstream& out, t_struct* tstruct); + void generate_standard_scheme_read_value(ofstream& out, t_struct* tstruct); + void generate_standard_scheme_write_value(ofstream& out, t_struct* tstruct); + void generate_tuple_scheme_read_value(ofstream& out, t_struct* tstruct); + void generate_tuple_scheme_write_value(ofstream& out, t_struct* tstruct); + void generate_get_field_desc(ofstream& out, t_struct* tstruct); + void generate_get_struct_desc(ofstream& out, t_struct* tstruct); + void generate_get_field_name(ofstream& out, t_struct* tstruct); + + void generate_union_comparisons(ofstream& out, t_struct* tstruct); + void generate_union_hashcode(ofstream& out, t_struct* tstruct); + + void generate_scheme_map(ofstream& out, t_struct* tstruct); + void generate_standard_writer(ofstream& out, t_struct* tstruct, bool is_result); + void generate_standard_reader(ofstream& out, t_struct* tstruct); + void generate_java_struct_standard_scheme(ofstream& out, t_struct* tstruct, bool is_result); + + void generate_java_struct_tuple_scheme(ofstream& out, t_struct* tstruct); + void generate_java_struct_tuple_reader(ofstream& out, t_struct* tstruct); + void generate_java_struct_tuple_writer(ofstream& out, t_struct* tstruct); + + void generate_java_scheme_lookup(ofstream& out); + + void generate_javax_generated_annotation(ofstream& out); + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool has_metadata = true); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, + t_type* ttype, + std::string prefix = "", + bool has_metadata = true); + + void generate_deserialize_set_element(std::ofstream& out, + t_set* tset, + std::string prefix = "", + std::string obj = "", + bool has_metadata = true); + + void generate_deserialize_map_element(std::ofstream& out, + t_map* tmap, + std::string prefix = "", + std::string obj = "", + bool has_metadata = true); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = "", + std::string obj = "", + bool has_metadata = true); + + void generate_serialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool has_metadata = true); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, + t_type* ttype, + std::string prefix = "", + bool has_metadata = true); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map, + bool has_metadata = true); + + void generate_serialize_set_element(std::ofstream& out, + t_set* tmap, + std::string iter, + bool has_metadata = true); + + void generate_serialize_list_element(std::ofstream& out, + t_list* tlist, + std::string iter, + bool has_metadata = true); + + void generate_deep_copy_container(std::ofstream& out, + std::string source_name_p1, + std::string source_name_p2, + std::string result_name, + t_type* type); + void generate_deep_copy_non_container(std::ofstream& out, + std::string source_name, + std::string dest_name, + t_type* type); + + enum isset_type { ISSET_NONE, ISSET_PRIMITIVE, ISSET_BITSET }; + isset_type needs_isset(t_struct* tstruct, std::string* outPrimitiveType = NULL); + + /** + * Helper rendering functions + */ + + std::string java_package(); + std::string java_suppressions(); + std::string type_name(t_type* ttype, + bool in_container = false, + bool in_init = false, + bool skip_generic = false, + bool force_namespace = false); + std::string base_type_name(t_base_type* tbase, bool in_container = false); + std::string declare_field(t_field* tfield, bool init = false, bool comment = false); + std::string function_signature(t_function* tfunction, std::string prefix = ""); + std::string function_signature_async(t_function* tfunction, + bool use_base_method = false, + std::string prefix = ""); + std::string argument_list(t_struct* tstruct, bool include_types = true); + std::string async_function_call_arglist(t_function* tfunc, + bool use_base_method = true, + bool include_types = true); + std::string async_argument_list(t_function* tfunct, + t_struct* tstruct, + t_type* ttype, + bool include_types = false); + std::string type_to_enum(t_type* ttype); + void generate_struct_desc(ofstream& out, t_struct* tstruct); + void generate_field_descs(ofstream& out, t_struct* tstruct); + void generate_field_name_constants(ofstream& out, t_struct* tstruct); + + std::string make_valid_java_filename(std::string const& fromName); + std::string make_valid_java_identifier(std::string const& fromName); + + 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() + || ttype->is_enum(); + } + + bool is_deprecated(const std::map& annotations) { + return annotations.find("deprecated") != annotations.end(); + } + + std::string constant_name(std::string name); + +private: + /** + * File streams + */ + + std::string package_name_; + std::ofstream f_service_; + std::string package_dir_; + + bool bean_style_; + bool android_style_; + bool private_members_; + bool nocamel_style_; + bool fullcamel_style_; + bool android_legacy_; + bool java5_; + bool sorted_containers_; + bool reuse_objects_; + bool use_option_type_; + bool undated_generated_annotations_; + bool suppress_generated_annotations_; + +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_java_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + package_name_ = program_->get_namespace("java"); + + string dir = package_name_; + string subdir = get_out_dir(); + 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()); + } + + package_dir_ = subdir; +} + +/** + * Packages the generated file + * + * @return String of the package, i.e. "package org.apache.thriftdemo;" + */ +string t_java_generator::java_package() { + if (!package_name_.empty()) { + return string("package ") + package_name_ + ";\n\n"; + } + return ""; +} + +string t_java_generator::java_suppressions() { + return "@SuppressWarnings({\"cast\", \"rawtypes\", \"serial\", \"unchecked\", \"unused\"})\n"; +} + +/** + * Nothing in Java + */ +void t_java_generator::close_generator() { +} + +/** + * Generates a typedef. This is not done in Java, since it does + * not support arbitrary name replacements, and it'd be a wacky waste + * of overhead to make wrapper classes. + * + * @param ttypedef The type definition + */ +void t_java_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Enums are a class with a set of static constants. + * + * @param tenum The enumeration + */ +void t_java_generator::generate_enum(t_enum* tenum) { + bool is_deprecated = this->is_deprecated(tenum->annotations_); + // Make output file + string f_enum_name = package_dir_ + "/" + make_valid_java_filename(tenum->get_name()) + ".java"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + // Comment and package it + f_enum << autogen_comment() << java_package() << endl; + + // Add java imports + f_enum << string() + "import java.util.Map;\n" + "import java.util.HashMap;\n" + + "import org.apache.thrift.TEnum;" << endl << endl; + + generate_java_doc(f_enum, tenum); + if (is_deprecated) { + indent(f_enum) << "@Deprecated" << endl; + } + indent(f_enum) << "public enum " << tenum->get_name() << " implements org.apache.thrift.TEnum "; + scope_up(f_enum); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + bool first = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + + if (first) { + first = false; + } else { + f_enum << "," << endl; + } + + generate_java_doc(f_enum, *c_iter); + if (this->is_deprecated((*c_iter)->annotations_)) { + indent(f_enum) << "@Deprecated" << endl; + } + indent(f_enum) << (*c_iter)->get_name() << "(" << value << ")"; + } + f_enum << ";" << endl << endl; + + // Field for thriftCode + indent(f_enum) << "private final int value;" << endl << endl; + + indent(f_enum) << "private " << tenum->get_name() << "(int value) {" << endl; + indent(f_enum) << " this.value = value;" << endl; + indent(f_enum) << "}" << endl << endl; + + indent(f_enum) << "/**" << endl; + indent(f_enum) << " * Get the integer value of this enum value, as defined in the Thrift IDL." + << endl; + indent(f_enum) << " */" << endl; + indent(f_enum) << "public int getValue() {" << endl; + indent(f_enum) << " return value;" << endl; + indent(f_enum) << "}" << endl << endl; + + indent(f_enum) << "/**" << endl; + indent(f_enum) << " * Find a the enum type by its integer value, as defined in the Thrift IDL." + << endl; + indent(f_enum) << " * @return null if the value is not found." << endl; + indent(f_enum) << " */" << endl; + indent(f_enum) << "public static " + tenum->get_name() + " findByValue(int value) { " << endl; + + indent_up(); + + indent(f_enum) << "switch (value) {" << endl; + indent_up(); + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << "case " << value << ":" << endl; + indent(f_enum) << " return " << (*c_iter)->get_name() << ";" << endl; + } + + indent(f_enum) << "default:" << endl; + indent(f_enum) << " return null;" << endl; + + indent_down(); + + indent(f_enum) << "}" << endl; + + indent_down(); + + indent(f_enum) << "}" << endl; + + scope_down(f_enum); + + f_enum.close(); +} + +/** + * Generates a class that holds all the constants. + */ +void t_java_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + string f_consts_name = package_dir_ + '/' + make_valid_java_filename(program_name_) + + "Constants.java"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + // Print header + f_consts << autogen_comment() << java_package() << java_suppressions(); + + f_consts << "public class " << make_valid_java_identifier(program_name_) << "Constants {" << endl + << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + generate_java_doc(f_consts, (*c_iter)); + print_const_value(f_consts, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false); + } + indent_down(); + indent(f_consts) << "}" << endl; + f_consts.close(); +} + +/** + * 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_java_generator::print_const_value(std::ofstream& out, + string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval) { + type = get_true_type(type); + + indent(out); + if (!defval) { + out << (in_static ? "" : "public static final ") << type_name(type) << " "; + } + if (type->is_base_type()) { + string v2 = render_const_value(out, type, value); + out << name << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + out << name << " = " << render_const_value(out, type, value) << ";" << endl << endl; + } 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; + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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); + indent(out) << name << "."; + std::string cap_name = get_cap_name(v_iter->first->get_string()); + out << "set" << cap_name << "(" << val << ");" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_map()) { + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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, ktype, v_iter->first); + string val = render_const_value(out, vtype, v_iter->second); + indent(out) << name << ".put(" << key << ", " << val << ");" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_list() || type->is_set()) { + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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, etype, *v_iter); + indent(out) << name << ".add(" << val << ");" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else { + throw "compiler error: no const of type " + type->get_name(); + } +} + +string t_java_generator::render_const_value(ofstream& out, t_type* type, t_const_value* value) { + 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) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + render << "(byte)" << value->get_integer(); + break; + case t_base_type::TYPE_I16: + render << "(short)" << value->get_integer(); + break; + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << "(double)" << 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()) { + std::string namespace_prefix = type->get_program()->get_namespace("java"); + if (namespace_prefix.length() > 0) { + namespace_prefix += "."; + } + render << namespace_prefix << value->get_identifier_with_parent(); + } else { + string t = tmp("tmp"); + print_const_value(out, t, type, value, true); + render << t; + } + + return render.str(); +} + +/** + * Generates a struct definition for a thrift data type. This will be a org.apache.thrift.TBase + * implementor. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_struct(t_struct* tstruct) { + if (tstruct->is_union()) { + generate_java_union(tstruct); + } else { + generate_java_struct(tstruct, false); + } +} + +/** + * Exceptions are structs, but they inherit from Exception + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_xception(t_struct* txception) { + generate_java_struct(txception, true); +} + +/** + * Java struct definition. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct(t_struct* tstruct, bool is_exception) { + // Make output file + string f_struct_name = package_dir_ + "/" + make_valid_java_filename(tstruct->get_name()) + + ".java"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << java_package() << java_suppressions(); + + generate_java_struct_definition(f_struct, tstruct, is_exception); + f_struct.close(); +} + +/** + * Java union definition. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_union(t_struct* tstruct) { + // Make output file + string f_struct_name = package_dir_ + "/" + make_valid_java_filename(tstruct->get_name()) + + ".java"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << java_package() << java_suppressions(); + + generate_java_doc(f_struct, tstruct); + + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + bool is_deprecated = this->is_deprecated(tstruct->annotations_); + + if (is_deprecated) { + indent(f_struct) << "@Deprecated" << endl; + } + indent(f_struct) << "public " << (is_final ? "final " : "") << "class " << tstruct->get_name() + << " extends org.apache.thrift.TUnion<" << tstruct->get_name() << ", " + << tstruct->get_name() << "._Fields> "; + + scope_up(f_struct); + + generate_struct_desc(f_struct, tstruct); + generate_field_descs(f_struct, tstruct); + + f_struct << endl; + + generate_field_name_constants(f_struct, tstruct); + + f_struct << endl; + + generate_java_meta_data_map(f_struct, tstruct); + + generate_union_constructor(f_struct, tstruct); + + f_struct << endl; + + generate_union_abstract_methods(f_struct, tstruct); + + f_struct << endl; + + generate_java_struct_field_by_id(f_struct, tstruct); + + f_struct << endl; + + generate_union_getters_and_setters(f_struct, tstruct); + + f_struct << endl; + + generate_union_is_set_methods(f_struct, tstruct); + + f_struct << endl; + + generate_union_comparisons(f_struct, tstruct); + + f_struct << endl; + + generate_union_hashcode(f_struct, tstruct); + + f_struct << endl; + + generate_java_struct_write_object(f_struct, tstruct); + + f_struct << endl; + + generate_java_struct_read_object(f_struct, tstruct); + + f_struct << endl; + + scope_down(f_struct); + + f_struct.close(); +} + +void t_java_generator::generate_union_constructor(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent(out) << "public " << type_name(tstruct) << "() {" << endl; + indent_up(); + 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) { + indent(out) << "super(_Fields." << constant_name((*m_iter)->get_name()) << ", " + << render_const_value(out, type, (*m_iter)->get_value()) << ");" << endl; + default_value = true; + break; + } + } + if (default_value == false) { + indent(out) << "super();" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + indent(out) << "public " << type_name(tstruct) << "(_Fields setField, java.lang.Object value) {" << endl; + indent(out) << " super(setField, value);" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public " << type_name(tstruct) << "(" << type_name(tstruct) << " other) {" + << endl; + indent(out) << " super(other);" << endl; + indent(out) << "}" << endl; + + indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl; + indent(out) << " return new " << tstruct->get_name() << "(this);" << endl; + indent(out) << "}" << endl << endl; + + // generate "constructors" for each field + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* type = (*m_iter)->get_type(); + indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name() << "(" + << type_name(type) << " value) {" << endl; + indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();" << endl; + indent(out) << " x.set" << get_cap_name((*m_iter)->get_name()) << "(value);" << endl; + indent(out) << " return x;" << endl; + indent(out) << "}" << endl << endl; + + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name() + << "(byte[] value) {" << endl; + indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();" + << endl; + indent(out) << " x.set" << get_cap_name((*m_iter)->get_name()) + << "(java.nio.ByteBuffer.wrap(value.clone()));" << endl; + indent(out) << " return x;" << endl; + indent(out) << "}" << endl << endl; + } + } +} + +void t_java_generator::generate_union_getters_and_setters(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (first) { + first = false; + } else { + out << endl; + } + + t_field* field = (*m_iter); + t_type* type = field->get_type(); + std::string cap_name = get_cap_name(field->get_name()); + bool is_deprecated = this->is_deprecated(field->annotations_); + + generate_java_doc(out, field); + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public byte[] get" << cap_name << "() {" << endl; + indent(out) << " set" << cap_name << "(org.apache.thrift.TBaseHelper.rightSize(buffer" + << get_cap_name("for") << cap_name << "()));" << endl; + indent(out) << " java.nio.ByteBuffer b = buffer" << get_cap_name("for") << cap_name << "();" << endl; + indent(out) << " return b == null ? null : b.array();" << endl; + indent(out) << "}" << endl; + + out << endl; + + indent(out) << "public java.nio.ByteBuffer buffer" << get_cap_name("for") + << get_cap_name(field->get_name()) << "() {" << endl; + indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {" + << endl; + indent(out) + << " return org.apache.thrift.TBaseHelper.copyBinary((java.nio.ByteBuffer)getFieldValue());" + << endl; + indent(out) << " } else {" << endl; + indent(out) << " throw new java.lang.RuntimeException(\"Cannot get field '" << field->get_name() + << "' because union is currently set to \" + getFieldDesc(getSetField()).name);" + << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + } else { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public " << type_name(field->get_type()) << " get" + << get_cap_name(field->get_name()) << "() {" << endl; + indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {" + << endl; + indent(out) << " return (" << type_name(field->get_type(), true) << ")getFieldValue();" + << endl; + indent(out) << " } else {" << endl; + indent(out) << " throw new java.lang.RuntimeException(\"Cannot get field '" << field->get_name() + << "' because union is currently set to \" + getFieldDesc(getSetField()).name);" + << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + } + + out << endl; + + generate_java_doc(out, field); + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void set" << get_cap_name(field->get_name()) << "(byte[] value) {" + << endl; + indent(out) << " set" << get_cap_name(field->get_name()) + << "(java.nio.ByteBuffer.wrap(value.clone()));" << endl; + indent(out) << "}" << endl; + + out << endl; + } + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void set" << get_cap_name(field->get_name()) << "(" + << type_name(field->get_type()) << " value) {" << endl; + if (type_can_be_null(field->get_type())) { + indent(out) << " if (value == null) throw new java.lang.NullPointerException();" << endl; + } + indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl; + indent(out) << " value_ = value;" << endl; + indent(out) << "}" << endl; + } +} + +void t_java_generator::generate_union_is_set_methods(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (first) { + first = false; + } else { + out << endl; + } + + std::string field_name = (*m_iter)->get_name(); + + indent(out) << "public boolean is" << get_cap_name("set") << get_cap_name(field_name) << "() {" + << endl; + indent_up(); + indent(out) << "return setField_ == _Fields." << constant_name(field_name) << ";" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } +} + +void t_java_generator::generate_union_abstract_methods(ofstream& out, t_struct* tstruct) { + generate_check_type(out, tstruct); + out << endl; + generate_standard_scheme_read_value(out, tstruct); + out << endl; + generate_standard_scheme_write_value(out, tstruct); + out << endl; + generate_tuple_scheme_read_value(out, tstruct); + out << endl; + generate_tuple_scheme_write_value(out, tstruct); + out << endl; + generate_get_field_desc(out, tstruct); + out << endl; + generate_get_struct_desc(out, tstruct); + out << endl; + indent(out) << "@Override" << endl; + indent(out) << "protected _Fields enumForId(short id) {" << endl; + indent(out) << " return _Fields.findByThriftIdOrThrow(id);" << endl; + indent(out) << "}" << endl; +} + +void t_java_generator::generate_check_type(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) + << "protected void checkType(_Fields setField, java.lang.Object value) throws java.lang.ClassCastException {" + << endl; + indent_up(); + + indent(out) << "switch (setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent(out) << " if (value instanceof " << type_name(field->get_type(), true, false, true) + << ") {" << endl; + indent(out) << " break;" << endl; + indent(out) << " }" << endl; + indent(out) << " throw new java.lang.ClassCastException(\"Was expecting value of type " + << type_name(field->get_type(), true, false) << " for field '" << field->get_name() + << "', but got \" + value.getClass().getSimpleName());" << endl; + // do the real check here + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalArgumentException(\"Unknown field id \" + setField);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_standard_scheme_read_value(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "protected java.lang.Object standardSchemeReadValue(org.apache.thrift.protocol.TProtocol " + "iprot, org.apache.thrift.protocol.TField field) throws " + "org.apache.thrift.TException {" << endl; + + indent_up(); + + indent(out) << "_Fields setField = _Fields.findByThriftId(field.id);" << endl; + indent(out) << "if (setField != null) {" << endl; + indent_up(); + indent(out) << "switch (setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << "if (field.type == " << constant_name(field->get_name()) << "_FIELD_DESC.type) {" + << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";" + << endl; + generate_deserialize_field(out, field, ""); + indent(out) << "return " << field->get_name() << ";" << endl; + indent_down(); + indent(out) << "} else {" << endl; + indent(out) << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);" << endl; + indent(out) << " return null;" << endl; + indent(out) << "}" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalStateException(\"setField wasn't null, but didn't match any " + "of the case statements!\");" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "org.apache.thrift.protocol.TProtocolUtil.skip(iprot, field.type);" << endl; + indent(out) << "return null;" << endl; + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_standard_scheme_write_value(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "protected void standardSchemeWriteValue(org.apache.thrift.protocol.TProtocol " + "oprot) throws org.apache.thrift.TException {" << endl; + + indent_up(); + + indent(out) << "switch (setField_) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = (" + << type_name(field->get_type(), true, false) << ")value_;" << endl; + generate_serialize_field(out, field, ""); + indent(out) << "return;" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalStateException(\"Cannot write union with unknown field \" + " + "setField_);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + + indent(out) << "}" << endl; +} + +void t_java_generator::generate_tuple_scheme_read_value(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "protected java.lang.Object tupleSchemeReadValue(org.apache.thrift.protocol.TProtocol " + "iprot, short fieldID) throws org.apache.thrift.TException {" << endl; + + indent_up(); + + indent(out) << "_Fields setField = _Fields.findByThriftId(fieldID);" << endl; + indent(out) << "if (setField != null) {" << endl; + indent_up(); + indent(out) << "switch (setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";" + << endl; + generate_deserialize_field(out, field, ""); + indent(out) << "return " << field->get_name() << ";" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalStateException(\"setField wasn't null, but didn't match any " + "of the case statements!\");" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "throw new org.apache.thrift.protocol.TProtocolException(\"Couldn't find a field with field id \" + fieldID);" + << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_tuple_scheme_write_value(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "protected void tupleSchemeWriteValue(org.apache.thrift.protocol.TProtocol oprot) " + "throws org.apache.thrift.TException {" << endl; + + indent_up(); + + indent(out) << "switch (setField_) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = (" + << type_name(field->get_type(), true, false) << ")value_;" << endl; + generate_serialize_field(out, field, ""); + indent(out) << "return;" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalStateException(\"Cannot write union with unknown field \" + " + "setField_);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + + indent(out) << "}" << endl; +} + +void t_java_generator::generate_get_field_desc(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "protected org.apache.thrift.protocol.TField getFieldDesc(_Fields setField) {" + << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent(out) << "switch (setField) {" << endl; + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent(out) << " return " << constant_name(field->get_name()) << "_FIELD_DESC;" << endl; + } + + indent(out) << "default:" << endl; + indent(out) << " throw new java.lang.IllegalArgumentException(\"Unknown field id \" + setField);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_get_struct_desc(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "@Override" << endl; + indent(out) << "protected org.apache.thrift.protocol.TStruct getStructDesc() {" << endl; + indent(out) << " return STRUCT_DESC;" << endl; + indent(out) << "}" << endl; +} + +void t_java_generator::generate_union_comparisons(ofstream& out, t_struct* tstruct) { + // equality + indent(out) << "public boolean equals(java.lang.Object other) {" << endl; + indent(out) << " if (other instanceof " << tstruct->get_name() << ") {" << endl; + indent(out) << " return equals((" << tstruct->get_name() << ")other);" << endl; + indent(out) << " } else {" << endl; + indent(out) << " return false;" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + + out << endl; + + indent(out) << "public boolean equals(" << tstruct->get_name() << " other) {" << endl; + indent(out) << " return other != null && getSetField() == other.getSetField() && " + "getFieldValue().equals(other.getFieldValue());" << endl; + indent(out) << "}" << endl; + out << endl; + + indent(out) << "@Override" << endl; + indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl; + indent(out) << " int lastComparison = org.apache.thrift.TBaseHelper.compareTo(getSetField(), " + "other.getSetField());" << endl; + indent(out) << " if (lastComparison == 0) {" << endl; + indent(out) << " return org.apache.thrift.TBaseHelper.compareTo(getFieldValue(), " + "other.getFieldValue());" << endl; + indent(out) << " }" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << "}" << endl; + out << endl; +} + +void t_java_generator::generate_union_hashcode(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "@Override" << endl; + indent(out) << "public int hashCode() {" << endl; + indent(out) << " java.util.List list = new java.util.ArrayList();" << endl; + indent(out) << " list.add(this.getClass().getName());" << endl; + indent(out) << " org.apache.thrift.TFieldIdEnum setField = getSetField();" << endl; + indent(out) << " if (setField != null) {" << endl; + indent(out) << " list.add(setField.getThriftFieldId());" << endl; + indent(out) << " java.lang.Object value = getFieldValue();" << endl; + indent(out) << " if (value instanceof org.apache.thrift.TEnum) {" << endl; + indent(out) << " list.add(((org.apache.thrift.TEnum)getFieldValue()).getValue());" << endl; + indent(out) << " } else {" << endl; + indent(out) << " list.add(value);" << endl; + indent(out) << " }" << endl; + indent(out) << " }" << endl; + indent(out) << " return list.hashCode();" << endl; + indent(out) << "}"; +} + +/** + * Java struct definition. This has various parameters, as it could be + * generated standalone or inside another class as a helper. If it + * is a helper than it is a static class. + * + * @param tstruct The struct definition + * @param is_exception Is this an exception? + * @param in_class If inside a class, needs to be static class + * @param is_result If this is a result it needs a different writer + */ +void t_java_generator::generate_java_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool in_class, + bool is_result) { + generate_java_doc(out, tstruct); + + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + bool is_deprecated = this->is_deprecated(tstruct->annotations_); + + if (!in_class && !suppress_generated_annotations_) { + generate_javax_generated_annotation(out); + } + + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public " << (is_final ? "final " : "") << (in_class ? "static " : "") << "class " + << tstruct->get_name() << " "; + + if (is_exception) { + out << "extends org.apache.thrift.TException "; + } + out << "implements org.apache.thrift.TBase<" << tstruct->get_name() << ", " << tstruct->get_name() + << "._Fields>, java.io.Serializable, Cloneable, Comparable<" << tstruct->get_name() << ">"; + + if (android_style_) { + out << ", android.os.Parcelable"; + } + + out << " "; + + scope_up(out); + + generate_struct_desc(out, tstruct); + + // Members are public for -java, private for -javabean + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + out << endl; + + generate_field_descs(out, tstruct); + + out << endl; + + generate_scheme_map(out, tstruct); + + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (bean_style_ || private_members_) { + indent(out) << "private "; + } else { + generate_java_doc(out, *m_iter); + indent(out) << "public "; + } + out << declare_field(*m_iter, false, true) << endl; + } + + out << endl; + + if (android_style_) { + generate_java_struct_parcelable(out, tstruct); + } + + generate_field_name_constants(out, tstruct); + + // isset data + if (members.size() > 0) { + out << endl; + + indent(out) << "// isset id assignments" << endl; + + int i = 0; + int optionals = 0; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() == t_field::T_OPTIONAL) { + optionals++; + } + if (!type_can_be_null((*m_iter)->get_type())) { + indent(out) << "private static final int " << isset_field_id(*m_iter) << " = " << i << ";" + << endl; + i++; + } + } + + std::string primitiveType; + switch (needs_isset(tstruct, &primitiveType)) { + case ISSET_NONE: + break; + case ISSET_PRIMITIVE: + indent(out) << "private " << primitiveType << " __isset_bitfield = 0;" << endl; + break; + case ISSET_BITSET: + indent(out) << "private java.util.BitSet __isset_bit_vector = new java.util.BitSet(" << i << ");" << endl; + break; + } + + if (optionals > 0) { + std::string output_string = "private static final _Fields optionals[] = {"; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() == t_field::T_OPTIONAL) { + output_string = output_string + "_Fields." + constant_name((*m_iter)->get_name()) + ","; + } + } + indent(out) << output_string.substr(0, output_string.length() - 1) << "};" << endl; + } + } + + generate_java_meta_data_map(out, tstruct); + + bool all_optional_members = true; + + // Default constructor + indent(out) << "public " << tstruct->get_name() << "() {" << endl; + 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) { + print_const_value(out, + "this." + (*m_iter)->get_name(), + t, + (*m_iter)->get_value(), + true, + true); + } + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + all_optional_members = false; + } + } + indent_down(); + indent(out) << "}" << endl << endl; + + if (!members.empty() && !all_optional_members) { + // Full constructor for all fields + indent(out) << "public " << tstruct->get_name() << "(" << endl; + indent_up(); + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + if (!first) { + out << "," << endl; + } + first = false; + indent(out) << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name(); + } + } + out << ")" << endl; + indent_down(); + indent(out) << "{" << endl; + indent_up(); + indent(out) << "this();" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + t_type* type = get_true_type((*m_iter)->get_type()); + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + indent(out) << "this." << (*m_iter)->get_name() + << " = org.apache.thrift.TBaseHelper.copyBinary(" << (*m_iter)->get_name() + << ");" << endl; + } else { + indent(out) << "this." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << ";" + << endl; + } + generate_isset_set(out, (*m_iter), ""); + } + } + + indent_down(); + indent(out) << "}" << endl << endl; + } + + // copy constructor + indent(out) << "/**" << endl; + indent(out) << " * Performs a deep copy on other." << endl; + indent(out) << " */" << endl; + indent(out) << "public " << tstruct->get_name() << "(" << tstruct->get_name() << " other) {" + << endl; + indent_up(); + + switch (needs_isset(tstruct)) { + case ISSET_NONE: + break; + case ISSET_PRIMITIVE: + indent(out) << "__isset_bitfield = other.__isset_bitfield;" << endl; + break; + case ISSET_BITSET: + indent(out) << "__isset_bit_vector.clear();" << endl; + indent(out) << "__isset_bit_vector.or(other.__isset_bit_vector);" << endl; + break; + } + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + std::string field_name = field->get_name(); + t_type* type = field->get_type()->get_true_type(); + bool can_be_null = type_can_be_null(type); + + if (can_be_null) { + indent(out) << "if (other." << generate_isset_check(field) << ") {" << endl; + indent_up(); + } + + if (type->is_container()) { + generate_deep_copy_container(out, "other", field_name, "__this__" + field_name, type); + indent(out) << "this." << field_name << " = __this__" << field_name << ";" << endl; + } else { + indent(out) << "this." << field_name << " = "; + generate_deep_copy_non_container(out, "other." + field_name, field_name, type); + out << ";" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; + + // clone method, so that you can deep copy an object when you don't know its class. + indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl; + indent(out) << " return new " << tstruct->get_name() << "(this);" << endl; + indent(out) << "}" << endl << endl; + + generate_java_struct_clear(out, tstruct); + + generate_java_bean_boilerplate(out, tstruct); + generate_generic_field_getters_setters(out, tstruct); + generate_generic_isset_method(out, tstruct); + + generate_java_struct_equality(out, tstruct); + generate_java_struct_compare_to(out, tstruct); + generate_java_struct_field_by_id(out, tstruct); + + generate_java_struct_reader(out, tstruct); + if (is_result) { + generate_java_struct_result_writer(out, tstruct); + } else { + generate_java_struct_writer(out, tstruct); + } + generate_java_struct_tostring(out, tstruct); + generate_java_validator(out, tstruct); + + generate_java_struct_write_object(out, tstruct); + generate_java_struct_read_object(out, tstruct); + + generate_java_struct_standard_scheme(out, tstruct, is_result); + generate_java_struct_tuple_scheme(out, tstruct); + generate_java_scheme_lookup(out); + + scope_down(out); + out << endl; +} + +/** + * generates parcelable interface implementation + */ +void t_java_generator::generate_java_struct_parcelable(ofstream& out, t_struct* tstruct) { + string tname = tstruct->get_name(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + out << indent() << "@Override" << endl << indent() + << "public void writeToParcel(android.os.Parcel out, int flags) {" << endl; + indent_up(); + string bitsetPrimitiveType = ""; + switch (needs_isset(tstruct, &bitsetPrimitiveType)) { + case ISSET_NONE: + break; + case ISSET_PRIMITIVE: + indent(out) << "//primitive bitfield of type: " << bitsetPrimitiveType << endl; + if (bitsetPrimitiveType == "byte") { + indent(out) << "out.writeByte(__isset_bitfield);" << endl; + } else if (bitsetPrimitiveType == "short") { + indent(out) << "out.writeInt(new Short(__isset_bitfield).intValue());" << endl; + } else if (bitsetPrimitiveType == "int") { + indent(out) << "out.writeInt(__isset_bitfield);" << endl; + } else if (bitsetPrimitiveType == "long") { + indent(out) << "out.writeLong(__isset_bitfield);" << endl; + } + out << endl; + break; + case ISSET_BITSET: + indent(out) << "//BitSet" << endl; + indent(out) << "out.writeSerializable(__isset_bit_vector);" << endl; + out << endl; + break; + } + 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 (t->is_struct()) { + indent(out) << "out.writeParcelable(" << name << ", flags);" << endl; + } else if (type_name(t) == "float") { + indent(out) << "out.writeFloat(" << name << ");" << endl; + } else if (t->is_enum()) { + indent(out) << "out.writeInt(" << name << " != null ? " << name << ".getValue() : -1);" << endl; + } else if (t->is_list()) { + if (((t_list*)t)->get_elem_type()->get_true_type()->is_struct()) { + indent(out) << "out.writeTypedList(" << name << ");" << endl; + } else { + indent(out) << "out.writeList(" << name << ");" << endl; + } + } else if (t->is_map()) { + indent(out) << "out.writeMap(" << name << ");" << endl; + } else if (t->is_base_type()) { + if (((t_base_type*)t)->is_binary()) { + indent(out) << "out.writeInt(" << name << "!=null ? 1 : 0);" << endl; + indent(out) << "if(" << name << " != null) { " << endl; + indent_up(); + indent(out) << "out.writeByteArray(" << name << ".array(), " << name << ".position() + " + << name << ".arrayOffset(), " << name << ".limit() - " << name + << ".position() );" << endl; + scope_down(out); + } else { + switch (((t_base_type*)t)->get_base()) { + case t_base_type::TYPE_I16: + indent(out) << "out.writeInt(new Short(" << name << ").intValue());" << endl; + break; + case t_base_type::TYPE_I32: + indent(out) << "out.writeInt(" << name << ");" << endl; + break; + case t_base_type::TYPE_I64: + indent(out) << "out.writeLong(" << name << ");" << endl; + break; + case t_base_type::TYPE_BOOL: + indent(out) << "out.writeInt(" << name << " ? 1 : 0);" << endl; + break; + case t_base_type::TYPE_I8: + indent(out) << "out.writeByte(" << name << ");" << endl; + break; + case t_base_type::TYPE_DOUBLE: + indent(out) << "out.writeDouble(" << name << ");" << endl; + break; + case t_base_type::TYPE_STRING: + indent(out) << "out.writeString(" << name << ");" << endl; + break; + case t_base_type::TYPE_VOID: + break; + } + } + } + } + scope_down(out); + out << endl; + + out << indent() << "@Override" << endl << indent() << "public int describeContents() {" << endl; + indent_up(); + out << indent() << "return 0;" << endl; + scope_down(out); + out << endl; + + indent(out) << "public " << tname << "(android.os.Parcel in) {" << endl; + indent_up(); + // read in the required bitfield + switch (needs_isset(tstruct, &bitsetPrimitiveType)) { + case ISSET_NONE: + break; + case ISSET_PRIMITIVE: + indent(out) << "//primitive bitfield of type: " << bitsetPrimitiveType << endl; + if (bitsetPrimitiveType == "byte") { + indent(out) << "__isset_bitfield = in.readByte();" << endl; + } else if (bitsetPrimitiveType == "short") { + indent(out) << "__isset_bitfield = (short) in.readInt();" << endl; + } else if (bitsetPrimitiveType == "int") { + indent(out) << "__isset_bitfield = in.readInt();" << endl; + } else if (bitsetPrimitiveType == "long") { + indent(out) << "__isset_bitfield = in.readLong();" << endl; + } + out << endl; + break; + case ISSET_BITSET: + indent(out) << "//BitSet" << endl; + indent(out) << "__isset_bit_vector = (java.util.BitSet) in.readSerializable();" << endl; + out << endl; + break; + } + // read all the fields + 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(); + string prefix = "this." + name; + + if (t->is_struct()) { + indent(out) << prefix << "= in.readParcelable(" << tname << ".class.getClassLoader());" + << endl; + } else if (t->is_enum()) { + indent(out) << prefix << " = " << type_name(t) << ".findByValue(in.readInt());" << endl; + } else if (t->is_list()) { + t_list* list = (t_list*)t; + indent(out) << prefix << " = new " << type_name(t, false, true) << "();" << endl; + if (list->get_elem_type()->get_true_type()->is_struct()) { + indent(out) << "in.readTypedList(" << prefix << ", " << type_name(list->get_elem_type()) + << ".CREATOR);" << endl; + } else { + indent(out) << "in.readList(" << prefix << ", " << tname << ".class.getClassLoader());" + << endl; + } + } else if (t->is_map()) { + indent(out) << prefix << " = new " << type_name(t, false, true) << "();" << endl; + indent(out) << " in.readMap(" << prefix << ", " << tname << ".class.getClassLoader());" + << endl; + } else if (type_name(t) == "float") { + indent(out) << prefix << " = in.readFloat();" << endl; + } else if (t->is_base_type()) { + t_base_type* bt = (t_base_type*)t; + if (bt->is_binary()) { + indent(out) << "if(in.readInt()==1) {" << endl; + indent_up(); + indent(out) << prefix << " = java.nio.ByteBuffer.wrap(in.createByteArray());" << endl; + scope_down(out); + } else { + switch (bt->get_base()) { + case t_base_type::TYPE_I16: + indent(out) << prefix << " = (short) in.readInt();" << endl; + break; + case t_base_type::TYPE_I32: + indent(out) << prefix << " = in.readInt();" << endl; + break; + case t_base_type::TYPE_I64: + indent(out) << prefix << " = in.readLong();" << endl; + break; + case t_base_type::TYPE_BOOL: + indent(out) << prefix << " = (in.readInt()==1);" << endl; + break; + case t_base_type::TYPE_I8: + indent(out) << prefix << " = in.readByte();" << endl; + break; + case t_base_type::TYPE_DOUBLE: + indent(out) << prefix << " = in.readDouble();" << endl; + break; + case t_base_type::TYPE_STRING: + indent(out) << prefix << "= in.readString();" << endl; + break; + case t_base_type::TYPE_VOID: + break; + } + } + } + } + + scope_down(out); + out << endl; + + indent(out) << "public static final android.os.Parcelable.Creator<" << tname + << "> CREATOR = new android.os.Parcelable.Creator<" << tname << ">() {" << endl; + indent_up(); + + indent(out) << "@Override" << endl << indent() << "public " << tname << "[] newArray(int size) {" + << endl; + indent_up(); + indent(out) << "return new " << tname << "[size];" << endl; + scope_down(out); + out << endl; + + indent(out) << "@Override" << endl << indent() << "public " << tname + << " createFromParcel(android.os.Parcel in) {" << endl; + indent_up(); + indent(out) << "return new " << tname << "(in);" << endl; + scope_down(out); + + indent_down(); + indent(out) << "};" << endl; + out << endl; +} + +/** + * Generates equals methods and a hashCode method for a structure. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct_equality(ofstream& out, t_struct* tstruct) { + 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() + << " return this.equals((" << tstruct->get_name() << ")that);" << endl << indent() + << "return false;" << endl; + scope_down(out); + out << endl; + + out << indent() << "public boolean equals(" << tstruct->get_name() << " that) {" << endl; + indent_up(); + out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl + << indent() << "if (this == that)" << endl << indent() << " return true;" << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << endl; + + t_type* t = get_true_type((*m_iter)->get_type()); + // Most existing Thrift code does not use isset or optional/required, + // so we treat "default" fields as required. + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + bool can_be_null = type_can_be_null(t); + string name = (*m_iter)->get_name(); + + string this_present = "true"; + string that_present = "true"; + string unequal; + + if (is_optional || can_be_null) { + this_present += " && this." + generate_isset_check(*m_iter); + that_present += " && that." + generate_isset_check(*m_iter); + } + + out << indent() << "boolean this_present_" << name << " = " << this_present << ";" << endl + << indent() << "boolean that_present_" << name << " = " << that_present << ";" << endl + << indent() << "if (" + << "this_present_" << name << " || that_present_" << name << ") {" << endl; + indent_up(); + out << indent() << "if (!(" + << "this_present_" << name << " && that_present_" << name << "))" << endl << indent() + << " return false;" << endl; + + if (t->is_base_type() && ((t_base_type*)t)->is_binary()) { + unequal = "!this." + name + ".equals(that." + name + ")"; + } else if (can_be_null) { + unequal = "!this." + name + ".equals(that." + name + ")"; + } else { + unequal = "this." + name + " != that." + name; + } + + out << indent() << "if (" << unequal << ")" << endl << indent() << " return false;" << endl; + + scope_down(out); + } + out << endl; + indent(out) << "return true;" << endl; + scope_down(out); + out << endl; + + const int MUL = 8191; // HashCode multiplier + const int B_YES = 131071; + const int B_NO = 524287; + out << indent() << "@Override" << endl << indent() << "public int hashCode() {" << endl; + indent_up(); + indent(out) << "int hashCode = 1;" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << endl; + + t_type* t = get_true_type((*m_iter)->get_type()); + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + bool can_be_null = type_can_be_null(t); + string name = (*m_iter)->get_name(); + + if (is_optional || can_be_null) { + indent(out) << "hashCode = hashCode * " << MUL << " + ((" << generate_isset_check(*m_iter) + << ") ? " << B_YES << " : " << B_NO << ");" << endl; + } + + if (is_optional || can_be_null) { + indent(out) << "if (" + generate_isset_check(*m_iter) + ")" << endl; + indent_up(); + } + + if (t->is_enum()) { + indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".getValue();" << endl; + } else if (t->is_base_type()) { + switch(((t_base_type*)t)->get_base()) { + case t_base_type::TYPE_STRING: + indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl; + break; + case t_base_type::TYPE_BOOL: + indent(out) << "hashCode = hashCode * " << MUL << " + ((" << name << ") ? " + << B_YES << " : " << B_NO << ");" << endl; + break; + case t_base_type::TYPE_I8: + indent(out) << "hashCode = hashCode * " << MUL << " + (int) (" << name << ");" << endl; + break; + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ";" << endl; + break; + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + indent(out) << "hashCode = hashCode * " << MUL << " + org.apache.thrift.TBaseHelper.hashCode(" << name << ");" << endl; + break; + case t_base_type::TYPE_VOID: + throw std::logic_error("compiler error: a struct field cannot be void"); + default: + throw std::logic_error("compiler error: the following base type has no hashcode generator: " + + t_base_type::t_base_name(((t_base_type*)t)->get_base())); + } + } else { + indent(out) << "hashCode = hashCode * " << MUL << " + " << name << ".hashCode();" << endl; + } + + if (is_optional || can_be_null) { + indent_down(); + } + } + + out << endl; + indent(out) << "return hashCode;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_java_generator::generate_java_struct_compare_to(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl; + indent_up(); + + indent(out) << "if (!getClass().equals(other.getClass())) {" << endl; + indent(out) << " return getClass().getName().compareTo(other.getClass().getName());" << endl; + indent(out) << "}" << endl; + out << endl; + + indent(out) << "int lastComparison = 0;" << endl; + out << endl; + + const vector& members = tstruct->get_members(); + 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) << "if (lastComparison != 0) {" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << "}" << endl; + + indent(out) << "if (" << generate_isset_check(field) << ") {" << endl; + indent(out) << " lastComparison = org.apache.thrift.TBaseHelper.compareTo(this." + << field->get_name() << ", other." << field->get_name() << ");" << endl; + indent(out) << " if (lastComparison != 0) {" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + } + + indent(out) << "return 0;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to read all the fields of the struct. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct_reader(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "public void read(org.apache.thrift.protocol.TProtocol iprot) throws " + "org.apache.thrift.TException {" << endl; + indent_up(); + indent(out) << "scheme(iprot).read(iprot, this);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +// generates java method to perform various checks +// (e.g. check that all required fields are set) +void t_java_generator::generate_java_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "public void validate() throws org.apache.thrift.TException {" << 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) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + if (bean_style_) { + out << indent() << "if (!" << generate_isset_check(*f_iter) << ") {" << endl << indent() + << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '" + << (*f_iter)->get_name() << "' is unset! Struct:\" + toString());" << endl << indent() + << "}" << endl << endl; + } else { + if (type_can_be_null((*f_iter)->get_type())) { + indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) + << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '" + << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name() + << "' because it's a primitive and you chose the non-beans generator." + << endl; + } + } + } + } + + out << indent() << "// check for sub-struct validity" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_type* type = (*f_iter)->get_type(); + if (type->is_struct() && !((t_struct*)type)->is_union()) { + out << indent() << "if (" << (*f_iter)->get_name() << " != null) {" << endl; + out << indent() << " " << (*f_iter)->get_name() << ".validate();" << endl; + out << indent() << "}" << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to write all the fields of the struct + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct_writer(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "public void write(org.apache.thrift.protocol.TProtocol oprot) throws " + "org.apache.thrift.TException {" << endl; + indent_up(); + indent(out) << "scheme(oprot).write(oprot, this);" << endl; + + indent_down(); + indent(out) << "}" << 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 in the Isset array, and only one of them + * can be set at a time. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct_result_writer(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "public void write(org.apache.thrift.protocol.TProtocol oprot) throws " + "org.apache.thrift.TException {" << endl; + indent_up(); + indent(out) << "scheme(oprot).write(oprot, this);" << endl; + + indent_down(); + indent(out) << " }" << endl << endl; +} + +void t_java_generator::generate_java_struct_field_by_id(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "public _Fields fieldForId(int fieldId) {" << endl; + indent(out) << " return _Fields.findByThriftId(fieldId);" << endl; + indent(out) << "}" << endl << endl; +} + +void t_java_generator::generate_reflection_getters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + indent(out) << "case " << constant_name(field_name) << ":" << endl; + indent_up(); + indent(out) << "return " << (type->is_bool() ? "is" : "get") << cap_name << "();" << endl << endl; + indent_down(); +} + +void t_java_generator::generate_reflection_setters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + const bool is_binary = type->is_base_type() && ((t_base_type*)type)->is_binary(); + indent(out) << "case " << constant_name(field_name) << ":" << endl; + indent_up(); + indent(out) << "if (value == null) {" << endl; + indent(out) << " unset" << get_cap_name(field_name) << "();" << endl; + indent(out) << "} else {" << endl; + if (is_binary) { + indent_up(); + indent(out) << "if (value instanceof byte[]) {" << endl; + indent(out) << " set" << cap_name << "((byte[])value);" << endl; + indent(out) << "} else {" << endl; + } + indent(out) << " set" << cap_name << "((" << type_name(type, true, false) << ")value);" << endl; + if (is_binary) { + indent(out) << "}" << endl; + indent_down(); + } + indent(out) << "}" << endl; + indent(out) << "break;" << endl << endl; + + indent_down(); +} + +void t_java_generator::generate_generic_field_getters_setters(std::ofstream& 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; + t_type* type = get_true_type(field->get_type()); + std::string field_name = field->get_name(); + std::string cap_name = get_cap_name(field_name); + + indent_up(); + generate_reflection_setters(setter_stream, type, field_name, cap_name); + generate_reflection_getters(getter_stream, type, field_name, cap_name); + indent_down(); + } + + // create the setter + + indent(out) << "public void setFieldValue(_Fields field, java.lang.Object value) {" << endl; + indent(out) << " switch (field) {" << endl; + out << setter_stream.str(); + indent(out) << " }" << endl; + indent(out) << "}" << endl << endl; + + // create the getter + indent(out) << "public java.lang.Object getFieldValue(_Fields field) {" << endl; + indent_up(); + indent(out) << "switch (field) {" << endl; + out << getter_stream.str(); + indent(out) << "}" << endl; + indent(out) << "throw new java.lang.IllegalStateException();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +// Creates a generic isSet method that takes the field number as argument +void t_java_generator::generate_generic_isset_method(std::ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // create the isSet method + indent(out) << "/** Returns true if field corresponding to fieldID is set (has been assigned a " + "value) and false otherwise */" << endl; + indent(out) << "public boolean isSet(_Fields field) {" << endl; + indent_up(); + indent(out) << "if (field == null) {" << endl; + indent(out) << " throw new java.lang.IllegalArgumentException();" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "switch (field) {" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << "return " << generate_isset_check(field) << ";" << endl; + indent_down(); + } + + indent(out) << "}" << endl; + indent(out) << "throw new java.lang.IllegalStateException();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a set of Java Bean boilerplate functions (setters, getters, etc.) + * for the given struct. + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_bean_boilerplate(ofstream& out, t_struct* tstruct) { + isset_type issetType = needs_isset(tstruct); + 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 = get_cap_name(field_name); + bool optional = use_option_type_ && field->get_req() == t_field::T_OPTIONAL; + bool is_deprecated = this->is_deprecated(field->annotations_); + + if (type->is_container()) { + // Method to return the size of the collection + if (optional) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public org.apache.thrift.Option get" << cap_name; + out << get_cap_name("size() {") << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.none();" << endl; + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ".size());" << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } else { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public int get" << cap_name; + out << get_cap_name("size() {") << endl; + + indent_up(); + indent(out) << "return (this." << field_name << " == null) ? 0 : " + << "this." << field_name << ".size();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + } + + if (type->is_set() || type->is_list()) { + t_type* element_type; + if (type->is_set()) { + element_type = ((t_set*)type)->get_elem_type(); + } else { + element_type = ((t_list*)type)->get_elem_type(); + } + + // Iterator getter for sets and lists + if (optional) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public org.apache.thrift.Option> get" << cap_name; + out << get_cap_name("iterator() {") << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.none();" << endl; + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ".iterator());" << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } else { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public java.util.Iterator<" << type_name(element_type, true, false) + << "> get" << cap_name; + out << get_cap_name("iterator() {") << endl; + + indent_up(); + indent(out) << "return (this." << field_name << " == null) ? null : " + << "this." << field_name << ".iterator();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + + // Add to set or list, create if the set/list is null + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void add" << get_cap_name("to"); + out << cap_name << "(" << type_name(element_type) << " elem) {" << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();" + << endl; + indent_down(); + indent(out) << "}" << endl; + indent(out) << "this." << field_name << ".add(elem);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } else if (type->is_map()) { + // Put to map + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void put" << get_cap_name("to"); + out << cap_name << "(" << type_name(key_type) << " key, " << type_name(val_type) << " val) {" + << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();" + << endl; + indent_down(); + indent(out) << "}" << endl; + indent(out) << "this." << field_name << ".put(key, val);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + + // Simple getter + generate_java_doc(out, field); + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public byte[] get" << cap_name << "() {" << endl; + indent(out) << " set" << cap_name << "(org.apache.thrift.TBaseHelper.rightSize(" + << field_name << "));" << endl; + indent(out) << " return " << field_name << " == null ? null : " << field_name << ".array();" + << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public java.nio.ByteBuffer buffer" << get_cap_name("for") << cap_name << "() {" + << endl; + indent(out) << " return org.apache.thrift.TBaseHelper.copyBinary(" << field_name << ");" + << endl; + indent(out) << "}" << endl << endl; + } else { + if (optional) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public org.apache.thrift.Option<" << type_name(type, true) << ">"; + if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) { + out << " is"; + } else { + out << " get"; + } + out << cap_name << "() {" << endl; + indent_up(); + + indent(out) << "if (this.isSet" << cap_name << "()) {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.some(this." << field_name << ");" << endl; + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "return org.apache.thrift.Option.none();" << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } else { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public " << type_name(type); + if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) { + out << " is"; + } else { + out << " get"; + } + out << cap_name << "() {" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + } + + // Simple setter + generate_java_doc(out, field); + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public "; + if (bean_style_) { + out << "void"; + } else { + out << type_name(tstruct); + } + out << " set" << cap_name << "(byte[] " << field_name << ") {" << endl; + indent(out) << " this." << field_name << " = " << field_name << " == null ? (java.nio.ByteBuffer)null" + << " : java.nio.ByteBuffer.wrap(" << field_name << ".clone());" << endl; + if (!bean_style_) { + indent(out) << " return this;" << endl; + } + indent(out) << "}" << endl << endl; + } + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public "; + if (bean_style_) { + out << "void"; + } else { + out << type_name(tstruct); + } + out << " set" << cap_name << "(" << type_name(type) << " " << field_name << ") {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = "; + if (type->is_base_type() && ((t_base_type*)type)->is_binary()) { + out << "org.apache.thrift.TBaseHelper.copyBinary(" << field_name << ")"; + } else { + out << field_name; + } + out << ";" << endl; + generate_isset_set(out, field, ""); + if (!bean_style_) { + indent(out) << "return this;" << endl; + } + + indent_down(); + indent(out) << "}" << endl << endl; + + // Unsetter + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void unset" << cap_name << "() {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "this." << field_name << " = null;" << endl; + } else if (issetType == ISSET_PRIMITIVE) { + indent(out) << "__isset_bitfield = org.apache.thrift.EncodingUtils.clearBit(__isset_bitfield, " + << isset_field_id(field) << ");" << endl; + } else { + indent(out) << "__isset_bit_vector.clear(" << isset_field_id(field) << ");" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + // isSet method + indent(out) << "/** Returns true if field " << field_name + << " is set (has been assigned a value) and false otherwise */" << endl; + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public boolean is" << get_cap_name("set") << cap_name << "() {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "return this." << field_name << " != null;" << endl; + } else if (issetType == ISSET_PRIMITIVE) { + indent(out) << "return org.apache.thrift.EncodingUtils.testBit(__isset_bitfield, " << isset_field_id(field) + << ");" << endl; + } else { + indent(out) << "return __isset_bit_vector.get(" << isset_field_id(field) << ");" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + if (is_deprecated) { + indent(out) << "@Deprecated" << endl; + } + indent(out) << "public void set" << cap_name << get_cap_name("isSet") << "(boolean value) {" + << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "if (!value) {" << endl; + indent(out) << " this." << field_name << " = null;" << endl; + indent(out) << "}" << endl; + } else if (issetType == ISSET_PRIMITIVE) { + indent(out) << "__isset_bitfield = org.apache.thrift.EncodingUtils.setBit(__isset_bitfield, " + << isset_field_id(field) << ", value);" << endl; + } else { + indent(out) << "__isset_bit_vector.set(" << isset_field_id(field) << ", value);" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + } +} + +/** + * Generates a toString() method for the given struct + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_struct_tostring(ofstream& out, t_struct* tstruct) { + out << indent() << "@Override" << endl << indent() << "public java.lang.String toString() {" << endl; + indent_up(); + + out << indent() << "java.lang.StringBuilder sb = new java.lang.StringBuilder(\"" << tstruct->get_name() << "(\");" + << endl; + out << indent() << "boolean first = true;" << endl << 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) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + + t_field* field = (*f_iter); + + if (!first) { + indent(out) << "if (!first) sb.append(\", \");" << endl; + } + indent(out) << "sb.append(\"" << (*f_iter)->get_name() << ":\");" << endl; + bool can_be_null = type_can_be_null(field->get_type()); + if (can_be_null) { + indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) << " sb.append(\"null\");" << endl; + indent(out) << "} else {" << endl; + indent_up(); + } + + if (get_true_type(field->get_type())->is_base_type() + && ((t_base_type*)(get_true_type(field->get_type())))->is_binary()) { + indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);" + << endl; + } else if ((field->get_type()->is_set()) + && (get_true_type(((t_set*)field->get_type())->get_elem_type())->is_base_type()) + && (((t_base_type*)get_true_type(((t_set*)field->get_type())->get_elem_type())) + ->is_binary())) { + indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);" + << endl; + } else if ((field->get_type()->is_list()) + && (get_true_type(((t_list*)field->get_type())->get_elem_type())->is_base_type()) + && (((t_base_type*)get_true_type(((t_list*)field->get_type())->get_elem_type())) + ->is_binary())) { + indent(out) << "org.apache.thrift.TBaseHelper.toString(this." << field->get_name() << ", sb);" + << endl; + } else { + indent(out) << "sb.append(this." << (*f_iter)->get_name() << ");" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + indent(out) << "first = false;" << endl; + + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + first = false; + } + out << indent() << "sb.append(\")\");" << endl << indent() << "return sb.toString();" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a static map with meta data to store information such as fieldID to + * fieldName mapping + * + * @param tstruct The struct definition + */ +void t_java_generator::generate_java_meta_data_map(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Static Map with fieldID -> org.apache.thrift.meta_data.FieldMetaData mappings + indent(out) + << "public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;" + << endl; + indent(out) << "static {" << endl; + indent_up(); + + indent(out) << "java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new " + "java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);" + << endl; + + // Populate map + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = *f_iter; + std::string field_name = field->get_name(); + indent(out) << "tmpMap.put(_Fields." << constant_name(field_name) + << ", new org.apache.thrift.meta_data.FieldMetaData(\"" << field_name << "\", "; + + // Set field requirement type (required, optional, etc.) + if (field->get_req() == t_field::T_REQUIRED) { + out << "org.apache.thrift.TFieldRequirementType.REQUIRED, "; + } else if (field->get_req() == t_field::T_OPTIONAL) { + out << "org.apache.thrift.TFieldRequirementType.OPTIONAL, "; + } else { + out << "org.apache.thrift.TFieldRequirementType.DEFAULT, "; + } + + // Create value meta data + generate_field_value_meta_data(out, field->get_type()); + out << "));" << endl; + } + + indent(out) << "metaDataMap = java.util.Collections.unmodifiableMap(tmpMap);" << endl; + + indent(out) << "org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(" + << type_name(tstruct) << ".class, metaDataMap);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Returns a string with the java representation of the given thrift type + * (e.g. for the type struct it returns "org.apache.thrift.protocol.TType.STRUCT") + */ +std::string t_java_generator::get_java_type_string(t_type* type) { + if (type->is_list()) { + return "org.apache.thrift.protocol.TType.LIST"; + } else if (type->is_map()) { + return "org.apache.thrift.protocol.TType.MAP"; + } else if (type->is_set()) { + return "org.apache.thrift.protocol.TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "org.apache.thrift.protocol.TType.STRUCT"; + } else if (type->is_enum()) { + return "org.apache.thrift.protocol.TType.ENUM"; + } else if (type->is_typedef()) { + return get_java_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID: + return "org.apache.thrift.protocol.TType.VOID"; + break; + case t_base_type::TYPE_STRING: + return "org.apache.thrift.protocol.TType.STRING"; + break; + case t_base_type::TYPE_BOOL: + return "org.apache.thrift.protocol.TType.BOOL"; + break; + case t_base_type::TYPE_I8: + return "org.apache.thrift.protocol.TType.BYTE"; + break; + case t_base_type::TYPE_I16: + return "org.apache.thrift.protocol.TType.I16"; + break; + case t_base_type::TYPE_I32: + return "org.apache.thrift.protocol.TType.I32"; + break; + case t_base_type::TYPE_I64: + return "org.apache.thrift.protocol.TType.I64"; + break; + case t_base_type::TYPE_DOUBLE: + return "org.apache.thrift.protocol.TType.DOUBLE"; + break; + default: + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_java_generator::get_java_type_string!"); + return "Unknown thrift type \"" + type->get_name() + + "\" passed to t_java_generator::get_java_type_string!"; + break; // This should never happen! + } + } else { + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_java_generator::get_java_type_string!"); + return "Unknown thrift type \"" + type->get_name() + + "\" passed to t_java_generator::get_java_type_string!"; + // This should never happen! + } +} + +void t_java_generator::generate_field_value_meta_data(std::ofstream& out, t_type* type) { + out << endl; + indent_up(); + indent_up(); + if (type->is_struct() || type->is_xception()) { + indent(out) << "new " + "org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType." + "STRUCT, " << type_name(type) << ".class"; + } else if (type->is_container()) { + if (type->is_list()) { + indent(out) + << "new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else if (type->is_set()) { + indent(out) + << "new org.apache.thrift.meta_data.SetMetaData(org.apache.thrift.protocol.TType.SET, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else { // map + indent(out) + << "new org.apache.thrift.meta_data.MapMetaData(org.apache.thrift.protocol.TType.MAP, "; + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + generate_field_value_meta_data(out, key_type); + out << ", "; + generate_field_value_meta_data(out, val_type); + } + } else if (type->is_enum()) { + indent(out) + << "new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, " + << type_name(type) << ".class"; + } else { + indent(out) << "new org.apache.thrift.meta_data.FieldValueMetaData(" + << get_java_type_string(type); + if (type->is_typedef()) { + indent(out) << ", \"" << ((t_typedef*)type)->get_symbolic() << "\""; + } else if (((t_base_type*)type)->is_binary()) { + indent(out) << ", true"; + } + } + out << ")"; + indent_down(); + indent_down(); +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_java_generator::generate_service(t_service* tservice) { + // Make output file + string f_service_name = package_dir_ + "/" + make_valid_java_filename(service_name_) + ".java"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << java_package() << java_suppressions(); + + if (!suppress_generated_annotations_) { + generate_javax_generated_annotation(f_service_); + } + f_service_ << "public class " << service_name_ << " {" << endl << endl; + indent_up(); + + // Generate the three main parts of the service + generate_service_interface(tservice); + generate_service_async_interface(tservice); + generate_service_client(tservice); + generate_service_async_client(tservice); + generate_service_server(tservice); + generate_service_async_server(tservice); + generate_service_helpers(tservice); + + indent_down(); + f_service_ << "}" << endl; + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_java_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_iface = " extends " + extends + ".Iface"; + } + + generate_java_doc(f_service_, tservice); + f_service_ << indent() << "public interface Iface" << extends_iface << " {" << endl << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_java_doc(f_service_, *f_iter); + indent(f_service_) << "public " << function_signature(*f_iter) << ";" << endl << endl; + } + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +void t_java_generator::generate_service_async_interface(t_service* tservice) { + string extends = ""; + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_iface = " extends " + extends + " .AsyncIface"; + } + + f_service_ << indent() << "public interface AsyncIface" << extends_iface << " {" << endl << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_) << "public " << function_signature_async(*f_iter, true) + << " throws org.apache.thrift.TException;" << endl << endl; + } + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Generates structs for all the service args and return types + * + * @param tservice The service + */ +void t_java_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_java_struct_definition(f_service_, ts, false, true); + generate_function_helpers(*f_iter); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_java_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() == NULL) { + extends_client = "org.apache.thrift.TServiceClient"; + } else { + extends = type_name(tservice->get_extends()); + extends_client = extends + ".Client"; + } + + indent(f_service_) << "public static class Client extends " << extends_client + << " implements Iface {" << endl; + indent_up(); + + indent(f_service_) + << "public static class Factory implements org.apache.thrift.TServiceClientFactory {" + << endl; + indent_up(); + indent(f_service_) << "public Factory() {}" << endl; + indent(f_service_) << "public Client getClient(org.apache.thrift.protocol.TProtocol prot) {" + << endl; + indent_up(); + indent(f_service_) << "return new Client(prot);" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + indent(f_service_) << "public Client getClient(org.apache.thrift.protocol.TProtocol iprot, " + "org.apache.thrift.protocol.TProtocol oprot) {" << endl; + indent_up(); + indent(f_service_) << "return new Client(iprot, oprot);" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + indent_down(); + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public Client(org.apache.thrift.protocol.TProtocol prot)" << endl; + scope_up(f_service_); + indent(f_service_) << "super(prot, prot);" << endl; + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << "public Client(org.apache.thrift.protocol.TProtocol iprot, " + "org.apache.thrift.protocol.TProtocol oprot) {" << endl; + indent(f_service_) << " super(iprot, oprot);" << endl; + indent(f_service_) << "}" << endl << 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) { + string funname = (*f_iter)->get_name(); + string sep = "_"; + string javaname = funname; + if (fullcamel_style_) { + sep = ""; + javaname = as_camel_case(funname); + } + + // Open function + indent(f_service_) << "public " << function_signature(*f_iter) << endl; + scope_up(f_service_); + indent(f_service_) << "send" << sep << javaname << "("; + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + // Declare the function arguments + 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) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << (*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" << sep << javaname << "();" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + t_function send_function(g_type_void, + string("send") + sep + javaname, + (*f_iter)->get_arglist()); + + string argsname = (*f_iter)->get_name() + "_args"; + + // Open function + indent(f_service_) << "public " << function_signature(&send_function) << endl; + scope_up(f_service_); + + // Serialize the request + indent(f_service_) << argsname << " args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + indent(f_service_) << "args.set" << get_cap_name((*fld_iter)->get_name()) << "(" + << (*fld_iter)->get_name() << ");" << endl; + } + + const string sendBaseName = (*f_iter)->is_oneway() ? "sendBaseOneway" : "sendBase"; + indent(f_service_) << sendBaseName << "(\"" << funname << "\", args);" << endl; + + scope_down(f_service_); + 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") + sep + javaname, + &noargs, + (*f_iter)->get_xceptions()); + // Open function + indent(f_service_) << "public " << function_signature(&recv_function) << endl; + scope_up(f_service_); + + f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl + << indent() << "receiveBase(result, \"" << funname << "\");" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl + << indent() << " return result.success;" << endl << indent() << "}" << endl; + } + + 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) { + f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl + << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl + << indent() << "}" << endl; + } + + // If you get here it's an exception, unless a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "return;" << endl; + } else { + f_service_ << indent() << "throw new " + "org.apache.thrift.TApplicationException(org.apache.thrift." + "TApplicationException.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + } + + // Close function + scope_down(f_service_); + f_service_ << endl; + } + } + + indent_down(); + indent(f_service_) << "}" << endl; +} + +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) { + extends = type_name(tservice->get_extends()) + ".AsyncClient"; + } + + indent(f_service_) << "public static class AsyncClient extends " << extends + << " implements AsyncIface {" << endl; + indent_up(); + + // Factory method + indent(f_service_) << "public static class Factory implements " + "org.apache.thrift.async.TAsyncClientFactory {" << endl; + indent(f_service_) << " private org.apache.thrift.async.TAsyncClientManager clientManager;" + << endl; + indent(f_service_) << " private org.apache.thrift.protocol.TProtocolFactory protocolFactory;" + << endl; + indent(f_service_) << " public Factory(org.apache.thrift.async.TAsyncClientManager " + "clientManager, org.apache.thrift.protocol.TProtocolFactory " + "protocolFactory) {" << endl; + indent(f_service_) << " this.clientManager = clientManager;" << endl; + indent(f_service_) << " this.protocolFactory = protocolFactory;" << endl; + indent(f_service_) << " }" << endl; + indent(f_service_) << " public AsyncClient " + "getAsyncClient(org.apache.thrift.transport.TNonblockingTransport " + "transport) {" << endl; + indent(f_service_) << " return new AsyncClient(protocolFactory, clientManager, transport);" + << endl; + indent(f_service_) << " }" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public AsyncClient(org.apache.thrift.protocol.TProtocolFactory " + "protocolFactory, org.apache.thrift.async.TAsyncClientManager " + "clientManager, org.apache.thrift.transport.TNonblockingTransport " + "transport) {" << endl; + indent(f_service_) << " super(protocolFactory, clientManager, transport);" << endl; + indent(f_service_) << "}" << endl << 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) { + string funname = (*f_iter)->get_name(); + string sep = "_"; + string javaname = funname; + if (fullcamel_style_) { + sep = ""; + javaname = as_camel_case(javaname); + } + t_type* ret_type = (*f_iter)->get_returntype(); + t_struct* arg_struct = (*f_iter)->get_arglist(); + string funclassname = funname + "_call"; + const vector& fields = arg_struct->get_members(); + const std::vector& xceptions = (*f_iter)->get_xceptions()->get_members(); + vector::const_iterator fld_iter; + string args_name = (*f_iter)->get_name() + "_args"; + string result_name = (*f_iter)->get_name() + "_result"; + + // Main method body + indent(f_service_) << "public " << function_signature_async(*f_iter, false) + << " throws org.apache.thrift.TException {" << endl; + indent(f_service_) << " checkReady();" << endl; + indent(f_service_) << " " << funclassname << " method_call = new " + funclassname + "(" + << async_argument_list(*f_iter, arg_struct, ret_type) + << ", this, ___protocolFactory, ___transport);" << endl; + indent(f_service_) << " this.___currentMethod = method_call;" << endl; + indent(f_service_) << " ___manager.call(method_call);" << endl; + indent(f_service_) << "}" << endl; + + f_service_ << endl; + + // TAsyncMethod object for this function call + indent(f_service_) << "public static class " + funclassname + + " extends org.apache.thrift.async.TAsyncMethodCall<" + + type_name((*f_iter)->get_returntype(), true) + "> {" << endl; + indent_up(); + + // Member variables + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + indent(f_service_) << "private " + type_name((*fld_iter)->get_type()) + " " + + (*fld_iter)->get_name() + ";" << endl; + } + + // NOTE since we use a new Client instance to deserialize, let's keep seqid to 0 for now + // indent(f_service_) << "private int seqid;" << endl << endl; + + // Constructor + indent(f_service_) << "public " + funclassname + "(" + + async_argument_list(*f_iter, arg_struct, ret_type, true) + << ", org.apache.thrift.async.TAsyncClient client, " + "org.apache.thrift.protocol.TProtocolFactory protocolFactory, " + "org.apache.thrift.transport.TNonblockingTransport transport) throws " + "org.apache.thrift.TException {" << endl; + indent(f_service_) << " super(client, protocolFactory, transport, resultHandler, " + << ((*f_iter)->is_oneway() ? "true" : "false") << ");" << endl; + + // Assign member variables + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + indent(f_service_) << " this." + (*fld_iter)->get_name() + " = " + (*fld_iter)->get_name() + + ";" << endl; + } + + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public void write_args(org.apache.thrift.protocol.TProtocol prot) " + "throws org.apache.thrift.TException {" << endl; + indent_up(); + + // Serialize request + // NOTE we are leaving seqid as 0, for now (see above) + f_service_ << indent() << "prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage(\"" + << funname << "\", org.apache.thrift.protocol." + << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") << ", 0));" + << endl << indent() << args_name << " args = new " << args_name << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args.set" << get_cap_name((*fld_iter)->get_name()) << "(" + << (*fld_iter)->get_name() << ");" << endl; + } + + f_service_ << indent() << "args.write(prot);" << endl << indent() << "prot.writeMessageEnd();" + << endl; + + indent_down(); + indent(f_service_) << "}" << endl << endl; + + // Return method + indent(f_service_) << "public " + type_name(ret_type, true) + " getResult() throws "; + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << type_name((*x_iter)->get_type(), false, false) + ", "; + } + f_service_ << "org.apache.thrift.TException {" << endl; + + indent_up(); + f_service_ + << indent() + << "if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) {" + << endl << indent() << " throw new java.lang.IllegalStateException(\"Method call not finished!\");" + << endl << indent() << "}" << endl << indent() + << "org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new " + "org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array());" << endl + << indent() << "org.apache.thrift.protocol.TProtocol prot = " + "client.getProtocolFactory().getProtocol(memoryTransport);" << endl; + indent(f_service_); + if (ret_type->is_void()) { // NB: Includes oneways which always return void. + f_service_ << "return null;" << endl; + } else { + f_service_ << "return (new Client(prot)).recv" + sep + javaname + "();" << endl; + } + + // Close function + indent_down(); + indent(f_service_) << "}" << endl; + + // Close class + indent_down(); + indent(f_service_) << "}" << endl << endl; + } + + // Close AsyncClient + scope_down(f_service_); + f_service_ << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_java_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() == NULL) { + extends_processor = "org.apache.thrift.TBaseProcessor"; + } else { + extends = type_name(tservice->get_extends()); + extends_processor = extends + ".Processor"; + } + + // Generate the header portion + indent(f_service_) << "public static class Processor extends " + << extends_processor << " implements org.apache.thrift.TProcessor {" << endl; + indent_up(); + + indent(f_service_) + << "private static final org.slf4j.Logger _LOGGER = org.slf4j.LoggerFactory.getLogger(Processor.class.getName());" + << endl; + + indent(f_service_) << "public Processor(I iface) {" << endl; + indent(f_service_) << " super(iface, getProcessMap(new java.util.HashMap>()));" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "protected Processor(I iface, java.util.Map> " + "processMap) {" << endl; + indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "private static java.util.Map> " + "getProcessMap(java.util.Map> processMap) {" << endl; + indent_up(); + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_) << "processMap.put(\"" << (*f_iter)->get_name() << "\", new " + << (*f_iter)->get_name() << "());" << endl; + } + indent(f_service_) << "return processMap;" << endl; + indent_down(); + indent(f_service_) << "}" << endl << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << "}" << endl << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_java_generator::generate_service_async_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() == NULL) { + extends_processor = "org.apache.thrift.TBaseAsyncProcessor"; + } else { + extends = type_name(tservice->get_extends()); + extends_processor = extends + ".AsyncProcessor"; + } + + // Generate the header portion + indent(f_service_) << "public static class AsyncProcessor extends " + << extends_processor << " {" << endl; + indent_up(); + + indent(f_service_) << "private static final org.slf4j.Logger _LOGGER = " + "org.slf4j.LoggerFactory.getLogger(AsyncProcessor.class.getName());" << endl; + + indent(f_service_) << "public AsyncProcessor(I iface) {" << endl; + indent(f_service_) << " super(iface, getProcessMap(new java.util.HashMap>()));" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "protected AsyncProcessor(I iface, java.util.Map> processMap) {" << endl; + indent(f_service_) << " super(iface, getProcessMap(processMap));" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "private static java.util.Map> getProcessMap(java.util.Map> processMap) {" << endl; + indent_up(); + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_) << "processMap.put(\"" << (*f_iter)->get_name() << "\", new " + << (*f_iter)->get_name() << "());" << endl; + } + indent(f_service_) << "return processMap;" << endl; + indent_down(); + indent(f_service_) << "}" << endl << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_async_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << "}" << endl << endl; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_java_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_java_struct_definition(f_service_, &result, false, true, true); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_java_generator::generate_process_async_function(t_service* tservice, t_function* tfunction) { + string argsname = tfunction->get_name() + "_args"; + + string resultname = tfunction->get_name() + "_result"; + if (tfunction->is_oneway()) { + resultname = "org.apache.thrift.TBase"; + } + + string resulttype = type_name(tfunction->get_returntype(), true); + + (void)tservice; + // Open class + indent(f_service_) << "public static class " << tfunction->get_name() + << " extends org.apache.thrift.AsyncProcessFunction {" << endl; + indent_up(); + + indent(f_service_) << "public " << tfunction->get_name() << "() {" << endl; + indent(f_service_) << " super(\"" << tfunction->get_name() << "\");" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public " << argsname << " getEmptyArgsInstance() {" << endl; + indent(f_service_) << " return new " << argsname << "();" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public org.apache.thrift.async.AsyncMethodCallback<" << resulttype + << "> getResultHandler(final org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer fb, final int seqid) {" << endl; + indent_up(); + indent(f_service_) << "final org.apache.thrift.AsyncProcessFunction fcall = this;" << endl; + indent(f_service_) << "return new org.apache.thrift.async.AsyncMethodCallback<" << resulttype + << ">() { " << endl; + indent_up(); + indent(f_service_) << "public void onComplete(" << resulttype << " o) {" << endl; + + indent_up(); + if (!tfunction->is_oneway()) { + indent(f_service_) << resultname << " result = new " << resultname << "();" << endl; + + if (!tfunction->get_returntype()->is_void()) { + indent(f_service_) << "result.success = o;" << endl; + // Set isset on success field + if (!type_can_be_null(tfunction->get_returntype())) { + indent(f_service_) << "result.set" << get_cap_name("success") << get_cap_name("isSet") + << "(true);" << endl; + } + } + + indent(f_service_) << "try {" << endl; + indent(f_service_) + << " fcall.sendResponse(fb, result, org.apache.thrift.protocol.TMessageType.REPLY,seqid);" + << endl; + indent(f_service_) << "} catch (org.apache.thrift.transport.TTransportException e) {" << endl; + indent_up(); + f_service_ << indent() + << "_LOGGER.error(\"TTransportException writing to internal frame buffer\", e);" + << endl + << indent() << "fb.close();" << endl; + indent_down(); + indent(f_service_) << "} catch (java.lang.Exception e) {" << endl; + indent_up(); + f_service_ << indent() << "_LOGGER.error(\"Exception writing to internal frame buffer\", e);" + << endl + << indent() << "onError(e);" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + + indent(f_service_) << "public void onError(java.lang.Exception e) {" << endl; + indent_up(); + + if (tfunction->is_oneway()) { + indent(f_service_) << "if (e instanceof org.apache.thrift.transport.TTransportException) {" + << endl; + indent_up(); + + f_service_ << indent() << "_LOGGER.error(\"TTransportException inside handler\", e);" << endl + << indent() << "fb.close();" << endl; + + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + + f_service_ << indent() << "_LOGGER.error(\"Exception inside oneway handler\", e);" << endl; + + indent_down(); + indent(f_service_) << "}" << endl; + } else { + indent(f_service_) << "byte msgType = org.apache.thrift.protocol.TMessageType.REPLY;" << endl; + indent(f_service_) << "org.apache.thrift.TSerializable msg;" << endl; + indent(f_service_) << resultname << " result = new " << resultname << "();" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + + vector::const_iterator x_iter; + if (xceptions.size() > 0) { + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + if (x_iter == xceptions.begin()) + f_service_ << indent(); + string type = type_name((*x_iter)->get_type(), false, false); + string name = (*x_iter)->get_name(); + f_service_ << "if (e instanceof " << type << ") {" << endl; + indent_up(); + f_service_ << indent() << "result." << name << " = (" << type << ") e;" << endl + << indent() << "result.set" << get_cap_name(name) << get_cap_name("isSet") + << "(true);" << endl + << indent() << "msg = result;" << endl; + indent_down(); + indent(f_service_) << "} else "; + } + } else { + indent(f_service_); + } + f_service_ << "if (e instanceof org.apache.thrift.transport.TTransportException) {" << endl; + indent_up(); + f_service_ << indent() << "_LOGGER.error(\"TTransportException inside handler\", e);" << endl + << indent() << "fb.close();" << endl + << indent() << "return;" << endl; + indent_down(); + indent(f_service_) << "} else if (e instanceof org.apache.thrift.TApplicationException) {" + << endl; + indent_up(); + f_service_ << indent() << "_LOGGER.error(\"TApplicationException inside handler\", e);" << endl + << indent() << "msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION;" << endl + << indent() << "msg = (org.apache.thrift.TApplicationException)e;" << endl; + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + f_service_ << indent() << "_LOGGER.error(\"Exception inside handler\", e);" << endl + << indent() << "msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION;" << endl + << indent() << "msg = new " + "org.apache.thrift.TApplicationException(org.apache.thrift." + "TApplicationException.INTERNAL_ERROR, e.getMessage());" + << endl; + indent_down(); + f_service_ << indent() << "}" << endl + << indent() << "try {" << endl + << indent() << " fcall.sendResponse(fb,msg,msgType,seqid);" << endl + << indent() << "} catch (java.lang.Exception ex) {" << endl + << indent() << " _LOGGER.error(\"Exception writing to internal frame buffer\", ex);" + << endl + << indent() << " fb.close();" << endl + << indent() << "}" << endl; + } + indent_down(); + indent(f_service_) << "}" << endl; + indent_down(); + indent(f_service_) << "};" << endl; + indent_down(); + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "protected boolean isOneway() {" << endl; + indent(f_service_) << " return " << ((tfunction->is_oneway()) ? "true" : "false") << ";" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public void start(I iface, " << argsname + << " args, org.apache.thrift.async.AsyncMethodCallback<" << resulttype + << "> resultHandler) throws org.apache.thrift.TException {" << endl; + indent_up(); + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + f_service_ << indent(); + + f_service_ << "iface." << get_rpc_method_name(tfunction->get_name()) << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + if (!first) + f_service_ << ","; + f_service_ << "resultHandler"; + f_service_ << ");" << endl; + + indent_down(); + indent(f_service_) << "}"; + + // Close function + f_service_ << endl; + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_java_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; + if (tfunction->is_oneway()) { + resultname = "org.apache.thrift.TBase"; + } + + (void)tservice; + // Open class + indent(f_service_) << "public static class " << tfunction->get_name() + << " extends org.apache.thrift.ProcessFunction {" << endl; + indent_up(); + + indent(f_service_) << "public " << tfunction->get_name() << "() {" << endl; + indent(f_service_) << " super(\"" << tfunction->get_name() << "\");" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public " << argsname << " getEmptyArgsInstance() {" << endl; + indent(f_service_) << " return new " << argsname << "();" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "protected boolean isOneway() {" << endl; + indent(f_service_) << " return " << ((tfunction->is_oneway()) ? "true" : "false") << ";" << endl; + indent(f_service_) << "}" << endl << endl; + + indent(f_service_) << "public " << resultname << " getResult(I iface, " << argsname + << " args) throws org.apache.thrift.TException {" << endl; + indent_up(); + if (!tfunction->is_oneway()) { + indent(f_service_) << resultname << " result = new " << resultname << "();" << endl; + } + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_ << indent() << "try {" << endl; + indent_up(); + } + + // Generate the function call + 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." << get_rpc_method_name(tfunction->get_name()) << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + // Set isset on success field + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void() + && !type_can_be_null(tfunction->get_returntype())) { + indent(f_service_) << "result.set" << get_cap_name("success") << get_cap_name("isSet") + << "(true);" << endl; + } + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + f_service_ << indent() << "}"; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " " + << (*x_iter)->get_name() << ") {" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << ";" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << "}"; + } + } + f_service_ << endl; + } + + if (tfunction->is_oneway()) { + indent(f_service_) << "return null;" << endl; + } else { + indent(f_service_) << "return result;" << endl; + } + indent_down(); + indent(f_service_) << "}"; + + // Close function + f_service_ << endl; + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Deserializes a field of any type. + * + * @param tfield The field + * @param prefix The variable name or container for this field + */ +void t_java_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool has_metadata) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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, has_metadata); + } else if (type->is_base_type()) { + indent(out) << name << " = iprot."; + + 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 (((t_base_type*)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 Java name for base type " + t_base_type::t_base_name(tbase); + } + out << endl; + } else if (type->is_enum()) { + indent(out) << name << " = " + << type_name(tfield->get_type(), true, false, false, true) + + ".findByValue(iprot.readI32());" << 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, invokes read() + */ +void t_java_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix) { + + if (reuse_objects_) { + indent(out) << "if (" << prefix << " == null) {" << endl; + indent_up(); + } + indent(out) << prefix << " = new " << type_name(tstruct) << "();" << endl; + if (reuse_objects_) { + indent_down(); + indent(out) << "}" << endl; + } + indent(out) << prefix << ".read(iprot);" << endl; +} + +/** + * Deserializes a container by reading its size and then iterating + */ +void t_java_generator::generate_deserialize_container(ofstream& out, + t_type* ttype, + string prefix, + bool has_metadata) { + + 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"); + } + + if (has_metadata) { + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "org.apache.thrift.protocol.TMap " << obj << " = iprot.readMapBegin();" + << endl; + } else if (ttype->is_set()) { + indent(out) << "org.apache.thrift.protocol.TSet " << obj << " = iprot.readSetBegin();" + << endl; + } else if (ttype->is_list()) { + indent(out) << "org.apache.thrift.protocol.TList " << obj << " = iprot.readListBegin();" + << endl; + } + } else { + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "org.apache.thrift.protocol.TMap " << obj + << " = new org.apache.thrift.protocol.TMap(" + << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "iprot.readI32());" << 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());" + << endl; + } else if (ttype->is_list()) { + indent(out) << "org.apache.thrift.protocol.TList " << obj + << " = new org.apache.thrift.protocol.TList(" + << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", iprot.readI32());" + << endl; + } + } + + if (reuse_objects_) { + indent(out) << "if (" << prefix << " == null) {" << endl; + indent_up(); + } + + out << indent() << prefix << " = new " << type_name(ttype, false, true); + + // size the collection correctly + if (sorted_containers_ && (ttype->is_map() || ttype->is_set())) { + // TreeSet and TreeMap don't have any constructor which takes a capactity as an argument + out << "();" << endl; + } else { + out << "(" << (ttype->is_list() ? "" : "2*") << obj << ".size" + << ");" << endl; + } + + if (reuse_objects_) { + indent_down(); + indent(out) << "}" << endl; + } + + if (ttype->is_map()) { + generate_deserialize_map_element(out, (t_map*)ttype, prefix, obj, has_metadata); + } else if (ttype->is_set()) { + generate_deserialize_set_element(out, (t_set*)ttype, prefix, obj, has_metadata); + } else if (ttype->is_list()) { + generate_deserialize_list_element(out, (t_list*)ttype, prefix, obj, has_metadata); + } + + scope_down(out); + + if (has_metadata) { + // Read container end + 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); +} + +/** + * Generates code to deserialize a map + */ +void t_java_generator::generate_deserialize_map_element(ofstream& out, + t_map* tmap, + string prefix, + string obj, + bool has_metadata) { + 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, reuse_objects_, false) << endl; + indent(out) << declare_field(&fval, reuse_objects_, false) << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size" + << "; " + << "++" << i << ")" << endl; + + scope_up(out); + + generate_deserialize_field(out, &fkey, "", has_metadata); + generate_deserialize_field(out, &fval, "", has_metadata); + + indent(out) << prefix << ".put(" << key << ", " << val << ");" << endl; + + if (reuse_objects_ && !get_true_type(fkey.get_type())->is_base_type()) { + indent(out) << key << " = null;" << endl; + } + + if (reuse_objects_ && !get_true_type(fval.get_type())->is_base_type()) { + indent(out) << val << " = null;" << endl; + } +} + +/** + * Deserializes a set element + */ +void t_java_generator::generate_deserialize_set_element(ofstream& out, + t_set* tset, + string prefix, + string obj, + bool has_metadata) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(out) << declare_field(&felem, reuse_objects_, false) << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size" + << "; " + << "++" << i << ")" << endl; + scope_up(out); + + generate_deserialize_field(out, &felem, "", has_metadata); + + indent(out) << prefix << ".add(" << elem << ");" << endl; + + if (reuse_objects_ && !get_true_type(felem.get_type())->is_base_type()) { + indent(out) << elem << " = null;" << endl; + } +} + +/** + * Deserializes a list element + */ +void t_java_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix, + string obj, + bool has_metadata) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(out) << declare_field(&felem, reuse_objects_, false) << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size" + << "; " + << "++" << i << ")" << endl; + scope_up(out); + + generate_deserialize_field(out, &felem, "", has_metadata); + + indent(out) << prefix << ".add(" << elem << ");" << endl; + + if (reuse_objects_ && !get_true_type(felem.get_type())->is_base_type()) { + indent(out) << elem << " = null;" << endl; + } +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_java_generator::generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool has_metadata) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name(), has_metadata); + } else if (type->is_enum()) { + indent(out) << "oprot.writeI32(" << prefix + tfield->get_name() << ".getValue());" << endl; + } else if (type->is_base_type()) { + string name = prefix + tfield->get_name(); + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(struct." << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_java_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + out << indent() << prefix << ".write(oprot);" << endl; +} + +/** + * Serializes a container by writing its size then the elements. + * + * @param ttype The type of container + * @param prefix String prefix for fields + */ +void t_java_generator::generate_serialize_container(ofstream& out, + t_type* ttype, + string prefix, + bool has_metadata) { + scope_up(out); + + if (has_metadata) { + if (ttype->is_map()) { + indent(out) << "oprot.writeMapBegin(new org.apache.thrift.protocol.TMap(" + << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".size()));" + << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(new org.apache.thrift.protocol.TSet(" + << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " << prefix + << ".size()));" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(new org.apache.thrift.protocol.TList(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix + << ".size()));" << endl; + } + } else { + indent(out) << "oprot.writeI32(" << prefix << ".size());" << endl; + } + + string iter = tmp("_iter"); + if (ttype->is_map()) { + indent(out) << "for (java.util.Map.Entry<" << type_name(((t_map*)ttype)->get_key_type(), true, false) + << ", " << type_name(((t_map*)ttype)->get_val_type(), true, false) << "> " << iter + << " : " << prefix << ".entrySet())"; + } else if (ttype->is_set()) { + indent(out) << "for (" << type_name(((t_set*)ttype)->get_elem_type()) << " " << iter << " : " + << prefix << ")"; + } else if (ttype->is_list()) { + indent(out) << "for (" << type_name(((t_list*)ttype)->get_elem_type()) << " " << iter << " : " + << prefix << ")"; + } + + out << endl; + scope_up(out); + if (ttype->is_map()) { + generate_serialize_map_element(out, (t_map*)ttype, iter, prefix, has_metadata); + } else if (ttype->is_set()) { + generate_serialize_set_element(out, (t_set*)ttype, iter, has_metadata); + } else if (ttype->is_list()) { + generate_serialize_list_element(out, (t_list*)ttype, iter, has_metadata); + } + scope_down(out); + + if (has_metadata) { + 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); +} + +/** + * Serializes the members of a map. + */ +void t_java_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string iter, + string map, + bool has_metadata) { + (void)map; + t_field kfield(tmap->get_key_type(), iter + ".getKey()"); + generate_serialize_field(out, &kfield, "", has_metadata); + t_field vfield(tmap->get_val_type(), iter + ".getValue()"); + generate_serialize_field(out, &vfield, "", has_metadata); +} + +/** + * Serializes the members of a set. + */ +void t_java_generator::generate_serialize_set_element(ofstream& out, + t_set* tset, + string iter, + bool has_metadata) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, "", has_metadata); +} + +/** + * Serializes the members of a list. + */ +void t_java_generator::generate_serialize_list_element(ofstream& out, + t_list* tlist, + string iter, + bool has_metadata) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, "", has_metadata); +} + +/** + * Returns a Java type name + * + * @param ttype The type + * @param container Is the type going inside a container? + * @return Java type name, i.e. java.util.HashMap + */ +string t_java_generator::type_name(t_type* ttype, + bool in_container, + bool in_init, + bool skip_generic, + bool force_namespace) { + // In Java typedefs are just resolved to their real type + ttype = get_true_type(ttype); + string prefix; + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype, in_container); + } else if (ttype->is_map()) { + t_map* tmap = (t_map*)ttype; + if (in_init) { + if (sorted_containers_) { + prefix = "java.util.TreeMap"; + } else { + prefix = "java.util.HashMap"; + } + } else { + prefix = "java.util.Map"; + } + return prefix + (skip_generic ? "" : "<" + 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; + if (in_init) { + if (sorted_containers_) { + prefix = "java.util.TreeSet"; + } else { + prefix = "java.util.HashSet"; + } + } else { + prefix = "java.util.Set"; + } + return prefix + (skip_generic ? "" : "<" + type_name(tset->get_elem_type(), true) + ">"); + } else if (ttype->is_list()) { + t_list* tlist = (t_list*)ttype; + if (in_init) { + prefix = "java.util.ArrayList"; + } else { + prefix = "java.util.List"; + } + return prefix + (skip_generic ? "" : "<" + type_name(tlist->get_elem_type(), true) + ">"); + } + + // Check for namespacing + t_program* program = ttype->get_program(); + if ((program != NULL) && ((program != program_) || force_namespace)) { + string package = program->get_namespace("java"); + if (!package.empty()) { + return package + "." + ttype->get_name(); + } + } + + return ttype->get_name(); +} + +/** + * Returns the Java type that corresponds to the thrift type. + * + * @param tbase The base type + * @param container Is it going in a Java container? + */ +string t_java_generator::base_type_name(t_base_type* type, bool in_container) { + t_base_type::t_base tbase = type->get_base(); + + switch (tbase) { + case t_base_type::TYPE_VOID: + return (in_container ? "Void" : "void"); + case t_base_type::TYPE_STRING: + if (type->is_binary()) { + return "java.nio.ByteBuffer"; + } else { + return "java.lang.String"; + } + case t_base_type::TYPE_BOOL: + return (in_container ? "java.lang.Boolean" : "boolean"); + case t_base_type::TYPE_I8: + return (in_container ? "java.lang.Byte" : "byte"); + case t_base_type::TYPE_I16: + return (in_container ? "java.lang.Short" : "short"); + case t_base_type::TYPE_I32: + return (in_container ? "java.lang.Integer" : "int"); + case t_base_type::TYPE_I64: + return (in_container ? "java.lang.Long" : "long"); + case t_base_type::TYPE_DOUBLE: + return (in_container ? "java.lang.Double" : "double"); + default: + throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param tfield The field + * @param init Whether to initialize the field + */ +string t_java_generator::declare_field(t_field* tfield, bool init, bool comment) { + // TODO(mcslee): do we ever need to initialize the field? + 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) { + ofstream dummy; + result += " = " + render_const_value(dummy, 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 += " = null"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype, false, true) + "()"; + } else { + result += " = new " + type_name(ttype, false, true) + "()"; + ; + } + } + result += ";"; + if (comment) { + result += " // "; + if (tfield->get_req() == t_field::T_OPTIONAL) { + result += "optional"; + } else { + result += "required"; + } + } + return result; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_java_generator::function_signature(t_function* tfunction, string prefix) { + t_type* ttype = tfunction->get_returntype(); + std::string fn_name = get_rpc_method_name(tfunction->get_name()); + std::string result = type_name(ttype) + " " + prefix + fn_name + "(" + + argument_list(tfunction->get_arglist()) + ") throws "; + 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) { + result += type_name((*x_iter)->get_type(), false, false) + ", "; + } + result += "org.apache.thrift.TException"; + return result; +} + +/** + * Renders a function signature of the form 'void name(args, resultHandler)' + * + * @params tfunction Function definition + * @return String of rendered function definition + */ +string t_java_generator::function_signature_async(t_function* tfunction, + bool use_base_method, + string prefix) { + std::string arglist = async_function_call_arglist(tfunction, use_base_method, true); + + std::string ret_type = ""; + if (use_base_method) { + ret_type += "AsyncClient."; + } + ret_type += tfunction->get_name() + "_call"; + + std::string fn_name = get_rpc_method_name(tfunction->get_name()); + + std::string result = prefix + "void " + fn_name + "(" + arglist + ")"; + return result; +} + +string t_java_generator::async_function_call_arglist(t_function* tfunc, + bool use_base_method, + bool include_types) { + (void)use_base_method; + std::string arglist = ""; + if (tfunc->get_arglist()->get_members().size() > 0) { + arglist = argument_list(tfunc->get_arglist(), include_types) + ", "; + } + + if (include_types) { + arglist += "org.apache.thrift.async.AsyncMethodCallback<"; + arglist += type_name(tfunc->get_returntype(), true) + "> "; + } + arglist += "resultHandler"; + + return arglist; +} + +/** + * Renders a comma separated field list, with type names + */ +string t_java_generator::argument_list(t_struct* tstruct, bool include_types) { + 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 += ", "; + } + if (include_types) { + result += type_name((*f_iter)->get_type()) + " "; + } + result += (*f_iter)->get_name(); + } + return result; +} + +string t_java_generator::async_argument_list(t_function* tfunct, + t_struct* tstruct, + t_type* ttype, + bool include_types) { + (void)tfunct; + (void)ttype; + 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 += ", "; + } + if (include_types) { + result += type_name((*f_iter)->get_type()) + " "; + } + result += (*f_iter)->get_name(); + } + if (!first) { + result += ", "; + } + if (include_types) { + result += "org.apache.thrift.async.AsyncMethodCallback<"; + result += type_name(tfunct->get_returntype(), true) + "> "; + } + result += "resultHandler"; + return result; +} + +/** + * Converts the parse type to a Java enum string for the given type. + */ +string t_java_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 "org.apache.thrift.protocol.TType.STRING"; + case t_base_type::TYPE_BOOL: + return "org.apache.thrift.protocol.TType.BOOL"; + case t_base_type::TYPE_I8: + return "org.apache.thrift.protocol.TType.BYTE"; + case t_base_type::TYPE_I16: + return "org.apache.thrift.protocol.TType.I16"; + case t_base_type::TYPE_I32: + return "org.apache.thrift.protocol.TType.I32"; + case t_base_type::TYPE_I64: + return "org.apache.thrift.protocol.TType.I64"; + case t_base_type::TYPE_DOUBLE: + return "org.apache.thrift.protocol.TType.DOUBLE"; + } + } else if (type->is_enum()) { + return "org.apache.thrift.protocol.TType.I32"; + } else if (type->is_struct() || type->is_xception()) { + return "org.apache.thrift.protocol.TType.STRUCT"; + } else if (type->is_map()) { + return "org.apache.thrift.protocol.TType.MAP"; + } else if (type->is_set()) { + return "org.apache.thrift.protocol.TType.SET"; + } else if (type->is_list()) { + return "org.apache.thrift.protocol.TType.LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Takes a name and produes a valid Java source file name from it + * + * @param fromName The name which shall become a valid Java source file name + * @return The produced identifier + */ +std::string t_java_generator::make_valid_java_filename(std::string const& fromName) { + // if any further rules apply to source file names in Java, modify as necessary + return make_valid_java_identifier(fromName); +} + +/** + * Takes a name and produes a valid Java identifier from it + * + * @param fromName The name which shall become a valid Java identifier + * @return The produced identifier + */ +std::string t_java_generator::make_valid_java_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; +} + +std::string t_java_generator::as_camel_case(std::string name, bool ucfirst) { + std::string new_name; + size_t i = 0; + for (i = 0; i < name.size(); i++) { + if (name[i] != '_') + break; + } + if (ucfirst) { + new_name += toupper(name[i++]); + } else { + new_name += tolower(name[i++]); + } + for (; i < name.size(); i++) { + if (name[i] == '_') { + if (i < name.size() - 1) { + i++; + new_name += toupper(name[i]); + } + } else { + new_name += name[i]; + } + } + return new_name; +} + +std::string t_java_generator::get_rpc_method_name(std::string name) { + if (fullcamel_style_) { + return as_camel_case(name, false); + } else { + return name; + } +} + +/** + * Applies the correct style to a string based on the value of nocamel_style_ + * and/or fullcamel_style_ + */ +std::string t_java_generator::get_cap_name(std::string name) { + if (nocamel_style_) { + return "_" + name; + } else if (fullcamel_style_) { + return as_camel_case(name); + } else { + name[0] = toupper(name[0]); + return name; + } +} + +string t_java_generator::constant_name(string name) { + string constant_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); + + bool is_upper = isupper(character); + + if (is_upper && !is_first && !was_previous_char_upper) { + constant_name += '_'; + } + constant_name += toupper(character); + + is_first = false; + was_previous_char_upper = is_upper; + } + + return constant_name; +} + +void t_java_generator::generate_deep_copy_container(ofstream& out, + std::string source_name_p1, + std::string source_name_p2, + std::string result_name, + t_type* type) { + + t_container* container = (t_container*)type; + std::string source_name; + if (source_name_p2 == "") + source_name = source_name_p1; + else + source_name = source_name_p1 + "." + source_name_p2; + + bool copy_construct_container; + if (container->is_map()) { + t_map* tmap = (t_map*)container; + copy_construct_container = tmap->get_key_type()->is_base_type() + && tmap->get_val_type()->is_base_type(); + } else { + t_type* elem_type = container->is_list() ? ((t_list*)container)->get_elem_type() + : ((t_set*)container)->get_elem_type(); + copy_construct_container = elem_type->is_base_type(); + } + + if (copy_construct_container) { + // deep copy of base types can be done much more efficiently than iterating over all the + // elements manually + indent(out) << type_name(type, true, false) << " " << result_name << " = new " + << type_name(container, false, true) << "(" << source_name << ");" << endl; + return; + } + + std::string capacity; + if (!(sorted_containers_ && (container->is_map() || container->is_set()))) { + // unsorted containers accept a capacity value + capacity = source_name + ".size()"; + } + indent(out) << type_name(type, true, false) << " " << result_name << " = new " + << type_name(container, false, true) << "(" << capacity << ");" << endl; + + std::string iterator_element_name = source_name_p1 + "_element"; + std::string result_element_name = result_name + "_copy"; + + if (container->is_map()) { + t_type* key_type = ((t_map*)container)->get_key_type(); + t_type* val_type = ((t_map*)container)->get_val_type(); + + indent(out) << "for (java.util.Map.Entry<" << type_name(key_type, true, false) << ", " + << type_name(val_type, true, false) << "> " << iterator_element_name << " : " + << source_name << ".entrySet()) {" << endl; + indent_up(); + + out << endl; + + indent(out) << type_name(key_type, true, false) << " " << iterator_element_name + << "_key = " << iterator_element_name << ".getKey();" << endl; + indent(out) << type_name(val_type, true, false) << " " << iterator_element_name + << "_value = " << iterator_element_name << ".getValue();" << endl; + + out << endl; + + if (key_type->is_container()) { + generate_deep_copy_container(out, + iterator_element_name + "_key", + "", + result_element_name + "_key", + key_type); + } else { + indent(out) << type_name(key_type, true, false) << " " << result_element_name << "_key = "; + generate_deep_copy_non_container(out, + iterator_element_name + "_key", + result_element_name + "_key", + key_type); + out << ";" << endl; + } + + out << endl; + + if (val_type->is_container()) { + generate_deep_copy_container(out, + iterator_element_name + "_value", + "", + result_element_name + "_value", + val_type); + } else { + indent(out) << type_name(val_type, true, false) << " " << result_element_name << "_value = "; + generate_deep_copy_non_container(out, + iterator_element_name + "_value", + result_element_name + "_value", + val_type); + out << ";" << endl; + } + + out << endl; + + indent(out) << result_name << ".put(" << result_element_name << "_key, " << result_element_name + << "_value);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + } else { + t_type* elem_type; + + if (container->is_set()) { + elem_type = ((t_set*)container)->get_elem_type(); + } else { + elem_type = ((t_list*)container)->get_elem_type(); + } + + indent(out) << "for (" << type_name(elem_type, true, false) << " " << iterator_element_name + << " : " << source_name << ") {" << endl; + + indent_up(); + + if (elem_type->is_container()) { + // recursive deep copy + generate_deep_copy_container(out, iterator_element_name, "", result_element_name, elem_type); + indent(out) << result_name << ".add(" << result_element_name << ");" << endl; + } else { + // iterative copy + if (((t_base_type*)elem_type)->is_binary()) { + indent(out) << "java.nio.ByteBuffer temp_binary_element = "; + generate_deep_copy_non_container(out, + iterator_element_name, + "temp_binary_element", + elem_type); + out << ";" << endl; + indent(out) << result_name << ".add(temp_binary_element);" << endl; + } else { + indent(out) << result_name << ".add("; + generate_deep_copy_non_container(out, iterator_element_name, result_name, elem_type); + out << ");" << endl; + } + } + + indent_down(); + + indent(out) << "}" << endl; + } +} + +void t_java_generator::generate_deep_copy_non_container(ofstream& out, + std::string source_name, + std::string dest_name, + t_type* type) { + (void)dest_name; + if (type->is_base_type() || type->is_enum() || type->is_typedef()) { + if (((t_base_type*)type)->is_binary()) { + out << "org.apache.thrift.TBaseHelper.copyBinary(" << source_name << ")"; + } else { + // everything else can be copied directly + out << source_name; + } + } else { + out << "new " << type_name(type, true, true) << "(" << source_name << ")"; + } +} + +std::string t_java_generator::generate_isset_check(t_field* field) { + return generate_isset_check(field->get_name()); +} + +std::string t_java_generator::isset_field_id(t_field* field) { + return "__" + upcase_string(field->get_name() + "_isset_id"); +} + +std::string t_java_generator::generate_isset_check(std::string field_name) { + return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; +} + +void t_java_generator::generate_isset_set(ofstream& out, t_field* field, string prefix) { + if (!type_can_be_null(field->get_type())) { + indent(out) << prefix << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet") + << "(true);" << endl; + } +} + +void t_java_generator::generate_struct_desc(ofstream& out, t_struct* tstruct) { + indent(out) << "private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new " + "org.apache.thrift.protocol.TStruct(\"" << tstruct->get_name() << "\");" << endl; +} + +void t_java_generator::generate_field_descs(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "private static final org.apache.thrift.protocol.TField " + << constant_name((*m_iter)->get_name()) + << "_FIELD_DESC = new org.apache.thrift.protocol.TField(\"" << (*m_iter)->get_name() + << "\", " << type_to_enum((*m_iter)->get_type()) << ", " + << "(short)" << (*m_iter)->get_key() << ");" << endl; + } +} + +void t_java_generator::generate_scheme_map(ofstream& out, t_struct* tstruct) { + indent(out) << "private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new " + << tstruct->get_name() << "StandardSchemeFactory();" << endl; + indent(out) << "private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new " + << tstruct->get_name() << "TupleSchemeFactory();" << endl; +} + +void t_java_generator::generate_field_name_constants(ofstream& out, t_struct* tstruct) { + indent(out) << "/** The set of fields this struct contains, along with convenience methods for " + "finding and manipulating them. */" << endl; + indent(out) << "public enum _Fields implements org.apache.thrift.TFieldIdEnum {" << endl; + + indent_up(); + bool first = true; + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!first) { + out << "," << endl; + } + first = false; + generate_java_doc(out, *m_iter); + indent(out) << constant_name((*m_iter)->get_name()) << "((short)" << (*m_iter)->get_key() + << ", \"" << (*m_iter)->get_name() << "\")"; + } + + out << ";" << endl << endl; + + indent(out) + << "private static final java.util.Map byName = new java.util.HashMap();" + << endl; + out << endl; + + indent(out) << "static {" << endl; + indent(out) << " for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) {" << endl; + indent(out) << " byName.put(field.getFieldName(), field);" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "/**" << endl; + indent(out) << " * Find the _Fields constant that matches fieldId, or null if its not found." + << endl; + indent(out) << " */" << endl; + indent(out) << "public static _Fields findByThriftId(int fieldId) {" << endl; + indent_up(); + indent(out) << "switch(fieldId) {" << endl; + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "case " << (*m_iter)->get_key() << ": // " + << constant_name((*m_iter)->get_name()) << endl; + indent(out) << " return " << constant_name((*m_iter)->get_name()) << ";" << endl; + } + + indent(out) << "default:" << endl; + indent(out) << " return null;" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; + + indent(out) << "/**" << endl; + indent(out) << " * Find the _Fields constant that matches fieldId, throwing an exception" << endl; + indent(out) << " * if it is not found." << endl; + indent(out) << " */" << endl; + indent(out) << "public static _Fields findByThriftIdOrThrow(int fieldId) {" << endl; + indent(out) << " _Fields fields = findByThriftId(fieldId);" << endl; + indent(out) << " if (fields == null) throw new java.lang.IllegalArgumentException(\"Field \" + fieldId + " + "\" doesn't exist!\");" << endl; + indent(out) << " return fields;" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "/**" << endl; + indent(out) << " * Find the _Fields constant that matches name, or null if its not found." + << endl; + indent(out) << " */" << endl; + indent(out) << "public static _Fields findByName(java.lang.String name) {" << endl; + indent(out) << " return byName.get(name);" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "private final short _thriftId;" << endl; + indent(out) << "private final java.lang.String _fieldName;" << endl << endl; + + indent(out) << "_Fields(short thriftId, java.lang.String fieldName) {" << endl; + indent(out) << " _thriftId = thriftId;" << endl; + indent(out) << " _fieldName = fieldName;" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public short getThriftFieldId() {" << endl; + indent(out) << " return _thriftId;" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public java.lang.String getFieldName() {" << endl; + indent(out) << " return _fieldName;" << endl; + indent(out) << "}" << endl; + + indent_down(); + + indent(out) << "}" << endl; +} + +t_java_generator::isset_type t_java_generator::needs_isset(t_struct* tstruct, + std::string* outPrimitiveType) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + int count = 0; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null(get_true_type((*m_iter)->get_type()))) { + count++; + } + } + if (count == 0) { + return ISSET_NONE; + } else if (count <= 64) { + if (outPrimitiveType != NULL) { + if (count <= 8) + *outPrimitiveType = "byte"; + else if (count <= 16) + *outPrimitiveType = "short"; + else if (count <= 32) + *outPrimitiveType = "int"; + else if (count <= 64) + *outPrimitiveType = "long"; + } + return ISSET_PRIMITIVE; + } else { + return ISSET_BITSET; + } +} + +void t_java_generator::generate_java_struct_clear(std::ofstream& out, t_struct* tstruct) { + if (!java5_) { + indent(out) << "@Override" << endl; + } + indent(out) << "public void clear() {" << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = *m_iter; + t_type* t = get_true_type(field->get_type()); + + if (field->get_value() != NULL) { + print_const_value(out, "this." + field->get_name(), t, field->get_value(), true, true); + continue; + } + + if (type_can_be_null(t)) { + + if (reuse_objects_ && (t->is_container() || t->is_struct())) { + indent(out) << "if (this." << field->get_name() << " != null) {" << endl; + indent_up(); + indent(out) << "this." << field->get_name() << ".clear();" << endl; + indent_down(); + indent(out) << "}" << endl; + + } else { + + indent(out) << "this." << field->get_name() << " = null;" << endl; + } + continue; + } + + // must be a base type + // means it also needs to be explicitly unset + indent(out) << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet") << "(false);" + << endl; + t_base_type* base_type = (t_base_type*)t; + + switch (base_type->get_base()) { + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + indent(out) << "this." << field->get_name() << " = 0;" << endl; + break; + case t_base_type::TYPE_DOUBLE: + indent(out) << "this." << field->get_name() << " = 0.0;" << endl; + break; + case t_base_type::TYPE_BOOL: + indent(out) << "this." << field->get_name() << " = false;" << endl; + break; + default: + throw "unsupported type: " + base_type->get_name() + " for field " + field->get_name(); + } + } + indent_down(); + + indent(out) << "}" << endl << endl; +} + +// generates java method to serialize (in the Java sense) the object +void t_java_generator::generate_java_struct_write_object(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) + << "private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {" + << endl; + indent(out) << " try {" << endl; + indent(out) << " write(new org.apache.thrift.protocol.TCompactProtocol(new " + "org.apache.thrift.transport.TIOStreamTransport(out)));" << endl; + indent(out) << " } catch (org.apache.thrift.TException te) {" << endl; + indent(out) << " throw new java.io.IOException(te" << (android_legacy_ ? ".getMessage()" : "") + << ");" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl << endl; +} + +// generates java method to serialize (in the Java sense) the object +void t_java_generator::generate_java_struct_read_object(ofstream& out, t_struct* tstruct) { + indent(out) << "private void readObject(java.io.ObjectInputStream in) throws " + "java.io.IOException, java.lang.ClassNotFoundException {" << endl; + indent(out) << " try {" << endl; + if (!tstruct->is_union()) { + switch (needs_isset(tstruct)) { + case ISSET_NONE: + break; + case ISSET_PRIMITIVE: + indent(out) << " // it doesn't seem like you should have to do this, but java " + "serialization is wacky, and doesn't call the default constructor." << endl; + indent(out) << " __isset_bitfield = 0;" << endl; + break; + case ISSET_BITSET: + indent(out) << " // it doesn't seem like you should have to do this, but java " + "serialization is wacky, and doesn't call the default constructor." << endl; + indent(out) << " __isset_bit_vector = new java.util.BitSet(1);" << endl; + break; + } + } + indent(out) << " read(new org.apache.thrift.protocol.TCompactProtocol(new " + "org.apache.thrift.transport.TIOStreamTransport(in)));" << endl; + indent(out) << " } catch (org.apache.thrift.TException te) {" << endl; + indent(out) << " throw new java.io.IOException(te" << (android_legacy_ ? ".getMessage()" : "") + << ");" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl << endl; +} + +void t_java_generator::generate_standard_reader(ofstream& out, t_struct* tstruct) { + out << indent() << "public void read(org.apache.thrift.protocol.TProtocol iprot, " + << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables and read struct header + out << indent() << "org.apache.thrift.protocol.TField schemeField;" << endl << indent() + << "iprot.readStructBegin();" << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "schemeField = iprot.readFieldBegin();" << endl; + + // Check for field STOP marker and break + indent(out) << "if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { " << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << "switch (schemeField.id) {" << endl; + + indent_up(); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << (*f_iter)->get_key() << ": // " + << constant_name((*f_iter)->get_name()) << endl; + indent_up(); + indent(out) << "if (schemeField.type == " << type_to_enum((*f_iter)->get_type()) << ") {" + << endl; + indent_up(); + + generate_deserialize_field(out, *f_iter, "struct.", true); + indent(out) << "struct." + << "set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet") + << "(true);" << endl; + indent_down(); + out << indent() << "} else { " << endl << indent() + << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);" << endl + << indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);" + << endl; + + indent_down(); + indent(out) << "}" << endl; + + // Read field end marker + indent(out) << "iprot.readFieldEnd();" << endl; + + indent_down(); + indent(out) << "}" << endl; + + out << indent() << "iprot.readStructEnd();" << endl; + + // in non-beans style, check for required fields of primitive type + // (which can be checked here but not in the general validate method) + if (!bean_style_) { + out << endl << indent() << "// check for required fields of primitive type, which can't be " + "checked in the validate method" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) { + out << indent() << "if (!struct." << generate_isset_check(*f_iter) << ") {" << endl + << indent() + << " throw new org.apache.thrift.protocol.TProtocolException(\"Required field '" + << (*f_iter)->get_name() + << "' was not found in serialized data! Struct: \" + toString());" << endl << indent() + << "}" << endl; + } + } + } + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "struct.validate();" << endl; + + indent_down(); + out << indent() << "}" << endl; +} + +void t_java_generator::generate_standard_writer(ofstream& out, t_struct* tstruct, bool is_result) { + indent_up(); + out << indent() << "public void write(org.apache.thrift.protocol.TProtocol oprot, " + << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl; + indent_up(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "struct.validate();" << endl << endl; + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + out << indent() << "if (struct." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + bool optional = ((*f_iter)->get_req() == t_field::T_OPTIONAL) || (is_result && !null_allowed); + if (optional) { + indent(out) << "if (" + << "struct." << generate_isset_check((*f_iter)) << ") {" << endl; + indent_up(); + } + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "struct.", true); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + if (optional) { + indent_down(); + indent(out) << "}" << endl; + } + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + } + // Write the struct map + out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();" + << endl; + + indent_down(); + out << indent() << "}" << endl << endl; + indent_down(); +} + +void t_java_generator::generate_java_struct_standard_scheme(ofstream& out, + t_struct* tstruct, + bool is_result) { + indent(out) << "private static class " << tstruct->get_name() + << "StandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {" << endl; + indent_up(); + indent(out) << "public " << tstruct->get_name() << "StandardScheme getScheme() {" << endl; + indent_up(); + indent(out) << "return new " << tstruct->get_name() << "StandardScheme();" << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + out << indent() << "private static class " << tstruct->get_name() + << "StandardScheme extends org.apache.thrift.scheme.StandardScheme<" << tstruct->get_name() << "> {" << endl << endl; + indent_up(); + generate_standard_reader(out, tstruct); + indent_down(); + out << endl; + generate_standard_writer(out, tstruct, is_result); + + out << indent() << "}" << endl << endl; +} + +void t_java_generator::generate_java_struct_tuple_reader(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "public void read(org.apache.thrift.protocol.TProtocol prot, " + << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl; + indent_up(); + indent(out) << "org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot;" << endl; + int optional_count = 0; + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + optional_count++; + } + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + generate_deserialize_field(out, (*f_iter), "struct.", false); + indent(out) << "struct.set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet") + << "(true);" << endl; + } + } + if (optional_count > 0) { + indent(out) << "java.util.BitSet incoming = iprot.readBitSet(" << optional_count << ");" << endl; + int i = 0; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + indent(out) << "if (incoming.get(" << i << ")) {" << endl; + indent_up(); + generate_deserialize_field(out, (*f_iter), "struct.", false); + indent(out) << "struct.set" << get_cap_name((*f_iter)->get_name()) << get_cap_name("isSet") + << "(true);" << endl; + indent_down(); + indent(out) << "}" << endl; + i++; + } + } + } + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_java_struct_tuple_writer(ofstream& out, t_struct* tstruct) { + indent(out) << "@Override" << endl; + indent(out) << "public void write(org.apache.thrift.protocol.TProtocol prot, " + << tstruct->get_name() << " struct) throws org.apache.thrift.TException {" << endl; + indent_up(); + indent(out) << "org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot;" << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + bool has_optional = false; + int optional_count = 0; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + optional_count++; + has_optional = true; + } + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + generate_serialize_field(out, (*f_iter), "struct.", false); + } + } + if (has_optional) { + indent(out) << "java.util.BitSet optionals = new java.util.BitSet();" << endl; + int i = 0; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + indent(out) << "if (struct." << generate_isset_check((*f_iter)) << ") {" << endl; + indent_up(); + indent(out) << "optionals.set(" << i << ");" << endl; + indent_down(); + indent(out) << "}" << endl; + i++; + } + } + + indent(out) << "oprot.writeBitSet(optionals, " << optional_count << ");" << endl; + int j = 0; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + indent(out) << "if (struct." << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + generate_serialize_field(out, (*f_iter), "struct.", false); + indent_down(); + indent(out) << "}" << endl; + j++; + } + } + } + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_java_struct_tuple_scheme(ofstream& out, t_struct* tstruct) { + indent(out) << "private static class " << tstruct->get_name() + << "TupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory {" << endl; + indent_up(); + indent(out) << "public " << tstruct->get_name() << "TupleScheme getScheme() {" << endl; + indent_up(); + indent(out) << "return new " << tstruct->get_name() << "TupleScheme();" << endl; + indent_down(); + indent(out) << "}" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + out << indent() << "private static class " << tstruct->get_name() + << "TupleScheme extends org.apache.thrift.scheme.TupleScheme<" << tstruct->get_name() << "> {" << endl << endl; + indent_up(); + generate_java_struct_tuple_writer(out, tstruct); + out << endl; + generate_java_struct_tuple_reader(out, tstruct); + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_java_generator::generate_java_scheme_lookup(ofstream& out) { + indent(out) << "private static S scheme(" + << "org.apache.thrift.protocol.TProtocol proto) {" << endl; + indent_up(); + indent(out) << "return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) " + << "? STANDARD_SCHEME_FACTORY " + << ": TUPLE_SCHEME_FACTORY" + << ").getScheme();" << endl; + indent_down(); + indent(out) << "}" << endl; +} + +void t_java_generator::generate_javax_generated_annotation(ofstream& out) { + time_t seconds = time(NULL); + struct tm* now = localtime(&seconds); + indent(out) << "@javax.annotation.Generated(value = \"" << autogen_summary() << "\""; + if (undated_generated_annotations_) { + out << ")" << endl; + } else { + indent(out) << ", date = \"" << (now->tm_year + 1900) << "-" << setfill('0') << setw(2) + << (now->tm_mon + 1) << "-" << setfill('0') << setw(2) << now->tm_mday + << "\")" << endl; + } +} + +THRIFT_REGISTER_GENERATOR( + java, + "Java", + " beans: Members will be private, and setter methods will return void.\n" + " private-members: Members will be private, but setter methods will return 'this' like " + "usual.\n" + " nocamel: Do not use CamelCase field accessors with beans.\n" + " fullcamel: Convert underscored_accessor_or_service_names to camelCase.\n" + " android: Generated structures are Parcelable.\n" + " android_legacy: Do not use java.io.IOException(throwable) (available for Android 2.3 and " + "above).\n" + " option_type: Wrap optional fields in an Option type.\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 " + "(read and write).\n" + " sorted_containers:\n" + " Use TreeSet/TreeMap instead of HashSet/HashMap as a implementation of " + "set/map.\n" + " generated_annotations=[undated|suppress]:\n" + " undated: suppress the date at @Generated annotations\n" + " suppress: suppress @Generated annotations entirely\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc new file mode 100644 index 00000000..94d66fce --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_javame_generator.cc @@ -0,0 +1,3296 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * Java code generator. + * + */ +class t_javame_generator : public t_oop_generator { +public: + t_javame_generator(t_program* program, + const std::map& parsed_options, + const std::string& option_string) + : t_oop_generator(program) { + (void)parsed_options; + (void)option_string; + std::map::const_iterator iter; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option javame:" + iter->first; + } + + out_dir_base_ = "gen-javame"; + } + + /** + * 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_union(t_struct* tunion); + void generate_xception(t_struct* txception); + void generate_service(t_service* tservice); + + void print_const_value(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval = false); + std::string render_const_value(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + /** + * Service-level generation functions + */ + + void generate_java_struct(t_struct* tstruct, bool is_exception); + + void generate_java_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool in_class = false, + bool is_result = false); + void generate_java_struct_equality(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_compare_to(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_java_validator(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_result_writer(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_tostring(std::ofstream& out, t_struct* tstruct); + void generate_java_struct_clear(std::ofstream& out, t_struct* tstruct); + void generate_field_value_meta_data(std::ofstream& out, t_type* type); + std::string get_java_type_string(t_type* type); + void generate_reflection_setters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_reflection_getters(std::ostringstream& out, + t_type* type, + std::string field_name, + std::string cap_name); + void generate_generic_field_getters_setters(std::ofstream& out, t_struct* tstruct); + void generate_java_bean_boilerplate(std::ofstream& out, t_struct* tstruct); + + void generate_function_helpers(t_function* tfunction); + std::string get_cap_name(std::string name); + std::string generate_isset_check(t_field* field); + std::string generate_isset_check(std::string field); + void generate_isset_set(ofstream& out, t_field* field); + std::string isset_field_id(t_field* field); + + void generate_primitive_service_interface(t_service* tservice); + void generate_service_interface(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_process_function(t_service* tservice, t_function* tfunction); + + void generate_java_union(t_struct* tstruct); + void generate_union_constructor(ofstream& out, t_struct* tstruct); + void generate_union_getters_and_setters(ofstream& out, t_struct* tstruct); + void generate_union_abstract_methods(ofstream& out, t_struct* tstruct); + void generate_check_type(ofstream& out, t_struct* tstruct); + void generate_read_value(ofstream& out, t_struct* tstruct); + void generate_write_value(ofstream& out, t_struct* tstruct); + void generate_get_field_desc(ofstream& out, t_struct* tstruct); + void generate_get_struct_desc(ofstream& out, t_struct* tstruct); + void generate_get_field_name(ofstream& out, t_struct* tstruct); + + void generate_union_comparisons(ofstream& out, t_struct* tstruct); + void generate_union_hashcode(ofstream& out, t_struct* tstruct); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string iter, + std::string map); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_java_doc(std::ofstream& out, t_field* field); + + void generate_java_doc(std::ofstream& out, t_doc* tdoc); + + void generate_java_doc(std::ofstream& out, t_function* tdoc); + + void generate_java_docstring_comment(std::ofstream& out, string contents); + + void generate_deep_copy_container(std::ofstream& out, + std::string source_name_p1, + std::string source_name_p2, + std::string result_name, + t_type* type); + void generate_deep_copy_non_container(std::ofstream& out, + std::string source_name, + std::string dest_name, + t_type* type); + + bool has_bit_vector(t_struct* tstruct); + + /** + * Helper rendering functions + */ + + std::string java_package(); + std::string java_type_imports(); + std::string java_thrift_imports(); + std::string type_name(t_type* ttype, + bool in_container = false, + bool in_init = false, + bool skip_generic = false); + std::string base_type_name(t_base_type* tbase, bool in_container = false); + std::string declare_field(t_field* tfield, bool init = false); + 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); + void generate_struct_desc(ofstream& out, t_struct* tstruct); + void generate_field_descs(ofstream& out, t_struct* tstruct); + std::string box_type(t_type* type, string value); + + 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() + || ttype->is_enum(); + } + + std::string constant_name(std::string name); + +private: + /** + * File streams + */ + + std::string package_name_; + std::ofstream f_service_; + std::string package_dir_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_javame_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + package_name_ = program_->get_namespace("java"); + + string dir = package_name_; + string subdir = get_out_dir(); + 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()); + } + + package_dir_ = subdir; +} + +/** + * Packages the generated file + * + * @return String of the package, i.e. "package org.apache.thriftdemo;" + */ +string t_javame_generator::java_package() { + if (!package_name_.empty()) { + return string("package ") + package_name_ + ";\n\n"; + } + return ""; +} + +/** + * Prints standard java imports + * + * @return List of imports for Java types that are used in here + */ +string t_javame_generator::java_type_imports() { + return string() + "import java.util.Hashtable;\n" + "import java.util.Vector;\n" + + "import java.util.Enumeration;\n\n"; +} + +/** + * Prints standard java imports + * + * @return List of imports necessary for thrift + */ +string t_javame_generator::java_thrift_imports() { + return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n" + + "import org.apache.thrift.transport.*;\n" + "import org.apache.thrift.protocol.*;\n\n"; +} + +/** + * Nothing in Java + */ +void t_javame_generator::close_generator() { +} + +/** + * Generates a typedef. This is not done in Java, since it does + * not support arbitrary name replacements, and it'd be a wacky waste + * of overhead to make wrapper classes. + * + * @param ttypedef The type definition + */ +void t_javame_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Enums are a class with a set of static constants. + * + * @param tenum The enumeration + */ +void t_javame_generator::generate_enum(t_enum* tenum) { + // Make output file + string f_enum_name = package_dir_ + "/" + (tenum->get_name()) + ".java"; + ofstream f_enum; + f_enum.open(f_enum_name.c_str()); + + // Comment and package it + f_enum << autogen_comment() << java_package(); + + generate_java_doc(f_enum, tenum); + indent(f_enum) << "public class " << tenum->get_name() << " implements org.apache.thrift.TEnum "; + scope_up(f_enum); + f_enum << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + generate_java_doc(f_enum, *c_iter); + indent(f_enum) << "public static final " << tenum->get_name() << " " << (*c_iter)->get_name() + << " = new " << tenum->get_name() << "(" << value << ");" << endl; + } + f_enum << endl; + + // Field for thriftCode + indent(f_enum) << "private final int value;" << endl << endl; + + indent(f_enum) << "private " << tenum->get_name() << "(int value) {" << endl; + indent(f_enum) << " this.value = value;" << endl; + indent(f_enum) << "}" << endl << endl; + + indent(f_enum) << "/**" << endl; + indent(f_enum) << " * Get the integer value of this enum value, as defined in the Thrift IDL." + << endl; + indent(f_enum) << " */" << endl; + indent(f_enum) << "public int getValue() {" << endl; + indent(f_enum) << " return value;" << endl; + indent(f_enum) << "}" << endl << endl; + + indent(f_enum) << "/**" << endl; + indent(f_enum) << " * Find a the enum type by its integer value, as defined in the Thrift IDL." + << endl; + indent(f_enum) << " * @return null if the value is not found." << endl; + indent(f_enum) << " */" << endl; + indent(f_enum) << "public static " + tenum->get_name() + " findByValue(int value) { " << endl; + + indent_up(); + + indent(f_enum) << "switch (value) {" << endl; + indent_up(); + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << "case " << value << ":" << endl; + indent(f_enum) << " return " << (*c_iter)->get_name() << ";" << endl; + } + + indent(f_enum) << "default:" << endl; + indent(f_enum) << " return null;" << endl; + + indent_down(); + + indent(f_enum) << "}" << endl; + + indent_down(); + + indent(f_enum) << "}" << endl; + + scope_down(f_enum); + + f_enum.close(); +} + +/** + * Generates a class that holds all the constants. + */ +void t_javame_generator::generate_consts(std::vector consts) { + if (consts.empty()) { + return; + } + + string f_consts_name = package_dir_ + "/" + program_name_ + "Constants.java"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + // Print header + f_consts << autogen_comment() << java_package() << java_type_imports(); + + f_consts << "public class " << program_name_ << "Constants {" << endl << endl; + indent_up(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value(), + false); + } + indent_down(); + indent(f_consts) << "}" << endl; + f_consts.close(); +} + +/** + * 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_javame_generator::print_const_value(std::ofstream& out, + string name, + t_type* type, + t_const_value* value, + bool in_static, + bool defval) { + type = get_true_type(type); + + indent(out); + if (!defval) { + out << (in_static ? "" : "public static final ") << type_name(type) << " "; + } + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + out << name << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + out << name << " = " << render_const_value(out, name, type, value) << ";" << endl << endl; + } 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; + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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, name, field_type, v_iter->second); + indent(out) << name << "."; + std::string cap_name = get_cap_name(v_iter->first->get_string()); + out << "set" << cap_name << "(" << val << ");" << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_map()) { + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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 << ".put(" << box_type(ktype, key) << ", " << box_type(vtype, val) << ");" + << endl; + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else if (type->is_list() || type->is_set()) { + out << name << " = new " << type_name(type, false, true) << "();" << endl; + if (!in_static) { + indent(out) << "static {" << endl; + indent_up(); + } + 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); + if (type->is_list()) { + indent(out) << name << ".addElement(" << box_type(etype, val) << ");" << endl; + } else { + indent(out) << name << ".put(" << box_type(etype, val) << ", " << box_type(etype, val) + << ");" << endl; + } + } + if (!in_static) { + indent_down(); + indent(out) << "}" << endl; + } + out << endl; + } else { + throw "compiler error: no const of type " + type->get_name(); + } +} + +string t_javame_generator::render_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + 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) ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + render << "(byte)" << value->get_integer(); + break; + case t_base_type::TYPE_I16: + render << "(short)" << value->get_integer(); + break; + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << "(double)" << 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_name(type, false, false) << "." << value->get_identifier(); + } else { + string t = tmp("tmp"); + print_const_value(out, t, type, value, true); + render << t; + } + + return render.str(); +} + +string t_javame_generator::box_type(t_type* type, string value) { + if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_BOOL: + return "new Boolean(" + value + ")"; + case t_base_type::TYPE_I8: + return "new Byte(" + value + ")"; + case t_base_type::TYPE_I16: + return "new Short(" + value + ")"; + case t_base_type::TYPE_I32: + return "new Integer(" + value + ")"; + case t_base_type::TYPE_I64: + return "new Long(" + value + ")"; + case t_base_type::TYPE_DOUBLE: + return "new Double(" + value + ")"; + default: + break; + } + } + return value; +} + +/** + * Generates a struct definition for a thrift data type. This will be a TBase + * implementor. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_struct(t_struct* tstruct) { + if (tstruct->is_union()) { + generate_java_union(tstruct); + } else { + generate_java_struct(tstruct, false); + } +} + +/** + * Exceptions are structs, but they inherit from Exception + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_xception(t_struct* txception) { + generate_java_struct(txception, true); +} + +/** + * Java struct definition. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct(t_struct* tstruct, bool is_exception) { + // Make output file + string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".java"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports(); + + generate_java_struct_definition(f_struct, tstruct, is_exception); + f_struct.close(); +} + +/** + * Java union definition. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_union(t_struct* tstruct) { + // Make output file + string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".java"; + ofstream f_struct; + f_struct.open(f_struct_name.c_str()); + + f_struct << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports(); + + generate_java_doc(f_struct, tstruct); + + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + + indent(f_struct) << "public " << (is_final ? "final " : "") << "class " << tstruct->get_name() + << " extends TUnion "; + + scope_up(f_struct); + + generate_struct_desc(f_struct, tstruct); + generate_field_descs(f_struct, tstruct); + + f_struct << endl; + + generate_union_constructor(f_struct, tstruct); + + f_struct << endl; + + generate_union_abstract_methods(f_struct, tstruct); + + f_struct << endl; + + generate_union_getters_and_setters(f_struct, tstruct); + + f_struct << endl; + + generate_union_comparisons(f_struct, tstruct); + + f_struct << endl; + + generate_union_hashcode(f_struct, tstruct); + + f_struct << endl; + + scope_down(f_struct); + + f_struct.close(); +} + +void t_javame_generator::generate_union_constructor(ofstream& out, t_struct* tstruct) { + indent(out) << "public " << type_name(tstruct) << "() {" << endl; + indent(out) << " super();" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public " << type_name(tstruct) << "(_Fields setField, Object value) {" << endl; + indent(out) << " super(setField, value);" << endl; + indent(out) << "}" << endl << endl; + + indent(out) << "public " << type_name(tstruct) << "(" << type_name(tstruct) << " other) {" + << endl; + indent(out) << " super(other);" << endl; + indent(out) << "}" << endl; + + indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl; + indent(out) << " return new " << tstruct->get_name() << "(this);" << endl; + indent(out) << "}" << endl << endl; + + // generate "constructors" for each field + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "public static " << type_name(tstruct) << " " << (*m_iter)->get_name() << "(" + << type_name((*m_iter)->get_type()) << " value) {" << endl; + indent(out) << " " << type_name(tstruct) << " x = new " << type_name(tstruct) << "();" << endl; + indent(out) << " x.set" << get_cap_name((*m_iter)->get_name()) << "(value);" << endl; + indent(out) << " return x;" << endl; + indent(out) << "}" << endl << endl; + } +} + +void t_javame_generator::generate_union_getters_and_setters(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (first) { + first = false; + } else { + out << endl; + } + + t_field* field = (*m_iter); + + generate_java_doc(out, field); + indent(out) << "public " << type_name(field->get_type()) << " get" + << get_cap_name(field->get_name()) << "() {" << endl; + indent(out) << " if (getSetField() == _Fields." << constant_name(field->get_name()) << ") {" + << endl; + indent(out) << " return (" << type_name(field->get_type(), true) << ")getFieldValue();" + << endl; + indent(out) << " } else {" << endl; + indent(out) << " throw new RuntimeException(\"Cannot get field '" << field->get_name() + << "' because union is currently set to \" + getFieldDesc(getSetField()).name);" + << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + + out << endl; + + generate_java_doc(out, field); + indent(out) << "public void set" << get_cap_name(field->get_name()) << "(" + << type_name(field->get_type()) << " value) {" << endl; + if (type_can_be_null(field->get_type())) { + indent(out) << " if (value == null) throw new NullPointerException();" << endl; + } + indent(out) << " setField_ = _Fields." << constant_name(field->get_name()) << ";" << endl; + indent(out) << " value_ = value;" << endl; + indent(out) << "}" << endl; + } +} + +void t_javame_generator::generate_union_abstract_methods(ofstream& out, t_struct* tstruct) { + generate_check_type(out, tstruct); + out << endl; + generate_read_value(out, tstruct); + out << endl; + generate_write_value(out, tstruct); + out << endl; + generate_get_field_desc(out, tstruct); + out << endl; + generate_get_struct_desc(out, tstruct); + out << endl; +} + +void t_javame_generator::generate_check_type(ofstream& out, t_struct* tstruct) { + indent(out) + << "protected void checkType(_Fields setField, Object value) throws ClassCastException {" + << endl; + indent_up(); + + indent(out) << "switch (setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent(out) << " if (value instanceof " << type_name(field->get_type(), true, false, true) + << ") {" << endl; + indent(out) << " break;" << endl; + indent(out) << " }" << endl; + indent(out) << " throw new ClassCastException(\"Was expecting value of type " + << type_name(field->get_type(), true, false) << " for field '" << field->get_name() + << "', but got \" + value.getClass().getSimpleName());" << endl; + // do the real check here + } + + indent(out) << "default:" << endl; + indent(out) << " throw new IllegalArgumentException(\"Unknown field id \" + setField);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_javame_generator::generate_read_value(ofstream& out, t_struct* tstruct) { + indent(out) << "protected Object readValue(TProtocol iprot, TField field) throws TException {" + << endl; + + indent_up(); + + indent(out) << "_Fields setField = _Fields.findByThriftId(field.id);" << endl; + indent(out) << "if (setField != null) {" << endl; + indent_up(); + indent(out) << "switch (setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << "if (field.type == " << constant_name(field->get_name()) << "_FIELD_DESC.type) {" + << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << ";" + << endl; + generate_deserialize_field(out, field, ""); + indent(out) << "return " << field->get_name() << ";" << endl; + indent_down(); + indent(out) << "} else {" << endl; + indent(out) << " TProtocolUtil.skip(iprot, field.type);" << endl; + indent(out) << " return null;" << endl; + indent(out) << "}" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new IllegalStateException(\"setField wasn't null, but didn't match any " + "of the case statements!\");" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "} else {" << endl; + indent_up(); + indent(out) << "TProtocolUtil.skip(iprot, field.type);" << endl; + indent(out) << "return null;" << endl; + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_javame_generator::generate_write_value(ofstream& out, t_struct* tstruct) { + indent(out) << "protected void writeValue(TProtocol oprot) throws TException {" << endl; + + indent_up(); + + indent(out) << "switch (setField_) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent_up(); + indent(out) << type_name(field->get_type(), true, false) << " " << field->get_name() << " = (" + << type_name(field->get_type(), true, false) << ")value_;" << endl; + generate_serialize_field(out, field, ""); + indent(out) << "return;" << endl; + indent_down(); + } + + indent(out) << "default:" << endl; + indent(out) << " throw new IllegalStateException(\"Cannot write union with unknown field \" + " + "setField_);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + + indent(out) << "}" << endl; +} + +void t_javame_generator::generate_get_field_desc(ofstream& out, t_struct* tstruct) { + indent(out) << "protected TField getFieldDesc(_Fields setField) {" << endl; + indent_up(); + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + indent(out) << "switch (setField) {" << endl; + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + indent(out) << "case " << constant_name(field->get_name()) << ":" << endl; + indent(out) << " return " << constant_name(field->get_name()) << "_FIELD_DESC;" << endl; + } + + indent(out) << "default:" << endl; + indent(out) << " throw new IllegalArgumentException(\"Unknown field id \" + setField);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + indent_down(); + indent(out) << "}" << endl; +} + +void t_javame_generator::generate_get_struct_desc(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "protected TStruct getStructDesc() {" << endl; + indent(out) << " return STRUCT_DESC;" << endl; + indent(out) << "}" << endl; +} + +void t_javame_generator::generate_union_comparisons(ofstream& out, t_struct* tstruct) { + // equality + indent(out) << "public boolean equals(Object other) {" << endl; + indent(out) << " if (other instanceof " << tstruct->get_name() << ") {" << endl; + indent(out) << " return equals((" << tstruct->get_name() << ")other);" << endl; + indent(out) << " } else {" << endl; + indent(out) << " return false;" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + + out << endl; + + indent(out) << "public boolean equals(" << tstruct->get_name() << " other) {" << endl; + indent(out) << " return other != null && getSetField() == other.getSetField() && " + "getFieldValue().equals(other.getFieldValue());" << endl; + indent(out) << "}" << endl; + out << endl; + + indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl; + indent(out) << " int lastComparison = TBaseHelper.compareTo(getSetField(), other.getSetField());" + << endl; + indent(out) << " if (lastComparison == 0) {" << endl; + indent(out) << " return TBaseHelper.compareTo(getFieldValue(), other.getFieldValue());" + << endl; + indent(out) << " }" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << "}" << endl; + out << endl; +} + +void t_javame_generator::generate_union_hashcode(ofstream& out, t_struct* tstruct) { + (void)tstruct; + indent(out) << "/**" << endl; + indent(out) + << " * If you'd like this to perform more respectably, use the hashcode generator option." + << endl; + indent(out) << " */" << endl; + indent(out) << "public int hashCode() {" << endl; + indent(out) << " return 0;" << endl; + indent(out) << "}" << endl; +} + +/** + * Java struct definition. This has various parameters, as it could be + * generated standalone or inside another class as a helper. If it + * is a helper than it is a static class. + * + * @param tstruct The struct definition + * @param is_exception Is this an exception? + * @param in_class If inside a class, needs to be static class + * @param is_result If this is a result it needs a different writer + */ +void t_javame_generator::generate_java_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool in_class, + bool is_result) { + generate_java_doc(out, tstruct); + + bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); + + indent(out) << "public " << (is_final ? "final " : "") << (in_class ? "static " : "") << "class " + << tstruct->get_name() << " "; + + if (is_exception) { + out << "extends Exception "; + } + out << "implements TBase "; + + scope_up(out); + + generate_struct_desc(out, tstruct); + + // Members are public for -java, private for -javabean + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + out << endl; + + generate_field_descs(out, tstruct); + + out << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "private "; + out << declare_field(*m_iter, false) << endl; + } + + // isset data + if (members.size() > 0) { + out << endl; + + indent(out) << "// isset id assignments" << endl; + + int i = 0; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null((*m_iter)->get_type())) { + indent(out) << "private static final int " << isset_field_id(*m_iter) << " = " << i << ";" + << endl; + i++; + } + } + + if (i > 0) { + indent(out) << "private boolean[] __isset_vector = new boolean[" << i << "];" << endl; + } + + out << endl; + } + + bool all_optional_members = true; + + // Default constructor + indent(out) << "public " << tstruct->get_name() << "() {" << endl; + 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) { + print_const_value(out, + "this." + (*m_iter)->get_name(), + t, + (*m_iter)->get_value(), + true, + true); + } + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + all_optional_members = false; + } + } + indent_down(); + indent(out) << "}" << endl << endl; + + if (!members.empty() && !all_optional_members) { + // Full constructor for all fields + indent(out) << "public " << tstruct->get_name() << "(" << endl; + indent_up(); + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + if (!first) { + out << "," << endl; + } + first = false; + indent(out) << type_name((*m_iter)->get_type()) << " " << (*m_iter)->get_name(); + } + } + out << ")" << endl; + indent_down(); + indent(out) << "{" << endl; + indent_up(); + indent(out) << "this();" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_OPTIONAL) { + indent(out) << "this." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << ";" + << endl; + generate_isset_set(out, (*m_iter)); + } + } + indent_down(); + indent(out) << "}" << endl << endl; + } + + // copy constructor + indent(out) << "/**" << endl; + indent(out) << " * Performs a deep copy on other." << endl; + indent(out) << " */" << endl; + indent(out) << "public " << tstruct->get_name() << "(" << tstruct->get_name() << " other) {" + << endl; + indent_up(); + + if (has_bit_vector(tstruct)) { + indent(out) << "System.arraycopy(other.__isset_vector, 0, __isset_vector, 0, " + "other.__isset_vector.length);" << endl; + } + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = (*m_iter); + std::string field_name = field->get_name(); + t_type* type = field->get_type(); + bool can_be_null = type_can_be_null(type); + + if (can_be_null) { + indent(out) << "if (other." << generate_isset_check(field) << ") {" << endl; + indent_up(); + } + + if (type->is_container()) { + generate_deep_copy_container(out, "other", field_name, "__this__" + field_name, type); + indent(out) << "this." << field_name << " = __this__" << field_name << ";" << endl; + } else { + indent(out) << "this." << field_name << " = "; + generate_deep_copy_non_container(out, "other." + field_name, field_name, type); + out << ";" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; + + // clone method, so that you can deep copy an object when you don't know its class. + indent(out) << "public " << tstruct->get_name() << " deepCopy() {" << endl; + indent(out) << " return new " << tstruct->get_name() << "(this);" << endl; + indent(out) << "}" << endl << endl; + + generate_java_struct_clear(out, tstruct); + + generate_java_bean_boilerplate(out, tstruct); + generate_generic_field_getters_setters(out, tstruct); + + generate_java_struct_equality(out, tstruct); + generate_java_struct_compare_to(out, tstruct); + + generate_java_struct_reader(out, tstruct); + if (is_result) { + generate_java_struct_result_writer(out, tstruct); + } else { + generate_java_struct_writer(out, tstruct); + } + generate_java_struct_tostring(out, tstruct); + generate_java_validator(out, tstruct); + scope_down(out); + out << endl; +} + +/** + * Generates equals methods and a hashCode method for a structure. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct_equality(ofstream& out, t_struct* tstruct) { + out << indent() << "public boolean equals(Object that) {" << endl; + indent_up(); + out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl + << indent() << "if (that instanceof " << tstruct->get_name() << ")" << endl << indent() + << " return this.equals((" << tstruct->get_name() << ")that);" << endl << indent() + << "return false;" << endl; + scope_down(out); + out << endl; + + out << indent() << "public boolean equals(" << tstruct->get_name() << " that) {" << endl; + indent_up(); + out << indent() << "if (that == null)" << endl << indent() << " return false;" << endl + << indent() << "if (this == that)" << endl << indent() << " return true;" << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << endl; + + t_type* t = get_true_type((*m_iter)->get_type()); + // Most existing Thrift code does not use isset or optional/required, + // so we treat "default" fields as required. + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + bool can_be_null = type_can_be_null(t); + string name = (*m_iter)->get_name(); + + string this_present = "true"; + string that_present = "true"; + string unequal; + + if (is_optional || can_be_null) { + this_present += " && this." + generate_isset_check(*m_iter); + that_present += " && that." + generate_isset_check(*m_iter); + } + + out << indent() << "boolean this_present_" << name << " = " << this_present << ";" << endl + << indent() << "boolean that_present_" << name << " = " << that_present << ";" << endl + << indent() << "if (" + << "this_present_" << name << " || that_present_" << name << ") {" << endl; + indent_up(); + out << indent() << "if (!(" + << "this_present_" << name << " && that_present_" << name << "))" << endl << indent() + << " return false;" << endl; + + if (t->is_base_type() && ((t_base_type*)t)->is_binary()) { + unequal = "TBaseHelper.compareTo(this." + name + ", that." + name + ") != 0"; + } else if (can_be_null) { + unequal = "!this." + name + ".equals(that." + name + ")"; + } else { + unequal = "this." + name + " != that." + name; + } + + out << indent() << "if (" << unequal << ")" << endl << indent() << " return false;" << endl; + + scope_down(out); + } + out << endl; + indent(out) << "return true;" << endl; + scope_down(out); + out << endl; + + out << indent() << "public int hashCode() {" << endl; + indent_up(); + indent(out) << "return 0;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_javame_generator::generate_java_struct_compare_to(ofstream& out, t_struct* tstruct) { + indent(out) << "public int compareTo(Object otherObject) {" << endl; + // indent(out) << "public int compareTo(" << type_name(tstruct) << " other) {" << endl; + indent_up(); + + indent(out) << "if (!getClass().equals(otherObject.getClass())) {" << endl; + indent(out) << " return getClass().getName().compareTo(otherObject.getClass().getName());" + << endl; + indent(out) << "}" << endl; + out << endl; + indent(out) << type_name(tstruct) << " other = (" << type_name(tstruct) << ")otherObject;"; + + indent(out) << "int lastComparison = 0;" << endl; + out << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_field* field = *m_iter; + indent(out) << "lastComparison = TBaseHelper.compareTo(" << generate_isset_check(field) + << ", other." << generate_isset_check(field) << ");" << endl; + indent(out) << "if (lastComparison != 0) {" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << "}" << endl; + + indent(out) << "if (" << generate_isset_check(field) << ") {" << endl; + if (field->get_type()->is_struct() || field->get_type()->is_xception()) { + indent(out) << " lastComparison = this." << field->get_name() << ".compareTo(other." + << field->get_name() << ");" << endl; + } else { + indent(out) << " lastComparison = TBaseHelper.compareTo(this." << field->get_name() + << ", other." << field->get_name() << ");" << endl; + } + + indent(out) << " if (lastComparison != 0) {" << endl; + indent(out) << " return lastComparison;" << endl; + indent(out) << " }" << endl; + indent(out) << "}" << endl; + } + + indent(out) << "return 0;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to read all the fields of the struct. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct_reader(ofstream& out, t_struct* tstruct) { + out << indent() << "public void read(TProtocol iprot) throws TException {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // Declare stack tmp variables and read struct header + out << indent() << "TField field;" << endl << indent() << "iprot.readStructBegin();" << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "field = iprot.readFieldBegin();" << endl; + + // Check for field STOP marker and break + indent(out) << "if (field.type == TType.STOP) { " << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << "switch (field.id) {" << endl; + + indent_up(); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << (*f_iter)->get_key() << ": // " + << constant_name((*f_iter)->get_name()) << endl; + indent_up(); + indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + generate_deserialize_field(out, *f_iter, "this."); + generate_isset_set(out, *f_iter); + 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(out) << " TProtocolUtil.skip(iprot, field.type);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + // Read field end marker + indent(out) << "iprot.readFieldEnd();" << endl; + + indent_down(); + indent(out) << "}" << endl; + + out << indent() << "iprot.readStructEnd();" << endl; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +// generates java method to perform various checks +// (e.g. check that all required fields are set) +void t_javame_generator::generate_java_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "public void validate() throws TException {" << 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) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + out << indent() << "if (!" << generate_isset_check(*f_iter) << ") {" << endl << indent() + << " throw new TProtocolException(\"Required field '" << (*f_iter)->get_name() + << "' is unset! Struct:\" + toString());" << endl << indent() << "}" << endl << endl; + } + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates a function to write all the fields of the struct + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public void write(TProtocol oprot) throws TException {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + // performs various checks (e.g. check that all required fields are set) + indent(out) << "validate();" << endl << endl; + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool null_allowed = type_can_be_null((*f_iter)->get_type()); + if (null_allowed) { + out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl; + indent_up(); + } + bool optional = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (optional) { + indent(out) << "if (" << generate_isset_check((*f_iter)) << ") {" << endl; + indent_up(); + } + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + if (optional) { + indent_down(); + indent(out) << "}" << endl; + } + if (null_allowed) { + indent_down(); + indent(out) << "}" << endl; + } + } + // Write the struct map + out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();" + << 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 in the Isset array, and only one of them + * can be set at a time. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct_result_writer(ofstream& out, t_struct* tstruct) { + out << indent() << "public void write(TProtocol oprot) throws TException {" << endl; + indent_up(); + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << 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 << "(this." << generate_isset_check(*f_iter) << ") {" << endl; + + indent_up(); + + indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) + << "_FIELD_DESC);" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}"; + } + // Write the struct map + out << endl << indent() << "oprot.writeFieldStop();" << endl << indent() + << "oprot.writeStructEnd();" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_javame_generator::generate_reflection_getters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + indent(out) << "case " << constant_name(field_name) << ":" << endl; + indent_up(); + + if (type->is_base_type() && !type->is_string()) { + t_base_type* base_type = (t_base_type*)type; + + indent(out) << "return new " << type_name(type, true, false) << "(" + << (base_type->is_bool() ? "is" : "get") << cap_name << "());" << endl << endl; + } else { + indent(out) << "return get" << cap_name << "();" << endl << endl; + } + + indent_down(); +} + +void t_javame_generator::generate_reflection_setters(ostringstream& out, + t_type* type, + string field_name, + string cap_name) { + indent(out) << "case " << constant_name(field_name) << ":" << endl; + indent_up(); + indent(out) << "if (value == null) {" << endl; + indent(out) << " unset" << get_cap_name(field_name) << "();" << endl; + indent(out) << "} else {" << endl; + indent(out) << " set" << cap_name << "((" << type_name(type, true, false) << ")value);" << endl; + indent(out) << "}" << endl; + indent(out) << "break;" << endl << endl; + + indent_down(); +} + +void t_javame_generator::generate_generic_field_getters_setters(std::ofstream& out, + t_struct* tstruct) { + (void)out; + 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; + t_type* type = get_true_type(field->get_type()); + std::string field_name = field->get_name(); + std::string cap_name = get_cap_name(field_name); + + indent_up(); + generate_reflection_setters(setter_stream, type, field_name, cap_name); + generate_reflection_getters(getter_stream, type, field_name, cap_name); + indent_down(); + } +} + +/** + * Generates a set of Java Bean boilerplate functions (setters, getters, etc.) + * for the given struct. + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_bean_boilerplate(ofstream& out, t_struct* tstruct) { + 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 = get_cap_name(field_name); + + if (type->is_container()) { + // Method to return the size of the collection + indent(out) << "public int get" << cap_name; + out << get_cap_name("size() {") << endl; + + indent_up(); + indent(out) << "return (this." << field_name << " == null) ? 0 : " + << "this." << field_name << ".size();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + + if (type->is_set() || type->is_list()) { + + t_type* element_type; + if (type->is_set()) { + element_type = ((t_set*)type)->get_elem_type(); + } else { + element_type = ((t_list*)type)->get_elem_type(); + } + + // Iterator getter for sets and lists + indent(out) << "public Enumeration get" << cap_name; + out << get_cap_name("Enumeration() {") << endl; + + indent_up(); + indent(out) << "return (this." << field_name << " == null) ? null : " + << "this." << field_name << ".elements();" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + // Add to set or list, create if the set/list is null + indent(out); + out << "public void add" << get_cap_name("to"); + out << cap_name << "(" << type_name(element_type) << " elem) {" << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();" + << endl; + indent_down(); + indent(out) << "}" << endl; + if (type->is_set()) { + indent(out) << "this." << field_name << ".put(" << box_type(element_type, "elem") << ", " + << box_type(element_type, "elem") << ");" << endl; + } else { + indent(out) << "this." << field_name << ".addElement(" << box_type(element_type, "elem") + << ");" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + } else if (type->is_map()) { + // Put to map + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + + indent(out); + out << "public void putTo" << cap_name << "(" << type_name(key_type, true) << " key, " + << type_name(val_type, true) << " val) {" << endl; + + indent_up(); + indent(out) << "if (this." << field_name << " == null) {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = new " << type_name(type, false, true) << "();" + << endl; + indent_down(); + indent(out) << "}" << endl; + indent(out) << "this." << field_name << ".put(key, val);" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + } + + // Simple getter + generate_java_doc(out, field); + indent(out) << "public " << type_name(type); + if (type->is_base_type() && ((t_base_type*)type)->get_base() == t_base_type::TYPE_BOOL) { + out << " is"; + } else { + out << " get"; + } + out << cap_name << "() {" << endl; + indent_up(); + indent(out) << "return this." << field_name << ";" << endl; + indent_down(); + indent(out) << "}" << endl << endl; + + // Simple setter + generate_java_doc(out, field); + indent(out) << "public "; + out << "void"; + out << " set" << cap_name << "(" << type_name(type) << " " << field_name << ") {" << endl; + indent_up(); + indent(out) << "this." << field_name << " = " << field_name << ";" << endl; + generate_isset_set(out, field); + + indent_down(); + indent(out) << "}" << endl << endl; + + // Unsetter + indent(out) << "public void unset" << cap_name << "() {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "this." << field_name << " = null;" << endl; + } else { + indent(out) << "__isset_vector[" << isset_field_id(field) << "] = false;" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + // isSet method + indent(out) << "/** Returns true if field " << field_name + << " is set (has been assigned a value) and false otherwise */" << endl; + indent(out) << "public boolean is" << get_cap_name("set") << cap_name << "() {" << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "return this." << field_name << " != null;" << endl; + } else { + indent(out) << "return __isset_vector[" << isset_field_id(field) << "];" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + + indent(out) << "public void set" << cap_name << get_cap_name("isSet") << "(boolean value) {" + << endl; + indent_up(); + if (type_can_be_null(type)) { + indent(out) << "if (!value) {" << endl; + indent(out) << " this." << field_name << " = null;" << endl; + indent(out) << "}" << endl; + } else { + indent(out) << "__isset_vector[" << isset_field_id(field) << "] = value;" << endl; + } + indent_down(); + indent(out) << "}" << endl << endl; + } +} + +/** + * Generates a toString() method for the given struct + * + * @param tstruct The struct definition + */ +void t_javame_generator::generate_java_struct_tostring(ofstream& out, t_struct* tstruct) { + out << indent() << "public String toString() {" << endl; + indent_up(); + + out << indent() << "StringBuffer sb = new StringBuffer(\"" << tstruct->get_name() << "(\");" + << endl; + out << indent() << "boolean first = true;" << endl << 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) { + bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; + if (could_be_unset) { + indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; + indent_up(); + } + + t_field* field = (*f_iter); + + if (!first) { + indent(out) << "if (!first) sb.append(\", \");" << endl; + } + indent(out) << "sb.append(\"" << (*f_iter)->get_name() << ":\");" << endl; + bool can_be_null = type_can_be_null(field->get_type()); + if (can_be_null) { + indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl; + indent(out) << " sb.append(\"null\");" << endl; + indent(out) << "} else {" << endl; + indent_up(); + } + + if (field->get_type()->is_base_type() && ((t_base_type*)(field->get_type()))->is_binary()) { + indent(out) << "TBaseHelper.toString(this." << field->get_name() << ", sb);" << endl; + } else { + indent(out) << "sb.append(this." << (*f_iter)->get_name() << ");" << endl; + } + + if (can_be_null) { + indent_down(); + indent(out) << "}" << endl; + } + indent(out) << "first = false;" << endl; + + if (could_be_unset) { + indent_down(); + indent(out) << "}" << endl; + } + first = false; + } + out << indent() << "sb.append(\")\");" << endl << indent() << "return sb.toString();" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Returns a string with the java representation of the given thrift type + * (e.g. for the type struct it returns "TType.STRUCT") + */ +std::string t_javame_generator::get_java_type_string(t_type* type) { + if (type->is_list()) { + return "TType.LIST"; + } else if (type->is_map()) { + return "TType.MAP"; + } else if (type->is_set()) { + return "TType.SET"; + } else if (type->is_struct() || type->is_xception()) { + return "TType.STRUCT"; + } else if (type->is_enum()) { + return "TType.ENUM"; + } else if (type->is_typedef()) { + return get_java_type_string(((t_typedef*)type)->get_type()); + } else if (type->is_base_type()) { + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_VOID: + return "TType.VOID"; + break; + case t_base_type::TYPE_STRING: + return "TType.STRING"; + break; + case t_base_type::TYPE_BOOL: + return "TType.BOOL"; + break; + case t_base_type::TYPE_I8: + return "TType.BYTE"; + break; + case t_base_type::TYPE_I16: + return "TType.I16"; + break; + case t_base_type::TYPE_I32: + return "TType.I32"; + break; + case t_base_type::TYPE_I64: + return "TType.I64"; + break; + case t_base_type::TYPE_DOUBLE: + return "TType.DOUBLE"; + break; + default: + throw std::runtime_error("Unknown thrift type \"" + type->get_name() + + "\" passed to t_javame_generator::get_java_type_string!"); + break; // This should never happen! + } + } else { + throw std::runtime_error( + "Unknown thrift type \"" + type->get_name() + + "\" passed to t_javame_generator::get_java_type_string!"); // This should never happen! + } +} + +void t_javame_generator::generate_field_value_meta_data(std::ofstream& out, t_type* type) { + out << endl; + indent_up(); + indent_up(); + if (type->is_struct() || type->is_xception()) { + indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type) << ".class"; + } else if (type->is_container()) { + if (type->is_list()) { + indent(out) << "new ListMetaData(TType.LIST, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else if (type->is_set()) { + indent(out) << "new SetMetaData(TType.SET, "; + t_type* elem_type = ((t_list*)type)->get_elem_type(); + generate_field_value_meta_data(out, elem_type); + } else { // map + indent(out) << "new MapMetaData(TType.MAP, "; + t_type* key_type = ((t_map*)type)->get_key_type(); + t_type* val_type = ((t_map*)type)->get_val_type(); + generate_field_value_meta_data(out, key_type); + out << ", "; + generate_field_value_meta_data(out, val_type); + } + } else if (type->is_enum()) { + indent(out) << "new EnumMetaData(TType.ENUM, " << type_name(type) << ".class"; + } else { + indent(out) << "new FieldValueMetaData(" << get_java_type_string(type); + if (type->is_typedef()) { + indent(out) << ", \"" << ((t_typedef*)type)->get_symbolic() << "\""; + } + } + out << ")"; + indent_down(); + indent_down(); +} + +/** + * Generates a thrift service. In C++, this comprises an entirely separate + * header and source file. The header file defines the methods and includes + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_javame_generator::generate_service(t_service* tservice) { + // Make output file + string f_service_name = package_dir_ + "/" + service_name_ + ".java"; + f_service_.open(f_service_name.c_str()); + + f_service_ << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports(); + + f_service_ << "public class " << service_name_ << " {" << endl << endl; + indent_up(); + + // Generate the three main parts of the service + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + + indent_down(); + f_service_ << "}" << endl; + f_service_.close(); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_javame_generator::generate_primitive_service_interface(t_service* tservice) { + f_service_ << indent() << "public interface Iface extends " << service_name_ << "Iface { }" + << endl << endl; + + string f_interface_name = package_dir_ + "/" + service_name_ + "Iface.java"; + std::ofstream f_iface; + f_iface.open(f_interface_name.c_str()); + + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends_iface = " extends " + type_name(tservice->get_extends()) + "Iface"; + } + + f_iface << autogen_comment() << java_package() << java_type_imports() << java_thrift_imports(); + generate_java_doc(f_iface, tservice); + f_iface << "public interface " << service_name_ << "Iface" << extends_iface << " {" << 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_iface, *f_iter); + f_iface << " public " << function_signature(*f_iter) << ";" << endl << endl; + } + f_iface << "}" << endl << endl; +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_javame_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_iface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_iface = " extends " + extends + ".Iface"; + } + + generate_java_doc(f_service_, tservice); + f_service_ << indent() << "public interface Iface" << extends_iface << " {" << endl << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_java_doc(f_service_, *f_iter); + indent(f_service_) << "public " << function_signature(*f_iter) << ";" << endl << endl; + } + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Generates structs for all the service args and return types + * + * @param tservice The service + */ +void t_javame_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_java_struct_definition(f_service_, ts, false, true); + generate_function_helpers(*f_iter); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_javame_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 " + extends + ".Client"; + } + + indent(f_service_) << "public static class Client" << extends_client + << " implements TServiceClient, Iface {" << endl; + indent_up(); + + indent(f_service_) << "public Client(TProtocol prot)" << endl; + scope_up(f_service_); + indent(f_service_) << "this(prot, prot);" << endl; + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << "public Client(TProtocol iprot, TProtocol oprot)" << endl; + scope_up(f_service_); + if (extends.empty()) { + f_service_ << indent() << "iprot_ = iprot;" << endl << indent() << "oprot_ = oprot;" << endl; + } else { + f_service_ << indent() << "super(iprot, oprot);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + if (extends.empty()) { + f_service_ << indent() << "protected TProtocol iprot_;" << endl << indent() + << "protected TProtocol oprot_;" << endl << endl << indent() + << "protected int seqid_;" << endl << endl; + + indent(f_service_) << "public TProtocol getInputProtocol()" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.iprot_;" << endl; + scope_down(f_service_); + f_service_ << endl; + + indent(f_service_) << "public TProtocol getOutputProtocol()" << endl; + scope_up(f_service_); + indent(f_service_) << "return this.oprot_;" << endl; + scope_down(f_service_); + f_service_ << 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) { + string funname = (*f_iter)->get_name(); + + // Open function + indent(f_service_) << "public " << function_signature(*f_iter) << endl; + scope_up(f_service_); + indent(f_service_) << "send_" << funname << "("; + + // Get the struct of function call params + t_struct* arg_struct = (*f_iter)->get_arglist(); + + // Declare the function arguments + 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) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << (*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; + } + scope_down(f_service_); + f_service_ << endl; + + t_function send_function(g_type_void, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + + string argsname = (*f_iter)->get_name() + "_args"; + + // Open function + indent(f_service_) << "public " << function_signature(&send_function) << endl; + scope_up(f_service_); + + // Serialize the request + 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.set" << get_cap_name((*fld_iter)->get_name()) << "(" + << (*fld_iter)->get_name() << ");" << endl; + } + + f_service_ << indent() << "args.write(oprot_);" << endl << indent() + << "oprot_.writeMessageEnd();" << endl << indent() + << "oprot_.getTransport().flush();" << endl; + + scope_down(f_service_); + 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()); + // Open function + indent(f_service_) << "public " << function_signature(&recv_function) << endl; + scope_up(f_service_); + + f_service_ << indent() << "TMessage msg = iprot_.readMessageBegin();" << endl << indent() + << "if (msg.type == TMessageType.EXCEPTION) {" << endl << indent() + << " TApplicationException x = TApplicationException.read(iprot_);" << endl + << indent() << " iprot_.readMessageEnd();" << endl << indent() << " throw x;" + << endl << indent() << "}" << endl << indent() << "if (msg.seqid != seqid_) {" + << endl << indent() + << " throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID, \"" + << (*f_iter)->get_name() << " failed: out of sequence response\");" << endl + << indent() << "}" << endl << indent() << resultname << " result = new " + << resultname << "();" << endl << indent() << "result.read(iprot_);" << endl + << indent() << "iprot_.readMessageEnd();" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl + << indent() << " return result.success;" << endl << indent() << "}" << endl; + } + + 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) { + f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl + << indent() << " throw result." << (*x_iter)->get_name() << ";" << endl + << indent() << "}" << endl; + } + + // If you get here it's an exception, unless a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "return;" << endl; + } else { + f_service_ << indent() + << "throw new TApplicationException(TApplicationException.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\");" << endl; + } + + // Close function + scope_down(f_service_); + f_service_ << endl; + } + } + + indent_down(); + indent(f_service_) << "}" << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_javame_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Extends stuff + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_processor = " extends " + extends + ".Processor"; + } + + // Generate the header portion + indent(f_service_) << "public static class Processor" << extends_processor + << " implements TProcessor {" << endl; + indent_up(); + + indent(f_service_) << "public Processor(Iface iface)" << endl; + scope_up(f_service_); + if (!extends.empty()) { + f_service_ << indent() << "super(iface);" << endl; + } + f_service_ << indent() << "iface_ = iface;" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "processMap_.put(\"" << (*f_iter)->get_name() << "\", new " + << (*f_iter)->get_name() << "());" << endl; + } + + scope_down(f_service_); + f_service_ << endl; + + if (extends.empty()) { + f_service_ + << indent() << "protected static interface ProcessFunction {" << endl << indent() + << " public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException;" + << endl << indent() << "}" << endl << endl; + } + + f_service_ << indent() << "private Iface iface_;" << endl; + + if (extends.empty()) { + f_service_ << indent() << "protected final Hashtable processMap_ = new Hashtable();" << endl; + } + + f_service_ << endl; + + // Generate the server implementation + indent(f_service_) << "public boolean process(TProtocol iprot, TProtocol oprot) throws TException" + << endl; + scope_up(f_service_); + + f_service_ << indent() << "TMessage msg = iprot.readMessageBegin();" << endl; + + // TODO(mcslee): validate message, was the seqid etc. legit? + + f_service_ + << indent() << "ProcessFunction fn = (ProcessFunction)processMap_.get(msg.name);" << endl + << indent() << "if (fn == null) {" << endl << indent() + << " TProtocolUtil.skip(iprot, TType.STRUCT);" << endl << indent() + << " iprot.readMessageEnd();" << endl << indent() + << " TApplicationException x = new " + "TApplicationException(TApplicationException.UNKNOWN_METHOD, \"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.getTransport().flush();" << endl << indent() + << " return true;" << endl << indent() << "}" << endl << indent() + << "fn.process(msg.seqid, iprot, oprot);" << endl; + + f_service_ << indent() << "return true;" << endl; + + scope_down(f_service_); + f_service_ << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent_down(); + indent(f_service_) << "}" << endl << endl; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_javame_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_java_struct_definition(f_service_, &result, false, true, true); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_javame_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open class + indent(f_service_) << "private class " << tfunction->get_name() << " implements ProcessFunction {" + << endl; + indent_up(); + + // Open function + indent(f_service_) + << "public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException" + << 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() + << "try {" << endl; + indent_up(); + f_service_ << indent() << "args.read(iprot);" << endl; + indent_down(); + f_service_ << indent() << "} catch (TProtocolException e) {" << endl; + indent_up(); + f_service_ << indent() << "iprot.readMessageEnd();" << endl << indent() + << "TApplicationException x = new " + "TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());" + << endl << indent() << "oprot.writeMessageBegin(new TMessage(\"" + << tfunction->get_name() << "\", TMessageType.EXCEPTION, seqid));" << endl << indent() + << "x.write(oprot);" << endl << indent() << "oprot.writeMessageEnd();" << endl + << indent() << "oprot.getTransport().flush();" << endl << indent() << "return;" + << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + f_service_ << indent() << "iprot.readMessageEnd();" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << resultname << " result = new " << resultname << "();" << endl; + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_ << indent() << "try {" << endl; + indent_up(); + } + + // Generate the function call + 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_." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + // Set isset on success field + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void() + && !type_can_be_null(tfunction->get_returntype())) { + f_service_ << indent() << "result.set" << get_cap_name("success") << get_cap_name("isSet") + << "(true);" << endl; + } + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + f_service_ << indent() << "}"; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << " catch (" << type_name((*x_iter)->get_type(), false, false) << " " + << (*x_iter)->get_name() << ") {" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << ";" << endl; + indent_down(); + f_service_ << indent() << "}"; + } else { + f_service_ << "}"; + } + } + f_service_ << " catch (Throwable th) {" << endl; + indent_up(); + f_service_ << indent() << "TApplicationException x = new " + "TApplicationException(TApplicationException.INTERNAL_ERROR, " + "\"Internal error processing " << tfunction->get_name() << "\");" + << endl << indent() << "oprot.writeMessageBegin(new TMessage(\"" + << tfunction->get_name() << "\", TMessageType.EXCEPTION, seqid));" << endl + << indent() << "x.write(oprot);" << endl << indent() << "oprot.writeMessageEnd();" + << endl << indent() << "oprot.getTransport().flush();" << endl << indent() + << "return;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "return;" << endl; + scope_down(f_service_); + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; + return; + } + + f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);" + << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() + << "oprot.getTransport().flush();" << endl; + + // Close function + scope_down(f_service_); + f_service_ << endl; + + // Close class + indent_down(); + f_service_ << indent() << "}" << endl << endl; +} + +/** + * Deserializes a field of any type. + * + * @param tfield The field + * @param prefix The variable name or container for this field + */ +void t_javame_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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()) { + indent(out) << name << " = iprot."; + + 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 (!((t_base_type*)type)->is_binary()) { + out << "readString();"; + } else { + out << "readBinary();"; + } + 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 Java name for base type " + t_base_type::t_base_name(tbase); + } + out << endl; + } else if (type->is_enum()) { + indent(out) << name << " = " + << type_name(tfield->get_type(), true, false) + ".findByValue(iprot.readI32());" + << 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, invokes read() + */ +void t_javame_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix) { + out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() + << prefix << ".read(iprot);" << endl; +} + +/** + * Deserializes a container by reading its size and then iterating + */ +void t_javame_generator::generate_deserialize_container(ofstream& 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"); + } + + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "TMap " << obj << " = iprot.readMapBegin();" << endl; + } else if (ttype->is_set()) { + indent(out) << "TSet " << obj << " = iprot.readSetBegin();" << endl; + } else if (ttype->is_list()) { + indent(out) << "TList " << obj << " = iprot.readListBegin();" << endl; + } + + indent(out) << prefix << " = new " << type_name(ttype, false, true) + // size the collection correctly + << "(" << (ttype->is_list() ? "" : "2*") << obj << ".size" + << ");" << endl; + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (int " << i << " = 0; " << i << " < " << obj << ".size" + << "; " + << "++" << 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); + + // Read container end + 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); +} + +/** + * Generates code to deserialize a map + */ +void t_javame_generator::generate_deserialize_map_element(ofstream& 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 << ".put(" << box_type(tmap->get_key_type(), key) << ", " + << box_type(tmap->get_val_type(), val) << ");" << endl; +} + +/** + * Deserializes a set element + */ +void t_javame_generator::generate_deserialize_set_element(ofstream& 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 << ".put(" << box_type(tset->get_elem_type(), elem) << ", " + << box_type(tset->get_elem_type(), elem) << ");" << endl; +} + +/** + * Deserializes a list element + */ +void t_javame_generator::generate_deserialize_list_element(ofstream& 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 << ".addElement(" << box_type(tlist->get_elem_type(), elem) << ");" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_javame_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_enum()) { + indent(out) << "oprot.writeI32(" << prefix + tfield->get_name() << ".getValue());" << endl; + } else if (type->is_base_type()) { + string name = prefix + tfield->get_name(); + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ");"; + } else { + out << "writeString(" << name << ");"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ");"; + break; + default: + throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ");"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type_name(type).c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_javame_generator::generate_serialize_struct(ofstream& out, + t_struct* tstruct, + string prefix) { + (void)tstruct; + out << indent() << prefix << ".write(oprot);" << endl; +} + +/** + * Serializes a container by writing its size then the elements. + * + * @param ttype The type of container + * @param prefix String prefix for fields + */ +void t_javame_generator::generate_serialize_container(ofstream& 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 + << ".size()));" << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " << prefix << ".size()));" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(new TList(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".size()));" + << endl; + } + + string iter = tmp("_iter"); + if (ttype->is_map()) { + string enumer = iter + "_enum"; + string key_type = type_name(((t_map*)ttype)->get_key_type(), true, false); + indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".keys(); " << enumer + << ".hasMoreElements(); ) "; + scope_up(out); + indent(out) << key_type << " " << iter << " = (" << key_type << ")" << enumer + << ".nextElement();" << endl; + } else if (ttype->is_set()) { + string enumer = iter + "_enum"; + string ele_type = type_name(((t_list*)ttype)->get_elem_type(), true); + indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".keys(); " << enumer + << ".hasMoreElements(); ) "; + scope_up(out); + indent(out) << ele_type << " " << iter << " = (" << ele_type << ")" << enumer + << ".nextElement();" << endl; + } else if (ttype->is_list()) { + string enumer = iter + "_enum"; + indent(out) << "for (Enumeration " << enumer << " = " << prefix << ".elements(); " << enumer + << ".hasMoreElements(); ) "; + scope_up(out); + string ele_type = type_name(((t_list*)ttype)->get_elem_type(), true); + indent(out) << ele_type << " " << iter << " = (" << ele_type << ")" << enumer + << ".nextElement();" << endl; + } + + 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); +} + +/** + * Serializes the members of a map. + */ +void t_javame_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string iter, + string map) { + t_field kfield(tmap->get_key_type(), iter); + generate_serialize_field(out, &kfield, ""); + string val_type = type_name(tmap->get_val_type(), true, false); + t_field vfield(tmap->get_val_type(), "((" + val_type + ")" + map + ".get(" + iter + "))"); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_javame_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_javame_generator::generate_serialize_list_element(ofstream& out, + t_list* tlist, + string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Returns a Java type name + * + * @param ttype The type + * @param container Is the type going inside a container? + * @return Java type name, i.e. Vector + */ +string t_javame_generator::type_name(t_type* ttype, + bool in_container, + bool in_init, + bool skip_generic) { + (void)in_init; + (void)skip_generic; + // In Java typedefs are just resolved to their real type + ttype = get_true_type(ttype); + string prefix; + + if (ttype->is_base_type()) { + return base_type_name((t_base_type*)ttype, in_container); + } else if (ttype->is_map()) { + return "Hashtable"; + } else if (ttype->is_set()) { + return "Hashtable"; + } else if (ttype->is_list()) { + return "Vector"; + } + + // Check for namespacing + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + string package = program->get_namespace("java"); + if (!package.empty()) { + return package + "." + ttype->get_name(); + } + } + + return ttype->get_name(); +} + +/** + * Returns the C++ type that corresponds to the thrift type. + * + * @param tbase The base type + * @param container Is it going in a Java container? + */ +string t_javame_generator::base_type_name(t_base_type* type, bool in_container) { + 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 "String"; + } else { + return "byte[]"; + } + case t_base_type::TYPE_BOOL: + return (in_container ? "Boolean" : "boolean"); + case t_base_type::TYPE_I8: + return (in_container ? "Byte" : "byte"); + case t_base_type::TYPE_I16: + return (in_container ? "Short" : "short"); + case t_base_type::TYPE_I32: + return (in_container ? "Integer" : "int"); + case t_base_type::TYPE_I64: + return (in_container ? "Long" : "long"); + case t_base_type::TYPE_DOUBLE: + return (in_container ? "Double" : "double"); + default: + throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_javame_generator::declare_field(t_field* tfield, bool init) { + // TODO(mcslee): do we ever need to initialize the field? + 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) { + 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 += " = 0"; + } else if (ttype->is_container()) { + result += " = new " + type_name(ttype, false, true) + "()"; + } else { + result += " = new " + type_name(ttype, false, true) + "()"; + ; + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_javame_generator::function_signature(t_function* tfunction, string prefix) { + t_type* ttype = tfunction->get_returntype(); + std::string result = type_name(ttype) + " " + prefix + tfunction->get_name() + "(" + + argument_list(tfunction->get_arglist()) + ") throws "; + 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) { + result += type_name((*x_iter)->get_type(), false, false) + ", "; + } + result += "TException"; + return result; +} + +/** + * Renders a comma separated field list, with type names + */ +string t_javame_generator::argument_list(t_struct* tstruct, bool include_types) { + 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 += ", "; + } + if (include_types) { + result += type_name((*f_iter)->get_type()) + " "; + } + result += (*f_iter)->get_name(); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_javame_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 "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(); +} + +/** + * Applies the correct style to a string based on the value of nocamel_style_ + */ +std::string t_javame_generator::get_cap_name(std::string name) { + name[0] = toupper(name[0]); + return name; +} + +string t_javame_generator::constant_name(string name) { + string constant_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); + + bool is_upper = isupper(character); + + if (is_upper && !is_first && !was_previous_char_upper) { + constant_name += '_'; + } + constant_name += toupper(character); + + is_first = false; + was_previous_char_upper = is_upper; + } + + return constant_name; +} + +void t_javame_generator::generate_java_docstring_comment(ofstream& out, string contents) { + generate_docstring_comment(out, "/**\n", " * ", contents, " */\n"); +} + +void t_javame_generator::generate_java_doc(ofstream& out, t_field* field) { + if (field->get_type()->is_enum()) { + string combined_message = field->get_doc() + "\n@see " + get_enum_class_name(field->get_type()); + generate_java_docstring_comment(out, combined_message); + } else { + generate_java_doc(out, (t_doc*)field); + } +} + +/** + * Emits a JavaDoc comment if the provided object has a doc in Thrift + */ +void t_javame_generator::generate_java_doc(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_java_docstring_comment(out, tdoc->get_doc()); + } +} + +/** + * Emits a JavaDoc comment if the provided function object has a doc in Thrift + */ +void t_javame_generator::generate_java_doc(ofstream& out, t_function* tfunction) { + if (tfunction->has_doc()) { + stringstream ss; + ss << tfunction->get_doc(); + 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; + ss << "\n@param " << p->get_name(); + if (p->has_doc()) { + ss << " " << p->get_doc(); + } + } + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); + } +} + +void t_javame_generator::generate_deep_copy_container(ofstream& out, + std::string source_name_p1, + std::string source_name_p2, + std::string result_name, + t_type* type) { + + t_container* container = (t_container*)type; + std::string source_name; + if (source_name_p2 == "") + source_name = source_name_p1; + else + source_name = source_name_p1 + "." + source_name_p2; + + indent(out) << type_name(type, true, false) << " " << result_name << " = new " + << type_name(container, false, true) << "();" << endl; + + std::string iterator_element_name = source_name_p1 + "_element"; + std::string enumeration_name = source_name_p1 + "_enum"; + std::string result_element_name = result_name + "_copy"; + + if (container->is_map()) { + t_type* key_type = ((t_map*)container)->get_key_type(); + t_type* val_type = ((t_map*)container)->get_val_type(); + + indent(out) << "for (Enumeration " << enumeration_name << " = " << source_name << ".keys(); " + << enumeration_name << ".hasMoreElements(); ) {" << endl; + indent_up(); + + out << endl; + + indent(out) << type_name(key_type, true, false) << " " << iterator_element_name << "_key = (" + << type_name(key_type, true, false) << ")" << enumeration_name << ".nextElement();" + << endl; + indent(out) << type_name(val_type, true, false) << " " << iterator_element_name << "_value = (" + << type_name(val_type, true, false) << ")" << source_name << ".get(" + << iterator_element_name << "_key);" << endl; + + out << endl; + + if (key_type->is_container()) { + generate_deep_copy_container(out, + iterator_element_name + "_key", + "", + result_element_name + "_key", + key_type); + } else { + indent(out) << type_name(key_type, true, false) << " " << result_element_name << "_key = "; + generate_deep_copy_non_container(out, + iterator_element_name + "_key", + result_element_name + "_key", + key_type); + out << ";" << endl; + } + + out << endl; + + if (val_type->is_container()) { + generate_deep_copy_container(out, + iterator_element_name + "_value", + "", + result_element_name + "_value", + val_type); + } else { + indent(out) << type_name(val_type, true, false) << " " << result_element_name << "_value = "; + generate_deep_copy_non_container(out, + iterator_element_name + "_value", + result_element_name + "_value", + val_type); + out << ";" << endl; + } + + out << endl; + + indent(out) << result_name << ".put(" << result_element_name << "_key, " << result_element_name + << "_value);" << endl; + + indent_down(); + indent(out) << "}" << endl; + + } else { + t_type* elem_type; + + if (container->is_set()) { + elem_type = ((t_set*)container)->get_elem_type(); + } else { + elem_type = ((t_list*)container)->get_elem_type(); + } + + indent(out) << "for (Enumeration " << enumeration_name << " = " << source_name + << ".elements(); " << enumeration_name << ".hasMoreElements(); ) {" << endl; + indent_up(); + indent(out) << type_name(elem_type, true, false) << " " << iterator_element_name << " = (" + << type_name(elem_type, true, false) << ")" << enumeration_name << ".nextElement();" + << endl; + if (elem_type->is_container()) { + // recursive deep copy + generate_deep_copy_container(out, iterator_element_name, "", result_element_name, elem_type); + if (elem_type->is_list()) { + indent(out) << result_name << ".addElement(" << result_element_name << ");" << endl; + } else { + indent(out) << result_name << ".put(" << result_element_name << ", " << result_element_name + << ");" << endl; + } + } else { + // iterative copy + if (((t_base_type*)elem_type)->is_binary()) { + indent(out) << type_name(elem_type, true, false) << " temp_binary_element = "; + generate_deep_copy_non_container(out, + iterator_element_name, + "temp_binary_element", + elem_type); + out << ";" << endl; + if (elem_type->is_list()) { + indent(out) << result_name << ".addElement(temp_binary_element);" << endl; + } else { + indent(out) << result_name << ".put(temp_binary_element, temp_binary_element);" << endl; + } + } else { + indent(out) << result_name << ".addElement("; + generate_deep_copy_non_container(out, iterator_element_name, result_name, elem_type); + out << ");" << endl; + } + } + + indent_down(); + + indent(out) << "}" << endl; + } +} + +void t_javame_generator::generate_deep_copy_non_container(ofstream& out, + std::string source_name, + std::string dest_name, + t_type* type) { + if (type->is_base_type() || type->is_enum() || type->is_typedef()) { + // binary fields need to be copied with System.arraycopy + if (((t_base_type*)type)->is_binary()) { + out << "new byte[" << source_name << ".length];" << endl; + indent(out) << "System.arraycopy(" << source_name << ", 0, " << dest_name << ", 0, " + << source_name << ".length)"; + } + // everything else can be copied directly + else + out << source_name; + } else { + out << "new " << type_name(type, true, true) << "(" << source_name << ")"; + } +} + +std::string t_javame_generator::generate_isset_check(t_field* field) { + return generate_isset_check(field->get_name()); +} + +std::string t_javame_generator::isset_field_id(t_field* field) { + return "__" + upcase_string(field->get_name() + "_isset_id"); +} + +std::string t_javame_generator::generate_isset_check(std::string field_name) { + return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; +} + +void t_javame_generator::generate_isset_set(ofstream& out, t_field* field) { + if (!type_can_be_null(field->get_type())) { + indent(out) << "set" << get_cap_name(field->get_name()) << get_cap_name("isSet") << "(true);" + << endl; + } +} + +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_) { + package = program->get_namespace("java") + "."; + } + return package + type->get_name(); +} + +void t_javame_generator::generate_struct_desc(ofstream& out, t_struct* tstruct) { + indent(out) << "private static final TStruct STRUCT_DESC = new TStruct(\"" << tstruct->get_name() + << "\");" << endl; +} + +void t_javame_generator::generate_field_descs(ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "private static final TField " << constant_name((*m_iter)->get_name()) + << "_FIELD_DESC = new TField(\"" << (*m_iter)->get_name() << "\", " + << type_to_enum((*m_iter)->get_type()) << ", " + << "(short)" << (*m_iter)->get_key() << ");" << endl; + } +} + +bool t_javame_generator::has_bit_vector(t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!type_can_be_null(get_true_type((*m_iter)->get_type()))) { + return true; + } + } + return false; +} + +void t_javame_generator::generate_java_struct_clear(std::ofstream& out, t_struct* tstruct) { + indent(out) << "public void clear() {" << endl; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + 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) { + print_const_value(out, + "this." + (*m_iter)->get_name(), + t, + (*m_iter)->get_value(), + true, + true); + } else { + if (type_can_be_null(t)) { + indent(out) << "this." << (*m_iter)->get_name() << " = null;" << endl; + } else { + // must be a base type + // means it also needs to be explicitly unset + indent(out) << "set" << get_cap_name((*m_iter)->get_name()) << get_cap_name("isSet") + << "(false);" << endl; + switch (((t_base_type*)t)->get_base()) { + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + indent(out) << "this." << (*m_iter)->get_name() << " = 0;" << endl; + break; + case t_base_type::TYPE_DOUBLE: + indent(out) << "this." << (*m_iter)->get_name() << " = 0.0;" << endl; + break; + case t_base_type::TYPE_BOOL: + indent(out) << "this." << (*m_iter)->get_name() << " = false;" << endl; + break; + default: // prevent gcc compiler warning + break; + } + } + } + } + indent_down(); + + indent(out) << "}" << endl << endl; +} + +THRIFT_REGISTER_GENERATOR(javame, "Java ME", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc new file mode 100644 index 00000000..3ccd2483 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_js_generator.cc @@ -0,0 +1,2273 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include "thrift/platform.h" +#include "thrift/version.h" + +using std::map; +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 + +#include "thrift/generate/t_oop_generator.h" + + +/** + * JS code generator. + */ +class t_js_generator : public t_oop_generator { +public: + t_js_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; + + gen_node_ = false; + gen_jquery_ = false; + gen_ts_ = false; + + bool with_ns_ = false; + + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("node") == 0) { + gen_node_ = true; + } else if( iter->first.compare("jquery") == 0) { + gen_jquery_ = true; + } else if( iter->first.compare("ts") == 0) { + gen_ts_ = true; + } else if( iter->first.compare("with_ns") == 0) { + with_ns_ = true; + } else { + throw "unknown option js:" + iter->first; + } + } + + if (gen_node_ && gen_ts_) { + throw "Invalid switch: [-gen js:node,ts] 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]"; + } + + if (!gen_node_ && with_ns_) { + throw "Invalid switch: [-gen js:with_ns] is only valid when using node.js"; + } + + if (gen_node_) { + out_dir_base_ = "gen-nodejs"; + no_ns_ = !with_ns_; + } else { + out_dir_base_ = "gen-js"; + no_ns_ = false; + } + + escape_['\''] = "\\'"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + std::string render_recv_throw(std::string var); + std::string render_recv_return(std::string var); + + std::string render_const_value(t_type* type, t_const_value* value); + + /** + * Structs! + */ + void generate_js_struct(t_struct* tstruct, bool is_exception); + void generate_js_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_exported = true); + void generate_js_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_js_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_js_function_helpers(t_function* tfunction); + + /** + * Service-level generation functions + */ + void generate_service_helpers(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_service_rest(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_processor(t_service* tservice); + void generate_process_function(t_service* tservice, t_function* tfunction); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool inclass = false); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + /** + * Helper rendering functions + */ + + std::string js_includes(); + std::string render_includes(); + std::string declare_field(t_field* tfield, bool init = false, bool obj = false); + std::string function_signature(t_function* tfunction, + std::string prefix = "", + bool include_callback = false); + 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 autogen_comment() { + 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"; + } + + t_type* get_contained_type(t_type* t); + + std::vector js_namespace_pieces(t_program* p) { + std::string ns = p->get_namespace("js"); + + std::string::size_type loc; + std::vector pieces; + + if (no_ns_) { + return pieces; + } + + if (ns.size() > 0) { + while ((loc = ns.find(".")) != std::string::npos) { + pieces.push_back(ns.substr(0, loc)); + ns = ns.substr(loc + 1); + } + } + + if (ns.size() > 0) { + pieces.push_back(ns); + } + + return pieces; + } + + std::string js_type_namespace(t_program* p) { + if (gen_node_) { + if (p != NULL && p != program_) { + return make_valid_nodeJs_identifier(p->get_name()) + "_ttypes."; + } + return "ttypes."; + } + return js_namespace(p); + } + + std::string js_export_namespace(t_program* p) { + if (gen_node_) { + return "exports."; + } + return js_namespace(p); + } + + bool has_js_namespace(t_program* p) { + if (no_ns_) { + return false; + } + std::string ns = p->get_namespace("js"); + return (ns.size() > 0); + } + + std::string js_namespace(t_program* p) { + if (no_ns_) { + return ""; + } + std::string ns = p->get_namespace("js"); + if (ns.size() > 0) { + ns += "."; + } + + return ns; + } + + /** + * TypeScript Definition File helper functions + */ + + string ts_function_signature(t_function* tfunction, bool include_callback); + string ts_get_type(t_type* type); + + /** + * Special indentation for TypeScript Definitions because of the module. + * Returns the normal indentation + " " if a module was defined. + * @return string + */ + string ts_indent() { return indent() + (!ts_module_.empty() ? " " : ""); } + + /** + * Returns "declare " if no module was defined. + * @return string + */ + string ts_declare() { return (ts_module_.empty() ? "declare " : ""); } + + /** + * Returns "?" if the given field is optional. + * @param t_field The field to check + * @return string + */ + string ts_get_req(t_field* field) { return (field->get_req() == t_field::T_OPTIONAL ? "?" : ""); } + + /** + * Returns the documentation, if the provided documentable object has one. + * @param t_doc The object to get the documentation from + * @return string The documentation + */ + string ts_print_doc(t_doc* tdoc) { + string result = endl; + + if (tdoc->has_doc()) { + std::stringstream doc(tdoc->get_doc()); + string item; + + result += ts_indent() + "/**" + endl; + while (std::getline(doc, item)) { + result += ts_indent() + " * " + item + endl; + } + result += ts_indent() + " */" + endl; + } + return result; + } + +private: + /** + * True if we should generate NodeJS-friendly RPC services. + */ + bool gen_node_; + + /** + * True if we should generate services that use jQuery ajax (async/sync). + */ + bool gen_jquery_; + + /** + * True if we should generate a TypeScript Definition File for each service. + */ + bool gen_ts_; + + /** + * The name of the defined module(s), for TypeScript Definition Files. + */ + string ts_module_; + + /** + * True if we should not generate namespace objects for node. + */ + bool no_ns_; + + /** + * File streams + */ + std::ofstream f_types_; + std::ofstream f_service_; + std::ofstream f_types_ts_; + std::ofstream f_service_ts_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_js_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + string outdir = get_out_dir(); + + // Make output file(s) + string f_types_name = outdir + program_->get_name() + "_types.js"; + f_types_.open(f_types_name.c_str()); + + if (gen_ts_) { + string f_types_ts_name = outdir + program_->get_name() + "_types.d.ts"; + f_types_ts_.open(f_types_ts_name.c_str()); + } + + // Print header + f_types_ << autogen_comment(); + + if (gen_node_ && no_ns_) { + f_types_ << "\"use strict\";" << endl << endl; + } + + f_types_ << js_includes() << endl << render_includes() << endl; + + if (gen_ts_) { + f_types_ts_ << autogen_comment() << endl; + } + + if (gen_node_) { + f_types_ << "var ttypes = module.exports = {};" << endl; + } + + string pns; + + // setup the namespace + // TODO should the namespace just be in the directory structure for node? + vector ns_pieces = js_namespace_pieces(program_); + if (ns_pieces.size() > 0) { + for (size_t i = 0; i < ns_pieces.size(); ++i) { + pns += ((i == 0) ? "" : ".") + ns_pieces[i]; + f_types_ << "if (typeof " << pns << " === 'undefined') {" << endl; + f_types_ << " " << pns << " = {};" << endl; + f_types_ << "}" << endl; + } + if (gen_ts_) { + ts_module_ = pns; + f_types_ts_ << "declare module " << ts_module_ << " {"; + } + } +} + +/** + * Prints standard js imports + */ +string t_js_generator::js_includes() { + if (gen_node_) { + return string( + "var thrift = require('thrift');\n" + "var Thrift = thrift.Thrift;\n" + "var Q = thrift.Q;\n"); + } + + return ""; +} + +/** + * Renders all the imports necessary for including another Thrift program + */ +string t_js_generator::render_includes() { + string result = ""; + + if (gen_node_) { + const vector& includes = program_->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + result += "var " + make_valid_nodeJs_identifier(includes[i]->get_name()) + "_ttypes = require('./" + includes[i]->get_name() + + "_types');\n"; + } + if (includes.size() > 0) { + result += "\n"; + } + } + + return result; +} + +/** + * Close up (or down) some filez. + */ +void t_js_generator::close_generator() { + // Close types file(s) + + f_types_.close(); + + if (gen_ts_) { + if (!ts_module_.empty()) { + f_types_ts_ << "}"; + } + f_types_ts_.close(); + } +} + +/** + * Generates a typedef. This is not done in JS, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_js_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates code for an enumerated type. Since define is expensive to lookup + * in JS, we use a global array for this. + * + * @param tenum The enumeration + */ +void t_js_generator::generate_enum(t_enum* tenum) { + f_types_ << js_type_namespace(tenum->get_program()) << tenum->get_name() << " = {" << endl; + + if (gen_ts_) { + f_types_ts_ << ts_print_doc(tenum) << ts_indent() << ts_declare() << "enum " + << tenum->get_name() << " {" << endl; + } + + indent_up(); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + if (gen_ts_) { + f_types_ts_ << ts_indent() << (*c_iter)->get_name() << " = " << value << "," << endl; + // add 'value: key' in addition to 'key: value' for TypeScript enums + f_types_ << indent() << "'" << value << "' : '" << (*c_iter)->get_name() << "'," << endl; + } + f_types_ << indent() << "'" << (*c_iter)->get_name() << "' : " << value; + if (c_iter != constants.end() - 1) { + f_types_ << ","; + } + f_types_ << endl; + } + + indent_down(); + + f_types_ << "};" << endl; + + if (gen_ts_) { + f_types_ts_ << ts_indent() << "}" << endl; + } +} + +/** + * Generate a constant value + */ +void t_js_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + f_types_ << js_type_namespace(program_) << name << " = "; + f_types_ << render_const_value(type, value) << ";" << endl; + + if (gen_ts_) { + f_types_ts_ << ts_print_doc(tconst) << ts_indent() << ts_declare() << "var " << name << ": " + << ts_get_type(type) << ";" << endl; + } +} + +/** + * 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 + */ +string t_js_generator::render_const_value(t_type* type, t_const_value* value) { + std::ostringstream out; + + 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_STRING: + out << "'" << get_escaped_string(value) << "'"; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << "new " << js_type_namespace(type->get_program()) << type->get_name() << "({" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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 (v_iter != val.begin()) + out << ","; + out << render_const_value(g_type_string, v_iter->first); + out << " : "; + out << render_const_value(field_type, v_iter->second); + } + + out << "})"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + + t_type* vtype = ((t_map*)type)->get_val_type(); + out << "{" << endl; + indent_up(); + + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + if (v_iter != val.begin()) + out << "," << endl; + + out << indent() << render_const_value(ktype, v_iter->first); + + out << " : "; + out << render_const_value(vtype, v_iter->second); + } + + indent_down(); + out << 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(); + } + out << "["; + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + if (v_iter != val.begin()) + out << ","; + out << render_const_value(etype, *v_iter); + } + out << "]"; + } + return out.str(); +} + +/** + * Make a struct + */ +void t_js_generator::generate_struct(t_struct* tstruct) { + generate_js_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_js_generator::generate_xception(t_struct* txception) { + generate_js_struct(txception, true); +} + +/** + * Structs can be normal or exceptions. + */ +void t_js_generator::generate_js_struct(t_struct* tstruct, bool is_exception) { + generate_js_struct_definition(f_types_, tstruct, is_exception); +} + +/** + * Return type of contained elements for a container type. For maps + * this is type of value (keys are always strings in js) + */ +t_type* t_js_generator::get_contained_type(t_type* t) { + t_type* etype; + if (t->is_list()) { + etype = ((t_list*)t)->get_elem_type(); + } else if (t->is_set()) { + etype = ((t_set*)t)->get_elem_type(); + } else { + etype = ((t_map*)t)->get_val_type(); + } + return etype; +} + +/** + * Generates a struct definition for a thrift data type. This is nothing in JS + * where the objects are all just associative arrays (unless of course we + * decide to start using objects for them...) + * + * @param tstruct The struct definition + */ +void t_js_generator::generate_js_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_exported) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + if (gen_node_) { + string prefix = has_js_namespace(tstruct->get_program()) ? js_namespace(tstruct->get_program()) : "var "; + if (is_exported) { + out << prefix << tstruct->get_name() << " = " + << "module.exports." << tstruct->get_name() << " = function(args) {" << endl; + } else { + out << prefix << tstruct->get_name() << " = function(args) {" + << endl; + } + } else { + out << js_namespace(tstruct->get_program()) << tstruct->get_name() << " = function(args) {" + << endl; + if (gen_ts_) { + f_types_ts_ << ts_print_doc(tstruct) << ts_indent() << ts_declare() << "class " + << tstruct->get_name() << (is_exception ? " extends Thrift.TException" : "") + << " {" << endl; + } + } + + indent_up(); + + if (gen_node_ && is_exception) { + out << indent() << "Thrift.TException.call(this, \"" << js_namespace(tstruct->get_program()) + << tstruct->get_name() << "\");" << endl; + out << indent() << "this.name = \"" << js_namespace(tstruct->get_program()) + << tstruct->get_name() << "\";" << endl; + } + + // members with arguments + 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())) { + dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value()); + out << indent() << "this." << (*m_iter)->get_name() << " = " << dval << ";" << endl; + } else { + out << indent() << dval << ";" << endl; + } + if (gen_ts_) { + f_types_ts_ << ts_indent() << (*m_iter)->get_name() << ": " + << ts_get_type((*m_iter)->get_type()) << ";" << endl; + } + } + + // Generate constructor from array + 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())) { + indent(out) << "this." << (*m_iter)->get_name() << " = " + << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; + } + } + + // Early returns for exceptions + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (t->is_xception()) { + out << indent() << "if (args instanceof " << js_type_namespace(t->get_program()) + << t->get_name() << ") {" << endl << indent() << indent() << "this." + << (*m_iter)->get_name() << " = args;" << endl << indent() << indent() << "return;" + << endl << indent() << "}" << endl; + } + } + + out << indent() << "if (args) {" << endl; + if (gen_ts_) { + f_types_ts_ << endl << ts_indent() << "constructor(args?: { "; + } + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + out << indent() << indent() << "if (args." << (*m_iter)->get_name() << " !== undefined && args." << (*m_iter)->get_name() << " !== null) {" + << endl << indent() << indent() << indent() << "this." << (*m_iter)->get_name(); + + if (t->is_struct()) { + out << (" = new " + js_type_namespace(t->get_program()) + t->get_name() + + "(args."+(*m_iter)->get_name() +");"); + out << endl; + } else if (t->is_container()) { + t_type* etype = get_contained_type(t); + string copyFunc = t->is_map() ? "Thrift.copyMap" : "Thrift.copyList"; + string type_list = ""; + + while (etype->is_container()) { + if (type_list.length() > 0) { + type_list += ", "; + } + type_list += etype->is_map() ? "Thrift.copyMap" : "Thrift.copyList"; + etype = get_contained_type(etype); + } + + if (etype->is_struct()) { + if (type_list.length() > 0) { + type_list += ", "; + } + type_list += js_type_namespace(etype->get_program()) + etype->get_name(); + } + else { + if (type_list.length() > 0) { + type_list += ", "; + } + type_list += "null"; + } + + out << (" = " + copyFunc + "(args." + (*m_iter)->get_name() + + ", [" + type_list + "]);"); + out << endl; + } else { + out << " = args." << (*m_iter)->get_name() << ";" << endl; + } + + if (!(*m_iter)->get_req()) { + out << indent() << indent() << "} else {" << endl << indent() << indent() << indent() + << "throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.UNKNOWN, " + "'Required field " << (*m_iter)->get_name() << " is unset!');" << endl; + } + out << indent() << indent() << "}" << endl; + if (gen_ts_) { + f_types_ts_ << (*m_iter)->get_name() << ts_get_req(*m_iter) << ": " + << ts_get_type((*m_iter)->get_type()) << "; "; + } + } + + out << indent() << "}" << endl; + if (gen_ts_) { + f_types_ts_ << "});" << endl; + } + } + + indent_down(); + out << "};" << endl; + if (gen_ts_) { + f_types_ts_ << ts_indent() << "}" << endl; + } + + if (is_exception) { + out << "Thrift.inherits(" << js_namespace(tstruct->get_program()) << tstruct->get_name() + << ", Thrift.TException);" << endl; + out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype.name = '" + << tstruct->get_name() << "';" << endl; + } else { + // init prototype + out << js_namespace(tstruct->get_program()) << tstruct->get_name() << ".prototype = {};" + << endl; + } + + generate_js_struct_reader(out, tstruct); + generate_js_struct_writer(out, tstruct); +} + +/** + * Generates the read() method for a struct + */ +void t_js_generator::generate_js_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + out << js_namespace(tstruct->get_program()) << tstruct->get_name() + << ".prototype.read = function(input) {" << endl; + + indent_up(); + + indent(out) << "input.readStructBegin();" << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + + scope_up(out); + + indent(out) << "var ret = input.readFieldBegin();" << endl; + indent(out) << "var fname = ret.fname;" << endl; + indent(out) << "var ftype = ret.ftype;" << endl; + indent(out) << "var fid = ret.fid;" << endl; + + // Check for field STOP marker and break + indent(out) << "if (ftype == Thrift.Type.STOP) {" << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + if (!fields.empty()) { + // Switch statement on the field we are reading + indent(out) << "switch (fid)" << 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(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + + indent_up(); + generate_deserialize_field(out, *f_iter, "this."); + indent_down(); + + indent(out) << "} else {" << endl; + + indent(out) << " input.skip(ftype);" << endl; + + out << indent() << "}" << endl << indent() << "break;" << endl; + } + if (fields.size() == 1) { + // pseudo case to make jslint happy + indent(out) << "case 0:" << endl; + indent(out) << " input.skip(ftype);" << endl; + indent(out) << " break;" << endl; + } + // In the default case we skip the field + indent(out) << "default:" << endl; + indent(out) << " input.skip(ftype);" << endl; + + scope_down(out); + } else { + indent(out) << "input.skip(ftype);" << endl; + } + + indent(out) << "input.readFieldEnd();" << endl; + + scope_down(out); + + indent(out) << "input.readStructEnd();" << endl; + + indent(out) << "return;" << endl; + + indent_down(); + out << indent() << "};" << endl << endl; +} + +/** + * Generates the write() method for a struct + */ +void t_js_generator::generate_js_struct_writer(ofstream& out, t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + out << js_namespace(tstruct->get_program()) << tstruct->get_name() + << ".prototype.write = function(output) {" << endl; + + indent_up(); + + indent(out) << "output.writeStructBegin('" << name << "');" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << indent() << "if (this." << (*f_iter)->get_name() << " !== null && this." + << (*f_iter)->get_name() << " !== undefined) {" << endl; + indent_up(); + + indent(out) << "output.writeFieldBegin(" + << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type()) + << ", " << (*f_iter)->get_key() << ");" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "this."); + + indent(out) << "output.writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}" << endl; + } + + out << indent() << "output.writeFieldStop();" << endl << indent() << "output.writeStructEnd();" + << endl; + + out << indent() << "return;" << endl; + + indent_down(); + out << indent() << "};" << endl << endl; +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +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_ts_) { + string f_service_ts_name = get_out_dir() + service_name_ + ".d.ts"; + f_service_ts_.open(f_service_ts_name.c_str()); + } + + f_service_ << autogen_comment(); + + if (gen_node_ && no_ns_) { + f_service_ << "\"use strict\";" << endl << endl; + } + + f_service_ << js_includes() << endl << render_includes() << endl; + + if (gen_ts_) { + if (tservice->get_extends() != NULL) { + f_service_ts_ << "/// get_extends()->get_name() + << ".d.ts\" />" << endl; + } + f_service_ts_ << autogen_comment() << endl; + if (!ts_module_.empty()) { + f_service_ts_ << "declare module " << ts_module_ << " {"; + } + } + + if (gen_node_) { + if (tservice->get_extends() != NULL) { + f_service_ << "var " << tservice->get_extends()->get_name() << " = require('./" + << tservice->get_extends()->get_name() << "');" << endl << "var " + << tservice->get_extends()->get_name() + << "Client = " << tservice->get_extends()->get_name() << ".Client;" << endl + << "var " << tservice->get_extends()->get_name() + << "Processor = " << tservice->get_extends()->get_name() << ".Processor;" << endl; + } + + f_service_ << "var ttypes = require('./" + program_->get_name() + "_types');" << endl; + } + + generate_service_helpers(tservice); + generate_service_interface(tservice); + generate_service_client(tservice); + + if (gen_node_) { + generate_service_processor(tservice); + } + + f_service_.close(); + if (gen_ts_) { + if (!ts_module_.empty()) { + f_service_ts_ << "}"; + } + f_service_ts_.close(); + } +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_js_generator::generate_service_processor(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + if (gen_node_) { + string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : "var "; + f_service_ << prefix << service_name_ << "Processor = " << "exports.Processor = function(handler) "; + } else { + f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Processor = " + << "exports.Processor = function(handler) "; + } + + scope_up(f_service_); + + f_service_ << indent() << "this._handler = handler;" << endl; + + scope_down(f_service_); + f_service_ << ";" << endl; + + if (tservice->get_extends() != NULL) { + indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program()) + << service_name_ << "Processor, " << tservice->get_extends()->get_name() + << "Processor);" << endl; + } + + // Generate the server implementation + indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ + << "Processor.prototype.process = function(input, output) "; + + scope_up(f_service_); + + f_service_ << indent() << "var r = input.readMessageBegin();" << endl << indent() + << "if (this['process_' + r.fname]) {" << endl << indent() + << " return this['process_' + r.fname].call(this, r.rseqid, input, output);" << endl + << indent() << "} else {" << endl << indent() << " input.skip(Thrift.Type.STRUCT);" + << endl << indent() << " input.readMessageEnd();" << endl << indent() + << " var x = new " + "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN_METHOD, " + "'Unknown function ' + r.fname);" << endl << indent() + << " output.writeMessageBegin(r.fname, Thrift.MessageType.EXCEPTION, r.rseqid);" + << endl << indent() << " x.write(output);" << endl << indent() + << " output.writeMessageEnd();" << endl << indent() << " output.flush();" << endl + << indent() << "}" << endl; + + scope_down(f_service_); + f_service_ << ";" << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_js_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ + << "Processor.prototype.process_" + tfunction->get_name() + + " = function(seqid, input, output) "; + + scope_up(f_service_); + + string argsname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name() + "_args"; + string resultname = js_namespace(program_) + service_name_ + "_" + tfunction->get_name() + + "_result"; + + f_service_ << indent() << "var args = new " << argsname << "();" << endl << indent() + << "args.read(input);" << endl << indent() << "input.readMessageEnd();" << endl; + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + indent(f_service_) << "this._handler." << tfunction->get_name() << "("; + + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + + f_service_ << ");" << endl; + scope_down(f_service_); + f_service_ << ";" << endl; + return; + } + + f_service_ << indent() << "if (this._handler." << tfunction->get_name() + << ".length === " << fields.size() << ") {" << endl; + indent_up(); + indent(f_service_) << "Q.fcall(this._handler." << tfunction->get_name(); + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << ", args." << (*f_iter)->get_name(); + } + + f_service_ << ")" << endl; + indent_up(); + indent(f_service_) << ".then(function(result) {" << endl; + indent_up(); + f_service_ << indent() << "var result_obj = new " << resultname << "({success: result});" << endl + << indent() << "output.writeMessageBegin(\"" << tfunction->get_name() + << "\", Thrift.MessageType.REPLY, seqid);" << endl << indent() + << "result_obj.write(output);" << endl << indent() << "output.writeMessageEnd();" << endl + << indent() << "output.flush();" << endl; + indent_down(); + indent(f_service_) << "}, function (err) {" << endl; + indent_up(); + indent(f_service_) << "var result;" << endl; + + bool has_exception = false; + 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()); + if (t->is_xception()) { + if (!has_exception) { + has_exception = true; + indent(f_service_) << "if (err instanceof " << js_type_namespace(t->get_program()) + << t->get_name(); + } else { + f_service_ << " || err instanceof " << js_type_namespace(t->get_program()) + << t->get_name(); + } + } + } + } + + if (has_exception) { + f_service_ << ") {" << endl; + indent_up(); + f_service_ << indent() << "result = new " << resultname << "(err);" << endl << indent() + << "output.writeMessageBegin(\"" << tfunction->get_name() + << "\", Thrift.MessageType.REPLY, seqid);" << endl; + + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + } + + f_service_ << indent() << "result = new " + "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN," + " err.message);" << endl << indent() << "output.writeMessageBegin(\"" + << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl; + + if (has_exception) { + indent_down(); + indent(f_service_) << "}" << endl; + } + + f_service_ << indent() << "result.write(output);" << endl << indent() + << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl; + indent_down(); + indent(f_service_) << "});" << endl; + indent_down(); + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + indent(f_service_) << "this._handler." << tfunction->get_name() << "("; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << "args." << (*f_iter)->get_name() << ", "; + } + + f_service_ << "function (err, result) {" << endl; + indent_up(); + indent(f_service_) << "var result_obj;" << endl; + + 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()); + if (t->is_xception()) { + f_service_ << " || err instanceof " << js_type_namespace(t->get_program()) << t->get_name(); + } + } + } + f_service_ << ") {" << endl; + indent_up(); + f_service_ << indent() << "result_obj = new " << resultname + << "((err !== null || typeof err === 'undefined') ? err : {success: result});" << endl << indent() + << "output.writeMessageBegin(\"" << tfunction->get_name() + << "\", Thrift.MessageType.REPLY, seqid);" << endl; + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + f_service_ << indent() << "result_obj = new " + "Thrift.TApplicationException(Thrift.TApplicationExceptionType.UNKNOWN," + " err.message);" << endl << indent() << "output.writeMessageBegin(\"" + << tfunction->get_name() << "\", Thrift.MessageType.EXCEPTION, seqid);" << endl; + indent_down(); + f_service_ << indent() << "}" << endl << indent() << "result_obj.write(output);" << endl << indent() + << "output.writeMessageEnd();" << endl << indent() << "output.flush();" << endl; + + indent_down(); + indent(f_service_) << "});" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + indent_down(); + indent(f_service_) << "};" << endl; +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_js_generator::generate_service_helpers(t_service* tservice) { + // Do not generate TS definitions for helper functions + bool gen_ts_tmp = gen_ts_; + gen_ts_ = false; + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_ << "//HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + string name = ts->get_name(); + ts->set_name(service_name_ + "_" + name); + generate_js_struct_definition(f_service_, ts, false, false); + generate_js_function_helpers(*f_iter); + ts->set_name(name); + } + + gen_ts_ = gen_ts_tmp; +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_js_generator::generate_js_function_helpers(t_function* tfunction) { + t_struct result(program_, service_name_ + "_" + 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_js_struct_definition(f_service_, &result, false, false); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_js_generator::generate_service_interface(t_service* tservice) { + (void)tservice; +} + +/** + * Generates a REST interface + */ +void t_js_generator::generate_service_rest(t_service* tservice) { + (void)tservice; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_js_generator::generate_service_client(t_service* tservice) { + if (gen_node_) { + string prefix = has_js_namespace(tservice->get_program()) ? js_namespace(tservice->get_program()) : "var "; + f_service_ << prefix << service_name_ << "Client = " + << "exports.Client = function(output, pClass) {" << endl; + } else { + f_service_ << js_namespace(tservice->get_program()) << service_name_ + << "Client = function(input, output) {" << endl; + if (gen_ts_) { + f_service_ts_ << ts_print_doc(tservice) << ts_indent() << ts_declare() << "class " + << service_name_ << "Client "; + if (tservice->get_extends() != NULL) { + f_service_ts_ << "extends " << tservice->get_extends()->get_name() << "Client "; + } + f_service_ts_ << "{" << endl; + } + } + + indent_up(); + + if (gen_node_) { + f_service_ << indent() << " this.output = output;" << endl << indent() + << " this.pClass = pClass;" << endl << indent() << " this._seqid = 0;" << endl + << indent() << " this._reqs = {};" << endl; + } else { + f_service_ << indent() << " this.input = input;" << endl << indent() + << " this.output = (!output) ? input : output;" << endl << indent() + << " this.seqid = 0;" << endl; + if (gen_ts_) { + f_service_ts_ << ts_indent() << "input: Thrift.TJSONProtocol;" << endl << ts_indent() + << "output: Thrift.TJSONProtocol;" << endl << ts_indent() << "seqid: number;" + << endl << endl << ts_indent() + << "constructor(input: Thrift.TJSONProtocol, output?: Thrift.TJSONProtocol);" + << endl; + } + } + + indent_down(); + + f_service_ << indent() << "};" << endl; + + if (tservice->get_extends() != NULL) { + indent(f_service_) << "Thrift.inherits(" << js_namespace(tservice->get_program()) + << service_name_ << "Client, " + << js_namespace(tservice->get_extends()->get_program()) + << tservice->get_extends()->get_name() << "Client);" << endl; + } else { + // init prototype + indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ + << "Client.prototype = {};" << endl; + } + + // utils for multiplexed services + if (gen_node_) { + indent(f_service_) << js_namespace(tservice->get_program()) << service_name_ + << "Client.prototype.seqid = function() { return this._seqid; };" << endl + << js_namespace(tservice->get_program()) << service_name_ + << "Client.prototype.new_seqid = function() { return this._seqid += 1; };" + << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + string arglist = argument_list(arg_struct); + + // Open function + f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype." + << function_signature(*f_iter, "", true) << " {" << endl; + + indent_up(); + + if (gen_ts_) { + f_service_ts_ << ts_print_doc(*f_iter) << + // function definition without callback + ts_indent() << ts_function_signature(*f_iter, false) << endl << ts_print_doc(*f_iter) << + // overload with callback + ts_indent() << ts_function_signature(*f_iter, true) << endl; + } + + if (gen_node_) { // Node.js output ./gen-nodejs + f_service_ << indent() << "this._seqid = this.new_seqid();" << endl << indent() + << "if (callback === undefined) {" << endl; + indent_up(); + f_service_ << indent() << "var _defer = Q.defer();" << endl << indent() + << "this._reqs[this.seqid()] = function(error, result) {" << endl; + indent_up(); + indent(f_service_) << "if (error) {" << endl; + indent_up(); + indent(f_service_) << "_defer.reject(error);" << endl; + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + indent(f_service_) << "_defer.resolve(result);" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + indent_down(); + indent(f_service_) << "};" << endl; + f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl + << indent() << "return _defer.promise;" << endl; + indent_down(); + indent(f_service_) << "} else {" << endl; + indent_up(); + f_service_ << indent() << "this._reqs[this.seqid()] = callback;" << endl << indent() + << "this.send_" << funname << "(" << arglist << ");" << endl; + indent_down(); + indent(f_service_) << "}" << endl; + } else if (gen_jquery_) { // jQuery output ./gen-js + f_service_ << indent() << "if (callback === undefined) {" << endl; + indent_up(); + f_service_ << indent() << "this.send_" << funname << "(" << arglist << ");" << endl; + if (!(*f_iter)->is_oneway()) { + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << "return "; + } + f_service_ << "this.recv_" << funname << "();" << endl; + } + indent_down(); + f_service_ << indent() << "} else {" << endl; + indent_up(); + f_service_ << indent() << "var postData = this.send_" << funname << "(" << arglist + << (arglist.empty() ? "" : ", ") << "true);" << endl; + f_service_ << indent() << "return this.output.getTransport()" << endl; + indent_up(); + f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname + << ");" << endl; + indent_down(); + indent_down(); + f_service_ << indent() << "}" << endl; + } else { // Standard JavaScript ./gen-js + f_service_ << indent() << "this.send_" << funname << "(" << arglist + << (arglist.empty() ? "" : ", ") << "callback); " << endl; + if (!(*f_iter)->is_oneway()) { + f_service_ << indent() << "if (!callback) {" << endl; + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << " return "; + } + f_service_ << "this.recv_" << funname << "();" << endl; + f_service_ << indent() << "}" << endl; + } + } + + indent_down(); + + f_service_ << "};" << endl << endl; + + // Send function + f_service_ << js_namespace(tservice->get_program()) << service_name_ << "Client.prototype.send_" + << function_signature(*f_iter, "", !gen_node_) << " {" << endl; + + indent_up(); + + std::string outputVar; + if (gen_node_) { + f_service_ << indent() << "var output = new this.pClass(this.output);" << endl; + outputVar = "output"; + } else { + outputVar = "this.output"; + } + + std::string argsname = js_namespace(program_) + service_name_ + "_" + (*f_iter)->get_name() + + "_args"; + + std::string messageType = (*f_iter)->is_oneway() ? "Thrift.MessageType.ONEWAY" + : "Thrift.MessageType.CALL"; + + // Serialize the request header + if (gen_node_) { + f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name() + << "', " << messageType << ", this.seqid());" << endl; + } else { + f_service_ << indent() << outputVar << ".writeMessageBegin('" << (*f_iter)->get_name() + << "', " << messageType << ", this.seqid);" << endl; + } + + f_service_ << indent() << "var args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = " + << (*fld_iter)->get_name() << ";" << endl; + } + + // Write to the stream + f_service_ << indent() << "args.write(" << outputVar << ");" << endl << indent() << outputVar + << ".writeMessageEnd();" << endl; + + if (gen_node_) { + f_service_ << indent() << "return this.output.flush();" << endl; + } else { + if (gen_jquery_) { + f_service_ << indent() << "return this.output.getTransport().flush(callback);" << endl; + } else { + f_service_ << indent() << "if (callback) {" << endl; + f_service_ << indent() << " var self = this;" << endl; + f_service_ << indent() << " this.output.getTransport().flush(true, function() {" << endl; + f_service_ << indent() << " var result = null;" << endl; + f_service_ << indent() << " try {" << endl; + f_service_ << indent() << " result = self.recv_" << funname << "();" << endl; + f_service_ << indent() << " } catch (e) {" << endl; + f_service_ << indent() << " result = e;" << endl; + f_service_ << indent() << " }" << endl; + f_service_ << indent() << " callback(result);" << endl; + f_service_ << indent() << " });" << endl; + f_service_ << indent() << "} else {" << endl; + f_service_ << indent() << " return this.output.getTransport().flush();" << endl; + f_service_ << indent() << "}" << endl; + } + } + + indent_down(); + + f_service_ << "};" << endl; + + if (!(*f_iter)->is_oneway()) { + std::string resultname = js_namespace(tservice->get_program()) + service_name_ + "_" + + (*f_iter)->get_name() + "_result"; + + if (gen_node_) { + // Open function + f_service_ << endl << js_namespace(tservice->get_program()) << service_name_ + << "Client.prototype.recv_" << (*f_iter)->get_name() + << " = function(input,mtype,rseqid) {" << endl; + } else { + t_struct noargs(program_); + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_ << endl << js_namespace(tservice->get_program()) << service_name_ + << "Client.prototype." << function_signature(&recv_function) << " {" << endl; + } + + indent_up(); + + std::string inputVar; + if (gen_node_) { + inputVar = "input"; + } else { + inputVar = "this.input"; + } + + if (gen_node_) { + f_service_ << indent() << "var callback = this._reqs[rseqid] || function() {};" << endl + << indent() << "delete this._reqs[rseqid];" << endl; + } else { + f_service_ << indent() << "var ret = this.input.readMessageBegin();" << endl << indent() + << "var fname = ret.fname;" << endl << indent() << "var mtype = ret.mtype;" + << endl << indent() << "var rseqid = ret.rseqid;" << endl; + } + + f_service_ << indent() << "if (mtype == Thrift.MessageType.EXCEPTION) {" << endl << indent() + << " var x = new Thrift.TApplicationException();" << endl << indent() + << " x.read(" << inputVar << ");" << endl << indent() << " " << inputVar + << ".readMessageEnd();" << endl << indent() << " " << render_recv_throw("x") + << endl << indent() << "}" << endl; + + f_service_ << indent() << "var result = new " << resultname << "();" << endl << indent() + << "result.read(" << inputVar << ");" << endl; + + f_service_ << indent() << inputVar << ".readMessageEnd();" << endl << endl; + + 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) { + f_service_ << indent() << "if (null !== result." << (*x_iter)->get_name() << ") {" << endl + << indent() << " " << render_recv_throw("result." + (*x_iter)->get_name()) + << endl << indent() << "}" << endl; + } + + // Careful, only return result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (null !== result.success) {" << endl << indent() << " " + << render_recv_return("result.success") << endl << indent() << "}" << endl; + f_service_ << indent() + << render_recv_throw("'" + (*f_iter)->get_name() + " failed: unknown result'") + << endl; + } else { + if (gen_node_) { + indent(f_service_) << "callback(null);" << endl; + } else { + indent(f_service_) << "return;" << endl; + } + } + + // Close function + indent_down(); + f_service_ << "};" << endl; + } + } + + if (gen_ts_) { + f_service_ts_ << ts_indent() << "}" << endl; + } +} + +std::string t_js_generator::render_recv_throw(std::string var) { + if (gen_node_) { + return "return callback(" + var + ");"; + } else { + return "throw " + var + ";"; + } +} + +std::string t_js_generator::render_recv_return(std::string var) { + if (gen_node_) { + return "return callback(null, " + var + ");"; + } else { + return "return " + var + ";"; + } +} + +/** + * Deserializes a field of any type. + */ +void t_js_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool inclass) { + (void)inclass; + 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(); + } + + string name = prefix + tfield->get_name(); + + 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 << " = input."; + + 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: + out << (((t_base_type*)type)->is_binary() ? "readBinary()" : "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 JS name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "readI32()"; + } + + if (!gen_node_) { + out << ".value"; + } + + out << ";" << endl; + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Generates an unserializer for a variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_js_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << indent() << prefix << " = new " << js_type_namespace(tstruct->get_program()) + << tstruct->get_name() << "();" << endl << indent() << prefix << ".read(input);" << endl; +} + +void t_js_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + string rtmp3 = tmp("_rtmp3"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + out << indent() << "var " << size << " = 0;" << endl; + out << indent() << "var " << rtmp3 << ";" << endl; + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << prefix << " = {};" << endl << indent() << "var " << ktype << " = 0;" << endl + << indent() << "var " << vtype << " = 0;" << endl; + + out << indent() << rtmp3 << " = input.readMapBegin();" << endl; + out << indent() << ktype << " = " << rtmp3 << ".ktype;" << endl; + out << indent() << vtype << " = " << rtmp3 << ".vtype;" << endl; + out << indent() << size << " = " << rtmp3 << ".size;" << endl; + + } else if (ttype->is_set()) { + + out << indent() << prefix << " = [];" << endl << indent() << "var " << etype << " = 0;" << endl + << indent() << rtmp3 << " = input.readSetBegin();" << endl << indent() << etype << " = " + << rtmp3 << ".etype;" << endl << indent() << size << " = " << rtmp3 << ".size;" << endl; + + } else if (ttype->is_list()) { + + out << indent() << prefix << " = [];" << endl << indent() << "var " << etype << " = 0;" << endl + << indent() << rtmp3 << " = input.readListBegin();" << endl << indent() << etype << " = " + << rtmp3 << ".etype;" << endl << indent() << size << " = " << rtmp3 << ".size;" << endl; + } + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (var " << i << " = 0; " << i << " < " << size << "; ++" << i << ")" << endl; + + scope_up(out); + + if (ttype->is_map()) { + if (!gen_node_) { + out << indent() << "if (" << i << " > 0 ) {" << endl << indent() + << " if (input.rstack.length > input.rpos[input.rpos.length -1] + 1) {" << endl + << indent() << " input.rstack.pop();" << endl << indent() << " }" << endl << indent() + << "}" << endl; + } + + 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); + + // Read container end + if (ttype->is_map()) { + indent(out) << "input.readMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "input.readSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "input.readListEnd();" << endl; + } +} + +/** + * Generates code to deserialize a map + */ +void t_js_generator::generate_deserialize_map_element(ofstream& 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, false, false) << ";" << endl; + indent(out) << declare_field(&fval, false, false) << ";" << endl; + + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); + + indent(out) << prefix << "[" << key << "] = " << val << ";" << endl; +} + +void t_js_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + string elem = tmp("elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(out) << "var " << elem << " = null;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << prefix << ".push(" << elem << ");" << endl; +} + +void t_js_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix) { + string elem = tmp("elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(out) << "var " << elem << " = null;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << prefix << ".push(" << elem << ");" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_js_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = tfield->get_name(); + + // Hack for when prefix is defined (always a hash ref) + if (!prefix.empty()) + name = prefix + tfield->get_name(); + + indent(out) << "output."; + + 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: + out << (((t_base_type*)type)->is_binary() ? "writeBinary(" : "writeString(") << name << ")"; + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ")"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ")"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ")"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ")"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ")"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ")"; + break; + default: + throw "compiler error: no JS name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ")"; + } + out << ";" << endl; + + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_js_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << prefix << ".write(output);" << endl; +} + +/** + * Writes out a container + */ +void t_js_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + if (ttype->is_map()) { + indent(out) << "output.writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "Thrift.objectLength(" << prefix << "));" << endl; + } else if (ttype->is_set()) { + indent(out) << "output.writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " + << prefix << ".length);" << endl; + + } else if (ttype->is_list()) { + + indent(out) << "output.writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ", " << prefix << ".length);" << endl; + } + + if (ttype->is_map()) { + string kiter = tmp("kiter"); + string viter = tmp("viter"); + indent(out) << "for (var " << kiter << " in " << prefix << ")" << endl; + scope_up(out); + indent(out) << "if (" << prefix << ".hasOwnProperty(" << kiter << "))" << endl; + scope_up(out); + indent(out) << "var " << viter << " = " << prefix << "[" << kiter << "];" << endl; + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + scope_down(out); + scope_down(out); + + } else if (ttype->is_set()) { + string iter = tmp("iter"); + indent(out) << "for (var " << iter << " in " << prefix << ")" << endl; + scope_up(out); + indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << "))" << endl; + scope_up(out); + indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl; + generate_serialize_set_element(out, (t_set*)ttype, iter); + scope_down(out); + scope_down(out); + + } else if (ttype->is_list()) { + string iter = tmp("iter"); + indent(out) << "for (var " << iter << " in " << prefix << ")" << endl; + scope_up(out); + indent(out) << "if (" << prefix << ".hasOwnProperty(" << iter << "))" << endl; + scope_up(out); + indent(out) << iter << " = " << prefix << "[" << iter << "];" << endl; + generate_serialize_list_element(out, (t_list*)ttype, iter); + scope_down(out); + scope_down(out); + } + + if (ttype->is_map()) { + indent(out) << "output.writeMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "output.writeSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "output.writeListEnd();" << endl; + } +} + +/** + * Serializes the members of a map. + * + */ +void t_js_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield); +} + +/** + * Serializes the members of a set. + */ +void t_js_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Serializes the members of a list. + */ +void t_js_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_js_generator::declare_field(t_field* tfield, bool init, bool obj) { + string result = "this." + tfield->get_name(); + + if (!obj) { + result = "var " + tfield->get_name(); + } + + if (init) { + t_type* type = get_true_type(tfield->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: + break; + case t_base_type::TYPE_STRING: + 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: + result += " = null"; + break; + default: + throw "compiler error: no JS initializer for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + result += " = null"; + } else if (type->is_map()) { + result += " = null"; + } else if (type->is_container()) { + result += " = null"; + } else if (type->is_struct() || type->is_xception()) { + if (obj) { + result += " = new " + js_type_namespace(type->get_program()) + type->get_name() + "()"; + } else { + result += " = null"; + } + } + } else { + result += " = null"; + } + return result; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_js_generator::function_signature(t_function* tfunction, + string prefix, + bool include_callback) { + + string str; + + str = prefix + tfunction->get_name() + " = function("; + + str += argument_list(tfunction->get_arglist(), include_callback); + + str += ")"; + return str; +} + +/** + * Renders a field list + */ +string t_js_generator::argument_list(t_struct* tstruct, bool include_callback) { + 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 += (*f_iter)->get_name(); + } + + if (include_callback) { + if (!fields.empty()) { + result += ", "; + } + result += "callback"; + } + + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_js_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 "Thrift.Type.STRING"; + case t_base_type::TYPE_BOOL: + return "Thrift.Type.BOOL"; + case t_base_type::TYPE_I8: + return "Thrift.Type.BYTE"; + case t_base_type::TYPE_I16: + return "Thrift.Type.I16"; + case t_base_type::TYPE_I32: + return "Thrift.Type.I32"; + case t_base_type::TYPE_I64: + return "Thrift.Type.I64"; + case t_base_type::TYPE_DOUBLE: + return "Thrift.Type.DOUBLE"; + } + } else if (type->is_enum()) { + return "Thrift.Type.I32"; + } else if (type->is_struct() || type->is_xception()) { + return "Thrift.Type.STRUCT"; + } else if (type->is_map()) { + return "Thrift.Type.MAP"; + } else if (type->is_set()) { + return "Thrift.Type.SET"; + } else if (type->is_list()) { + return "Thrift.Type.LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts a t_type to a TypeScript type (string). + * @param t_type Type to convert to TypeScript + * @return String TypeScript type + */ +string t_js_generator::ts_get_type(t_type* type) { + std::string ts_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_STRING: + ts_type = "string"; + break; + case t_base_type::TYPE_BOOL: + ts_type = "boolean"; + break; + case t_base_type::TYPE_I8: + ts_type = "any"; + 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_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()); + } + type_name.append(type->get_name()); + ts_type = type_name; + } 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(); + } + + ts_type = ts_get_type(etype) + "[]"; + } else if (type->is_map()) { + string ktype = ts_get_type(((t_map*)type)->get_key_type()); + string vtype = ts_get_type(((t_map*)type)->get_val_type()); + + + if (ktype == "number" || ktype == "string" ) { + ts_type = "{ [k: " + ktype + "]: " + vtype + "; }"; + } else if ((((t_map*)type)->get_key_type())->is_enum()) { + // Not yet supported (enum map): https://github.com/Microsoft/TypeScript/pull/2652 + //ts_type = "{ [k: " + ktype + "]: " + vtype + "; }"; + ts_type = "{ [k: number /*" + ktype + "*/]: " + vtype + "; }"; + } else { + ts_type = "any"; + } + } + + return ts_type; +} + +/** + * Renders a TypeScript function signature of the form 'name(args: types): type;' + * + * @param t_function Function definition + * @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) { + string str; + const vector& fields = tfunction->get_arglist()->get_members(); + vector::const_iterator f_iter; + + str = tfunction->get_name() + "("; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + str += (*f_iter)->get_name() + ts_get_req(*f_iter) + ": " + ts_get_type((*f_iter)->get_type()); + + if (f_iter + 1 != fields.end() || (include_callback && fields.size() > 0)) { + str += ", "; + } + } + + if (include_callback) { + str += "callback: Function): "; + + if (gen_jquery_) { + str += "JQueryXHR;"; + } else { + str += "void;"; + } + } else { + str += "): " + ts_get_type(tfunction->get_returntype()) + ";"; + } + + return str; +} + +/** + * Takes a name and produces a valid NodeJS identifier from it + * + * @param name The name which shall become a valid NodeJS identifier + * @return The modified name with the updated identifier + */ +std::string t_js_generator::make_valid_nodeJs_identifier(std::string const& name) { + std::string str = name; + 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) && ('$' != c)) { + str.replace(i, 1, "_"); + } + } + + return str; +} + +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") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc new file mode 100644 index 00000000..36e92166 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_json_generator.cc @@ -0,0 +1,727 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * 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 "thrift/platform.h" +#include "thrift/generate/t_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; +using std::stack; + +static const string endl = "\n"; +static const string quot = "\""; +static const bool NO_INDENT = false; +static const bool FORCE_STRING = true; + +class t_json_generator : public t_generator { +public: + t_json_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; + + should_merge_includes_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("merge") == 0) { + should_merge_includes_ = true; + } else { + throw "unknown option json:" + iter->first; + } + } + + out_dir_base_ = "gen-json"; + } + + virtual ~t_json_generator() {} + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + void generate_program(); + void generate_function(t_function* tfunc); + void generate_field(t_field* field); + + void generate_service(t_service* tservice); + void generate_struct(t_struct* tstruct); + +private: + bool should_merge_includes_; + + std::ofstream f_json_; + std::stack comma_needed_; + + template + string number_to_string(T t) { + std::ostringstream out; + out.imbue(std::locale::classic()); + out.precision(std::numeric_limits::digits10); + out << t; + return out.str(); + } + + template + void write_number(T n) { + f_json_ << number_to_string(n); + } + + string get_type_name(t_type* ttype); + string get_qualified_name(t_type* ttype); + + void start_object(bool should_indent = true); + void start_array(); + void end_object(); + void end_array(); + void write_comma_if_needed(); + void indicate_comma_needed(); + string escape_json_string(const string& input); + string json_str(const string& str); + void merge_includes(t_program*); + + void generate_constant(t_const* con); + + void write_type_spec_entry(const char* name, t_type* ttype); + void write_type_spec_object(const char* name, t_type* ttype); + void write_type_spec(t_type* ttype); + void write_string(const string& value); + void write_value(t_type* tvalue); + void write_const_value(t_const_value* value, bool force_string = false); + void write_key_and(string key); + void write_key_and_string(string key, string val); + void write_key_and_integer(string key, int val); + void write_key_and_bool(string key, bool val); +}; + +void t_json_generator::init_generator() { + MKDIR(get_out_dir().c_str()); + + string f_json_name = get_out_dir() + program_->get_name() + ".json"; + f_json_.open(f_json_name.c_str()); + + // Merge all included programs into this one so we can output one big file. + if (should_merge_includes_) { + merge_includes(program_); + } +} + +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) { + case '\\': + ss << "\\\\"; + break; + case '"': + ss << "\\\""; + break; + case '/': + ss << "\\/"; + break; + case '\b': + ss << "\\b"; + break; + case '\f': + ss << "\\f"; + break; + case '\n': + ss << "\\n"; + break; + case '\r': + ss << "\\r"; + break; + case '\t': + ss << "\\t"; + break; + default: + ss << *iter; + break; + } + } + return ss.str(); +} + +void t_json_generator::start_object(bool should_indent) { + f_json_ << (should_indent ? indent() : "") << "{" << endl; + indent_up(); + comma_needed_.push(false); +} + +void t_json_generator::start_array() { + f_json_ << "[" << endl; + indent_up(); + comma_needed_.push(false); +} + +void t_json_generator::write_comma_if_needed() { + if (comma_needed_.top()) { + f_json_ << "," << endl; + } +} + +void t_json_generator::indicate_comma_needed() { + comma_needed_.pop(); + comma_needed_.push(true); +} + +void t_json_generator::write_key_and(string key) { + write_comma_if_needed(); + indent(f_json_) << json_str(key) << ": "; + indicate_comma_needed(); +} + +void t_json_generator::write_key_and_integer(string key, int val) { + write_comma_if_needed(); + indent(f_json_) << json_str(key) << ": " << number_to_string(val); + indicate_comma_needed(); +} + +void t_json_generator::write_key_and_string(string key, string val) { + write_comma_if_needed(); + indent(f_json_) << json_str(key) << ": " << json_str(val); + indicate_comma_needed(); +} + +void t_json_generator::write_key_and_bool(string key, bool val) { + write_comma_if_needed(); + indent(f_json_) << json_str(key) << ": " << (val ? "true" : "false"); + indicate_comma_needed(); +} + +void t_json_generator::end_object() { + indent_down(); + f_json_ << endl << indent() << "}"; + comma_needed_.pop(); +} + +void t_json_generator::end_array() { + indent_down(); + if (comma_needed_.top()) { + f_json_ << endl; + } + indent(f_json_) << "]"; + comma_needed_.pop(); +} + +void t_json_generator::write_type_spec_object(const char* name, t_type* ttype) { + ttype = ttype->get_true_type(); + if (ttype->is_struct() || ttype->is_xception() || ttype->is_container()) { + write_key_and(name); + start_object(NO_INDENT); + write_key_and("typeId"); + write_type_spec(ttype); + end_object(); + } +} + +void t_json_generator::write_type_spec_entry(const char* name, t_type* ttype) { + write_key_and(name); + write_type_spec(ttype); +} + +void t_json_generator::write_type_spec(t_type* ttype) { + ttype = ttype->get_true_type(); + + write_string(get_type_name(ttype)); + + if (ttype->is_struct() || ttype->is_xception()) { + write_key_and_string("class", get_qualified_name(ttype)); + } else if (ttype->is_map()) { + t_type* ktype = ((t_map*)ttype)->get_key_type(); + t_type* vtype = ((t_map*)ttype)->get_val_type(); + write_key_and_string("keyTypeId", get_type_name(ktype)); + write_key_and_string("valueTypeId", get_type_name(vtype)); + write_type_spec_object("keyType", ktype); + write_type_spec_object("valueType", vtype); + } else if (ttype->is_list() || ttype->is_set()) { + t_type* etype = ((t_list*)ttype)->get_elem_type(); + write_key_and_string("elemTypeId", get_type_name(etype)); + write_type_spec_object("elemType", etype); + } +} + +void t_json_generator::close_generator() { + f_json_ << endl; + f_json_.close(); +} + +void t_json_generator::merge_includes(t_program* program) { + vector includes = program->get_includes(); + vector::iterator inc_iter; + for (inc_iter = includes.begin(); inc_iter != includes.end(); ++inc_iter) { + t_program* include = *inc_iter; + // recurse in case we get crazy + merge_includes(include); + // merge enums + vector enums = include->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + program->add_enum(*en_iter); + } + // merge typedefs + vector typedefs = include->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + program->add_typedef(*td_iter); + } + // merge structs + vector objects = include->get_objects(); + vector::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + program->add_struct(*o_iter); + } + // merge constants + vector consts = include->get_consts(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + program->add_const(*c_iter); + } + + // merge services + vector services = include->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + program->add_service(*sv_iter); + } + } +} + +void t_json_generator::generate_program() { + + init_generator(); + + start_object(); + write_key_and_string("name", program_->get_name()); + if (program_->has_doc()) { + write_key_and_string("doc", program_->get_doc()); + } + + // When merging includes, the "namespaces" and "includes" sections + // become ambiguous, so just skip them. + if (!should_merge_includes_) { + // Generate namespaces + write_key_and("namespaces"); + start_object(NO_INDENT); + const map& namespaces = program_->get_namespaces(); + map::const_iterator ns_it; + for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) { + write_key_and_string(ns_it->first, ns_it->second); + indicate_comma_needed(); + } + end_object(); + + // Generate includes + write_key_and("includes"); + start_array(); + const vector includes = program_->get_includes(); + vector::const_iterator inc_it; + for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) { + write_comma_if_needed(); + write_string((*inc_it)->get_name()); + indicate_comma_needed(); + } + end_array(); + } + + // Generate enums + write_key_and("enums"); + start_array(); + vector enums = program_->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + write_comma_if_needed(); + generate_enum(*en_iter); + indicate_comma_needed(); + } + end_array(); + + // Generate typedefs + write_key_and("typedefs"); + start_array(); + vector typedefs = program_->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + write_comma_if_needed(); + generate_typedef(*td_iter); + indicate_comma_needed(); + } + end_array(); + + // Generate structs, exceptions, and unions in declared order + write_key_and("structs"); + start_array(); + vector objects = program_->get_objects(); + vector::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + write_comma_if_needed(); + if ((*o_iter)->is_xception()) { + generate_xception(*o_iter); + } else { + generate_struct(*o_iter); + } + indicate_comma_needed(); + } + end_array(); + + // Generate constants + write_key_and("constants"); + start_array(); + vector consts = program_->get_consts(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + write_comma_if_needed(); + generate_constant(*c_iter); + indicate_comma_needed(); + } + end_array(); + + // Generate services + write_key_and("services"); + start_array(); + vector services = program_->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + write_comma_if_needed(); + generate_service(*sv_iter); + indicate_comma_needed(); + } + end_array(); + + end_object(); + + // Close the generator + close_generator(); +} + +void t_json_generator::generate_typedef(t_typedef* ttypedef) { + start_object(); + write_key_and_string("name", get_qualified_name(ttypedef)); + write_key_and_string("typeId", get_type_name(ttypedef->get_true_type())); + write_type_spec_object("type", ttypedef->get_true_type()); + if (ttypedef->has_doc()) { + write_key_and_string("doc", ttypedef->get_doc()); + } + end_object(); +} + +void t_json_generator::write_string(const string& value) { + f_json_ << quot << escape_json_string(value) << quot; +} + +void t_json_generator::write_const_value(t_const_value* value, bool should_force_string) { + + switch (value->get_type()) { + + case t_const_value::CV_IDENTIFIER: + case t_const_value::CV_INTEGER: + if (should_force_string) { + write_string(number_to_string(value->get_integer())); + } else { + write_number(value->get_integer()); + } + break; + + case t_const_value::CV_DOUBLE: + if (should_force_string) { + write_string(number_to_string(value->get_double())); + } else { + write_number(value->get_double()); + } + break; + + case t_const_value::CV_STRING: + write_string(value->get_string()); + break; + + case t_const_value::CV_LIST: { + start_array(); + std::vector list = value->get_list(); + std::vector::iterator lit; + for (lit = list.begin(); lit != list.end(); ++lit) { + write_comma_if_needed(); + f_json_ << indent(); + write_const_value(*lit); + indicate_comma_needed(); + } + end_array(); + break; + } + + case t_const_value::CV_MAP: { + start_object(NO_INDENT); + std::map map = value->get_map(); + std::map::iterator mit; + for (mit = map.begin(); mit != map.end(); ++mit) { + write_comma_if_needed(); + f_json_ << indent(); + // JSON objects only allow string keys + write_const_value(mit->first, FORCE_STRING); + f_json_ << ": "; + write_const_value(mit->second); + indicate_comma_needed(); + } + end_object(); + break; + } + + default: + f_json_ << "null"; + break; + } +} + +string t_json_generator::json_str(const string& str) { + return quot + escape_json_string(str) + quot; +} + +void t_json_generator::generate_constant(t_const* con) { + start_object(); + + write_key_and_string("name", con->get_name()); + write_key_and_string("typeId", get_type_name(con->get_type())); + write_type_spec_object("type", con->get_type()); + + if (con->has_doc()) { + write_key_and_string("doc", con->get_doc()); + } + + write_key_and("value"); + write_const_value(con->get_value()); + + end_object(); +} + +void t_json_generator::generate_enum(t_enum* tenum) { + start_object(); + + write_key_and_string("name", tenum->get_name()); + + if (tenum->has_doc()) { + write_key_and_string("doc", tenum->get_doc()); + } + + write_key_and("members"); + start_array(); + vector values = tenum->get_constants(); + vector::iterator val_iter; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + write_comma_if_needed(); + t_enum_value* val = (*val_iter); + start_object(); + write_key_and_string("name", val->get_name()); + write_key_and_integer("value", val->get_value()); + if (val->has_doc()) { + write_key_and_string("doc", val->get_doc()); + } + end_object(); + indicate_comma_needed(); + } + end_array(); + + end_object(); +} + +void t_json_generator::generate_struct(t_struct* tstruct) { + start_object(); + + write_key_and_string("name", tstruct->get_name()); + + if (tstruct->has_doc()) { + write_key_and_string("doc", tstruct->get_doc()); + } + + write_key_and_bool("isException", tstruct->is_xception()); + + write_key_and_bool("isUnion", tstruct->is_union()); + + write_key_and("fields"); + start_array(); + vector members = tstruct->get_members(); + vector::iterator mem_iter; + for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) { + write_comma_if_needed(); + generate_field(*mem_iter); + indicate_comma_needed(); + } + end_array(); + + end_object(); +} + +void t_json_generator::generate_service(t_service* tservice) { + start_object(); + + write_key_and_string("name", get_qualified_name(tservice)); + + if (tservice->get_extends()) { + write_key_and_string("extends", get_qualified_name(tservice->get_extends())); + } + + if (tservice->has_doc()) { + write_key_and_string("doc", tservice->get_doc()); + } + + write_key_and("functions"); + start_array(); + vector functions = tservice->get_functions(); + vector::iterator fn_iter = functions.begin(); + for (; fn_iter != functions.end(); fn_iter++) { + write_comma_if_needed(); + generate_function(*fn_iter); + indicate_comma_needed(); + } + end_array(); + + end_object(); +} + +void t_json_generator::generate_function(t_function* tfunc) { + start_object(); + + write_key_and_string("name", tfunc->get_name()); + + write_key_and_string("returnTypeId", get_type_name(tfunc->get_returntype())); + write_type_spec_object("returnType", tfunc->get_returntype()); + + write_key_and_bool("oneway", tfunc->is_oneway()); + + if (tfunc->has_doc()) { + write_key_and_string("doc", tfunc->get_doc()); + } + + write_key_and("arguments"); + start_array(); + vector members = tfunc->get_arglist()->get_members(); + vector::iterator mem_iter = members.begin(); + for (; mem_iter != members.end(); mem_iter++) { + write_comma_if_needed(); + generate_field(*mem_iter); + indicate_comma_needed(); + } + end_array(); + + write_key_and("exceptions"); + start_array(); + vector excepts = tfunc->get_xceptions()->get_members(); + vector::iterator ex_iter = excepts.begin(); + for (; ex_iter != excepts.end(); ex_iter++) { + write_comma_if_needed(); + generate_field(*ex_iter); + indicate_comma_needed(); + } + end_array(); + + end_object(); +} + +void t_json_generator::generate_field(t_field* field) { + start_object(); + + write_key_and_integer("key", field->get_key()); + write_key_and_string("name", field->get_name()); + write_key_and_string("typeId", get_type_name(field->get_type())); + write_type_spec_object("type", field->get_type()); + + if (field->has_doc()) { + write_key_and_string("doc", field->get_doc()); + } + + write_key_and("required"); + switch (field->get_req()) { + case t_field::T_REQUIRED: + write_string("required"); + break; + case t_field::T_OPT_IN_REQ_OUT: + write_string("req_out"); + break; + default: + write_string("optional"); + break; + } + + if (field->get_value()) { + write_key_and("default"); + write_const_value(field->get_value()); + } + + end_object(); +} + +string t_json_generator::get_type_name(t_type* ttype) { + ttype = ttype->get_true_type(); + if (ttype->is_list()) { + return "list"; + } + if (ttype->is_set()) { + return "set"; + } + if (ttype->is_map()) { + return "map"; + } + if (ttype->is_enum()) { + return "i32"; + } + if (ttype->is_struct()) { + return ((t_struct*)ttype)->is_union() ? "union" : "struct"; + } + if (ttype->is_xception()) { + return "exception"; + } + if (ttype->is_base_type()) { + t_base_type* tbasetype = (t_base_type*)ttype; + return tbasetype->is_binary() ? "binary" : t_base_type::t_base_name(tbasetype->get_base()); + } + + return "(unknown)"; +} + +string t_json_generator::get_qualified_name(t_type* ttype) { + if (should_merge_includes_ || ttype->get_program() == program_) { + return ttype->get_name(); + } + return ttype->get_program()->get_name() + "." + ttype->get_name(); +} + +THRIFT_REGISTER_GENERATOR(json, + "JSON", + " merge: Generate output with included files merged\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc new file mode 100644 index 00000000..97b8aa3e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_lua_generator.cc @@ -0,0 +1,1138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::ofstream; +using std::string; +using std::vector; +using std::map; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * LUA code generator. + * + */ +class t_lua_generator : public t_oop_generator { +public: + t_lua_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; + + gen_requires_ = true; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("omit_requires") == 0) { + gen_requires_ = false; + } else { + throw "unknown option lua:" + iter->first; + } + } + + out_dir_base_ = "gen-lua"; + } + + /** + * Init and close methods + */ + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + std::string render_const_value(t_type* type, t_const_value* value); + +private: + /** + * True iff we should generate lua require statements. + */ + bool gen_requires_; + + /** + * Struct-level generation functions + */ + void generate_lua_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false); + void generate_lua_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_lua_struct_writer(std::ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + void generate_service_client(std::ofstream& out, t_service* tservice); + void generate_service_interface(std::ofstream& out, t_service* tservice); + void generate_service_processor(std::ofstream& out, t_service* tservice); + void generate_process_function(std::ofstream& out, t_service* tservice, t_function* tfunction); + void generate_service_helpers(ofstream& out, t_service* tservice); + void generate_function_helpers(ofstream& out, t_function* tfunction); + + /** + * Deserialization (Read) + */ + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + bool local, + std::string prefix = ""); + + void generate_deserialize_struct(std::ofstream& out, + t_struct* tstruct, + bool local, + std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, + t_type* ttype, + bool local, + std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + /** + * Serialization (Write) + */ + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + /** + * Helper rendering functions + */ + std::string lua_includes(); + std::string function_signature(t_function* tfunction, std::string prefix = ""); + std::string argument_list(t_struct* tstruct, std::string prefix = ""); + std::string type_to_enum(t_type* ttype); + static std::string get_namespace(const t_program* program); + + std::string autogen_comment() { + 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" + + "--\n"; + } + + /** + * File streams + */ + std::ofstream f_types_; + std::ofstream f_consts_; + std::ofstream f_service_; +}; + +/** + * Init and close methods + */ +void t_lua_generator::init_generator() { + // Make output directory + string outdir = get_out_dir(); + MKDIR(outdir.c_str()); + + // Make output files + string cur_namespace = get_namespace(program_); + string f_consts_name = outdir + cur_namespace + "constants.lua"; + f_consts_.open(f_consts_name.c_str()); + string f_types_name = outdir + cur_namespace + "ttypes.lua"; + f_types_.open(f_types_name.c_str()); + + // Add headers + f_consts_ << autogen_comment() << lua_includes(); + f_types_ << autogen_comment() << lua_includes(); + if (gen_requires_) { + f_types_ << endl << "require '" << cur_namespace << "constants'"; + } +} + +void t_lua_generator::close_generator() { + // Close types file + f_types_.close(); + f_consts_.close(); +} + +/** + * Generate a typedef (essentially a constant) + */ +void t_lua_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << endl << endl << indent() << ttypedef->get_symbolic() << " = " + << ttypedef->get_type()->get_name(); +} + +/** + * Generates code for an enumerated type (table) + */ +void t_lua_generator::generate_enum(t_enum* tenum) { + f_types_ << endl << endl << tenum->get_name() << " = {" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end();) { + int32_t value = (*c_iter)->get_value(); + + f_types_ << " " << (*c_iter)->get_name() << " = " << value; + ++c_iter; + if (c_iter != constants.end()) { + f_types_ << ","; + } + f_types_ << endl; + } + f_types_ << "}"; +} + +/** + * Generate a constant (non-local) value + */ +void t_lua_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + f_consts_ << endl << endl << name << " = "; + f_consts_ << render_const_value(type, value); +} + +/** + * Prints the value of a constant with the given type. + */ +string t_lua_generator::render_const_value(t_type* type, t_const_value* value) { + std::ostringstream out; + + 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_STRING: + out << "'" << value->get_string() << "'"; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_I64: + out << "lualongnumber.new('" << value->get_string() << "')"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << type->get_name() << " = {" << endl; + indent_up(); + + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + + indent(out); + out << render_const_value(g_type_string, v_iter->first); + out << " = "; + out << render_const_value(field_type, v_iter->second); + ++v_iter; + if (v_iter != val.end()) { + out << ","; + } + } + + out << "}"; + indent_down(); + } else if (type->is_map()) { + out << type->get_name() << "{" << endl; + indent_up(); + + 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();) { + indent(out) << "[" << render_const_value(ktype, v_iter->first) + << "] = " << render_const_value(vtype, v_iter->second); + ++v_iter; + if (v_iter != val.end()) { + out << ","; + } + out << endl; + } + indent_down(); + indent(out) << "}"; + } 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(); + } + out << type->get_name() << " = {" << endl; + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end();) { + indent(out); + out << "[" << render_const_value(etype, *v_iter) << "]"; + if (type->is_set()) { + out << " = true"; + } else { + out << " = false"; + } + ++v_iter; + if (v_iter != val.end()) { + out << "," << endl; + } + } + out << "}"; + } + return out.str(); +} + +/** + * Generate a thrift struct + */ +void t_lua_generator::generate_struct(t_struct* tstruct) { + generate_lua_struct_definition(f_types_, tstruct, false); +} + +/** + * Generate a thrift exception + */ +void t_lua_generator::generate_xception(t_struct* txception) { + generate_lua_struct_definition(f_types_, txception, true); +} + +/** + * Generate a thrift struct or exception (lua table) + */ +void t_lua_generator::generate_lua_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception) { + vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + + indent(out) << endl << endl << tstruct->get_name(); + if (is_exception) { + out << " = TException:new{" << endl << indent() << " __type = '" << tstruct->get_name() << "'"; + if (members.size() > 0) { + out << ","; + } + out << endl; + } else { + out << " = __TObject:new{" << endl; + } + indent_up(); + for (m_iter = members.begin(); m_iter != members.end();) { + indent(out); + out << (*m_iter)->get_name(); + ++m_iter; + if (m_iter != members.end()) { + out << "," << endl; + } + } + indent_down(); + indent(out); + out << endl << "}"; + + generate_lua_struct_reader(out, tstruct); + generate_lua_struct_writer(out, tstruct); +} + +/** + * Generate a struct/exception reader + */ +void t_lua_generator::generate_lua_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // function + indent(out) << endl << endl << "function " << tstruct->get_name() << ":read(iprot)" << endl; + indent_up(); + + indent(out) << "iprot:readStructBegin()" << endl; + + // while: Read in fields + indent(out) << "while true do" << endl; + indent_up(); + + // if: Check what to read + indent(out) << "local fname, ftype, fid = iprot:readFieldBegin()" << endl; + indent(out) << "if ftype == TType.STOP then" << endl; + indent_up(); + indent(out) << "break" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent_down(); + indent(out) << "elseif fid == " << (*f_iter)->get_key() << " then" << endl; + indent_up(); + indent(out) << "if ftype == " << type_to_enum((*f_iter)->get_type()) << " then" << endl; + indent_up(); + + // Read field contents + generate_deserialize_field(out, *f_iter, false, "self."); + + indent_down(); + indent(out) << "else" << endl; + indent(out) << " iprot:skip(ftype)" << endl; + indent(out) << "end" << endl; + } + + // end if + indent_down(); + indent(out) << "else" << endl; + indent(out) << " iprot:skip(ftype)" << endl; + indent(out) << "end" << endl; + indent(out) << "iprot:readFieldEnd()" << endl; + + // end while + indent_down(); + indent(out) << "end" << endl; + indent(out) << "iprot:readStructEnd()" << endl; + + // end function + indent_down(); + indent(out); + out << "end"; +} + +/** + * Generate a struct/exception writer + */ +void t_lua_generator::generate_lua_struct_writer(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + // function + indent(out) << endl << endl << "function " << tstruct->get_name() << ":write(oprot)" << endl; + indent_up(); + + indent(out) << "oprot:writeStructBegin('" << tstruct->get_name() << "')" << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // To check element of self whether nil or not. + // avoid the value(false) of BOOL is lost. + indent(out) << "if self." << (*f_iter)->get_name() << " ~= nil then" << endl; + indent_up(); + indent(out) << "oprot:writeFieldBegin('" << (*f_iter)->get_name() << "', " + << type_to_enum((*f_iter)->get_type()) << ", " << (*f_iter)->get_key() << ")" + << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "self."); + + indent(out) << "oprot:writeFieldEnd()" << endl; + indent_down(); + indent(out) << "end" << endl; + } + indent(out) << "oprot:writeFieldStop()" << endl; + indent(out) << "oprot:writeStructEnd()" << endl; + + // end function + indent_down(); + indent(out); + out << "end"; +} + +/** + * Generate a thrift service + */ +void t_lua_generator::generate_service(t_service* tservice) { + // Get output directory + string outdir = get_out_dir(); + + // Open the file for writing + string cur_ns = get_namespace(program_); + string f_service_name = outdir + cur_ns + tservice->get_name() + ".lua"; + f_service_.open(f_service_name.c_str()); + + // Headers + f_service_ << autogen_comment() << lua_includes(); + if (gen_requires_) { + f_service_ << endl << "require '" << cur_ns << "ttypes'" << endl; + + if (tservice->get_extends() != NULL) { + f_service_ << "require '" << get_namespace(tservice->get_extends()->get_program()) + << tservice->get_extends()->get_name() << "'" << endl; + } + } + + f_service_ << endl; + + generate_service_client(f_service_, tservice); + generate_service_interface(f_service_, tservice); + generate_service_processor(f_service_, tservice); + generate_service_helpers(f_service_, tservice); + + // Close the file + f_service_.close(); +} + +void t_lua_generator::generate_service_interface(ofstream& out, t_service* tservice) { + string classname = tservice->get_name() + "Iface"; + t_service* extends_s = tservice->get_extends(); + + // Interface object definition + out << classname << " = "; + if (extends_s) { + out << extends_s->get_name() << "Iface:new{" << endl; + } else { + out << "__TObject:new{" << endl; + } + out << " __type = '" << classname << "'" << endl << "}" << endl << endl; +} + +void t_lua_generator::generate_service_client(ofstream& out, t_service* tservice) { + string classname = tservice->get_name() + "Client"; + t_service* extends_s = tservice->get_extends(); + + // Client object definition + out << classname << " = __TObject.new("; + if (extends_s != NULL) { + out << extends_s->get_name() << "Client"; + } else { + out << "__TClient"; + } + out << ", {" << endl << " __type = '" << classname << "'" << endl << "})" << endl; + + // Send/Recv functions + vector functions = tservice->get_functions(); + vector::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string sig = function_signature(*f_iter); + string funcname = (*f_iter)->get_name(); + + // Wrapper function + indent(out) << endl << "function " << classname << ":" << sig << endl; + indent_up(); + + indent(out) << "self:send_" << sig << endl << indent(); + if (!(*f_iter)->is_oneway()) { + if (!(*f_iter)->get_returntype()->is_void()) { + out << "return "; + } + out << "self:recv_" << sig << endl; + } + + indent_down(); + indent(out) << "end" << endl; + + // Send function + indent(out) << endl << "function " << classname << ":send_" << sig << endl; + indent_up(); + + indent(out) << "self.oprot:writeMessageBegin('" << funcname << "', " + << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") + << ", self._seqid)" << endl; + indent(out) << "local args = " << funcname << "_args:new{}" << endl; + + // Set the args + const vector& args = (*f_iter)->get_arglist()->get_members(); + vector::const_iterator fld_iter; + for (fld_iter = args.begin(); fld_iter != args.end(); ++fld_iter) { + std::string argname = (*fld_iter)->get_name(); + indent(out) << "args." << argname << " = " << argname << endl; + } + + indent(out) << "args:write(self.oprot)" << endl; + indent(out) << "self.oprot:writeMessageEnd()" << endl; + indent(out) << "self.oprot.trans:flush()" << endl; + + indent_down(); + indent(out) << "end" << endl; + + // Recv function + if (!(*f_iter)->is_oneway()) { + indent(out) << endl << "function " << classname << ":recv_" << sig << endl; + indent_up(); + + out << indent() << "local fname, mtype, rseqid = self.iprot:" + << "readMessageBegin()" << endl << indent() << "if mtype == TMessageType.EXCEPTION then" + << endl << indent() << " local x = TApplicationException:new{}" << endl << indent() + << " x:read(self.iprot)" << endl << indent() << " self.iprot:readMessageEnd()" << endl + << indent() << " error(x)" << endl << indent() << "end" << endl << indent() + << "local result = " << funcname << "_result:new{}" << endl << indent() + << "result:read(self.iprot)" << endl << indent() << "self.iprot:readMessageEnd()" << endl; + + // Return the result if it's not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + out << indent() << "if result.success ~= nil then" << endl << indent() << " return result.success" + << endl; + + // Throw custom exceptions + const std::vector& xf = (*f_iter)->get_xceptions()->get_members(); + vector::const_iterator x_iter; + for (x_iter = xf.begin(); x_iter != xf.end(); ++x_iter) { + out << indent() << "elseif result." << (*x_iter)->get_name() << " then" << endl + << indent() << " error(result." << (*x_iter)->get_name() << ")" << endl; + } + + out << indent() << "end" << endl << indent() + << "error(TApplicationException:new{errorCode = " + << "TApplicationException.MISSING_RESULT})" << endl; + } + + indent_down(); + indent(out) << "end" << endl; + } + } +} + +void t_lua_generator::generate_service_processor(ofstream& out, t_service* tservice) { + string classname = tservice->get_name() + "Processor"; + t_service* extends_s = tservice->get_extends(); + + // Define processor table + out << endl << classname << " = __TObject.new("; + if (extends_s != NULL) { + out << extends_s << "Processor" << endl; + } else { + out << "__TProcessor" << endl; + } + out << ", {" << endl << " __type = '" << classname << "'" << endl << "})" << endl; + + // Process function + indent(out) << endl << "function " << classname << ":process(iprot, oprot, server_ctx)" << endl; + indent_up(); + + 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_up(); + out << endl << indent() << "iprot:skip(TType.STRUCT)" << endl << indent() + << "iprot:readMessageEnd()" << endl << indent() << "x = TApplicationException:new{" << endl + << indent() << " errorCode = TApplicationException.UNKNOWN_METHOD" << endl << indent() << "}" + << endl << indent() << "oprot:writeMessageBegin(name, TMessageType.EXCEPTION, " + << "seqid)" << endl << indent() << "x:write(oprot)" << endl << indent() + << "oprot:writeMessageEnd()" << endl << indent() << "oprot.trans:flush()" << endl; + indent_down(); + indent(out) << "else" << endl << indent() + << " self[func_name](self, seqid, iprot, oprot, server_ctx)" << endl << indent() + << "end" << endl; + + indent_down(); + indent(out) << "end" << endl; + + // Generate the process subfunctions + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(out, tservice, *f_iter); + } +} + +void t_lua_generator::generate_process_function(ofstream& out, + t_service* tservice, + t_function* tfunction) { + string classname = tservice->get_name() + "Processor"; + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; + string fn_name = tfunction->get_name(); + + indent(out) << endl << "function " << classname << ":process_" << fn_name + << "(seqid, iprot, oprot, server_ctx)" << endl; + indent_up(); + + // 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"; + + // Print arguments + t_struct* args = tfunction->get_arglist(); + if (args->get_members().size() > 0) { + out << ", " << argument_list(args, "args."); + } + + // 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; + + indent_down(); + indent(out) << "end" << endl; +} + +// Service helpers +void t_lua_generator::generate_service_helpers(ofstream& out, t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + out << endl << "-- HELPER FUNCTIONS AND STRUCTURES"; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_lua_struct_definition(out, ts, false); + generate_function_helpers(out, *f_iter); + } +} + +void t_lua_generator::generate_function_helpers(ofstream& out, t_function* tfunction) { + if (!tfunction->is_oneway()) { + 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_lua_struct_definition(out, &result, false); + } +} + +/** + * Deserialize (Read) + */ +void t_lua_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + bool local, + string prefix) { + 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(); + } + + string name = prefix + tfield->get_name(); + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type, local, name); + } else if (type->is_container()) { + generate_deserialize_container(out, type, local, name); + } else if (type->is_base_type() || type->is_enum()) { + indent(out) << (local ? "local " : "") << name << " = 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: + 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 PHP 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->get_name().c_str()); + } +} + +void t_lua_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + bool local, + string prefix) { + indent(out) << (local ? "local " : "") << prefix << " = " << tstruct->get_name() << ":new{}" + << endl << indent() << prefix << ":read(iprot)" << endl; +} + +void t_lua_generator::generate_deserialize_container(ofstream& out, + t_type* ttype, + bool local, + string prefix) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + // Declare variables, read header + indent(out) << (local ? "local " : "") << prefix << " = {}" << endl; + if (ttype->is_map()) { + indent(out) << "local " << ktype << ", " << vtype << ", " << size << " = iprot:readMapBegin() " + << endl; + } else if (ttype->is_set()) { + indent(out) << "local " << etype << ", " << size << " = iprot:readSetBegin()" << endl; + } else if (ttype->is_list()) { + indent(out) << "local " << etype << ", " << size << " = iprot:readListBegin()" << endl; + } + + // Deserialize + indent(out) << "for _i=1," << size << " do" << endl; + indent_up(); + + 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); + } + + indent_down(); + indent(out) << "end" << endl; + + // Read container end + 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; + } +} + +void t_lua_generator::generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix) { + // A map is represented by a table indexable by any lua type + string key = tmp("_key"); + string val = tmp("_val"); + t_field fkey(tmap->get_key_type(), key); + t_field fval(tmap->get_val_type(), val); + + generate_deserialize_field(out, &fkey, true); + generate_deserialize_field(out, &fval, true); + + indent(out) << prefix << "[" << key << "] = " << val << endl; +} + +void t_lua_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + // A set is represented by a table indexed by the value + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + generate_deserialize_field(out, &felem, true); + + indent(out) << prefix << "[" << elem << "] = " << elem << endl; +} + +void t_lua_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix) { + // A list is represented by a table indexed by integer values + // LUA natively provides all of the functions required to maintain a list + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + generate_deserialize_field(out, &felem, true); + + indent(out) << "table.insert(" << prefix << ", " << elem << ")" << endl; +} + +/** + * Serialize (Write) + */ +void t_lua_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = get_true_type(tfield->get_type()); + string name = prefix + tfield->get_name(); + + // Do nothing for void types + 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:"; + + 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: + out << "writeString(" << name << ")"; + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ")"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ")"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ")"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ")"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ")"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ")"; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ")"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", + name.c_str(), + type->get_name().c_str()); + } +} + +void t_lua_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << prefix << ":write(oprot)" << endl; +} + +void t_lua_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + // Begin writing + if (ttype->is_map()) { + indent(out) << "oprot:writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "ttable_size(" << prefix << "))" << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot:writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " + << "ttable_size(" << prefix << "))" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot:writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ", " + << "#" << prefix << ")" << endl; + } + + // Serialize + if (ttype->is_map()) { + string kiter = tmp("kiter"); + string viter = tmp("viter"); + indent(out) << "for " << kiter << "," << viter << " in pairs(" << prefix << ") do" << endl; + indent_up(); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + indent_down(); + indent(out) << "end" << endl; + } else if (ttype->is_set()) { + string iter = tmp("iter"); + indent(out) << "for " << iter << ",_ in pairs(" << prefix << ") do" << endl; + indent_up(); + generate_serialize_set_element(out, (t_set*)ttype, iter); + indent_down(); + indent(out) << "end" << endl; + } else if (ttype->is_list()) { + string iter = tmp("iter"); + indent(out) << "for _," << iter << " in ipairs(" << prefix << ") do" << endl; + indent_up(); + generate_serialize_list_element(out, (t_list*)ttype, iter); + indent_down(); + indent(out) << "end" << endl; + } + + // Finish writing + 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; + } +} + +void t_lua_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield, ""); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield, ""); +} + +void t_lua_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +void t_lua_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Helper rendering functions + */ +string t_lua_generator::lua_includes() { + if (gen_requires_) { + return "\n\nrequire 'Thrift'"; + } else { + return ""; + } +} + +string t_lua_generator::get_namespace(const t_program* program) { + std::string real_module = program->get_namespace("lua"); + if (real_module.empty()) { + return program->get_name() + "_"; + } + return real_module + "_"; +} + +string t_lua_generator::function_signature(t_function* tfunction, string prefix) { + (void)prefix; + std::string ret = tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")"; + return ret; +} + +string t_lua_generator::argument_list(t_struct* tstruct, string prefix) { + const vector& fields = tstruct->get_members(); + vector::const_iterator fld_iter; + std::string ret = ""; + for (fld_iter = fields.begin(); fld_iter != fields.end();) { + ret += prefix + (*fld_iter)->get_name(); + ++fld_iter; + if (fld_iter != fields.end()) { + ret += ", "; + } + } + return ret; +} + +string t_lua_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 "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(); +} + +THRIFT_REGISTER_GENERATOR( + lua, + "Lua", + " omit_requires: Suppress generation of require 'somefile'.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc new file mode 100644 index 00000000..594219ae --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_ocaml_generator.cc @@ -0,0 +1,1762 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/version.h" +#include "thrift/generate/t_oop_generator.h" + +using std::ios; +using std::map; +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 + +/** + * OCaml code generator. + * + */ +class t_ocaml_generator : public t_oop_generator { +public: + t_ocaml_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; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option ocaml:" + iter->first; + } + + out_dir_base_ = "gen-ocaml"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + /** + * 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); + + std::string render_const_value(t_type* type, t_const_value* value); + bool struct_member_persistent(t_field* tmember); + bool struct_member_omitable(t_field* tmember); + bool struct_member_default_cheaply_comparable(t_field* tmember); + std::string struct_member_copy_of(t_type* type, string what); + + /** + * Struct generation code + */ + + void generate_ocaml_struct(t_struct* tstruct, bool is_exception); + void generate_ocaml_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false); + void generate_ocaml_struct_member(std::ofstream& out, string tname, t_field* tmember); + void generate_ocaml_struct_sig(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_ocaml_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_ocaml_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_ocaml_function_helpers(t_function* tfunction); + void generate_ocaml_method_copy(std::ofstream& out, const vector& members); + void generate_ocaml_member_copy(std::ofstream& out, t_field* member); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(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::ofstream& out, t_field* tfield, std::string prefix); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + void generate_deserialize_type(std::ofstream& out, t_type* type); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string name = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + /** + * Helper rendering functions + */ + + std::string ocaml_autogen_comment(); + std::string ocaml_imports(); + std::string type_name(t_type* ttype); + std::string function_signature(t_function* tfunction, std::string prefix = ""); + std::string function_type(t_function* tfunc, bool method = false, bool options = false); + std::string argument_list(t_struct* tstruct); + std::string type_to_enum(t_type* ttype); + std::string render_ocaml_type(t_type* type); + +private: + /** + * File streams + */ + + std::ofstream f_types_; + std::ofstream f_consts_; + std::ofstream f_service_; + + std::ofstream f_types_i_; + std::ofstream f_service_i_; +}; + +/* + * This is necessary because we want typedefs to appear later, + * after all the types have been declared. + */ +void t_ocaml_generator::generate_program() { + // Initialize the generator + init_generator(); + + // 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); + } + + // Generate structs + vector structs = program_->get_structs(); + vector::iterator st_iter; + for (st_iter = structs.begin(); st_iter != structs.end(); ++st_iter) { + generate_struct(*st_iter); + } + + // Generate xceptions + vector xceptions = program_->get_xceptions(); + vector::iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + generate_xception(*x_iter); + } + + // 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); + } + + // 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); + } + + // Generate constants + vector consts = program_->get_consts(); + generate_consts(consts); + + // Close the generator + close_generator(); +} + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_ocaml_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Make output file + string f_types_name = get_out_dir() + program_name_ + "_types.ml"; + f_types_.open(f_types_name.c_str()); + string f_types_i_name = get_out_dir() + program_name_ + "_types.mli"; + f_types_i_.open(f_types_i_name.c_str()); + + string f_consts_name = get_out_dir() + program_name_ + "_consts.ml"; + f_consts_.open(f_consts_name.c_str()); + + // Print header + f_types_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; + f_types_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; + f_consts_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl << "open " + << capitalize(program_name_) << "_types" << endl; +} + +/** + * Autogen'd comment + */ +string t_ocaml_generator::ocaml_autogen_comment() { + return std::string("(*\n") + " Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n" + + " DO NOT EDIT UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING\n" + "*)\n"; +} + +/** + * Prints standard thrift imports + */ +string t_ocaml_generator::ocaml_imports() { + return "open Thrift"; +} + +/** + * Closes the type files + */ +void t_ocaml_generator::close_generator() { + // Close types file + f_types_.close(); +} + +/** + * Generates a typedef. Ez. + * + * @param ttypedef The type definition + */ +void t_ocaml_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = " + << render_ocaml_type(ttypedef->get_type()) << endl << endl; + f_types_i_ << indent() << "type " << decapitalize(ttypedef->get_symbolic()) << " = " + << render_ocaml_type(ttypedef->get_type()) << endl << endl; +} + +/** + * Generates code for an enumerated type. + * the values. + * + * @param tenum The enumeration + */ +void t_ocaml_generator::generate_enum(t_enum* tenum) { + indent(f_types_) << "module " << capitalize(tenum->get_name()) << " = " << endl << "struct" + << endl; + indent(f_types_i_) << "module " << capitalize(tenum->get_name()) << " : " << endl << "sig" + << endl; + indent_up(); + indent(f_types_) << "type t = " << endl; + indent(f_types_i_) << "type t = " << endl; + indent_up(); + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + string name = capitalize((*c_iter)->get_name()); + indent(f_types_) << "| " << name << endl; + indent(f_types_i_) << "| " << name << endl; + } + indent_down(); + + indent(f_types_) << "let to_i = function" << endl; + indent(f_types_i_) << "val to_i : t -> Int32.t" << endl; + 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()); + indent(f_types_) << "| " << name << " -> " << value << "l" << endl; + } + indent_down(); + + indent(f_types_) << "let of_i = function" << endl; + indent(f_types_i_) << "val of_i : Int32.t -> t" << endl; + 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()); + indent(f_types_) << "| " << value << "l -> " << name << endl; + } + indent(f_types_) << "| _ -> raise Thrift_error" << endl; + indent_down(); + indent_down(); + indent(f_types_) << "end" << endl; + indent(f_types_i_) << "end" << endl; +} + +/** + * Generate a constant value + */ +void t_ocaml_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = decapitalize(tconst->get_name()); + t_const_value* value = tconst->get_value(); + + indent(f_consts_) << "let " << name << " = " << render_const_value(type, value) << endl << endl; +} + +/** + * 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 + */ +string t_ocaml_generator::render_const_value(t_type* type, t_const_value* value) { + type = get_true_type(type); + std::ostringstream out; + // OCaml requires all floating point numbers contain a decimal point + out.setf(ios::showpoint); + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (value->get_integer() > 0 ? "true" : "false"); + break; + case t_base_type::TYPE_I8: + case t_base_type::TYPE_I16: + out << value->get_integer(); + break; + case t_base_type::TYPE_I32: + out << value->get_integer() << "l"; + break; + case t_base_type::TYPE_I64: + out << value->get_integer() << "L"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer() << ".0"; + } else { + out << 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()) { + t_enum* tenum = (t_enum*)type; + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int val = (*c_iter)->get_value(); + if (val == value->get_integer()) { + indent(out) << capitalize(tenum->get_name()) << "." << capitalize((*c_iter)->get_name()); + break; + } + } + } else if (type->is_struct() || type->is_xception()) { + string cname = type_name(type); + string ct = tmp("_c"); + out << endl; + indent_up(); + indent(out) << "(let " << ct << " = new " << cname << " in" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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 fname = v_iter->first->get_string(); + out << indent(); + out << ct << "#set_" << fname << " "; + out << render_const_value(field_type, v_iter->second); + out << ";" << endl; + } + indent(out) << ct << ")"; + indent_down(); + indent_down(); + } 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; + string hm = tmp("_hm"); + out << endl; + indent_up(); + indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl; + indent_up(); + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string key = render_const_value(ktype, v_iter->first); + string val = render_const_value(vtype, v_iter->second); + indent(out) << "Hashtbl.add " << hm << " " << key << " " << val << ";" << endl; + } + indent(out) << hm << ")"; + indent_down(); + indent_down(); + } else if (type->is_list()) { + t_type* etype; + etype = ((t_list*)type)->get_elem_type(); + out << "[" << endl; + indent_up(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent(); + out << render_const_value(etype, *v_iter); + out << ";" << endl; + } + indent_down(); + indent(out) << "]"; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + string hm = tmp("_hm"); + indent(out) << "(let " << hm << " = Hashtbl.create " << val.size() << " in" << endl; + indent_up(); + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string val = render_const_value(etype, *v_iter); + indent(out) << "Hashtbl.add " << hm << " " << val << " true;" << endl; + } + indent(out) << hm << ")" << endl; + indent_down(); + out << endl; + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + return out.str(); +} + +/** + * Generates a "struct" + */ +void t_ocaml_generator::generate_struct(t_struct* tstruct) { + generate_ocaml_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct, but also has an exception declaration. + * + * @param txception The struct definition + */ +void t_ocaml_generator::generate_xception(t_struct* txception) { + generate_ocaml_struct(txception, true); +} + +/** + * Generates an OCaml struct + */ +void t_ocaml_generator::generate_ocaml_struct(t_struct* tstruct, bool is_exception) { + generate_ocaml_struct_definition(f_types_, tstruct, is_exception); + generate_ocaml_struct_sig(f_types_i_, tstruct, is_exception); +} + +void t_ocaml_generator::generate_ocaml_method_copy(ofstream& out, const vector& members) { + vector::const_iterator m_iter; + + /* Create a copy of the current object */ + indent(out) << "method copy =" << endl; + indent_up(); + indent_up(); + indent(out) << "let _new = Oo.copy self in" << endl; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) + generate_ocaml_member_copy(out, *m_iter); + + indent_down(); + indent(out) << "_new" << endl; + indent_down(); +} + +string t_ocaml_generator::struct_member_copy_of(t_type* type, string what) { + if (type->is_struct() || type->is_xception()) { + return what + string("#copy"); + } + if (type->is_map()) { + string copy_of_k = struct_member_copy_of(((t_map*)type)->get_key_type(), "k"); + string copy_of_v = struct_member_copy_of(((t_map*)type)->get_val_type(), "v"); + + if (copy_of_k == "k" && copy_of_v == "v") { + return string("(Hashtbl.copy ") + what + string(")"); + } else { + return string( + "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v " + "-> Hashtbl.add nh ") + copy_of_k + string(" ") + copy_of_v + string(") oh; nh) ") + + what + ")"; + } + } + if (type->is_set()) { + string copy_of = struct_member_copy_of(((t_set*)type)->get_elem_type(), "k"); + + if (copy_of == "k") { + return string("(Hashtbl.copy ") + what + string(")"); + } else { + return string( + "((fun oh -> let nh = Hashtbl.create (Hashtbl.length oh) in Hashtbl.iter (fun k v " + "-> Hashtbl.add nh ") + copy_of + string(" true") + string(") oh; nh) ") + what + + ")"; + } + } + if (type->is_list()) { + string copy_of = struct_member_copy_of(((t_list*)type)->get_elem_type(), "x"); + if (copy_of != "x") { + return string("(List.map (fun x -> ") + copy_of + string(") ") + what + string(")"); + } else { + return what; + } + } + return what; +} + +void t_ocaml_generator::generate_ocaml_member_copy(ofstream& out, t_field* tmember) { + string mname = decapitalize(tmember->get_name()); + t_type* type = get_true_type(tmember->get_type()); + + string grab_field = string("self#grab_") + mname; + string copy_of = struct_member_copy_of(type, grab_field); + if (copy_of != grab_field) { + indent(out); + if (!struct_member_persistent(tmember)) { + out << "if _" << mname << " <> None then" << endl; + indent(out) << " "; + } + out << "_new#set_" << mname << " " << copy_of << ";" << endl; + } +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_ocaml_generator::generate_ocaml_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + string tname = type_name(tstruct); + indent(out) << "class " << tname << " =" << endl; + indent(out) << "object (self)" << endl; + + indent_up(); + + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_ocaml_struct_member(out, tname, (*m_iter)); + out << endl; + } + } + generate_ocaml_method_copy(out, members); + generate_ocaml_struct_writer(out, tstruct); + indent_down(); + indent(out) << "end" << endl; + + if (is_exception) { + indent(out) << "exception " << capitalize(tname) << " of " << tname << endl; + } + + generate_ocaml_struct_reader(out, tstruct); +} + +/** + * Generates a structure member for a thrift data type. + * + * @param tname Name of the parent structure for the member + * @param tmember Member definition + */ +void t_ocaml_generator::generate_ocaml_struct_member(ofstream& out, + string tname, + t_field* tmember) { + string x = tmp("_x"); + string mname = decapitalize(tmember->get_name()); + + indent(out) << "val mutable _" << mname << " : " << render_ocaml_type(tmember->get_type()); + t_const_value* val = tmember->get_value(); + if (val) { + if (struct_member_persistent(tmember)) + out << " = " << render_const_value(tmember->get_type(), tmember->get_value()) << endl; + else + out << " option = Some " << render_const_value(tmember->get_type(), tmember->get_value()) + << endl; + } else { + // assert(!struct_member_persistent(tmember)) + out << " option = None" << endl; + } + + if (struct_member_persistent(tmember)) { + indent(out) << "method get_" << mname << " = Some _" << mname << endl; + indent(out) << "method grab_" << mname << " = _" << mname << endl; + indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- " << x << endl; + } else { + indent(out) << "method get_" << mname << " = _" << mname << endl; + indent(out) << "method grab_" << mname << " = match _" << mname + << " with None->raise (Field_empty \"" << tname << "." << mname << "\") | Some " + << x << " -> " << x << endl; + indent(out) << "method set_" << mname << " " << x << " = _" << mname << " <- Some " << x + << endl; + indent(out) << "method unset_" << mname << " = _" << mname << " <- None" << endl; + } + + indent(out) << "method reset_" << mname << " = _" << mname << " <- "; + if (val) { + if (struct_member_persistent(tmember)) + out << render_const_value(tmember->get_type(), tmember->get_value()) << endl; + else + out << "Some " << render_const_value(tmember->get_type(), tmember->get_value()) << endl; + } else { + out << "None" << endl; + } +} + +/** + * Check whether a member of the structure can not have undefined value + * + * @param tmember Member definition + */ +bool t_ocaml_generator::struct_member_persistent(t_field* tmember) { + t_const_value* val = tmember->get_value(); + return (val ? true : false); +} + +/** + * Check whether a member of the structure can be skipped during encoding + * + * @param tmember Member definition + */ +bool t_ocaml_generator::struct_member_omitable(t_field* tmember) { + return (tmember->get_req() != t_field::T_REQUIRED); +} + +/** + * Figure out whether a member of the structure has + * a cheaply comparable default value. + * + * @param tmember Member definition + */ +bool t_ocaml_generator::struct_member_default_cheaply_comparable(t_field* tmember) { + t_type* type = get_true_type(tmember->get_type()); + t_const_value* val = tmember->get_value(); + if (!val) { + return false; + } else if (type->is_base_type()) { + // Base types are generally cheaply compared for structural equivalence. + switch (((t_base_type*)type)->get_base()) { + case t_base_type::TYPE_DOUBLE: + if (val->get_double() == 0.0) + return true; + else + return false; + default: + return true; + } + } else if (type->is_list()) { + // Empty lists are cheaply compared for structural equivalence. + // Is empty list? + if (val->get_list().size() == 0) + return true; + else + return false; + } else { + return false; + } +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_ocaml_generator::generate_ocaml_struct_sig(ofstream& out, + t_struct* tstruct, + bool is_exception) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + string tname = type_name(tstruct); + indent(out) << "class " << tname << " :" << endl; + indent(out) << "object ('a)" << endl; + + indent_up(); + + string x = tmp("_x"); + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + string mname = decapitalize((*m_iter)->get_name()); + string type = render_ocaml_type((*m_iter)->get_type()); + indent(out) << "method get_" << mname << " : " << type << " option" << endl; + indent(out) << "method grab_" << mname << " : " << type << endl; + indent(out) << "method set_" << mname << " : " << type << " -> unit" << endl; + if (!struct_member_persistent(*m_iter)) + indent(out) << "method unset_" << mname << " : unit" << endl; + indent(out) << "method reset_" << mname << " : unit" << endl; + } + } + indent(out) << "method copy : 'a" << endl; + indent(out) << "method write : Protocol.t -> unit" << endl; + indent_down(); + indent(out) << "end" << endl; + + if (is_exception) { + indent(out) << "exception " << capitalize(tname) << " of " << tname << endl; + } + + indent(out) << "val read_" << tname << " : Protocol.t -> " << tname << endl; +} + +/** + * Generates the read method for a struct + */ +void t_ocaml_generator::generate_ocaml_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + string sname = type_name(tstruct); + string str = tmp("_str"); + string t = tmp("_t"); + string id = tmp("_id"); + indent(out) << "let rec read_" << sname << " (iprot : Protocol.t) =" << endl; + indent_up(); + indent(out) << "let " << str << " = new " << sname << " in" << endl; + indent_up(); + indent(out) << "ignore(iprot#readStructBegin);" << endl; + + // Loop over reading in fields + indent(out) << "(try while true do" << endl; + indent_up(); + indent_up(); + + // Read beginning field marker + indent(out) << "let (_," << t << "," << id << ") = iprot#readFieldBegin in" << endl; + + // Check for field STOP marker and break + indent(out) << "if " << t << " = Protocol.T_STOP then" << endl; + indent_up(); + indent(out) << "raise Break" << endl; + indent_down(); + indent(out) << "else ();" << endl; + + indent(out) << "(match " << id << " with " << endl; + indent_up(); + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "| " << (*f_iter)->get_key() << " -> ("; + out << "if " << t << " = " << type_to_enum((*f_iter)->get_type()) << " then" << endl; + indent_up(); + indent_up(); + generate_deserialize_field(out, *f_iter, str); + indent_down(); + out << indent() << "else" << endl << indent() << " iprot#skip " << t << ")" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "| _ -> " + << "iprot#skip " << t << ");" << endl; + indent_down(); + // Read field end marker + indent(out) << "iprot#readFieldEnd;" << endl; + indent_down(); + indent(out) << "done; ()" << endl; + indent_down(); + indent(out) << "with Break -> ());" << endl; + + indent(out) << "iprot#readStructEnd;" << endl; + + indent(out) << str << endl << endl; + indent_down(); + indent_down(); +} + +void t_ocaml_generator::generate_ocaml_struct_writer(ofstream& out, t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + string str = tmp("_str"); + string f = tmp("_f"); + + indent(out) << "method write (oprot : Protocol.t) =" << endl; + indent_up(); + indent(out) << "oprot#writeStructBegin \"" << name << "\";" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* tmember = (*f_iter); + string mname = "_" + decapitalize(tmember->get_name()); + string _v; + + if (struct_member_persistent(tmember)) { + + if (struct_member_omitable(tmember) && struct_member_default_cheaply_comparable(tmember)) { + _v = "_v"; + // Avoid redundant encoding of members having default values. + indent(out) << "(match " << mname << " with " + << render_const_value(tmember->get_type(), tmember->get_value()) << " -> () | " + << _v << " -> " << endl; + } else { + _v = mname; + indent(out) << "(" << endl; + } + + } else { + + indent(out) << "(match " << mname << " with "; + + if (struct_member_omitable(tmember)) { + out << "None -> ()"; + + if (struct_member_default_cheaply_comparable(tmember)) { + // Avoid redundant encoding of members having default values. + out << " | Some " << render_const_value(tmember->get_type(), tmember->get_value()) + << " -> ()"; + } + out << " | Some _v -> " << endl; + } else { + out << endl; + indent(out) << "| None -> raise (Field_empty \"" << type_name(tstruct) << "." << mname + << "\")" << endl; + indent(out) << "| Some _v -> " << endl; + } + + _v = "_v"; + } + indent_up(); + // Write field header + indent(out) << "oprot#writeFieldBegin(\"" << tmember->get_name() << "\"," + << type_to_enum(tmember->get_type()) << "," << tmember->get_key() << ");" << endl; + + // Write field contents + generate_serialize_field(out, tmember, _v); + + // Write field closer + indent(out) << "oprot#writeFieldEnd" << endl; + + indent_down(); + indent(out) << ");" << endl; + } + + // Write the struct map + out << indent() << "oprot#writeFieldStop;" << endl << indent() << "oprot#writeStructEnd" << endl; + + indent_down(); +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_ocaml_generator::generate_service(t_service* tservice) { + string f_service_name = get_out_dir() + capitalize(service_name_) + ".ml"; + f_service_.open(f_service_name.c_str()); + string f_service_i_name = get_out_dir() + capitalize(service_name_) + ".mli"; + f_service_i_.open(f_service_i_name.c_str()); + + f_service_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; + f_service_i_ << ocaml_autogen_comment() << endl << ocaml_imports() << endl; + + /* if (tservice->get_extends() != NULL) { + f_service_ << + "open " << capitalize(tservice->get_extends()->get_name()) << endl; + f_service_i_ << + "open " << capitalize(tservice->get_extends()->get_name()) << endl; + } + */ + f_service_ << "open " << capitalize(program_name_) << "_types" << endl << endl; + + f_service_i_ << "open " << capitalize(program_name_) << "_types" << endl << endl; + + // Generate the three main parts of the service + generate_service_helpers(tservice); + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + + // Close service file + f_service_.close(); + f_service_i_.close(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_ocaml_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + indent(f_service_) << "(* HELPER FUNCTIONS AND STRUCTURES *)" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_ocaml_struct_definition(f_service_, ts, false); + generate_ocaml_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_ocaml_generator::generate_ocaml_function_helpers(t_function* tfunction) { + t_struct result(program_, decapitalize(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_ocaml_struct_definition(f_service_, &result, false); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_ocaml_generator::generate_service_interface(t_service* tservice) { + f_service_ << indent() << "class virtual iface =" << endl << "object (self)" << endl; + f_service_i_ << indent() << "class virtual iface :" << endl << "object" << endl; + + indent_up(); + + if (tservice->get_extends() != NULL) { + string extends = type_name(tservice->get_extends()); + indent(f_service_) << "inherit " << extends << ".iface" << endl; + indent(f_service_i_) << "inherit " << extends << ".iface" << endl; + } + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string ft = function_type(*f_iter, true, true); + f_service_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " + << ft << endl; + f_service_i_ << indent() << "method virtual " << decapitalize((*f_iter)->get_name()) << " : " + << ft << endl; + } + indent_down(); + indent(f_service_) << "end" << endl << endl; + indent(f_service_i_) << "end" << endl << endl; +} + +/** + * Generates a service client definition. Note that in OCaml, the client doesn't implement iface. + *This is because + * The client does not (and should not have to) deal with arguments being None. + * + * @param tservice The service to generate a server for. + */ +void t_ocaml_generator::generate_service_client(t_service* tservice) { + string extends = ""; + indent(f_service_) << "class client (iprot : Protocol.t) (oprot : Protocol.t) =" << endl + << "object (self)" << endl; + indent(f_service_i_) << "class client : Protocol.t -> Protocol.t -> " << endl << "object" << endl; + indent_up(); + + if (tservice->get_extends() != NULL) { + 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; + } + indent(f_service_) << "val mutable seqid = 0" << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + // Open function + indent(f_service_) << "method " << function_signature(*f_iter) << " = " << endl; + indent(f_service_i_) << "method " << decapitalize((*f_iter)->get_name()) << " : " + << function_type(*f_iter, true, false) << endl; + indent_up(); + indent(f_service_) << "self#send_" << funname; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << " " << decapitalize((*fld_iter)->get_name()); + } + f_service_ << ";" << endl; + + if (!(*f_iter)->is_oneway()) { + f_service_ << indent(); + f_service_ << "self#recv_" << funname << endl; + } + indent_down(); + + indent(f_service_) << "method private send_" << function_signature(*f_iter) << " = " << endl; + indent_up(); + + std::string argsname = decapitalize((*f_iter)->get_name() + "_args"); + + // Serialize the request header + f_service_ << indent() << "oprot#writeMessageBegin (\"" << (*f_iter)->get_name() << "\", " + << ((*f_iter)->is_oneway() ? "Protocol.ONEWAY" : "Protocol.CALL") << ", seqid);" + << endl; + + f_service_ << indent() << "let args = new " << argsname << " in" << endl; + indent_up(); + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args#set_" << (*fld_iter)->get_name() << " " + << (*fld_iter)->get_name() << ";" << endl; + } + + // Write to the stream + f_service_ << indent() << "args#write oprot;" << endl << indent() << "oprot#writeMessageEnd;" + << endl << indent() << "oprot#getTransport#flush" << endl; + + indent_down(); + indent_down(); + + if (!(*f_iter)->is_oneway()) { + std::string resultname = decapitalize((*f_iter)->get_name() + "_result"); + t_struct noargs(program_); + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_ << indent() << "method private " << function_signature(&recv_function) << " =" + << endl; + indent_up(); + + // TODO(mcslee): Validate message reply here, seq ids etc. + + f_service_ << indent() << "let (fname, mtype, rseqid) = iprot#readMessageBegin in" << endl; + indent_up(); + f_service_ << indent() << "(if mtype = Protocol.EXCEPTION then" << endl << indent() + << " let x = Application_Exn.read iprot in" << endl; + indent_up(); + f_service_ << indent() << " (iprot#readMessageEnd;" << indent() + << " raise (Application_Exn.E x))" << endl; + indent_down(); + f_service_ << indent() << "else ());" << endl; + string res = "_"; + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + + if (!(*f_iter)->get_returntype()->is_void() || xceptions.size() > 0) { + res = "result"; + } + f_service_ << indent() << "let " << res << " = read_" << resultname << " iprot in" << endl; + indent_up(); + f_service_ << indent() << "iprot#readMessageEnd;" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "match result#get_success with Some v -> v | None -> (" << endl; + indent_up(); + } + + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << indent() << "(match result#get_" << (*x_iter)->get_name() + << " with None -> () | Some _v ->" << endl; + indent(f_service_) << " raise (" << capitalize(type_name((*x_iter)->get_type())) + << " _v));" << endl; + } + + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "()" << endl; + } else { + f_service_ + << indent() + << "raise (Application_Exn.E (Application_Exn.create Application_Exn.MISSING_RESULT \"" + << (*f_iter)->get_name() << " failed: unknown result\")))" << endl; + indent_down(); + } + + // Close function + indent_down(); + indent_down(); + indent_down(); + } + } + + indent_down(); + indent(f_service_) << "end" << endl << endl; + indent(f_service_i_) << "end" << endl << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_ocaml_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + // Generate the header portion + indent(f_service_) << "class processor (handler : iface) =" << endl << indent() << "object (self)" + << endl; + indent(f_service_i_) << "class processor : iface ->" << endl << indent() << "object" << endl; + indent_up(); + + f_service_ << indent() << "inherit Processor.t" << endl << endl; + f_service_i_ << indent() << "inherit Processor.t" << endl << endl; + string extends = ""; + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + indent(f_service_) << "inherit " + extends + ".processor (handler :> " + extends + ".iface)" + << endl; + indent(f_service_i_) << "inherit " + extends + ".processor" << endl; + } + + if (extends.empty()) { + indent(f_service_) << "val processMap = Hashtbl.create " << functions.size() << endl; + } + indent(f_service_i_) + << "val processMap : (string, int * Protocol.t * Protocol.t -> unit) Hashtbl.t" << endl; + + // Generate the server implementation + indent(f_service_) << "method process iprot oprot =" << endl; + indent(f_service_i_) << "method process : Protocol.t -> Protocol.t -> bool" << endl; + indent_up(); + + f_service_ << indent() << "let (name, typ, seqid) = iprot#readMessageBegin in" << endl; + indent_up(); + // TODO(mcslee): validate message + + // HOT: dictionary function lookup + f_service_ << indent() << "if Hashtbl.mem processMap name then" << endl << indent() + << " (Hashtbl.find processMap name) (seqid, iprot, oprot)" << endl << indent() + << "else (" << endl << indent() << " iprot#skip(Protocol.T_STRUCT);" << endl + << indent() << " iprot#readMessageEnd;" << endl << indent() + << " let x = Application_Exn.create Application_Exn.UNKNOWN_METHOD (\"Unknown " + "function \"^name) in" << endl << indent() + << " oprot#writeMessageBegin(name, Protocol.EXCEPTION, seqid);" << endl << indent() + << " x#write oprot;" << endl << indent() << " oprot#writeMessageEnd;" << endl + << indent() << " oprot#getTransport#flush" << endl << indent() << ");" << endl; + + // Read end of args field, the T_STOP, and the struct close + f_service_ << indent() << "true" << endl; + indent_down(); + indent_down(); + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + indent(f_service_) << "initializer" << endl; + indent_up(); + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "Hashtbl.add processMap \"" << (*f_iter)->get_name() + << "\" self#process_" << (*f_iter)->get_name() << ";" << endl; + } + indent_down(); + + indent_down(); + indent(f_service_) << "end" << endl << endl; + indent(f_service_i_) << "end" << endl << endl; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_ocaml_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open function + indent(f_service_) << "method private process_" << tfunction->get_name() + << " (seqid, iprot, oprot) =" << endl; + indent_up(); + + string argsname = decapitalize(tfunction->get_name()) + "_args"; + string resultname = decapitalize(tfunction->get_name()) + "_result"; + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + string args = "args"; + if (fields.size() == 0) { + args = "_"; + } + + f_service_ << indent() << "let " << args << " = read_" << argsname << " iprot in" << endl; + indent_up(); + f_service_ << indent() << "iprot#readMessageEnd;" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << "let result = new " << resultname << " in" << endl; + indent_up(); + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_ << indent() << "(try" << endl; + indent_up(); + } + + f_service_ << indent(); + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + f_service_ << "result#set_success "; + } + f_service_ << "(handler#" << tfunction->get_name(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + f_service_ << " args#get_" << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + if (xceptions.size() > 0) { + indent_down(); + indent(f_service_) << "with" << endl; + indent_up(); + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << indent() << "| " << capitalize(type_name((*x_iter)->get_type())) << " " + << (*x_iter)->get_name() << " -> " << endl; + indent_up(); + indent_up(); + if (!tfunction->is_oneway()) { + f_service_ << indent() << "result#set_" << (*x_iter)->get_name() << " " + << (*x_iter)->get_name() << endl; + } else { + indent(f_service_) << "()"; + } + indent_down(); + indent_down(); + } + indent_down(); + f_service_ << indent() << ");" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "()" << endl; + indent_down(); + indent_down(); + return; + } + + f_service_ << indent() << "oprot#writeMessageBegin (\"" << tfunction->get_name() + << "\", Protocol.REPLY, seqid);" << endl << indent() << "result#write oprot;" << endl + << indent() << "oprot#writeMessageEnd;" << endl << indent() + << "oprot#getTransport#flush" << endl; + + // Close function + indent_down(); + indent_down(); + indent_down(); +} + +/** + * Deserializes a field of any type. + */ +void t_ocaml_generator::generate_deserialize_field(ofstream& out, t_field* tfield, string prefix) { + t_type* type = tfield->get_type(); + + string name = decapitalize(tfield->get_name()); + indent(out) << prefix << "#set_" << name << " "; + generate_deserialize_type(out, type); + out << endl; +} + +/** + * Deserializes a field of any type. + */ +void t_ocaml_generator::generate_deserialize_type(ofstream& out, t_type* type) { + type = get_true_type(type); + + if (type->is_void()) { + throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE"; + } + + if (type->is_struct() || type->is_xception()) { + generate_deserialize_struct(out, (t_struct*)type); + } else if (type->is_container()) { + generate_deserialize_container(out, type); + } else if (type->is_base_type()) { + out << "iprot#"; + 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"; + break; + case t_base_type::TYPE_STRING: + 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 ocaml name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + string ename = capitalize(type->get_name()); + out << "(" << ename << ".of_i iprot#readI32)"; + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE TYPE '%s'\n", type->get_name().c_str()); + } +} + +/** + * Generates an unserializer for a struct, calling read() + */ +void t_ocaml_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct) { + string prefix = ""; + t_program* program = tstruct->get_program(); + if (program != NULL && program != program_) { + prefix = capitalize(program->get_name()) + "_types."; + } + string name = decapitalize(tstruct->get_name()); + out << "(" << prefix << "read_" << name << " iprot)"; +} + +/** + * Serialize a container by writing out the header followed by + * data and then a footer. + */ +void t_ocaml_generator::generate_deserialize_container(ofstream& out, t_type* ttype) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + string con = tmp("_con"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + out << endl; + indent_up(); + // Declare variables, read header + if (ttype->is_map()) { + indent(out) << "(let (" << ktype << "," << vtype << "," << size << ") = iprot#readMapBegin in" + << endl; + indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl; + indent_up(); + indent(out) << "for i = 1 to " << size << " do" << endl; + indent_up(); + indent(out) << "let _k = "; + generate_deserialize_type(out, ((t_map*)ttype)->get_key_type()); + out << " in" << endl; + indent(out) << "let _v = "; + generate_deserialize_type(out, ((t_map*)ttype)->get_val_type()); + out << " in" << endl; + indent_up(); + indent(out) << "Hashtbl.add " << con << " _k _v" << endl; + indent_down(); + indent_down(); + indent(out) << "done; iprot#readMapEnd; " << con << ")"; + indent_down(); + } else if (ttype->is_set()) { + indent(out) << "(let (" << etype << "," << size << ") = iprot#readSetBegin in" << endl; + indent(out) << "let " << con << " = Hashtbl.create " << size << " in" << endl; + indent_up(); + indent(out) << "for i = 1 to " << size << " do" << endl; + indent_up(); + indent(out) << "Hashtbl.add " << con << " "; + generate_deserialize_type(out, ((t_set*)ttype)->get_elem_type()); + out << " true" << endl; + indent_down(); + indent(out) << "done; iprot#readSetEnd; " << con << ")"; + indent_down(); + } else if (ttype->is_list()) { + indent(out) << "(let (" << etype << "," << size << ") = iprot#readListBegin in" << endl; + indent_up(); + indent(out) << "let " << con << " = (Array.to_list (Array.init " << size << " (fun _ -> "; + generate_deserialize_type(out, ((t_list*)ttype)->get_elem_type()); + out << "))) in" << endl; + indent_up(); + indent(out) << "iprot#readListEnd; " << con << ")"; + indent_down(); + indent_down(); + } + indent_down(); +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_ocaml_generator::generate_serialize_field(ofstream& out, t_field* tfield, string name) { + 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 (name.length() == 0) { + name = decapitalize(tfield->get_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#"; + + 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: + out << "writeString(" << name << ")"; + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ")"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ")"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ")"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ")"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ")"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ")"; + break; + default: + throw "compiler error: no ocaml name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + string ename = capitalize(type->get_name()); + out << "writeI32(" << ename << ".to_i " << name << ")"; + } + + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), + type->get_name().c_str()); + } + out << ";" << endl; +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_ocaml_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << prefix << "#write(oprot)"; +} + +void t_ocaml_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + if (ttype->is_map()) { + indent(out) << "oprot#writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ","; + out << type_to_enum(((t_map*)ttype)->get_val_type()) << ","; + out << "Hashtbl.length " << prefix << ");" << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot#writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ","; + out << "Hashtbl.length " << prefix << ");" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot#writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ","; + out << "List.length " << prefix << ");" << endl; + } + + if (ttype->is_map()) { + string kiter = tmp("_kiter"); + string viter = tmp("_viter"); + indent(out) << "Hashtbl.iter (fun " << kiter << " -> fun " << viter << " -> " << endl; + indent_up(); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + indent_down(); + indent(out) << ") " << prefix << ";" << endl; + } else if (ttype->is_set()) { + string iter = tmp("_iter"); + indent(out) << "Hashtbl.iter (fun " << iter << " -> fun _ -> "; + indent_up(); + generate_serialize_set_element(out, (t_set*)ttype, iter); + indent_down(); + indent(out) << ") " << prefix << ";" << endl; + } else if (ttype->is_list()) { + string iter = tmp("_iter"); + indent(out) << "List.iter (fun " << iter << " -> "; + indent_up(); + generate_serialize_list_element(out, (t_list*)ttype, iter); + indent_down(); + indent(out) << ") " << prefix << ";" << endl; + } + + if (ttype->is_map()) { + indent(out) << "oprot#writeMapEnd"; + } else if (ttype->is_set()) { + indent(out) << "oprot#writeSetEnd"; + } else if (ttype->is_list()) { + indent(out) << "oprot#writeListEnd"; + } +} + +/** + * Serializes the members of a map. + * + */ +void t_ocaml_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield); +} + +/** + * Serializes the members of a set. + */ +void t_ocaml_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Serializes the members of a list. + */ +void t_ocaml_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Renders a function signature of the form 'name args' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_ocaml_generator::function_signature(t_function* tfunction, string prefix) { + return prefix + decapitalize(tfunction->get_name()) + " " + + argument_list(tfunction->get_arglist()); +} + +string t_ocaml_generator::function_type(t_function* tfunc, bool method, bool options) { + string result = ""; + + const vector& fields = tfunc->get_arglist()->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + result += render_ocaml_type((*f_iter)->get_type()); + if (options) + result += " option"; + result += " -> "; + } + if (fields.empty() && !method) { + result += "unit -> "; + } + result += render_ocaml_type(tfunc->get_returntype()); + return result; +} + +/** + * Renders a field list + */ +string t_ocaml_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 += (*f_iter)->get_name(); + } + return result; +} + +string t_ocaml_generator::type_name(t_type* ttype) { + string prefix = ""; + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + if (!ttype->is_service()) { + prefix = capitalize(program->get_name()) + "_types."; + } + } + + string name = ttype->get_name(); + if (ttype->is_service()) { + name = capitalize(name); + } else { + name = decapitalize(name); + } + return prefix + name; +} + +/** + * Converts the parse type to a Protocol.t_type enum + */ +string t_ocaml_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: + return "Protocol.T_VOID"; + case t_base_type::TYPE_STRING: + return "Protocol.T_STRING"; + case t_base_type::TYPE_BOOL: + return "Protocol.T_BOOL"; + case t_base_type::TYPE_I8: + return "Protocol.T_BYTE"; + case t_base_type::TYPE_I16: + return "Protocol.T_I16"; + case t_base_type::TYPE_I32: + return "Protocol.T_I32"; + case t_base_type::TYPE_I64: + return "Protocol.T_I64"; + case t_base_type::TYPE_DOUBLE: + return "Protocol.T_DOUBLE"; + } + } else if (type->is_enum()) { + return "Protocol.T_I32"; + } else if (type->is_struct() || type->is_xception()) { + return "Protocol.T_STRUCT"; + } else if (type->is_map()) { + return "Protocol.T_MAP"; + } else if (type->is_set()) { + return "Protocol.T_SET"; + } else if (type->is_list()) { + return "Protocol.T_LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts the parse type to an ocaml type + */ +string t_ocaml_generator::render_ocaml_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: + return "unit"; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_I8: + return "int"; + case t_base_type::TYPE_I16: + return "int"; + case t_base_type::TYPE_I32: + return "Int32.t"; + case t_base_type::TYPE_I64: + return "Int64.t"; + case t_base_type::TYPE_DOUBLE: + return "float"; + } + } else if (type->is_enum()) { + return capitalize(((t_enum*)type)->get_name()) + ".t"; + } else if (type->is_struct() || type->is_xception()) { + return type_name((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(); + return "(" + render_ocaml_type(ktype) + "," + render_ocaml_type(vtype) + ") Hashtbl.t"; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + return "(" + render_ocaml_type(etype) + ",bool) Hashtbl.t"; + } else if (type->is_list()) { + t_type* etype = ((t_list*)type)->get_elem_type(); + return render_ocaml_type(etype) + " list"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +THRIFT_REGISTER_GENERATOR(ocaml, "OCaml", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h new file mode 100644 index 00000000..8fb580df --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_oop_generator.h @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_OOP_GENERATOR_H +#define T_OOP_GENERATOR_H + +#include +#include + +#include "thrift/common.h" +#include "thrift/generate/t_generator.h" + +#include + +/** + * Class with utility methods shared across common object oriented languages. + * Specifically, most of this stuff is for C++/Java. + * + */ +class t_oop_generator : public t_generator { +public: + t_oop_generator(t_program* program) : t_generator(program) {} + + /** + * Scoping, using curly braces! + */ + + void scope_up(std::ostream& out) { + indent(out) << "{" << std::endl; + indent_up(); + } + + void scope_down(std::ostream& out) { + indent_down(); + indent(out) << "}" << std::endl; + } + + std::string upcase_string(std::string original) { + std::transform(original.begin(), original.end(), original.begin(), (int (*)(int))toupper); + return original; + } + + virtual std::string get_enum_class_name(t_type* type) { + std::string package = ""; + t_program* program = type->get_program(); + if (program != NULL && program != program_) { + package = program->get_namespace("java") + "."; + } + return package + type->get_name(); + } + + virtual void generate_java_docstring_comment(std::ofstream& out, std::string contents) { + generate_docstring_comment(out, "/**\n", " * ", contents, " */\n"); + } + + virtual void generate_java_doc(std::ofstream& out, t_field* field) { + if (field->get_type()->is_enum()) { + std::string combined_message = field->get_doc() + "\n@see " + + get_enum_class_name(field->get_type()); + generate_java_docstring_comment(out, combined_message); + } else { + generate_java_doc(out, (t_doc*)field); + } + } + + /** + * Emits a JavaDoc comment if the provided object has a doc in Thrift + */ + virtual void generate_java_doc(std::ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_java_docstring_comment(out, tdoc->get_doc()); + } + } + + /** + * Emits a JavaDoc comment if the provided function object has a doc in Thrift + */ + virtual void generate_java_doc(std::ofstream& out, t_function* tfunction) { + if (tfunction->has_doc()) { + std::stringstream ss; + ss << tfunction->get_doc(); + const std::vector& fields = tfunction->get_arglist()->get_members(); + std::vector::const_iterator p_iter; + for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { + t_field* p = *p_iter; + ss << "\n@param " << p->get_name(); + if (p->has_doc()) { + ss << " " << p->get_doc(); + } + } + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); + } + } +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc new file mode 100644 index 00000000..bfe08f9f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_perl_generator.cc @@ -0,0 +1,1648 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/version.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * PERL code generator. + * + */ +class t_perl_generator : public t_oop_generator { +public: + t_perl_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; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option perl:" + iter->first; + } + + out_dir_base_ = "gen-perl"; + escape_['$'] = "\\$"; + escape_['@'] = "\\@"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + std::string render_const_value(t_type* type, t_const_value* value); + + /** + * Structs! + */ + + void generate_perl_struct(t_struct* tstruct, bool is_exception); + void generate_perl_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false); + void generate_perl_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_perl_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_perl_function_helpers(t_function* tfunction); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_service_rest(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_processor(t_service* tservice); + void generate_process_function(t_service* tservice, t_function* tfunction); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool inclass = false); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + /** + * Helper rendering functions + */ + + std::string perl_includes(); + std::string declare_field(t_field* tfield, bool init = false, bool obj = false); + 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 autogen_comment() { + 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"; + } + + void perl_namespace_dirs(t_program* p, std::list& dirs) { + std::string ns = p->get_namespace("perl"); + std::string::size_type loc; + + if (ns.size() > 0) { + while ((loc = ns.find(".")) != std::string::npos) { + dirs.push_back(ns.substr(0, loc)); + ns = ns.substr(loc + 1); + } + } + + if (ns.size() > 0) { + dirs.push_back(ns); + } + } + + std::string perl_namespace(t_program* p) { + std::string ns = p->get_namespace("perl"); + std::string result = ""; + std::string::size_type loc; + + if (ns.size() > 0) { + while ((loc = ns.find(".")) != std::string::npos) { + result += ns.substr(0, loc); + result += "::"; + ns = ns.substr(loc + 1); + } + + if (ns.size() > 0) { + result += ns + "::"; + } + } + + return result; + } + + std::string get_namespace_out_dir() { + std::string outdir = get_out_dir(); + std::list dirs; + perl_namespace_dirs(program_, dirs); + std::list::iterator it; + for (it = dirs.begin(); it != dirs.end(); it++) { + outdir += *it + "/"; + } + return outdir; + } + +private: + /** + * File streams + */ + std::ofstream f_types_; + std::ofstream f_consts_; + std::ofstream f_helpers_; + std::ofstream f_service_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_perl_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + string outdir = get_out_dir(); + std::list dirs; + perl_namespace_dirs(program_, dirs); + std::list::iterator it; + for (it = dirs.begin(); it != dirs.end(); it++) { + outdir += *it + "/"; + MKDIR(outdir.c_str()); + } + + // Make output file + string f_types_name = outdir + "Types.pm"; + f_types_.open(f_types_name.c_str()); + string f_consts_name = outdir + "Constants.pm"; + f_consts_.open(f_consts_name.c_str()); + + // Print header + f_types_ << autogen_comment() << perl_includes(); + + // Print header + f_consts_ << autogen_comment() << "package " << perl_namespace(program_) << "Constants;" << endl + << perl_includes() << endl; +} + +/** + * Prints standard java imports + */ +string t_perl_generator::perl_includes() { + string inc; + + inc = "require 5.6.0;\n"; + inc += "use strict;\n"; + inc += "use warnings;\n"; + inc += "use Thrift;\n\n"; + + return inc; +} + +/** + * Close up (or down) some filez. + */ +void t_perl_generator::close_generator() { + // Close types file + f_types_ << "1;" << endl; + f_types_.close(); + + f_consts_ << "1;" << endl; + f_consts_.close(); +} + +/** + * Generates a typedef. This is not done in PERL, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_perl_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates code for an enumerated type. Since define is expensive to lookup + * in PERL, we use a global array for this. + * + * @param tenum The enumeration + */ +void t_perl_generator::generate_enum(t_enum* tenum) { + f_types_ << "package " << perl_namespace(program_) << tenum->get_name() << ";" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + f_types_ << "use constant " << (*c_iter)->get_name() << " => " << value << ";" << endl; + } +} + +/** + * Generate a constant value + */ +void t_perl_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + f_consts_ << "use constant " << name << " => "; + f_consts_ << render_const_value(type, value); + f_consts_ << ";" << endl << endl; +} + +/** + * 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 + */ +string t_perl_generator::render_const_value(t_type* type, t_const_value* value) { + std::ostringstream out; + + 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_STRING: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (value->get_integer() > 0 ? "1" : "0"); + 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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << "new " << perl_namespace(type->get_program()) << type->get_name() << "({" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + out << render_const_value(g_type_string, v_iter->first); + out << " => "; + out << render_const_value(field_type, v_iter->second); + out << ","; + out << endl; + } + + out << "})"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + out << "{" << endl; + + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << render_const_value(ktype, v_iter->first); + out << " => "; + out << render_const_value(vtype, v_iter->second); + out << "," << endl; + } + + out << "}"; + } 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(); + } + out << "[" << endl; + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + + out << render_const_value(etype, *v_iter); + if (type->is_set()) { + out << " => 1"; + } + out << "," << endl; + } + out << "]"; + } + return out.str(); +} + +/** + * Make a struct + */ +void t_perl_generator::generate_struct(t_struct* tstruct) { + generate_perl_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_perl_generator::generate_xception(t_struct* txception) { + generate_perl_struct(txception, true); +} + +/** + * Structs can be normal or exceptions. + */ +void t_perl_generator::generate_perl_struct(t_struct* tstruct, bool is_exception) { + generate_perl_struct_definition(f_types_, tstruct, is_exception); +} + +/** + * Generates a struct definition for a thrift data type. This is nothing in PERL + * where the objects are all just associative arrays (unless of course we + * decide to start using objects for them...) + * + * @param tstruct The struct definition + */ +void t_perl_generator::generate_perl_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + out << "package " << perl_namespace(tstruct->get_program()) << tstruct->get_name() << ";\n"; + if (is_exception) { + out << "use base qw(Thrift::TException);\n"; + } + + // Create simple acessor methods + out << "use base qw(Class::Accessor);\n"; + + if (members.size() > 0) { + out << perl_namespace(tstruct->get_program()) << tstruct->get_name() << "->mk_accessors( qw( "; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + t_type* t = get_true_type((*m_iter)->get_type()); + if (!t->is_xception()) { + out << (*m_iter)->get_name() << " "; + } + } + + out << ") );\n"; + } + + out << endl; + + // new() + indent_up(); + out << "sub new {" << endl << indent() << "my $classname = shift;" << endl << indent() + << "my $self = {};" << endl << indent() << "my $vals = shift || {};" << endl; + + 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())) { + dval = render_const_value((*m_iter)->get_type(), (*m_iter)->get_value()); + } + out << indent() << "$self->{" << (*m_iter)->get_name() << "} = " << dval << ";" << endl; + } + + // Generate constructor from array + 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())) { + indent(out) << "$self->{" << (*m_iter)->get_name() + << "} = " << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; + } + } + + out << indent() << "if (UNIVERSAL::isa($vals,'HASH')) {" << endl; + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << indent() << "if (defined $vals->{" << (*m_iter)->get_name() << "}) {" << endl + << indent() << " $self->{" << (*m_iter)->get_name() << "} = $vals->{" + << (*m_iter)->get_name() << "};" << endl << indent() << "}" << endl; + } + indent_down(); + out << indent() << "}" << endl; + } + + out << indent() << "return bless ($self, $classname);" << endl; + indent_down(); + out << "}\n\n"; + + out << "sub getName {" << endl << indent() << " return '" << tstruct->get_name() << "';" << endl + << indent() << "}" << endl << endl; + + generate_perl_struct_reader(out, tstruct); + generate_perl_struct_writer(out, tstruct); +} + +/** + * Generates the read() method for a struct + */ +void t_perl_generator::generate_perl_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + out << "sub read {" << endl; + + indent_up(); + + out << indent() << "my ($self, $input) = @_;" << endl << indent() << "my $xfer = 0;" << endl + << indent() << "my $fname;" << endl << indent() << "my $ftype = 0;" << endl << indent() + << "my $fid = 0;" << endl; + + indent(out) << "$xfer += $input->readStructBegin(\\$fname);" << endl; + + // Loop over reading in fields + indent(out) << "while (1) " << endl; + + scope_up(out); + + indent(out) << "$xfer += $input->readFieldBegin(\\$fname, \\$ftype, \\$fid);" << endl; + + // Check for field STOP marker and break + indent(out) << "if ($ftype == TType::STOP) {" << endl; + indent_up(); + indent(out) << "last;" << endl; + indent_down(); + indent(out) << "}" << endl; + + // Switch statement on the field we are reading + indent(out) << "SWITCH: for($fid)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + + indent(out) << "/^" << (*f_iter)->get_key() << "$/ && do{"; + indent(out) << "if ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + + indent_up(); + generate_deserialize_field(out, *f_iter, "self->"); + indent_down(); + + indent(out) << "} else {" << endl; + + indent(out) << " $xfer += $input->skip($ftype);" << endl; + + out << indent() << "}" << endl << indent() << "last; };" << endl; + } + // In the default case we skip the field + + indent(out) << " $xfer += $input->skip($ftype);" << endl; + + scope_down(out); + + indent(out) << "$xfer += $input->readFieldEnd();" << endl; + + scope_down(out); + + indent(out) << "$xfer += $input->readStructEnd();" << endl; + + indent(out) << "return $xfer;" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +/** + * Generates the write() method for a struct + */ +void t_perl_generator::generate_perl_struct_writer(ofstream& out, t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + out << "sub write {" << endl; + + indent_up(); + indent(out) << "my ($self, $output) = @_;" << endl; + indent(out) << "my $xfer = 0;" << endl; + + indent(out) << "$xfer += $output->writeStructBegin('" << name << "');" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << indent() << "if (defined $self->{" << (*f_iter)->get_name() << "}) {" << endl; + indent_up(); + + indent(out) << "$xfer += $output->writeFieldBegin(" + << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type()) + << ", " << (*f_iter)->get_key() << ");" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "self->"); + + indent(out) << "$xfer += $output->writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}" << endl; + } + + out << indent() << "$xfer += $output->writeFieldStop();" << endl << indent() + << "$xfer += $output->writeStructEnd();" << endl; + + out << indent() << "return $xfer;" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_perl_generator::generate_service(t_service* tservice) { + string f_service_name = get_namespace_out_dir() + service_name_ + ".pm"; + f_service_.open(f_service_name.c_str()); + + f_service_ << + /// "package "<get_program()) << "Types;" << endl; + + t_service* extends_s = tservice->get_extends(); + if (extends_s != NULL) { + f_service_ << "use " << perl_namespace(extends_s->get_program()) << extends_s->get_name() << ";" + << endl; + } + + f_service_ << endl; + + // Generate the three main parts of the service (well, two for now in PERL) + generate_service_helpers(tservice); + generate_service_interface(tservice); + generate_service_rest(tservice); + generate_service_client(tservice); + generate_service_processor(tservice); + + // Close service file + f_service_ << "1;" << endl; + f_service_.close(); +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_perl_generator::generate_service_processor(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + t_service* extends_s = tservice->get_extends(); + if (extends_s != NULL) { + extends = perl_namespace(extends_s->get_program()) + extends_s->get_name(); + extends_processor = "use base qw(" + extends + "Processor);"; + } + + indent_up(); + + // Generate the header portion + f_service_ << "package " << perl_namespace(program_) << service_name_ << "Processor;" << endl + << endl << "use strict;" << endl << extends_processor << endl << endl; + + if (extends.empty()) { + f_service_ << "sub new {" << endl; + + indent_up(); + + f_service_ << indent() << "my ($classname, $handler) = @_;" << endl << indent() + << "my $self = {};" << endl; + + f_service_ << indent() << "$self->{handler} = $handler;" << endl; + + f_service_ << indent() << "return bless ($self, $classname);" << endl; + + indent_down(); + + f_service_ << "}" << endl << endl; + } + + // Generate the server implementation + f_service_ << "sub process {" << endl; + indent_up(); + + f_service_ << indent() << "my ($self, $input, $output) = @_;" << endl; + + f_service_ << indent() << "my $rseqid = 0;" << endl << indent() << "my $fname = undef;" << endl + << indent() << "my $mtype = 0;" << endl << endl; + + f_service_ << indent() << "$input->readMessageBegin(\\$fname, \\$mtype, \\$rseqid);" << endl; + + // HOT: check for method implementation + f_service_ << indent() << "my $methodname = 'process_'.$fname;" << endl << indent() + << "if (!$self->can($methodname)) {" << endl; + indent_up(); + + f_service_ << indent() << "$input->skip(TType::STRUCT);" << endl << indent() + << "$input->readMessageEnd();" << endl << indent() + << "my $x = new TApplicationException('Function '.$fname.' not implemented.', " + "TApplicationException::UNKNOWN_METHOD);" << endl << indent() + << "$output->writeMessageBegin($fname, TMessageType::EXCEPTION, $rseqid);" << endl + << indent() << "$x->write($output);" << endl << indent() + << "$output->writeMessageEnd();" << endl << indent() + << "$output->getTransport()->flush();" << endl << indent() << "return;" << endl; + + indent_down(); + f_service_ << indent() << "}" << endl << indent() + << "$self->$methodname($rseqid, $input, $output);" << endl << indent() << "return 1;" + << endl; + + indent_down(); + + f_service_ << "}" << endl << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_perl_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + // Open function + f_service_ << "sub process_" << tfunction->get_name() << " {" << endl; + + indent_up(); + + f_service_ << indent() << "my ($self, $seqid, $input, $output) = @_;" << endl; + + string argsname = perl_namespace(tservice->get_program()) + service_name_ + "_" + + tfunction->get_name() + "_args"; + string resultname = perl_namespace(tservice->get_program()) + service_name_ + "_" + + tfunction->get_name() + "_result"; + + f_service_ << indent() << "my $args = new " << argsname << "();" << endl << indent() + << "$args->read($input);" << endl; + + f_service_ << indent() << "$input->readMessageEnd();" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << "my $result = new " << resultname << "();" << endl; + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_ << indent() << "eval {" << endl; + indent_up(); + } + + // Generate the function call + 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_ << "$self->{handler}->" << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "$args->" << (*f_iter)->get_name(); + } + f_service_ << ");" << endl; + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << indent() << "}; if( UNIVERSAL::isa($@,'" + << perl_namespace((*x_iter)->get_type()->get_program()) + << (*x_iter)->get_type()->get_name() << "') ){ " << endl; + + indent_up(); + f_service_ << indent() << "$result->{" << (*x_iter)->get_name() << "} = $@;" << endl; + f_service_ << indent() << "$@ = undef;" << endl; + indent_down(); + f_service_ << indent(); + } + f_service_ << "}" << endl; + + // catch-all for unexpected exceptions (THRIFT-3191) + f_service_ << indent() << "if ($@) {" << endl; + indent_up(); + f_service_ << indent() << "$@ =~ s/^\\s+|\\s+$//g;" << endl + << indent() << "my $err = new TApplicationException(\"Unexpected Exception: \" . $@, TApplicationException::INTERNAL_ERROR);" << endl + << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', TMessageType::EXCEPTION, $seqid);" << endl + << indent() << "$err->write($output);" << endl + << indent() << "$output->writeMessageEnd();" << endl + << indent() << "$output->getTransport()->flush();" << endl + << indent() << "$@ = undef;" << endl + << indent() << "return;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "return;" << endl; + indent_down(); + f_service_ << "}" << endl; + return; + } + + // Serialize the reply + f_service_ << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', TMessageType::REPLY, $seqid);" << endl + << indent() << "$result->write($output);" << endl + << indent() << "$output->writeMessageEnd();" << endl + << indent() << "$output->getTransport()->flush();" << endl; + + // Close function + indent_down(); + f_service_ << "}" << endl << endl; +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_perl_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_ << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + string name = ts->get_name(); + ts->set_name(service_name_ + "_" + name); + generate_perl_struct_definition(f_service_, ts, false); + generate_perl_function_helpers(*f_iter); + ts->set_name(name); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_perl_generator::generate_perl_function_helpers(t_function* tfunction) { + t_struct result(program_, service_name_ + "_" + 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_perl_struct_definition(f_service_, &result, false); +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_perl_generator::generate_service_interface(t_service* tservice) { + string extends_if = ""; + t_service* extends_s = tservice->get_extends(); + if (extends_s != NULL) { + extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name() + + "If);"; + } + + f_service_ << "package " << perl_namespace(program_) << service_name_ << "If;" << endl << endl + << "use strict;" << endl << extends_if << endl << endl; + + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << "sub " << function_signature(*f_iter) << endl << " die 'implement interface';\n}" + << endl << endl; + } + indent_down(); +} + +/** + * Generates a REST interface + */ +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) { + extends = extends_s->get_name(); + extends_if = "use base qw(" + perl_namespace(extends_s->get_program()) + extends_s->get_name() + + "Rest);"; + } + f_service_ << "package " << perl_namespace(program_) << service_name_ << "Rest;" << endl << endl + << "use strict;" << endl << extends_if << endl << endl; + + if (extends.empty()) { + f_service_ << "sub new {" << endl; + + indent_up(); + + f_service_ << indent() << "my ($classname, $impl) = @_;" << endl << indent() + << "my $self ={ impl => $impl };" << endl << endl << indent() + << "return bless($self,$classname);" << endl; + + indent_down(); + + f_service_ << "}" << endl << endl; + } + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << "sub " << (*f_iter)->get_name() << "{" << endl; + + indent_up(); + + f_service_ << indent() << "my ($self, $request) = @_;" << endl << endl; + + const vector& args = (*f_iter)->get_arglist()->get_members(); + vector::const_iterator a_iter; + for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) { + t_type* atype = get_true_type((*a_iter)->get_type()); + string req = "$request->{'" + (*a_iter)->get_name() + "'}"; + f_service_ << indent() << "my $" << (*a_iter)->get_name() << " = (" << req << ") ? " << req + << " : undef;" << endl; + if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) { + f_service_ << indent() << "my @" << (*a_iter)->get_name() << " = split(/,/, $" + << (*a_iter)->get_name() << ");" << endl << indent() << "$" + << (*a_iter)->get_name() << " = \\@" << (*a_iter)->get_name() << endl; + } + } + f_service_ << indent() << "return $self->{impl}->" << (*f_iter)->get_name() << "(" + << argument_list((*f_iter)->get_arglist()) << ");" << endl; + indent_down(); + indent(f_service_) << "}" << endl << endl; + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +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) { + extends = perl_namespace(extends_s->get_program()) + extends_s->get_name(); + extends_client = "use base qw(" + extends + "Client);"; + } + + f_service_ << "package " << perl_namespace(program_) << service_name_ << "Client;" << endl << endl + << extends_client << endl << "use base qw(" << perl_namespace(program_) + << service_name_ << "If);" << endl; + + // Constructor function + f_service_ << "sub new {" << endl; + + indent_up(); + + f_service_ << indent() << "my ($classname, $input, $output) = @_;" << endl << indent() + << "my $self = {};" << endl; + + if (!extends.empty()) { + f_service_ << indent() << "$self = $classname->SUPER::new($input, $output);" << endl; + } else { + f_service_ << indent() << "$self->{input} = $input;" << endl << indent() + << "$self->{output} = defined $output ? $output : $input;" << endl << indent() + << "$self->{seqid} = 0;" << endl; + } + + f_service_ << indent() << "return bless($self,$classname);" << endl; + + indent_down(); + + f_service_ << "}" << endl << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + // Open function + f_service_ << "sub " << function_signature(*f_iter) << endl; + + indent_up(); + + indent(f_service_) << indent() << "$self->send_" << funname << "("; + + bool 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; + + if (!(*f_iter)->is_oneway()) { + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << "return "; + } + f_service_ << "$self->recv_" << funname << "();" << endl; + } + + indent_down(); + + f_service_ << "}" << endl << endl; + + f_service_ << "sub send_" << function_signature(*f_iter) << endl; + + indent_up(); + + std::string argsname = perl_namespace(tservice->get_program()) + service_name_ + "_" + + (*f_iter)->get_name() + "_args"; + + // Serialize the request header + f_service_ << indent() << "$self->{output}->writeMessageBegin('" << (*f_iter)->get_name() + << "', " << ((*f_iter)->is_oneway() ? "TMessageType::ONEWAY" : "TMessageType::CALL") + << ", $self->{seqid});" << endl; + + f_service_ << indent() << "my $args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "$args->{" << (*fld_iter)->get_name() << "} = $" + << (*fld_iter)->get_name() << ";" << endl; + } + + // Write to the stream + f_service_ << indent() << "$args->write($self->{output});" << endl << indent() + << "$self->{output}->writeMessageEnd();" << endl << indent() + << "$self->{output}->getTransport()->flush();" << endl; + + indent_down(); + + f_service_ << "}" << endl; + + if (!(*f_iter)->is_oneway()) { + std::string resultname = perl_namespace(tservice->get_program()) + service_name_ + "_" + + (*f_iter)->get_name() + "_result"; + t_struct noargs(program_); + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_ << endl << "sub " << function_signature(&recv_function) << endl; + + indent_up(); + + f_service_ << indent() << "my $rseqid = 0;" << endl << indent() << "my $fname;" << endl + << indent() << "my $mtype = 0;" << endl << endl; + + f_service_ << indent() << "$self->{input}->readMessageBegin(\\$fname, \\$mtype, \\$rseqid);" + << endl << indent() << "if ($mtype == TMessageType::EXCEPTION) {" << endl + << indent() << " my $x = new TApplicationException();" << endl << indent() + << " $x->read($self->{input});" << endl << indent() + << " $self->{input}->readMessageEnd();" << endl << indent() << " die $x;" << endl + << indent() << "}" << endl; + + f_service_ << indent() << "my $result = new " << resultname << "();" << endl << indent() + << "$result->read($self->{input});" << endl; + + f_service_ << indent() << "$self->{input}->readMessageEnd();" << endl << endl; + + // Careful, only return result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if (defined $result->{success} ) {" << endl << indent() + << " return $result->{success};" << endl << indent() << "}" << endl; + } + + 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) { + f_service_ << indent() << "if (defined $result->{" << (*x_iter)->get_name() << "}) {" + << endl << indent() << " die $result->{" << (*x_iter)->get_name() << "};" + << endl << indent() << "}" << endl; + } + + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_) << "return;" << endl; + } else { + f_service_ << indent() << "die \"" << (*f_iter)->get_name() << " failed: unknown result\";" + << endl; + } + + // Close function + indent_down(); + f_service_ << "}" << endl; + } + } +} + +/** + * Deserializes a field of any type. + */ +void t_perl_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool inclass) { + (void)inclass; + 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(); + } + + string name = tfield->get_name(); + + // Hack for when prefix is defined (always a hash ref) + if (!prefix.empty()) { + name = prefix + "{" + tfield->get_name() + "}"; + } + + 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) << "$xfer += $input->"; + + 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: + out << "readString(\\$" << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "readBool(\\$" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "readByte(\\$" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "readI16(\\$" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "readI32(\\$" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "readI64(\\$" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "readDouble(\\$" << name << ");"; + break; + default: + throw "compiler error: no PERL name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "readI32(\\$" << name << ");"; + } + out << endl; + + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Generates an unserializer for a variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_perl_generator::generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix) { + out << indent() << "$" << prefix << " = new " << perl_namespace(tstruct->get_program()) + << tstruct->get_name() << "();" << endl << indent() << "$xfer += $" << prefix + << "->read($input);" << endl; +} + +void t_perl_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + out << indent() << "my $" << size << " = 0;" << endl; + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << "$" << prefix << " = {};" << endl << indent() << "my $" << ktype << " = 0;" + << endl << indent() << "my $" << vtype << " = 0;" << endl; + + out << indent() << "$xfer += $input->readMapBegin(" + << "\\$" << ktype << ", \\$" << vtype << ", \\$" << size << ");" << endl; + + } else if (ttype->is_set()) { + + out << indent() << "$" << prefix << " = {};" << endl << indent() << "my $" << etype << " = 0;" + << endl << indent() << "$xfer += $input->readSetBegin(" + << "\\$" << etype << ", \\$" << size << ");" << endl; + + } else if (ttype->is_list()) { + + out << indent() << "$" << prefix << " = [];" << endl << indent() << "my $" << etype << " = 0;" + << endl << indent() << "$xfer += $input->readListBegin(" + << "\\$" << etype << ", \\$" << size << ");" << endl; + } + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for (my $" << i << " = 0; $" << i << " < $" << size << "; ++$" << 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); + + // Read container end + if (ttype->is_map()) { + indent(out) << "$xfer += $input->readMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "$xfer += $input->readSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "$xfer += $input->readListEnd();" << endl; + } + + scope_down(out); +} + +/** + * Generates code to deserialize a map + */ +void t_perl_generator::generate_deserialize_map_element(ofstream& 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, true, true) << endl; + indent(out) << declare_field(&fval, true, true) << endl; + + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); + + indent(out) << "$" << prefix << "->{$" << key << "} = $" << val << ";" << endl; +} + +void t_perl_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + string elem = tmp("elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(out) << "my $" << elem << " = undef;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << "$" << prefix << "->{$" << elem << "} = 1;" << endl; +} + +void t_perl_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix) { + string elem = tmp("elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(out) << "my $" << elem << " = undef;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << "push(@{$" << prefix << "},$" << elem << ");" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_perl_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + "{" + tfield->get_name() + "}"); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + "{" + tfield->get_name() + "}"); + } else if (type->is_base_type() || type->is_enum()) { + + string name = tfield->get_name(); + + // Hack for when prefix is defined (always a hash ref) + if (!prefix.empty()) + name = prefix + "{" + tfield->get_name() + "}"; + + indent(out) << "$xfer += $output->"; + + 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: + out << "writeString($" << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "writeBool($" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte($" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16($" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32($" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64($" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble($" << name << ");"; + break; + default: + throw "compiler error: no PERL name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32($" << name << ");"; + } + out << endl; + + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_perl_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << "$xfer += $" << prefix << "->write($output);" << endl; +} + +/** + * Writes out a container + */ +void t_perl_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + indent(out) << "$xfer += $output->writeMapBegin(" + << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "scalar(keys %{$" << prefix << "}));" << endl; + } else if (ttype->is_set()) { + indent(out) << "$xfer += $output->writeSetBegin(" + << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " + << "scalar(@{$" << prefix << "}));" << endl; + + } else if (ttype->is_list()) { + + indent(out) << "$xfer += $output->writeListBegin(" + << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " + << "scalar(@{$" << prefix << "}));" << endl; + } + + scope_up(out); + + if (ttype->is_map()) { + string kiter = tmp("kiter"); + string viter = tmp("viter"); + indent(out) << "while( my ($" << kiter << ",$" << viter << ") = each %{$" << prefix << "}) " + << endl; + + scope_up(out); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + scope_down(out); + + } else if (ttype->is_set()) { + string iter = tmp("iter"); + indent(out) << "foreach my $" << iter << " (@{$" << prefix << "})" << endl; + scope_up(out); + generate_serialize_set_element(out, (t_set*)ttype, iter); + scope_down(out); + + } else if (ttype->is_list()) { + string iter = tmp("iter"); + indent(out) << "foreach my $" << iter << " (@{$" << prefix << "}) " << endl; + scope_up(out); + generate_serialize_list_element(out, (t_list*)ttype, iter); + scope_down(out); + } + + scope_down(out); + + if (ttype->is_map()) { + indent(out) << "$xfer += $output->writeMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "$xfer += $output->writeSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "$xfer += $output->writeListEnd();" << endl; + } + + scope_down(out); +} + +/** + * Serializes the members of a map. + * + */ +void t_perl_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield); +} + +/** + * Serializes the members of a set. + */ +void t_perl_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Serializes the members of a list. + */ +void t_perl_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield); +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_perl_generator::declare_field(t_field* tfield, bool init, bool obj) { + string result = "my $" + tfield->get_name(); + if (init) { + t_type* type = get_true_type(tfield->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: + break; + case t_base_type::TYPE_STRING: + result += " = ''"; + break; + case t_base_type::TYPE_BOOL: + result += " = 0"; + 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 += " = 0.0"; + break; + default: + throw "compiler error: no PERL initializer for base type " + + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + result += " = 0"; + } else if (type->is_container()) { + result += " = []"; + } else if (type->is_struct() || type->is_xception()) { + if (obj) { + result += " = new " + perl_namespace(type->get_program()) + type->get_name() + "()"; + } else { + result += " = undef"; + } + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_perl_generator::function_signature(t_function* tfunction, string prefix) { + + string str; + + str = prefix + tfunction->get_name() + "{\n"; + str += " my $self = shift;\n"; + + // Need to create perl function arg inputs + const vector& fields = tfunction->get_arglist()->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + str += " my $" + (*f_iter)->get_name() + " = shift;\n"; + } + + return str; +} + +/** + * Renders a field list + */ +string t_perl_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 += "$" + (*f_iter)->get_name(); + } + return result; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_perl_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 "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(); +} + +THRIFT_REGISTER_GENERATOR(perl, "Perl", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc new file mode 100644 index 00000000..515e165d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_php_generator.cc @@ -0,0 +1,2656 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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::ofstream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +#define NSGLOBAL (nsglobal_.size() ? nsglobal_ : "") +#define NSGLOBAL_A ("\\" + NSGLOBAL) +#define NSGLOBAL_B (NSGLOBAL + "\\") +#define NSGLOBAL_AB ("\\" + NSGLOBAL + "\\") + +/** + * PHP code generator. + * + */ +class t_php_generator : public t_oop_generator { +public: + t_php_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; + + binary_inline_ = false; + rest_ = false; + phps_ = false; + oop_ = false; + validate_ = false; + json_serializable_ = false; + nsglobal_ = ""; // by default global namespace is empty + psr4_ = false; + for (iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if (iter->first.compare("inlined") == 0) { + binary_inline_ = true; + } else if (iter->first.compare("rest") == 0) { + rest_ = true; + } else if (iter->first.compare("server") == 0) { + phps_ = true; + } else if (iter->first.compare("oop") == 0) { + oop_ = true; + } else if (iter->first.compare("validate") == 0) { + validate_ = true; + } else if (iter->first.compare("json") == 0) { + json_serializable_ = true; + } else if (iter->first.compare("nsglobal") == 0) { + nsglobal_ = iter->second; + } else if (iter->first.compare("psr4") == 0) { + psr4_ = true; + } else { + throw "unknown option php:" + iter->first; + } + } + + if (oop_ && binary_inline_) { + throw "oop and inlined are mutually exclusive."; + } + + out_dir_base_ = (binary_inline_ ? "gen-phpi" : "gen-php"); + escape_['$'] = "\\$"; + } + + static bool is_valid_namespace(const std::string& sub_namespace); + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + /** + * 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); + + std::string render_const_value(t_type* type, t_const_value* value); + + /** + * Structs! + */ + + void generate_php_struct(t_struct* tstruct, bool is_exception); + void generate_php_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false, + bool is_result = false); + void generate_php_struct_reader(std::ofstream& out, t_struct* tstruct, bool is_result); + void generate_php_struct_writer(std::ofstream& out, t_struct* tstruct, bool is_result); + void generate_php_function_helpers(t_service* tservice, t_function* tfunction); + void generate_php_struct_required_validator(ofstream& out, + t_struct* tstruct, + std::string method_name, + bool write_mode); + void generate_php_struct_read_validator(ofstream& out, t_struct* tstruct); + void generate_php_struct_write_validator(ofstream& out, t_struct* tstruct); + void generate_php_struct_json_serialize(ofstream& out, t_struct* tstruct, bool is_result); + bool needs_php_write_validator(t_struct* tstruct, bool is_result); + bool needs_php_read_validator(t_struct* tstruct, bool is_result); + int get_php_num_required_fields(const vector& fields, bool write_mode); + + void generate_php_type_spec(std::ofstream& out, t_type* t); + void generate_php_struct_spec(std::ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_service_rest(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_processor(t_service* tservice); + void generate_process_function(std::ofstream& out, t_service* tservice, t_function* tfunction); + void generate_service_header(t_service* tservice, std::ofstream& file); + void generate_program_header(std::ofstream& file); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + bool inclass = false); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_php_doc(std::ofstream& out, t_doc* tdoc); + + void generate_php_doc(std::ofstream& out, t_field* tfield); + + void generate_php_doc(std::ofstream& out, t_function* tfunction); + + void generate_php_docstring_comment(std::ofstream& out, string contents); + + /** + * Helper rendering functions + */ + + std::string php_includes(); + std::string declare_field(t_field* tfield, bool init = false, bool obj = false); + std::string function_signature(t_function* tfunction, std::string prefix = ""); + std::string argument_list(t_struct* tstruct, bool addTypeHints = true); + std::string type_to_cast(t_type* ttype); + std::string type_to_enum(t_type* ttype); + std::string type_to_phpdoc(t_type* ttype); + + std::string php_namespace_base(const t_program* p) { + std::string ns = p->get_namespace("php"); + const char* delimiter = "\\"; + size_t position = ns.find('.'); + while (position != string::npos) { + ns.replace(position, 1, delimiter); + position = ns.find('.', position + 1); + } + return ns; + } + + // general use namespace prefixing: \my\namespace\ or my_namespace_ + string php_namespace(const t_program* p) { + string ns = php_namespace_base(p); + return (nsglobal_.size() ? NSGLOBAL_AB : NSGLOBAL_B) + (ns.size() ? (ns + "\\") : ""); + } + + // return the namespace of a file: + // global\ns\sub\ns or global\ns or sub\ns + string php_namespace_suffix(const t_program* p) { + string ns = php_namespace_base(p); + + return NSGLOBAL + + (ns.size() && NSGLOBAL.size() ? "\\" : "") + + ns; + } + + // add a directory to already existing namespace + string php_namespace_directory(string directory, bool end = true) { + (void)directory; + if (end) { + return ";"; + } else { + return ""; + } + } + + // writing an autload identifier into globa;ls: my\namespace\ or my_namespace_ + string php_namespace_autoload(const t_program* p) { + std::string ns = php_namespace_base(p); + return (nsglobal_.size() ? NSGLOBAL_B : NSGLOBAL) + (ns.size() ? (ns + "\\") : ""); + } + + // declaring a type: typename or my_namespace_typename + string php_namespace_declaration(t_type* t) { return t->get_name(); } + + std::string php_path(t_program* p) { + std::string ns = p->get_namespace("php.path"); + if (ns.empty()) { + return p->get_name(); + } + + // Transform the java-style namespace into a path. + for (std::string::iterator it = ns.begin(); it != ns.end(); ++it) { + if (*it == '.') { + *it = '/'; + } + } + + return ns + '/'; + } + + /** + * Transform class_method into ClassMethod + * + * @param str + * @return stirng + */ + string classify(string str) { + string classe = ""; + + vector x = split(str, '_'); + + for (size_t i = 0; i < x.size(); ++i) { + classe = classe + capitalize(x[i]); + } + + return classe; + } + + /** + * Split method + * @param s + * @param delim + * @param elems + * @return + */ + vector& split(const string& s, char delim, vector& elems) { + stringstream ss(s); + string item; + + while (getline(ss, item, delim)) { + elems.push_back(item); + } + + return elems; + } + + vector split(const string& s, char delim) { + vector elems; + + return split(s, delim, elems); + } + + /** + * Capitalize method + * @param str + * @return + */ + string capitalize(string str) { + string::iterator it(str.begin()); + + if (it != str.end()) + str[0] = toupper((unsigned char)str[0]); + + // while(++it != str.end()) + // { + // *it = tolower((unsigned char)*it); + // } + return str; + } + +private: + /** + * File streams + */ + std::ofstream f_types_; + std::ofstream f_service_; + + std::string package_dir_; + /** + * Generate protocol-independent template? Or Binary inline code? + */ + bool binary_inline_; + + /** + * Generate a REST handler class + */ + bool rest_; + + /** + * Generate stubs for a PHP server + */ + bool phps_; + + /** + * Whether to use OOP base class TBase + */ + bool oop_; + + /** + * Whether to hold each class in separate file to allow PSR4-autoloading + */ + bool psr4_; + + /** + * Whether to generate validator code + */ + bool validate_; + + /** + * Whether to generate JsonSerializable classes + */ + bool json_serializable_; + + /** + * Global namespace for PHP 5.3 + */ + std::string nsglobal_; +}; + +bool t_php_generator::is_valid_namespace(const std::string& sub_namespace) { + return sub_namespace == "path"; +} + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_php_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Create Real directory Namespaces + 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] + "/"; + MKDIR(package_dir_.c_str()); + } + + // Prepare output file for all the types in non-psr4 mode + if (!psr4_) { + // Make output file + string f_types_name = package_dir_ + "Types.php"; + f_types_.open(f_types_name.c_str()); + generate_program_header(f_types_); + } +} + +/** + * Prints standard php includes + */ +string t_php_generator::php_includes() { + string includes = "use Thrift\\Base\\TBase;\n" + "use Thrift\\Type\\TType;\n" + "use Thrift\\Type\\TMessageType;\n" + "use Thrift\\Exception\\TException;\n" + "use Thrift\\Exception\\TProtocolException;\n" + "use Thrift\\Protocol\\TProtocol;\n" + "use Thrift\\Protocol\\TBinaryProtocolAccelerated;\n" + "use Thrift\\Exception\\TApplicationException;\n"; + + if (json_serializable_) { + includes += "use JsonSerializable;\n" + "use stdClass;\n"; + } + + return includes + "\n"; +} + +/** + * Close up (or down) some filez. + */ +void t_php_generator::close_generator() { + if (!psr4_) { + // Close types file + f_types_ << endl; + f_types_.close(); + } +} + +/** + * Generates a typedef. This is not done in PHP, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_php_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates service header contains namespace suffix and includes inside file specified + */ +void t_php_generator::generate_service_header(t_service* tservice, std::ofstream& file) { + file << "get_program()).empty()) { + file << "namespace " << php_namespace_suffix(tservice->get_program()) << ";" << endl; + } + file << autogen_comment() << php_includes(); + + file << endl; +} + +/** + * Generates program header contains namespace suffix and includes inside file specified + */ +void t_php_generator::generate_program_header(std::ofstream& file) { + file << "get_name() + ".php"; + f_enum.open(f_enum_name.c_str()); + generate_program_header(f_enum); + } + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + + // We're also doing it this way to see how it performs. It's more legible + // code but you can't do things like an 'extract' on it, which is a bit of + // a downer. + generate_php_doc(f_enum, tenum); + f_enum << "final class " << tenum->get_name() << " {" << endl; + indent_up(); + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + generate_php_doc(f_enum, *c_iter); + indent(f_enum) << "const " << (*c_iter)->get_name() << " = " << value << ";" << endl; + } + + indent(f_enum) << "static public $__names = array(" << endl; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_enum) << " " << value << " => '" << (*c_iter)->get_name() << "'," << endl; + } + indent(f_enum) << ");" << endl; + + indent_down(); + + f_enum << "}" << endl << endl; + if (psr4_) { + f_enum.close(); + } +} + +/** + * Generate constant class + * + * Override the one from t_generator + */ +void t_php_generator::generate_consts(vector consts) { + vector::iterator c_iter; + + // Create class only if needed + if (consts.size() > 0) { + + std::ofstream& f_consts = f_types_; + if (psr4_) { + string f_consts_name = package_dir_ + "Constant.php"; + f_consts.open(f_consts_name.c_str()); + generate_program_header(f_consts); + } + f_consts << "final class Constant extends \\Thrift\\Type\\TConstant {" << endl; + + indent_up(); + + // Create static property + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + + indent(f_consts) << "static protected $" << name << ";" << endl; + } + + // Create init function + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + + f_consts << endl; + + indent(f_consts) << "static protected function init_" << name << "() {" << endl; + indent_up(); + + indent(f_consts) << "return "; + generate_php_doc(f_consts, *c_iter); + f_consts << render_const_value((*c_iter)->get_type(), (*c_iter)->get_value()); + f_consts << ";" << endl; + + indent_down(); + indent(f_consts) << "}" << endl; + } + + indent_down(); + f_consts << "}" << endl << endl; + if (psr4_) { + f_consts.close(); + } + } +} + +/** + * 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 + */ +string t_php_generator::render_const_value(t_type* type, t_const_value* value) { + std::ostringstream out; + 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_STRING: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + indent(out) << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << "new " << php_namespace(type->get_program()) << type->get_name() << "(array(" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + out << indent(); + out << render_const_value(g_type_string, v_iter->first); + out << " => "; + out << render_const_value(field_type, v_iter->second); + out << "," << endl; + } + indent_down(); + indent(out) << "))"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + out << "array(" << endl; + indent_up(); + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent(); + out << render_const_value(ktype, v_iter->first); + out << " => "; + out << render_const_value(vtype, v_iter->second); + out << "," << endl; + } + indent_down(); + indent(out) << ")"; + } 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(); + } + out << "array(" << endl; + indent_up(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent(); + out << render_const_value(etype, *v_iter); + if (type->is_set()) { + out << " => true"; + } + out << "," << endl; + } + indent_down(); + indent(out) << ")"; + } + return out.str(); +} + +/** + * Make a struct + */ +void t_php_generator::generate_struct(t_struct* tstruct) { + generate_php_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_php_generator::generate_xception(t_struct* txception) { + generate_php_struct(txception, true); +} + +/** + * Structs can be normal or exceptions. + */ +void t_php_generator::generate_php_struct(t_struct* tstruct, bool is_exception) { + std::ofstream& f_struct = f_types_; + if (psr4_) { + string f_struct_name = package_dir_ + tstruct->get_name() + ".php"; + f_struct.open(f_struct_name.c_str()); + generate_program_header(f_struct); + } + generate_php_struct_definition(f_struct, tstruct, is_exception); + if (psr4_) { + f_struct.close(); + } +} + +void t_php_generator::generate_php_type_spec(ofstream& 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()) { + // Noop, type is all we need + } else if (t->is_struct() || t->is_xception()) { + indent(out) << "'class' => '" << php_namespace(t->get_program()) << t->get_name() << "'," + << endl; + } else if (t->is_map()) { + t_type* ktype = get_true_type(((t_map*)t)->get_key_type()); + t_type* vtype = get_true_type(((t_map*)t)->get_val_type()); + indent(out) << "'ktype' => " << type_to_enum(ktype) << "," << endl; + indent(out) << "'vtype' => " << type_to_enum(vtype) << "," << endl; + indent(out) << "'key' => array(" << endl; + indent_up(); + generate_php_type_spec(out, ktype); + indent_down(); + indent(out) << ")," << endl; + indent(out) << "'val' => array(" << endl; + indent_up(); + generate_php_type_spec(out, vtype); + indent(out) << ")," << endl; + indent_down(); + } else if (t->is_list() || t->is_set()) { + t_type* etype; + if (t->is_list()) { + etype = get_true_type(((t_list*)t)->get_elem_type()); + } else { + etype = get_true_type(((t_set*)t)->get_elem_type()); + } + indent(out) << "'etype' => " << type_to_enum(etype) << "," << endl; + indent(out) << "'elem' => array(" << endl; + indent_up(); + generate_php_type_spec(out, etype); + indent(out) << ")," << endl; + indent_down(); + } else { + throw "compiler error: no type for php struct spec field"; + } +} + +/** + * Generates the struct specification structure, which fully qualifies enough + * type information to generalize serialization routines. + */ +void t_php_generator::generate_php_struct_spec(ofstream& out, t_struct* tstruct) { + indent(out) << "if (!isset(self::$_TSPEC)) {" << endl; + indent_up(); + + indent(out) << "self::$_TSPEC = array(" << endl; + indent_up(); + + 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()); + indent(out) << (*m_iter)->get_key() << " => array(" << endl; + indent_up(); + out << indent() << "'var' => '" << (*m_iter)->get_name() << "'," << endl; + generate_php_type_spec(out, t); + indent(out) << ")," << endl; + indent_down(); + } + + indent_down(); + indent(out) << " );" << endl; + indent_down(); + indent(out) << "}" << endl; +} + +/** + * 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 + * decide to start using objects for them...) + * + * @param tstruct The struct definition + */ +void t_php_generator::generate_php_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_result) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + generate_php_doc(out, tstruct); + out << "class " << php_namespace_declaration(tstruct); + if (is_exception) { + out << " extends " + << "TException"; + } else if (oop_) { + out << " extends " + << "TBase"; + } + if (json_serializable_) { + out << " implements JsonSerializable"; + } + out << " {" << endl; + indent_up(); + + indent(out) << "static $_TSPEC;" << endl << endl; + + 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())) { + 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; + } + + out << endl; + + // Generate constructor from array + string param = (members.size() > 0) ? "$vals=null" : ""; + out << indent() << "public function __construct(" << param << ") {" << endl; + indent_up(); + + generate_php_struct_spec(out, tstruct); + + 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())) { + indent(out) << "$this->" << (*m_iter)->get_name() << " = " + << render_const_value(t, (*m_iter)->get_value()) << ";" << endl; + } + } + out << indent() << "if (is_array($vals)) {" << endl; + indent_up(); + if (oop_) { + out << indent() << "parent::__construct(self::$_TSPEC, $vals);" << endl; + } else { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << indent() << "if (isset($vals['" << (*m_iter)->get_name() << "'])) {" << endl + << indent() << " $this->" << (*m_iter)->get_name() << " = $vals['" + << (*m_iter)->get_name() << "'];" << endl << indent() << "}" << endl; + } + } + indent_down(); + out << indent() << "}" << endl; + } + scope_down(out); + out << endl; + + out << indent() << "public function getName() {" << endl << indent() << " return '" + << tstruct->get_name() << "';" << endl << indent() << "}" << endl << endl; + + generate_php_struct_reader(out, tstruct, is_result); + generate_php_struct_writer(out, tstruct, is_result); + if (needs_php_read_validator(tstruct, is_result)) { + generate_php_struct_read_validator(out, tstruct); + } + if (needs_php_write_validator(tstruct, is_result)) { + generate_php_struct_write_validator(out, tstruct); + } + if (json_serializable_) { + generate_php_struct_json_serialize(out, tstruct, is_result); + } + + indent_down(); + out << indent() << "}" << endl << endl; +} + +/** + * Generates the read() method for a struct + */ +void t_php_generator::generate_php_struct_reader(ofstream& out, t_struct* tstruct, bool is_result) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "public function read($input)" << endl; + scope_up(out); + + if (oop_) { + if (needs_php_read_validator(tstruct, is_result)) { + indent(out) << "$tmp = $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);" + << endl; + indent(out) << "$this->_validateForRead();" << endl; + indent(out) << "return $tmp;" << endl; + } else { + indent(out) << "return $this->_read('" << tstruct->get_name() << "', self::$_TSPEC, $input);" + << endl; + } + scope_down(out); + out << endl; + return; + } + + out << indent() << "$xfer = 0;" << endl << indent() << "$fname = null;" << endl << indent() + << "$ftype = 0;" << endl << indent() << "$fid = 0;" << endl; + + // Declare stack tmp variables + if (!binary_inline_) { + indent(out) << "$xfer += $input->readStructBegin($fname);" << endl; + } + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + + scope_up(out); + + // Read beginning field marker + if (binary_inline_) { + t_field fftype(g_type_i8, "ftype"); + t_field ffid(g_type_i16, "fid"); + generate_deserialize_field(out, &fftype); + out << indent() << "if ($ftype == " + << "TType::STOP) {" << endl << indent() << " break;" << endl << indent() << "}" << endl; + generate_deserialize_field(out, &ffid); + } else { + indent(out) << "$xfer += $input->readFieldBegin($fname, $ftype, $fid);" << endl; + // Check for field STOP marker and break + indent(out) << "if ($ftype == " + << "TType::STOP) {" << endl; + indent_up(); + indent(out) << "break;" << endl; + indent_down(); + indent(out) << "}" << endl; + } + + // Switch statement on the field we are reading + indent(out) << "switch ($fid)" << 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 ($ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + generate_deserialize_field(out, *f_iter, "this->"); + indent_down(); + out << indent() << "} else {" << endl; + if (binary_inline_) { + indent(out) << " $xfer += " + << "TProtocol::skipBinary($input, $ftype);" << endl; + } else { + indent(out) << " $xfer += $input->skip($ftype);" << endl; + } + out << indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + indent(out) << "default:" << endl; + if (binary_inline_) { + indent(out) << " $xfer += " + << "TProtocol::skipBinary($input, $ftype);" << endl; + } else { + indent(out) << " $xfer += $input->skip($ftype);" << endl; + } + indent(out) << " break;" << endl; + + scope_down(out); + + if (!binary_inline_) { + // Read field end marker + indent(out) << "$xfer += $input->readFieldEnd();" << endl; + } + + scope_down(out); + + if (!binary_inline_) { + indent(out) << "$xfer += $input->readStructEnd();" << endl; + } + + if (needs_php_read_validator(tstruct, is_result)) { + indent(out) << "$this->_validateForRead();" << endl; + } + + indent(out) << "return $xfer;" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +/** + * Generates the write() method for a struct + */ +void t_php_generator::generate_php_struct_writer(ofstream& out, t_struct* tstruct, bool is_result) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + if (binary_inline_) { + indent(out) << "public function write(&$output) {" << endl; + } else { + indent(out) << "public function write($output) {" << endl; + } + indent_up(); + + if (needs_php_write_validator(tstruct, is_result)) { + indent(out) << "$this->_validateForWrite();" << endl; + } + + if (oop_) { + indent(out) << "return $this->_write('" << tstruct->get_name() << "', self::$_TSPEC, $output);" + << endl; + scope_down(out); + out << endl; + return; + } + + indent(out) << "$xfer = 0;" << endl; + + if (!binary_inline_) { + indent(out) << "$xfer += $output->writeStructBegin('" << name << "');" << endl; + } + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << indent() << "if ($this->" << (*f_iter)->get_name() << " !== null) {" << endl; + indent_up(); + + t_type* type = get_true_type((*f_iter)->get_type()); + string expect; + if (type->is_container()) { + expect = "array"; + } else if (type->is_struct()) { + expect = "object"; + } + if (!expect.empty()) { + out << indent() << "if (!is_" << expect << "($this->" << (*f_iter)->get_name() << ")) {" + << endl; + indent_up(); + out << indent() << "throw new " + << "TProtocolException('Bad type in structure.', " + << "TProtocolException::INVALID_DATA);" << endl; + scope_down(out); + } + + // Write field header + if (binary_inline_) { + out << indent() << "$output .= pack('c', " << type_to_enum((*f_iter)->get_type()) << ");" + << endl << indent() << "$output .= pack('n', " << (*f_iter)->get_key() << ");" << endl; + } else { + indent(out) << "$xfer += $output->writeFieldBegin(" + << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type()) + << ", " << (*f_iter)->get_key() << ");" << endl; + } + + // Write field contents + generate_serialize_field(out, *f_iter, "this->"); + + // Write field closer + if (!binary_inline_) { + indent(out) << "$xfer += $output->writeFieldEnd();" << endl; + } + + indent_down(); + indent(out) << "}" << endl; + } + + if (binary_inline_) { + out << indent() << "$output .= pack('c', " + << "TType::STOP);" << endl; + } else { + out << indent() << "$xfer += $output->writeFieldStop();" << endl << indent() + << "$xfer += $output->writeStructEnd();" << endl; + } + + out << indent() << "return $xfer;" << endl; + + indent_down(); + out << indent() << "}" << endl << endl; +} + +void t_php_generator::generate_php_struct_read_validator(ofstream& out, t_struct* tstruct) { + generate_php_struct_required_validator(out, tstruct, "_validateForRead", false); +} + +void t_php_generator::generate_php_struct_write_validator(ofstream& out, t_struct* tstruct) { + generate_php_struct_required_validator(out, tstruct, "_validateForWrite", true); +} + +void t_php_generator::generate_php_struct_required_validator(ofstream& out, + t_struct* tstruct, + std::string method_name, + bool write_mode) { + indent(out) << "private function " << method_name << "() {" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + + if (fields.size() > 0) { + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + if (field->get_req() == t_field::T_REQUIRED + || (field->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) { + indent(out) << "if ($this->" << field->get_name() << " === null) {" << endl; + indent_up(); + indent(out) << "throw new TProtocolException('Required field " << tstruct->get_name() << "." + << field->get_name() << " is unset!');" << endl; + indent_down(); + indent(out) << "}" << endl; + } + } + } + + indent_down(); + indent(out) << "}" << endl << endl; +} + +void t_php_generator::generate_php_struct_json_serialize(ofstream& out, + t_struct* tstruct, + bool is_result) { + indent(out) << "public function jsonSerialize() {" << endl; + indent_up(); + + if (needs_php_write_validator(tstruct, is_result)) { + indent(out) << "$this->_validateForWrite();" << endl; + } + + indent(out) << "$json = new stdClass;" << endl; + + const vector& fields = tstruct->get_members(); + + if (fields.size() > 0) { + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + t_type* type = field->get_type(); + const string& name = field->get_name(); + if (type->is_map()) { + t_type* key_type = ((t_map*)type)->get_key_type(); + if (!(key_type->is_base_type() || key_type->is_enum())) { + // JSON object keys must be strings. PHP's json_encode() + // function will convert any scalar key to strings, but + // we skip thrift maps with non-scalar keys. + continue; + } + } + indent(out) << "if ($this->" << name << " !== null) {" << endl; + indent_up(); + indent(out) << "$json->" << name << " = "; + if (type->is_map()) { + out << "(object)"; + } else { + out << type_to_cast(type); + } + out << "$this->" << name << ";" << endl; + indent_down(); + indent(out) << "}" << endl; + } + } + + indent(out) << "return $json;" << endl; + indent_down(); + + indent(out) << "}" << endl << endl; +} + +int t_php_generator::get_php_num_required_fields(const vector& fields, bool write_mode) { + int num_req = 0; + + if (fields.size() > 0) { + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED + || ((*f_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT && write_mode)) { + ++num_req; + } + } + } + return num_req; +} + +bool t_php_generator::needs_php_write_validator(t_struct* tstruct, bool is_result) { + return (validate_ && !is_result && !tstruct->is_union() + && get_php_num_required_fields(tstruct->get_members(), true) > 0); +} + +bool t_php_generator::needs_php_read_validator(t_struct* tstruct, bool is_result) { + return (validate_ && !is_result + && (get_php_num_required_fields(tstruct->get_members(), false) > 0)); +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_php_generator::generate_service(t_service* tservice) { + if(!psr4_) { + string f_service_name = package_dir_ + service_name_ + ".php"; + f_service_.open(f_service_name.c_str()); + generate_service_header(tservice, f_service_); + } + + // Generate the three main parts of the service (well, two for now in PHP) + generate_service_interface(tservice); + if (rest_) { + generate_service_rest(tservice); + } + generate_service_client(tservice); + generate_service_helpers(tservice); + if (phps_) { + generate_service_processor(tservice); + } + + if(!psr4_) { + // Close service file + f_service_ << endl; + f_service_.close(); + } +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_php_generator::generate_service_processor(t_service* tservice) { + std::ofstream& f_service_processor = f_service_; + if (psr4_) { + string f_service_processor_name = package_dir_ + service_name_ + "Processor.php"; + f_service_processor.open(f_service_processor_name.c_str()); + generate_service_header(tservice, f_service_processor); + } + + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = tservice->get_extends()->get_name(); + extends_processor = " extends " + php_namespace(tservice->get_extends()->get_program()) + + extends + "Processor"; + } + + // Generate the header portion + f_service_processor << "class " << service_name_ << "Processor" << extends_processor << " {" << endl; + indent_up(); + + if (extends.empty()) { + f_service_processor << indent() << "protected $handler_ = null;" << endl; + } + + f_service_processor << indent() << "public function __construct($handler) {" << endl; + if (extends.empty()) { + f_service_processor << indent() << " $this->handler_ = $handler;" << endl; + } else { + f_service_processor << indent() << " parent::__construct($handler);" << endl; + } + f_service_processor << indent() << "}" << endl << endl; + + // Generate the server implementation + indent(f_service_processor) << "public function process($input, $output) {" << endl; + indent_up(); + + f_service_processor << indent() << "$rseqid = 0;" << endl << indent() << "$fname = null;" << endl + << indent() << "$mtype = 0;" << endl << endl; + + if (binary_inline_) { + t_field ffname(g_type_string, "fname"); + t_field fmtype(g_type_i8, "mtype"); + t_field fseqid(g_type_i32, "rseqid"); + generate_deserialize_field(f_service_processor, &ffname, "", true); + generate_deserialize_field(f_service_processor, &fmtype, "", true); + generate_deserialize_field(f_service_processor, &fseqid, "", true); + } else { + f_service_processor << indent() << "$input->readMessageBegin($fname, $mtype, $rseqid);" << endl; + } + + // HOT: check for method implementation + f_service_processor << indent() << "$methodname = 'process_'.$fname;" << endl << indent() + << "if (!method_exists($this, $methodname)) {" << endl; + if (binary_inline_) { + f_service_processor << indent() << " throw new \\Exception('Function '.$fname.' not implemented.');" + << endl; + } else { + f_service_processor << indent() << " $input->skip(" + << "TType::STRUCT);" << endl << indent() << " $input->readMessageEnd();" << endl + << indent() << " $x = new " + << "TApplicationException('Function '.$fname.' not implemented.', " + << "TApplicationException::UNKNOWN_METHOD);" << endl << indent() + << " $output->writeMessageBegin($fname, " + << "TMessageType::EXCEPTION, $rseqid);" << endl << indent() + << " $x->write($output);" << endl << indent() << " $output->writeMessageEnd();" + << endl << indent() << " $output->getTransport()->flush();" << endl << indent() + << " return;" << endl; + } + f_service_processor << indent() << "}" << endl << indent() + << "$this->$methodname($rseqid, $input, $output);" << endl << indent() + << "return true;" << endl; + indent_down(); + f_service_processor << indent() << "}" << endl << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(f_service_processor, tservice, *f_iter); + } + + indent_down(); + f_service_processor << "}" << endl; + + if (psr4_) { + f_service_processor.close(); + } +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_php_generator::generate_process_function(std::ofstream& out, t_service* tservice, t_function* tfunction) { + // Open function + indent(out) << "protected function process_" << tfunction->get_name() + << "($seqid, $input, $output) {" << endl; + indent_up(); + + string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + + tfunction->get_name() + "_args"; + string resultname = php_namespace(tservice->get_program()) + service_name_ + "_" + + tfunction->get_name() + "_result"; + + out << indent() << "$args = new " << argsname << "();" << endl << indent() + << "$args->read($input);" << endl; + if (!binary_inline_) { + out << indent() << "$input->readMessageEnd();" << endl; + } + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + out << indent() << "$result = new " << resultname << "();" << endl; + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + out << indent() << "try {" << endl; + indent_up(); + } + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + out << indent(); + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + out << "$result->success = "; + } + out << "$this->handler_->" << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + out << ", "; + } + out << "$args->" << (*f_iter)->get_name(); + } + out << ");" << endl; + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + out << indent() << "} catch (" + << php_namespace(get_true_type((*x_iter)->get_type())->get_program()) + << (*x_iter)->get_type()->get_name() << " $" << (*x_iter)->get_name() << ") {" + << endl; + if (!tfunction->is_oneway()) { + indent_up(); + out << indent() << "$result->" << (*x_iter)->get_name() << " = $" + << (*x_iter)->get_name() << ";" << endl; + indent_down(); + out << indent(); + } + } + out << "}" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + out << indent() << "return;" << endl; + indent_down(); + out << indent() << "}" << endl; + return; + } + + out << indent() << "$bin_accel = ($output instanceof " + << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');" + << endl; + + out << indent() << "if ($bin_accel)" << endl; + scope_up(out); + + out << indent() << "thrift_protocol_write_binary($output, '" << tfunction->get_name() + << "', " + << "TMessageType::REPLY, $result, $seqid, $output->isStrictWrite());" << endl; + + scope_down(out); + out << indent() << "else" << endl; + scope_up(out); + + // Serialize the request header + if (binary_inline_) { + out << indent() << "$buff = pack('N', (0x80010000 | " + << "TMessageType::REPLY)); " << endl << indent() << "$buff .= pack('N', strlen('" + << tfunction->get_name() << "'));" << endl << indent() << "$buff .= '" + << tfunction->get_name() << "';" << endl << indent() << "$buff .= pack('N', $seqid);" + << endl << indent() << "$result->write($buff);" << endl << indent() + << "$output->write($buff);" << endl << indent() << "$output->flush();" << endl; + } else { + out << indent() << "$output->writeMessageBegin('" << tfunction->get_name() << "', " + << "TMessageType::REPLY, $seqid);" << endl << indent() << "$result->write($output);" + << endl << indent() << "$output->writeMessageEnd();" << endl << indent() + << "$output->getTransport()->flush();" << endl; + } + + scope_down(out); + + // Close function + indent_down(); + out << indent() << "}" << endl; +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_php_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + std::ofstream& f_struct_definition = f_service_; + if (!psr4_) { + f_struct_definition << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + } + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + string name = ts->get_name(); + ts->set_name(service_name_ + "_" + name); + + if (psr4_) { + string f_struct_definition_name = package_dir_ + service_name_ + "_" + name + ".php"; + f_struct_definition.open(f_struct_definition_name.c_str()); + generate_service_header(tservice, f_struct_definition); + } + + generate_php_struct_definition(f_struct_definition, ts); + if (psr4_) { + f_struct_definition.close(); + } + + generate_php_function_helpers(tservice, *f_iter); + ts->set_name(name); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_php_generator::generate_php_function_helpers(t_service* tservice, t_function* tfunction) { + if (!tfunction->is_oneway()) { + t_struct result(program_, service_name_ + "_" + 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); + } + + std::ofstream& f_struct_helper = f_service_; + if (psr4_) { + string f_struct_helper_name = package_dir_ + result.get_name() + ".php"; + f_struct_helper.open(f_struct_helper_name.c_str()); + generate_service_header(tservice, f_struct_helper); + } + generate_php_struct_definition(f_struct_helper, &result, false, true); + if (psr4_) { + f_struct_helper.close(); + } + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_php_generator::generate_service_interface(t_service* tservice) { + std::ofstream& f_service_interface = f_service_; + if (psr4_) { + string f_service_interface_name = package_dir_ + service_name_ + "If.php"; + f_service_interface.open(f_service_interface_name.c_str()); + generate_service_header(tservice, f_service_interface); + } + + string extends = ""; + string extends_if = ""; + if (tservice->get_extends() != NULL) { + extends = " extends " + php_namespace(tservice->get_extends()->get_program()) + + tservice->get_extends()->get_name(); + extends_if = " extends " + php_namespace(tservice->get_extends()->get_program()) + + tservice->get_extends()->get_name() + "If"; + } + generate_php_doc(f_service_interface, tservice); + f_service_interface << "interface " << php_namespace_declaration(tservice) << "If" << extends_if << " {" + << endl; + indent_up(); + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_php_doc(f_service_interface, *f_iter); + indent(f_service_interface) << "public function " << function_signature(*f_iter) << ";" << endl; + } + indent_down(); + f_service_interface << "}" << endl << endl; + + // Close service interface file + f_service_interface << endl; + if (psr4_) { + f_service_interface.close(); + } +} + +/** + * Generates a REST interface + */ +void t_php_generator::generate_service_rest(t_service* tservice) { + std::ofstream& f_service_rest = f_service_; + if (psr4_) { + string f_service_rest_name = package_dir_ + service_name_ + "Rest.php"; + f_service_rest.open(f_service_rest_name.c_str()); + generate_service_header(tservice, f_service_rest); + } + + string extends = ""; + string extends_if = ""; + if (tservice->get_extends() != NULL) { + extends = " extends " + php_namespace(tservice->get_extends()->get_program()) + + tservice->get_extends()->get_name(); + extends_if = " extends " + php_namespace(tservice->get_extends()->get_program()) + + tservice->get_extends()->get_name() + "Rest"; + } + f_service_rest << "class " << service_name_ << "Rest" << extends_if << " {" << endl; + indent_up(); + + if (extends.empty()) { + f_service_rest << indent() << "protected $impl_;" << endl << endl; + } + + f_service_rest << indent() << "public function __construct($impl) {" << endl << indent() + << " $this->impl_ = $impl;" << endl << indent() << "}" << endl << endl; + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_service_rest) << "public function " << (*f_iter)->get_name() << "($request) {" << endl; + indent_up(); + const vector& args = (*f_iter)->get_arglist()->get_members(); + vector::const_iterator a_iter; + for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) { + t_type* atype = get_true_type((*a_iter)->get_type()); + string cast = type_to_cast(atype); + string req = "$request['" + (*a_iter)->get_name() + "']"; + if (atype->is_bool()) { + f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = " << cast << "(!empty(" << req + << ") && (" << req << " !== 'false'));" << endl; + } else { + f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = isset(" << req << ") ? " + << cast << req << " : null;" << endl; + } + if (atype->is_string() && ((t_base_type*)atype)->is_string_list()) { + f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = explode(',', $" + << (*a_iter)->get_name() << ");" << endl; + } else if (atype->is_map() || atype->is_list()) { + f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = json_decode($" + << (*a_iter)->get_name() << ", true);" << endl; + } else if (atype->is_set()) { + f_service_rest << indent() << "$" << (*a_iter)->get_name() << " = array_fill_keys(json_decode($" + << (*a_iter)->get_name() << ", true), 1);" << endl; + } else if (atype->is_struct() || atype->is_xception()) { + f_service_rest << indent() << "if ($" << (*a_iter)->get_name() << " !== null) {" << endl + << indent() << " $" << (*a_iter)->get_name() << " = new " + << php_namespace(atype->get_program()) << atype->get_name() << "(json_decode($" + << (*a_iter)->get_name() << ", true));" << endl << indent() << "}" << endl; + } + } + f_service_rest << indent() << "return $this->impl_->" << (*f_iter)->get_name() << "(" + << argument_list((*f_iter)->get_arglist(), false) << ");" << endl; + indent_down(); + indent(f_service_rest) << "}" << endl << endl; + } + indent_down(); + f_service_rest << "}" << endl << endl; + + // Close service rest file + f_service_rest << endl; + if (psr4_) { + f_service_rest.close(); + } +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_php_generator::generate_service_client(t_service* tservice) { + std::ofstream& f_service_client = f_service_; + if (psr4_) { + string f_service_client_name = package_dir_ + service_name_ + "Client.php"; + f_service_client.open(f_service_client_name.c_str()); + generate_service_header(tservice, f_service_client); + } + + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = tservice->get_extends()->get_name(); + extends_client = " extends " + php_namespace(tservice->get_extends()->get_program()) + extends + + "Client"; + } + + f_service_client << "class " << php_namespace_declaration(tservice) << "Client" << extends_client + << " implements " << php_namespace(tservice->get_program()) << service_name_ << "If {" + << endl; + indent_up(); + + // Private members + if (extends.empty()) { + f_service_client << indent() << "protected $input_ = null;" << endl << indent() + << "protected $output_ = null;" << endl << endl; + f_service_client << indent() << "protected $seqid_ = 0;" << endl << endl; + } + + // Constructor function + f_service_client << indent() << "public function __construct($input, $output=null) {" << endl; + if (!extends.empty()) { + f_service_client << indent() << " parent::__construct($input, $output);" << endl; + } else { + f_service_client << indent() << " $this->input_ = $input;" << endl << indent() + << " $this->output_ = $output ? $output : $input;" << endl; + } + f_service_client << indent() << "}" << endl << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + // Open function + indent(f_service_client) << "public function " << function_signature(*f_iter) << endl; + scope_up(f_service_client); + indent(f_service_client) << "$this->send_" << funname << "("; + + bool first = true; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + if (first) { + first = false; + } else { + f_service_client << ", "; + } + f_service_client << "$" << (*fld_iter)->get_name(); + } + f_service_client << ");" << endl; + + if (!(*f_iter)->is_oneway()) { + f_service_client << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_client << "return "; + } + f_service_client << "$this->recv_" << funname << "();" << endl; + } + scope_down(f_service_client); + f_service_client << endl; + + indent(f_service_client) << "public function send_" << function_signature(*f_iter) << endl; + scope_up(f_service_client); + + std::string argsname = php_namespace(tservice->get_program()) + service_name_ + "_" + + (*f_iter)->get_name() + "_args"; + + f_service_client << indent() << "$args = new " << argsname << "();" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_client << indent() << "$args->" << (*fld_iter)->get_name() << " = $" + << (*fld_iter)->get_name() << ";" << endl; + } + + f_service_client << indent() << "$bin_accel = ($this->output_ instanceof " + << "TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary');" + << endl; + + f_service_client << indent() << "if ($bin_accel)" << endl; + scope_up(f_service_client); + + string messageType = (*f_iter)->is_oneway() ? "TMessageType::ONEWAY" : "TMessageType::CALL"; + + f_service_client << indent() << "thrift_protocol_write_binary($this->output_, '" + << (*f_iter)->get_name() << "', " << messageType + << ", $args, $this->seqid_, $this->output_->isStrictWrite());" << endl; + + scope_down(f_service_client); + f_service_client << indent() << "else" << endl; + scope_up(f_service_client); + + // Serialize the request header + if (binary_inline_) { + f_service_client << indent() << "$buff = pack('N', (0x80010000 | " << messageType << "));" << endl + << indent() << "$buff .= pack('N', strlen('" << funname << "'));" << endl + << indent() << "$buff .= '" << funname << "';" << endl << indent() + << "$buff .= pack('N', $this->seqid_);" << endl; + } else { + f_service_client << indent() << "$this->output_->writeMessageBegin('" << (*f_iter)->get_name() + << "', " << messageType << ", $this->seqid_);" << endl; + } + + // Write to the stream + if (binary_inline_) { + f_service_client << indent() << "$args->write($buff);" << endl << indent() + << "$this->output_->write($buff);" << endl << indent() + << "$this->output_->flush();" << endl; + } else { + f_service_client << indent() << "$args->write($this->output_);" << endl << indent() + << "$this->output_->writeMessageEnd();" << endl << indent() + << "$this->output_->getTransport()->flush();" << endl; + } + + scope_down(f_service_client); + + scope_down(f_service_client); + + if (!(*f_iter)->is_oneway()) { + std::string resultname = php_namespace(tservice->get_program()) + service_name_ + "_" + + (*f_iter)->get_name() + "_result"; + t_struct noargs(program_); + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_client << endl << indent() << "public function " << function_signature(&recv_function) + << endl; + scope_up(f_service_client); + + f_service_client << indent() << "$bin_accel = ($this->input_ instanceof " + << "TBinaryProtocolAccelerated)" + << " && function_exists('thrift_protocol_read_binary');" << endl; + + f_service_client << indent() + << "if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, '" + << resultname << "', $this->input_->isStrictRead());" << endl; + f_service_client << indent() << "else" << endl; + scope_up(f_service_client); + + f_service_client << indent() << "$rseqid = 0;" << endl << indent() << "$fname = null;" << endl + << indent() << "$mtype = 0;" << endl << endl; + + if (binary_inline_) { + t_field ffname(g_type_string, "fname"); + t_field fseqid(g_type_i32, "rseqid"); + f_service_client << indent() << "$ver = unpack('N', $this->input_->readAll(4));" << endl + << indent() << "$ver = $ver[1];" << endl << indent() << "$mtype = $ver & 0xff;" + << endl << indent() << "$ver = $ver & 0xffff0000;" << endl << indent() + << "if ($ver != 0x80010000) throw new " + << "TProtocolException('Bad version identifier: '.$ver, " + << "TProtocolException::BAD_VERSION);" << endl; + generate_deserialize_field(f_service_client, &ffname, "", true); + generate_deserialize_field(f_service_client, &fseqid, "", true); + } else { + f_service_client << indent() << "$this->input_->readMessageBegin($fname, $mtype, $rseqid);" + << endl << indent() << "if ($mtype == " + << "TMessageType::EXCEPTION) {" << endl << indent() << " $x = new " + << "TApplicationException();" << endl << indent() << " $x->read($this->input_);" + << endl << indent() << " $this->input_->readMessageEnd();" << endl << indent() + << " throw $x;" << endl << indent() << "}" << endl; + } + + f_service_client << indent() << "$result = new " << resultname << "();" << endl << indent() + << "$result->read($this->input_);" << endl; + + if (!binary_inline_) { + f_service_client << indent() << "$this->input_->readMessageEnd();" << endl; + } + + scope_down(f_service_client); + + // Careful, only return result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_client << indent() << "if ($result->success !== null) {" << endl << indent() + << " return $result->success;" << endl << indent() << "}" << endl; + } + + 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) { + f_service_client << indent() << "if ($result->" << (*x_iter)->get_name() << " !== null) {" << endl + << indent() << " throw $result->" << (*x_iter)->get_name() << ";" << endl + << indent() << "}" << endl; + } + + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + indent(f_service_client) << "return;" << endl; + } else { + f_service_client << indent() << "throw new \\Exception(\"" << (*f_iter)->get_name() + << " failed: unknown result\");" << endl; + } + + // Close function + scope_down(f_service_client); + f_service_client << endl; + } + } + + indent_down(); + f_service_client << "}" << endl << endl; + + // Close service client file + f_service_client << endl; + if (psr4_) { + f_service_client.close(); + } +} + +/** + * Deserializes a field of any type. + */ +void t_php_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + bool inclass) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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()) { + + if (binary_inline_) { + std::string itrans = (inclass ? "$this->input_" : "$input"); + + 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: + out << indent() << "$len = unpack('N', " << itrans << "->readAll(4));" << endl + << indent() << "$len = $len[1];" << endl << indent() << "if ($len > 0x7fffffff) {" + << endl << indent() << " $len = 0 - (($len - 1) ^ 0xffffffff);" << endl << indent() + << "}" << endl << indent() << "$" << name << " = " << itrans << "->readAll($len);" + << endl; + break; + case t_base_type::TYPE_BOOL: + out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));" + << endl << indent() << "$" << name << " = (bool)$" << name << "[1];" << endl; + break; + case t_base_type::TYPE_I8: + out << indent() << "$" << name << " = unpack('c', " << itrans << "->readAll(1));" + << endl << indent() << "$" << name << " = $" << name << "[1];" << endl; + break; + case t_base_type::TYPE_I16: + out << indent() << "$val = unpack('n', " << itrans << "->readAll(2));" << endl + << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fff) {" + << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffff);" << endl << indent() + << "}" << endl << indent() << "$" << name << " = $val;" << endl; + break; + case t_base_type::TYPE_I32: + out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl + << indent() << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {" + << endl << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent() + << "}" << endl << indent() << "$" << name << " = $val;" << endl; + break; + case t_base_type::TYPE_I64: + out << indent() << "$arr = unpack('N2', " << itrans << "->readAll(8));" << endl + << indent() << "if ($arr[1] & 0x80000000) {" << endl << indent() + << " $arr[1] = $arr[1] ^ 0xFFFFFFFF;" << endl << indent() + << " $arr[2] = $arr[2] ^ 0xFFFFFFFF;" << endl << indent() << " $" << name + << " = 0 - $arr[1]*4294967296 - $arr[2] - 1;" << endl << indent() << "} else {" + << endl << indent() << " $" << name << " = $arr[1]*4294967296 + $arr[2];" << endl + << indent() << "}" << endl; + break; + case t_base_type::TYPE_DOUBLE: + out << indent() << "$arr = unpack('d', strrev(" << itrans << "->readAll(8)));" << endl + << indent() << "$" << name << " = $arr[1];" << endl; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase) + + tfield->get_name(); + } + } else if (type->is_enum()) { + out << indent() << "$val = unpack('N', " << itrans << "->readAll(4));" << endl << indent() + << "$val = $val[1];" << endl << indent() << "if ($val > 0x7fffffff) {" << endl + << indent() << " $val = 0 - (($val - 1) ^ 0xffffffff);" << endl << indent() << "}" + << endl << indent() << "$" << name << " = $val;" << endl; + } + } else { + + indent(out) << "$xfer += $input->"; + + 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: + out << "readString($" << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "readBool($" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "readByte($" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "readI16($" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "readI32($" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "readI64($" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "readDouble($" << name << ");"; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "readI32($" << name << ");"; + } + out << endl; + } + } else { + printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", + tfield->get_name().c_str(), + type->get_name().c_str()); + } + } +} + +/** + * Generates an unserializer for a variable. This makes two key assumptions, + * first that there is a const char* variable named data that points to the + * buffer for deserialization, and that there is a variable protocol which + * is a reference to a TProtocol serialization object. + */ +void t_php_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + out << indent() << "$" << prefix << " = new " << php_namespace(tstruct->get_program()) + << tstruct->get_name() << "();" << endl << indent() << "$xfer += $" << prefix + << "->read($input);" << endl; +} + +void t_php_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + out << indent() << "$" << prefix << " = array();" << endl << indent() << "$" << size << " = 0;" + << endl; + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << "$" << ktype << " = 0;" << endl << indent() << "$" << vtype << " = 0;" + << endl; + if (binary_inline_) { + generate_deserialize_field(out, &fktype); + generate_deserialize_field(out, &fvtype); + generate_deserialize_field(out, &fsize); + } else { + out << indent() << "$xfer += $input->readMapBegin(" + << "$" << ktype << ", $" << vtype << ", $" << size << ");" << endl; + } + } else if (ttype->is_set()) { + if (binary_inline_) { + generate_deserialize_field(out, &fetype); + generate_deserialize_field(out, &fsize); + } else { + out << indent() << "$" << etype << " = 0;" << endl << indent() + << "$xfer += $input->readSetBegin(" + << "$" << etype << ", $" << size << ");" << endl; + } + } else if (ttype->is_list()) { + if (binary_inline_) { + generate_deserialize_field(out, &fetype); + generate_deserialize_field(out, &fsize); + } else { + out << indent() << "$" << etype << " = 0;" << endl << indent() + << "$xfer += $input->readListBegin(" + << "$" << etype << ", $" << size << ");" << endl; + } + } + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << "for ($" << i << " = 0; $" << i << " < $" << size << "; ++$" << 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 (!binary_inline_) { + // Read container end + if (ttype->is_map()) { + indent(out) << "$xfer += $input->readMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "$xfer += $input->readSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "$xfer += $input->readListEnd();" << endl; + } + } +} + +/** + * Generates code to deserialize a map + */ +void t_php_generator::generate_deserialize_map_element(ofstream& 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, true, true) << endl; + indent(out) << declare_field(&fval, true, true) << endl; + + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); + + indent(out) << "$" << prefix << "[$" << key << "] = $" << val << ";" << endl; +} + +void t_php_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + string elem = tmp("elem"); + t_field felem(tset->get_elem_type(), elem); + + indent(out) << "$" << elem << " = null;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << "if (is_scalar($" << elem << ")) {" << endl; + indent(out) << " $" << prefix << "[$" << elem << "] = true;" << endl; + indent(out) << "} else {" << endl; + indent(out) << " $" << prefix << " []= $" << elem << ";" << endl; + indent(out) << "}" << endl; +} + +void t_php_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix) { + string elem = tmp("elem"); + t_field felem(tlist->get_elem_type(), elem); + + indent(out) << "$" << elem << " = null;" << endl; + + generate_deserialize_field(out, &felem); + + indent(out) << "$" << prefix << " []= $" << elem << ";" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_php_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + tfield->get_name(); + + if (binary_inline_) { + 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: + out << indent() << "$output .= pack('N', strlen($" << name << "));" << endl << indent() + << "$output .= $" << name << ";" << endl; + break; + case t_base_type::TYPE_BOOL: + out << indent() << "$output .= pack('c', $" << name << " ? 1 : 0);" << endl; + break; + case t_base_type::TYPE_I8: + out << indent() << "$output .= pack('c', $" << name << ");" << endl; + break; + case t_base_type::TYPE_I16: + out << indent() << "$output .= pack('n', $" << name << ");" << endl; + break; + case t_base_type::TYPE_I32: + out << indent() << "$output .= pack('N', $" << name << ");" << endl; + break; + case t_base_type::TYPE_I64: + out << indent() << "$output .= pack('N2', $" << name << " >> 32, $" << name + << " & 0xFFFFFFFF);" << endl; + break; + case t_base_type::TYPE_DOUBLE: + out << indent() << "$output .= strrev(pack('d', $" << name << "));" << endl; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << indent() << "$output .= pack('N', $" << name << ");" << endl; + } + } else { + + indent(out) << "$xfer += $output->"; + + 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: + out << "writeString($" << name << ");"; + break; + case t_base_type::TYPE_BOOL: + out << "writeBool($" << name << ");"; + break; + case t_base_type::TYPE_I8: + out << "writeByte($" << name << ");"; + break; + case t_base_type::TYPE_I16: + out << "writeI16($" << name << ");"; + break; + case t_base_type::TYPE_I32: + out << "writeI32($" << name << ");"; + break; + case t_base_type::TYPE_I64: + out << "writeI64($" << name << ");"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble($" << name << ");"; + break; + default: + throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32($" << name << ");"; + } + out << endl; + } + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_php_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << "$xfer += $" << prefix << "->write($output);" << endl; +} + +/** + * Writes out a container + */ +void t_php_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + scope_up(out); + + if (ttype->is_map()) { + if (binary_inline_) { + out << indent() << "$output .= pack('c', " << type_to_enum(((t_map*)ttype)->get_key_type()) + << ");" << endl << indent() << "$output .= pack('c', " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ");" << endl << indent() + << "$output .= strrev(pack('l', count($" << prefix << ")));" << endl; + } else { + indent(out) << "$output->writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) + << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "count($" << prefix << "));" << endl; + } + } else if (ttype->is_set()) { + if (binary_inline_) { + out << indent() << "$output .= pack('c', " << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));" + << endl; + + } else { + indent(out) << "$output->writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) + << ", " + << "count($" << prefix << "));" << endl; + } + } else if (ttype->is_list()) { + if (binary_inline_) { + out << indent() << "$output .= pack('c', " << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ");" << endl << indent() << "$output .= strrev(pack('l', count($" << prefix << ")));" + << endl; + + } else { + indent(out) << "$output->writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ", " + << "count($" << prefix << "));" << endl; + } + } + + scope_up(out); + + if (ttype->is_map()) { + string kiter = tmp("kiter"); + string viter = tmp("viter"); + indent(out) << "foreach ($" << prefix << " as " + << "$" << kiter << " => $" << viter << ")" << endl; + scope_up(out); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + scope_down(out); + } else if (ttype->is_set()) { + string iter = tmp("iter"); + string iter_val = tmp("iter"); + indent(out) << "foreach ($" << prefix << " as $" << iter << " => $" << iter_val << ")" << endl; + scope_up(out); + indent(out) << "if (is_scalar($" << iter_val << ")) {" << endl; + generate_serialize_set_element(out, (t_set*)ttype, iter); + indent(out) << "} else {" << endl; + generate_serialize_set_element(out, (t_set*)ttype, iter_val); + indent(out) << "}" << endl; + scope_down(out); + } else if (ttype->is_list()) { + string iter = tmp("iter"); + indent(out) << "foreach ($" << prefix << " as $" << iter << ")" << endl; + scope_up(out); + generate_serialize_list_element(out, (t_list*)ttype, iter); + scope_down(out); + } + + scope_down(out); + + if (!binary_inline_) { + if (ttype->is_map()) { + indent(out) << "$output->writeMapEnd();" << endl; + } else if (ttype->is_set()) { + indent(out) << "$output->writeSetEnd();" << endl; + } else if (ttype->is_list()) { + indent(out) << "$output->writeListEnd();" << endl; + } + } + + scope_down(out); +} + +/** + * Serializes the members of a map. + * + */ +void t_php_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield, ""); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_php_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_php_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Emits a PHPDoc comment for the given contents + */ +void t_php_generator::generate_php_docstring_comment(ofstream& out, string contents) { + generate_docstring_comment(out, "/**\n", " * ", contents, " */\n"); +} + +/** + * Emits a PHPDoc comment if the provided object has a doc in Thrift + */ +void t_php_generator::generate_php_doc(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_php_docstring_comment(out, tdoc->get_doc()); + } +} + +/** + * Emits a PHPDoc comment for a field + */ +void t_php_generator::generate_php_doc(ofstream& out, t_field* field) { + stringstream ss; + + // prepend free-style doc if available + if (field->has_doc()) { + ss << field->get_doc() << endl; + } + + // append @var tag + t_type* type = get_true_type(field->get_type()); + ss << "@var " << type_to_phpdoc(type) << endl; + + generate_php_docstring_comment(out, ss.str()); +} + +/** + * Emits a PHPDoc comment for a function + */ +void t_php_generator::generate_php_doc(ofstream& out, t_function* function) { + stringstream ss; + if (function->has_doc()) { + ss << function->get_doc() << endl; + } + + // generate parameter types doc + const vector& args = function->get_arglist()->get_members(); + vector::const_iterator a_iter; + for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) { + t_field* arg = *a_iter; + ss << "@param " << type_to_phpdoc(arg->get_type()) << " $" << arg->get_name(); + if (arg->has_doc()) { + ss << " " << arg->get_doc(); + } + ss << endl; + } + + // generate return type doc + t_type* ret_type = function->get_returntype(); + if (!ret_type->is_void() || ret_type->has_doc()) { + ss << "@return " << type_to_phpdoc(ret_type); + if (ret_type->has_doc()) { + ss << " " << ret_type->get_doc(); + } + ss << endl; + } + + // generate exceptions doc + const vector& excs = function->get_xceptions()->get_members(); + vector::const_iterator e_iter; + for (e_iter = excs.begin(); e_iter != excs.end(); ++e_iter) { + t_field* exc = *e_iter; + ss << "@throws " << type_to_phpdoc(exc->get_type()); + if (exc->has_doc()) { + ss << " " << exc->get_doc(); + } + ss << endl; + } + + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); +} + +/** + * Declares a field, which may include initialization as necessary. + * + * @param ttype The type + */ +string t_php_generator::declare_field(t_field* tfield, bool init, bool obj) { + string result = "$" + tfield->get_name(); + if (init) { + t_type* type = get_true_type(tfield->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: + break; + case t_base_type::TYPE_STRING: + result += " = ''"; + 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 += " = 0.0"; + break; + default: + throw "compiler error: no PHP initializer for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + result += " = 0"; + } else if (type->is_container()) { + result += " = array()"; + } else if (type->is_struct() || type->is_xception()) { + if (obj) { + result += " = new " + php_namespace(type->get_program()) + type->get_name() + "()"; + } else { + result += " = null"; + } + } + } + return result + ";"; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_php_generator::function_signature(t_function* tfunction, string prefix) { + return prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")"; +} + +/** + * Renders a field list + */ +string t_php_generator::argument_list(t_struct* tstruct, bool addTypeHints) { + 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 += ", "; + } + + t_type* type = (*f_iter)->get_type(); + + // Set type name + if (addTypeHints) { + if (type->is_struct()) { + string className = php_namespace(type->get_program()) + + php_namespace_directory("Definition", false) + + classify(type->get_name()); + + result += className + " "; + } else if (type->is_container()) { + result += "array "; + } + } + + result += "$" + (*f_iter)->get_name(); + } + return result; +} + +/** + * Gets a typecast string for a particular type. + */ +string t_php_generator::type_to_cast(t_type* type) { + if (type->is_base_type()) { + t_base_type* btype = (t_base_type*)type; + switch (btype->get_base()) { + case t_base_type::TYPE_BOOL: + return "(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: + return "(int)"; + case t_base_type::TYPE_DOUBLE: + return "(double)"; + case t_base_type::TYPE_STRING: + return "(string)"; + default: + return ""; + } + } else if (type->is_enum()) { + return "(int)"; + } + return ""; +} + +/** + * Converts the parse type to a C++ enum string for the given type. + */ +string t_php_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 "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::LST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +/** + * Converts the parse type to a PHPDoc string for the given type. + */ +string t_php_generator::type_to_phpdoc(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: + return "void"; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "bool"; + case t_base_type::TYPE_I8: + return "int"; + case t_base_type::TYPE_I16: + return "int"; + case t_base_type::TYPE_I32: + return "int"; + case t_base_type::TYPE_I64: + return "int"; + case t_base_type::TYPE_DOUBLE: + return "double"; + } + } else if (type->is_enum()) { + return "int"; + } else if (type->is_struct() || type->is_xception()) { + return php_namespace(type->get_program()) + type->get_name(); + } else if (type->is_map()) { + return "array"; + } else if (type->is_set()) { + t_set* tset = static_cast(type); + t_type* t_elem = tset->get_elem_type(); + if (t_elem->is_container()) { + return "(" + type_to_phpdoc(t_elem) + ")[]"; + } else { + return type_to_phpdoc(t_elem) + "[]"; + } + } else if (type->is_list()) { + t_list* tlist = static_cast(type); + t_type* t_elem = tlist->get_elem_type(); + if (t_elem->is_container()) { + return "(" + type_to_phpdoc(t_elem) + ")[]"; + } else { + return type_to_phpdoc(t_elem) + "[]"; + } + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +THRIFT_REGISTER_GENERATOR( + php, + "PHP", + " inlined: Generate PHP inlined files\n" + " server: Generate PHP server stubs\n" + " oop: Generate PHP with object oriented subclasses\n" + " psr4: Generate each PHP class in separate file (allows PSR4 autoloading)\n" + " 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") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc new file mode 100644 index 00000000..1d11144a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_py_generator.cc @@ -0,0 +1,2648 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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/version.h" +#include "thrift/generate/t_generator.h" + +using std::map; +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 + +/** + * Python code generator. + * + */ +class t_py_generator : public t_generator { +public: + t_py_generator(t_program* program, + const std::map& parsed_options, + const std::string& option_string) + : t_generator(program) { + std::map::const_iterator iter; + + + gen_newstyle_ = true; + gen_utf8strings_ = true; + gen_dynbase_ = false; + gen_slots_ = false; + gen_tornado_ = false; + gen_twisted_ = false; + gen_dynamic_ = false; + coding_ = ""; + gen_dynbaseclass_ = ""; + gen_dynbaseclass_exc_ = ""; + gen_dynbaseclass_frozen_ = ""; + import_dynbase_ = ""; + package_prefix_ = ""; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("new_style") == 0) { + pwarning(0, "new_style is enabled by default, so the option will be removed in the near future.\n"); + } else if( iter->first.compare("old_style") == 0) { + gen_newstyle_ = false; + pwarning(0, "old_style is deprecated and may be removed in the future.\n"); + } else if( iter->first.compare("utf8strings") == 0) { + pwarning(0, "utf8strings is enabled by default, so the option will be removed in the near future.\n"); + } else if( iter->first.compare("no_utf8strings") == 0) { + gen_utf8strings_ = false; + } else if( iter->first.compare("slots") == 0) { + gen_slots_ = true; + } else if( iter->first.compare("package_prefix") == 0) { + package_prefix_ = iter->second; + } else if( iter->first.compare("dynamic") == 0) { + gen_dynamic_ = true; + gen_newstyle_ = false; // dynamic is newstyle + if( gen_dynbaseclass_.empty()) { + gen_dynbaseclass_ = "TBase"; + } + if( gen_dynbaseclass_frozen_.empty()) { + gen_dynbaseclass_frozen_ = "TFrozenBase"; + } + if( gen_dynbaseclass_exc_.empty()) { + gen_dynbaseclass_exc_ = "TExceptionBase"; + } + if( import_dynbase_.empty()) { + import_dynbase_ = "from thrift.protocol.TBase import TBase, TFrozenBase, TExceptionBase, TTransport\n"; + } + } else if( iter->first.compare("dynbase") == 0) { + gen_dynbase_ = true; + gen_dynbaseclass_ = (iter->second); + } else if( iter->first.compare("dynfrozen") == 0) { + gen_dynbaseclass_frozen_ = (iter->second); + } else if( iter->first.compare("dynexc") == 0) { + gen_dynbaseclass_exc_ = (iter->second); + } else if( iter->first.compare("dynimport") == 0) { + gen_dynbase_ = true; + import_dynbase_ = (iter->second); + } else if( iter->first.compare("twisted") == 0) { + gen_twisted_ = true; + } else if( iter->first.compare("tornado") == 0) { + gen_tornado_ = true; + } else if( iter->first.compare("coding") == 0) { + coding_ = iter->second; + } else { + throw "unknown option py:" + iter->first; + } + } + + if (gen_twisted_ && gen_tornado_) { + throw "at most one of 'twisted' and 'tornado' are allowed"; + } + + copy_options_ = option_string; + + if (gen_twisted_) { + out_dir_base_ = "gen-py.twisted"; + } else if (gen_tornado_) { + out_dir_base_ = "gen-py.tornado"; + } else { + out_dir_base_ = "gen-py"; + } + } + + virtual std::string indent_str() const { + return " "; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + + std::string render_const_value(t_type* type, t_const_value* value); + + /** + * Struct generation code + */ + + void generate_py_struct(t_struct* tstruct, bool is_exception); + void generate_py_struct_definition(std::ofstream& out, + t_struct* tstruct, + bool is_xception = false); + void generate_py_struct_reader(std::ofstream& out, t_struct* tstruct); + void generate_py_struct_writer(std::ofstream& out, t_struct* tstruct); + void generate_py_struct_required_validator(std::ofstream& out, t_struct* tstruct); + void generate_py_function_helpers(t_function* tfunction); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(t_service* tservice); + void generate_service_client(t_service* tservice); + void generate_service_remote(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::ofstream& out, + t_field* tfield, + std::string prefix = ""); + + void generate_deserialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(std::ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(std::ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_python_docstring(std::ofstream& out, t_struct* tstruct); + + void generate_python_docstring(std::ofstream& out, t_function* tfunction); + + void generate_python_docstring(std::ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader); + + void generate_python_docstring(std::ofstream& out, t_doc* tdoc); + + /** + * Helper rendering functions + */ + + std::string py_autogen_comment(); + std::string py_imports(); + std::string render_includes(); + std::string declare_argument(t_field* tfield); + std::string render_field_default_value(t_field* tfield); + 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::string type_to_enum(t_type* ttype); + std::string type_to_spec_args(t_type* ttype); + + static bool is_valid_namespace(const std::string& sub_namespace) { + return sub_namespace == "twisted"; + } + + static std::string get_real_py_module(const t_program* program, bool gen_twisted, std::string package_dir="") { + if (gen_twisted) { + std::string twisted_module = program->get_namespace("py.twisted"); + if (!twisted_module.empty()) { + return twisted_module; + } + } + + std::string real_module = program->get_namespace("py"); + if (real_module.empty()) { + return program->get_name(); + } + return package_dir + real_module; + } + + static bool is_immutable(t_type* ttype) { + return ttype->annotations_.find("python.immutable") != ttype->annotations_.end(); + } + +private: + + /** + * True if we should generate new-style classes. + */ + bool gen_newstyle_; + + /** + * True if we should generate dynamic style classes. + */ + bool gen_dynamic_; + + bool gen_dynbase_; + std::string gen_dynbaseclass_; + std::string gen_dynbaseclass_frozen_; + std::string gen_dynbaseclass_exc_; + + std::string import_dynbase_; + + bool gen_slots_; + + std::string copy_options_; + + /** + * True if we should generate Twisted-friendly RPC services. + */ + bool gen_twisted_; + + /** + * True if we should generate code for use with Tornado + */ + bool gen_tornado_; + + /** + * True if strings should be encoded using utf-8. + */ + bool gen_utf8strings_; + + /** + * specify generated file encoding + * eg. # -*- coding: utf-8 -*- + */ + string coding_; + + string package_prefix_; + + /** + * File streams + */ + + std::ofstream f_types_; + std::ofstream f_consts_; + std::ofstream f_service_; + + std::string package_dir_; + std::string module_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_py_generator::init_generator() { + // Make output directory + string module = get_real_py_module(program_, gen_twisted_); + package_dir_ = get_out_dir(); + module_ = module; + while (true) { + // TODO: Do better error checking here. + MKDIR(package_dir_.c_str()); + std::ofstream init_py((package_dir_ + "/__init__.py").c_str(), std::ios_base::app); + init_py.close(); + if (module.empty()) { + break; + } + string::size_type pos = module.find('.'); + if (pos == string::npos) { + package_dir_ += "/"; + package_dir_ += module; + module.clear(); + } else { + package_dir_ += "/"; + package_dir_ += module.substr(0, pos); + module.erase(0, pos + 1); + } + } + + // Make output file + string f_types_name = package_dir_ + "/" + "ttypes.py"; + f_types_.open(f_types_name.c_str()); + + string f_consts_name = package_dir_ + "/" + "constants.py"; + f_consts_.open(f_consts_name.c_str()); + + string f_init_name = package_dir_ + "/__init__.py"; + ofstream f_init; + f_init.open(f_init_name.c_str()); + f_init << "__all__ = ['ttypes', 'constants'"; + vector services = program_->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + f_init << ", '" << (*sv_iter)->get_name() << "'"; + } + f_init << "]" << endl; + f_init.close(); + + // Print header + f_types_ << py_autogen_comment() << endl + << py_imports() << endl + << render_includes() << endl + << "from thrift.transport import TTransport" << endl + << import_dynbase_; + + f_consts_ << + py_autogen_comment() << endl << + py_imports() << endl << + "from .ttypes import *" << endl; +} + +/** + * Renders all the imports necessary for including another Thrift program + */ +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"; + } + return result; +} + +/** + * Autogen'd comment + */ +string t_py_generator::py_autogen_comment() { + string coding; + if (!coding_.empty()) { + coding = "# -*- coding: " + coding_ + " -*-\n"; + } + return coding + 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" + + "# options string: " + copy_options_ + "\n" + "#\n"; +} + +/** + * Prints standard thrift imports + */ +string t_py_generator::py_imports() { + ostringstream ss; + ss << "from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, " + "TApplicationException" + << endl + << "from thrift.protocol.TProtocol import TProtocolException"; + if (gen_utf8strings_) { + ss << endl << "import sys"; + } + return ss.str(); +} + +/** + * Closes the type files + */ +void t_py_generator::close_generator() { + // Close types file + f_types_.close(); + f_consts_.close(); +} + +/** + * Generates a typedef. This is not done in Python, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_py_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_py_generator::generate_enum(t_enum* tenum) { + std::ostringstream to_string_mapping, from_string_mapping; + + f_types_ << endl << endl << "class " << tenum->get_name() << (gen_newstyle_ ? "(object)" : "") + << (gen_dynamic_ ? "(" + gen_dynbaseclass_ + ")" : "") << ":" << endl; + indent_up(); + generate_python_docstring(f_types_, tenum); + + to_string_mapping << indent() << "_VALUES_TO_NAMES = {" << endl; + from_string_mapping << indent() << "_NAMES_TO_VALUES = {" << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + indent(f_types_) << (*c_iter)->get_name() << " = " << value << endl; + + // Dictionaries to/from string names of enums + to_string_mapping << indent() << indent() << value << ": \"" + << escape_string((*c_iter)->get_name()) << "\"," << endl; + from_string_mapping << indent() << indent() << '"' << escape_string((*c_iter)->get_name()) + << "\": " << value << ',' << endl; + } + to_string_mapping << indent() << "}" << endl; + from_string_mapping << indent() << "}" << endl; + + indent_down(); + f_types_ << endl; + f_types_ << to_string_mapping.str() << endl << from_string_mapping.str(); +} + +/** + * Generate a constant value + */ +void t_py_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + indent(f_consts_) << name << " = " << render_const_value(type, value); + f_consts_ << endl; +} + +/** + * 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 + */ +string t_py_generator::render_const_value(t_type* type, t_const_value* value) { + type = get_true_type(type); + std::ostringstream out; + + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << type_name(type) << "(**{" << endl; + indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + indent(out) << render_const_value(g_type_string, v_iter->first) << ": " + << render_const_value(field_type, v_iter->second) << "," << endl; + } + indent_down(); + indent(out) << "})"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + if (is_immutable(type)) { + out << "TFrozenDict("; + } + out << "{" << endl; + indent_up(); + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + indent(out) << render_const_value(ktype, v_iter->first) << ": " + << render_const_value(vtype, v_iter->second) << "," << endl; + } + indent_down(); + indent(out) << "}"; + if (is_immutable(type)) { + out << ")"; + } + } 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(); + } + if (type->is_set()) { + if (is_immutable(type)) { + out << "frozen"; + } + out << "set("; + } + if (is_immutable(type) || type->is_set()) { + out << "(" << endl; + } else { + out << "[" << endl; + } + indent_up(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + indent(out) << render_const_value(etype, *v_iter) << "," << endl; + } + indent_down(); + if (is_immutable(type) || type->is_set()) { + indent(out) << ")"; + } else { + indent(out) << "]"; + } + if (type->is_set()) { + out << ")"; + } + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + + return out.str(); +} + +/** + * Generates a python struct + */ +void t_py_generator::generate_struct(t_struct* tstruct) { + generate_py_struct(tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_py_generator::generate_xception(t_struct* txception) { + generate_py_struct(txception, true); +} + +/** + * Generates a python struct + */ +void t_py_generator::generate_py_struct(t_struct* tstruct, bool is_exception) { + generate_py_struct_definition(f_types_, tstruct, is_exception); +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_py_generator::generate_py_struct_definition(ofstream& out, + t_struct* tstruct, + bool is_exception) { + const vector& members = tstruct->get_members(); + const vector& sorted_members = tstruct->get_sorted_members(); + vector::const_iterator m_iter; + + out << endl << endl << "class " << tstruct->get_name(); + if (is_exception) { + if (gen_dynamic_) { + out << "(" << gen_dynbaseclass_exc_ << ")"; + } else { + out << "(TException)"; + } + } else if (gen_dynamic_) { + if (is_immutable(tstruct)) { + out << "(" << gen_dynbaseclass_frozen_ << ")"; + } else { + out << "(" << gen_dynbaseclass_ << ")"; + } + } else if (gen_newstyle_) { + out << "(object)"; + } + out << ":" << endl; + indent_up(); + generate_python_docstring(out, tstruct); + + out << endl; + + /* + Here we generate the structure specification for the fastbinary codec. + These specifications have the following structure: + thrift_spec -> tuple of item_spec + item_spec -> None | (tag, type_enum, name, spec_args, default) + tag -> integer + type_enum -> TType.I32 | TType.STRING | TType.STRUCT | ... + name -> string_literal + default -> None # Handled by __init__ + spec_args -> None # For simple types + | (type_enum, spec_args) # Value type for list/set + | (type_enum, spec_args, type_enum, spec_args) + # Key and value for map + | (class_name, spec_args_ptr) # For struct/exception + class_name -> identifier # Basically a pointer to the class + spec_args_ptr -> expression # just class_name.spec_args + + TODO(dreiss): Consider making this work for structs with negative tags. + */ + + if (gen_slots_) { + indent(out) << "__slots__ = (" << endl; + indent_up(); + for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) { + indent(out) << "'" << (*m_iter)->get_name() << "'," << endl; + } + indent_down(); + indent(out) << ")" << endl << endl; + } + + // TODO(dreiss): Look into generating an empty tuple instead of None + // for structures with no members. + // TODO(dreiss): Test encoding of structs where some inner structs + // don't have thrift_spec. + if (sorted_members.empty() || (sorted_members[0]->get_key() >= 0)) { + indent(out) << "thrift_spec = (" << endl; + indent_up(); + + int sorted_keys_pos = 0; + for (m_iter = sorted_members.begin(); m_iter != sorted_members.end(); ++m_iter) { + + for (; sorted_keys_pos != (*m_iter)->get_key(); sorted_keys_pos++) { + indent(out) << "None, # " << sorted_keys_pos << endl; + } + + indent(out) << "(" << (*m_iter)->get_key() << ", " << type_to_enum((*m_iter)->get_type()) + << ", " + << "'" << (*m_iter)->get_name() << "'" + << ", " << type_to_spec_args((*m_iter)->get_type()) << ", " + << render_field_default_value(*m_iter) << ", " + << ")," + << " # " << sorted_keys_pos << endl; + + sorted_keys_pos++; + } + + indent_down(); + indent(out) << ")" << endl; + } else { + indent(out) << "thrift_spec = None" << endl; + } + + if (members.size() > 0) { + out << endl; + out << indent() << "def __init__(self,"; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + // This fills in default values, as opposed to nulls + out << " " << declare_argument(*m_iter) << ","; + } + + out << "):" << endl; + + indent_up(); + + 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) { + indent(out) << "if " << (*m_iter)->get_name() << " is " + << "self.thrift_spec[" << (*m_iter)->get_key() << "][4]:" << endl; + indent_up(); + indent(out) << (*m_iter)->get_name() << " = " << render_field_default_value(*m_iter) + << endl; + indent_down(); + } + + if (is_immutable(tstruct)) { + if (gen_newstyle_ || gen_dynamic_) { + indent(out) << "super(" << tstruct->get_name() << ", self).__setattr__('" + << (*m_iter)->get_name() << "', " << (*m_iter)->get_name() << ")" << endl; + } else { + indent(out) << "self.__dict__['" << (*m_iter)->get_name() + << "'] = " << (*m_iter)->get_name() << endl; + } + } else { + indent(out) << "self." << (*m_iter)->get_name() << " = " << (*m_iter)->get_name() << endl; + } + } + + indent_down(); + } + + if (is_immutable(tstruct)) { + out << endl; + out << indent() << "def __setattr__(self, *args):" << endl + << indent() << indent_str() << "raise TypeError(\"can't modify immutable instance\")" << endl + << endl; + out << indent() << "def __delattr__(self, *args):" << endl + << indent() << indent_str() << "raise TypeError(\"can't modify immutable instance\")" << endl + << endl; + + // Hash all of the members in order, and also hash in the class + // to avoid collisions for stuff like single-field structures. + out << indent() << "def __hash__(self):" << endl + << indent() << indent_str() << "return hash(self.__class__) ^ hash(("; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << "self." << (*m_iter)->get_name() << ", "; + } + + out << "))" << endl; + } + + if (!gen_dynamic_) { + out << endl; + generate_py_struct_reader(out, tstruct); + generate_py_struct_writer(out, tstruct); + } + + // For exceptions only, generate a __str__ method. This is + // because when raised exceptions are printed to the console, __repr__ + // isn't used. See python bug #5882 + if (is_exception) { + out << endl; + out << indent() << "def __str__(self):" << endl + << indent() << indent_str() << "return repr(self)" << endl; + } + + if (!gen_slots_) { + out << endl; + // Printing utilities so that on the command line thrift + // structs look pretty like dictionaries + indent(out) << "def __repr__(self):" << endl; + indent_up(); + out << indent() << "L = ['%s=%r' % (key, value)" << endl + << indent() << " for key, value in self.__dict__.items()]" << endl + << indent() << "return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl + << endl; + indent_down(); + + // Equality and inequality methods that compare by value + out << indent() << "def __eq__(self, other):" << endl; + indent_up(); + out << indent() << "return isinstance(other, self.__class__) and " + "self.__dict__ == other.__dict__" << endl; + indent_down(); + out << endl; + + out << indent() << "def __ne__(self, other):" << endl; + indent_up(); + + out << indent() << "return not (self == other)" << endl; + indent_down(); + } else if (!gen_dynamic_) { + out << endl; + // no base class available to implement __eq__ and __repr__ and __ne__ for us + // so we must provide one that uses __slots__ + indent(out) << "def __repr__(self):" << endl; + indent_up(); + out << indent() << "L = ['%s=%r' % (key, getattr(self, key))" << endl + << indent() << " for key in self.__slots__]" << endl + << indent() << "return '%s(%s)' % (self.__class__.__name__, ', '.join(L))" << endl + << endl; + indent_down(); + + // Equality method that compares each attribute by value and type, walking __slots__ + out << indent() << "def __eq__(self, other):" << endl; + indent_up(); + out << indent() << "if not isinstance(other, self.__class__):" << endl + << indent() << indent_str() << "return False" << endl + << indent() << "for attr in self.__slots__:" << endl + << indent() << indent_str() << "my_val = getattr(self, attr)" << endl + << indent() << indent_str() << "other_val = getattr(other, attr)" << endl + << indent() << indent_str() << "if my_val != other_val:" << endl + << indent() << indent_str() << indent_str() << "return False" << endl + << indent() << "return True" << endl + << endl; + indent_down(); + + out << indent() << "def __ne__(self, other):" << endl + << indent() << indent_str() << "return not (self == other)" << endl; + } + indent_down(); +} + +/** + * Generates the read method for a struct + */ +void t_py_generator::generate_py_struct_reader(ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + if (is_immutable(tstruct)) { + out << indent() << "@classmethod" << endl << indent() << "def read(cls, iprot):" << endl; + } else { + indent(out) << "def read(self, iprot):" << endl; + } + indent_up(); + + const char* id = is_immutable(tstruct) ? "cls" : "self"; + + indent(out) << "if iprot._fast_decode is not None " + "and isinstance(iprot.trans, TTransport.CReadableTransport) " + "and " + << id << ".thrift_spec is not None:" << endl; + indent_up(); + + if (is_immutable(tstruct)) { + indent(out) << "return iprot._fast_decode(None, iprot, (cls, cls.thrift_spec))" << endl; + } else { + indent(out) << "iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))" << endl; + indent(out) << "return" << endl; + } + indent_down(); + + indent(out) << "iprot.readStructBegin()" << endl; + + // Loop over reading in fields + indent(out) << "while True:" << endl; + indent_up(); + + // Read beginning field marker + indent(out) << "(fname, ftype, fid) = iprot.readFieldBegin()" << endl; + + // Check for field STOP marker and break + indent(out) << "if ftype == TType.STOP:" << endl; + indent_up(); + indent(out) << "break" << endl; + indent_down(); + + // Switch statement on the field we are reading + bool first = true; + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + out << indent() << "if "; + } else { + out << indent() << "elif "; + } + out << "fid == " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + indent(out) << "if ftype == " << type_to_enum((*f_iter)->get_type()) << ":" << endl; + indent_up(); + if (is_immutable(tstruct)) { + generate_deserialize_field(out, *f_iter); + } else { + generate_deserialize_field(out, *f_iter, "self."); + } + indent_down(); + out << indent() << "else:" << endl << indent() << indent_str() << "iprot.skip(ftype)" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "else:" << endl << indent() << indent_str() << "iprot.skip(ftype)" << endl; + + // Read field end marker + indent(out) << "iprot.readFieldEnd()" << endl; + + indent_down(); + + indent(out) << "iprot.readStructEnd()" << endl; + + if (is_immutable(tstruct)) { + indent(out) << "return cls(" << endl; + indent_up(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << (*f_iter)->get_name() << "=" << (*f_iter)->get_name() << "," << endl; + } + indent_down(); + indent(out) << ")" << endl; + } + + indent_down(); + out << endl; +} + +void t_py_generator::generate_py_struct_writer(ofstream& out, t_struct* tstruct) { + string name = tstruct->get_name(); + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator f_iter; + + indent(out) << "def write(self, oprot):" << endl; + indent_up(); + + indent(out) << "if oprot._fast_encode is not None and self.thrift_spec is not None:" << endl; + indent_up(); + + indent(out) + << "oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))" + << endl; + indent(out) << "return" << endl; + indent_down(); + + indent(out) << "oprot.writeStructBegin('" << name << "')" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // Write field header + indent(out) << "if self." << (*f_iter)->get_name() << " is not None:" << endl; + indent_up(); + indent(out) << "oprot.writeFieldBegin(" + << "'" << (*f_iter)->get_name() << "', " << type_to_enum((*f_iter)->get_type()) + << ", " << (*f_iter)->get_key() << ")" << endl; + + // Write field contents + generate_serialize_field(out, *f_iter, "self."); + + // Write field closer + indent(out) << "oprot.writeFieldEnd()" << endl; + + indent_down(); + } + + // Write the struct map + out << indent() << "oprot.writeFieldStop()" << endl << indent() << "oprot.writeStructEnd()" + << endl; + + out << endl; + + indent_down(); + generate_py_struct_required_validator(out, tstruct); +} + +void t_py_generator::generate_py_struct_required_validator(ofstream& out, t_struct* tstruct) { + indent(out) << "def validate(self):" << endl; + indent_up(); + + const vector& fields = tstruct->get_members(); + + if (fields.size() > 0) { + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + if (field->get_req() == t_field::T_REQUIRED) { + indent(out) << "if self." << field->get_name() << " is None:" << endl; + indent(out) << indent_str() << "raise TProtocolException(message='Required field " + << field->get_name() << " is unset!')" << endl; + } + } + } + + indent(out) << "return" << endl; + indent_down(); +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_py_generator::generate_service(t_service* tservice) { + string f_service_name = package_dir_ + "/" + service_name_ + ".py"; + f_service_.open(f_service_name.c_str()); + + f_service_ << py_autogen_comment() << endl << py_imports() << endl; + + if (tservice->get_extends() != NULL) { + f_service_ << "import " + << get_real_py_module(tservice->get_extends()->get_program(), gen_twisted_, package_prefix_) << "." + << tservice->get_extends()->get_name() << endl; + } + + f_service_ << "import logging" << endl + << "from .ttypes import *" << endl + << "from thrift.Thrift import TProcessor" << endl + << "from thrift.transport import TTransport" << endl + << import_dynbase_; + + if (gen_twisted_) { + f_service_ << "from zope.interface import Interface, implements" << endl + << "from twisted.internet import defer" << endl + << "from thrift.transport import TTwisted" << endl; + } else if (gen_tornado_) { + f_service_ << "from tornado import gen" << endl; + f_service_ << "from tornado import concurrent" << endl; + } + + // Generate the three main parts of the service + generate_service_interface(tservice); + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + generate_service_remote(tservice); + + // Close service file + f_service_.close(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_py_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_ << endl << "# HELPER FUNCTIONS AND STRUCTURES" << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_py_struct_definition(f_service_, ts, false); + generate_py_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_py_generator::generate_py_function_helpers(t_function* tfunction) { + if (!tfunction->is_oneway()) { + 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_py_struct_definition(f_service_, &result, false); + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_py_generator::generate_service_interface(t_service* tservice) { + string extends = ""; + string extends_if = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_if = "(" + extends + ".Iface)"; + } else { + if (gen_twisted_) { + extends_if = "(Interface)"; + } else if (gen_newstyle_ || gen_dynamic_ || gen_tornado_) { + extends_if = "(object)"; + } + } + + f_service_ << endl << endl << "class Iface" << extends_if << ":" << endl; + indent_up(); + generate_python_docstring(f_service_, tservice); + vector functions = tservice->get_functions(); + if (functions.empty()) { + f_service_ << indent() << "pass" << endl; + } else { + vector::iterator f_iter; + bool first = true; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << endl; + } + f_service_ << indent() << "def " << function_signature(*f_iter, true) << ":" << endl; + indent_up(); + generate_python_docstring(f_service_, (*f_iter)); + f_service_ << indent() << "pass" << endl; + indent_down(); + } + } + + indent_down(); +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_py_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + if (gen_twisted_) { + extends_client = "(" + extends + ".Client)"; + } else { + extends_client = extends + ".Client, "; + } + } else { + if (gen_twisted_ && (gen_newstyle_ || gen_dynamic_)) { + extends_client = "(object)"; + } + } + + f_service_ << endl << endl; + + if (gen_twisted_) { + f_service_ << "class Client" << extends_client << ":" << endl + << indent_str() << "implements(Iface)" << endl + << endl; + } else { + f_service_ << "class Client(" << extends_client << "Iface):" << endl; + } + indent_up(); + generate_python_docstring(f_service_, tservice); + + // Constructor function + if (gen_twisted_) { + f_service_ << indent() << "def __init__(self, transport, oprot_factory):" << endl; + } else if (gen_tornado_) { + f_service_ << indent() + << "def __init__(self, transport, iprot_factory, oprot_factory=None):" << endl; + } else { + f_service_ << indent() << "def __init__(self, iprot, oprot=None):" << endl; + } + indent_up(); + if (extends.empty()) { + if (gen_twisted_) { + f_service_ << indent() << "self._transport = transport" << endl + << indent() << "self._oprot_factory = oprot_factory" << endl + << indent() << "self._seqid = 0" << endl + << indent() << "self._reqs = {}" << endl; + } else if (gen_tornado_) { + f_service_ << indent() << "self._transport = transport" << endl + << indent() << "self._iprot_factory = iprot_factory" << endl + << indent() << "self._oprot_factory = (oprot_factory if oprot_factory is not None" + << endl + << indent() << " else iprot_factory)" << endl + << indent() << "self._seqid = 0" << endl + << indent() << "self._reqs = {}" << endl + << indent() << "self._transport.io_loop.spawn_callback(self._start_receiving)" + << endl; + } else { + f_service_ << indent() << "self._iprot = self._oprot = iprot" << endl + << indent() << "if oprot is not None:" << endl + << indent() << indent_str() << "self._oprot = oprot" << endl + << indent() << "self._seqid = 0" << endl; + } + } else { + if (gen_twisted_) { + f_service_ << indent() << extends + << ".Client.__init__(self, transport, oprot_factory)" << endl; + } else if (gen_tornado_) { + f_service_ << indent() << extends + << ".Client.__init__(self, transport, iprot_factory, oprot_factory)" << endl; + } else { + f_service_ << indent() << extends << ".Client.__init__(self, iprot, oprot)" << endl; + } + } + indent_down(); + + if (gen_tornado_ && extends.empty()) { + f_service_ << endl << + indent() << "@gen.engine" << endl << + indent() << "def _start_receiving(self):" << endl; + indent_up(); + indent(f_service_) << "while True:" << endl; + indent_up(); + f_service_ << indent() << "try:" << endl + << indent() << indent_str() << "frame = yield self._transport.readFrame()" << endl + << indent() << "except TTransport.TTransportException as e:" << endl + << indent() << indent_str() << "for future in self._reqs.values():" << endl + << indent() << indent_str() << indent_str() << "future.set_exception(e)" << endl + << indent() << indent_str() << "self._reqs = {}" << endl + << indent() << indent_str() << "return" << endl + << indent() << "tr = TTransport.TMemoryBuffer(frame)" << endl + << indent() << "iprot = self._iprot_factory.getProtocol(tr)" << endl + << indent() << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << endl + << indent() << "method = getattr(self, 'recv_' + fname)" << endl + << indent() << "future = self._reqs.pop(rseqid, None)" << endl + << indent() << "if not future:" << endl + << indent() << indent_str() << "# future has already been discarded" << endl + << indent() << indent_str() << "continue" << endl + << indent() << "try:" << endl + << indent() << indent_str() << "result = method(iprot, mtype, rseqid)" << endl + << indent() << "except Exception as e:" << endl + << indent() << indent_str() << "future.set_exception(e)" << endl + << indent() << "else:" << endl + << indent() << indent_str() << "future.set_result(result)" << endl; + indent_down(); + indent_down(); + } + + // 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + f_service_ << endl; + // Open function + indent(f_service_) << "def " << function_signature(*f_iter, false) << ":" << endl; + indent_up(); + generate_python_docstring(f_service_, (*f_iter)); + if (gen_twisted_) { + indent(f_service_) << "seqid = self._seqid = self._seqid + 1" << endl; + indent(f_service_) << "self._reqs[seqid] = defer.Deferred()" << endl << endl; + indent(f_service_) << "d = defer.maybeDeferred(self.send_" << funname; + + } else if (gen_tornado_) { + indent(f_service_) << "self._seqid += 1" << endl; + if (!(*f_iter)->is_oneway()) { + indent(f_service_) << "future = self._reqs[self._seqid] = concurrent.Future()" << endl; + } + indent(f_service_) << "self.send_" << funname << "("; + + } else { + indent(f_service_) << "self.send_" << funname << "("; + } + + bool first = true; + if (gen_twisted_) { + // we need a leading comma if there are args, since it's called as maybeDeferred(funcname, + // arg) + first = false; + } + 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; + + if (!(*f_iter)->is_oneway()) { + if (gen_twisted_) { + // nothing. See the next block. + } else if (gen_tornado_) { + indent(f_service_) << "return future" << endl; + } else { + f_service_ << indent(); + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << "return "; + } + f_service_ << "self.recv_" << funname << "()" << endl; + } + } + indent_down(); + + if (gen_twisted_) { + // This block injects the body of the send_<> method for twisted (and a cb/eb pair) + indent_up(); + indent(f_service_) << "d.addCallbacks(" << endl; + + indent_up(); + f_service_ << indent() << "callback=self.cb_send_" << funname << "," << endl << indent() + << "callbackArgs=(seqid,)," << endl << indent() << "errback=self.eb_send_" + << funname << "," << endl << indent() << "errbackArgs=(seqid,))" << endl; + indent_down(); + + indent(f_service_) << "return d" << endl; + indent_down(); + f_service_ << endl; + + indent(f_service_) << "def cb_send_" << funname << "(self, _, seqid):" << endl; + indent_up(); + if ((*f_iter)->is_oneway()) { + // if one-way, fire the deferred & remove it from _reqs + f_service_ << indent() << "d = self._reqs.pop(seqid)" << endl << indent() + << "d.callback(None)" << endl << indent() << "return d" << endl; + } else { + f_service_ << indent() << "return self._reqs[seqid]" << endl; + } + indent_down(); + f_service_ << endl; + + // add an errback to fail the request if the call to send_<> raised an exception + indent(f_service_) << "def eb_send_" << funname << "(self, f, seqid):" << endl; + indent_up(); + f_service_ << indent() << "d = self._reqs.pop(seqid)" << endl << indent() << "d.errback(f)" + << endl << indent() << "return d" << endl; + indent_down(); + } + + f_service_ << endl; + indent(f_service_) << "def send_" << function_signature(*f_iter, false) << ":" << endl; + indent_up(); + + std::string argsname = (*f_iter)->get_name() + "_args"; + std::string messageType = (*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL"; + + // Serialize the request header + if (gen_twisted_ || gen_tornado_) { + f_service_ << indent() << "oprot = self._oprot_factory.getProtocol(self._transport)" << endl + << indent() << "oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', " + << messageType << ", self._seqid)" << endl; + } else { + f_service_ << indent() << "self._oprot.writeMessageBegin('" << (*f_iter)->get_name() << "', " + << messageType << ", self._seqid)" << endl; + } + + f_service_ << indent() << "args = " << argsname << "()" << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = " + << (*fld_iter)->get_name() << endl; + } + + // Write to the stream + if (gen_twisted_ || gen_tornado_) { + f_service_ << indent() << "args.write(oprot)" << endl << indent() << "oprot.writeMessageEnd()" + << endl << indent() << "oprot.trans.flush()" << endl; + } else { + f_service_ << indent() << "args.write(self._oprot)" << endl << indent() + << "self._oprot.writeMessageEnd()" << endl << indent() + << "self._oprot.trans.flush()" << endl; + } + + indent_down(); + + if (!(*f_iter)->is_oneway()) { + std::string resultname = (*f_iter)->get_name() + "_result"; + // Open function + f_service_ << endl; + if (gen_twisted_ || gen_tornado_) { + f_service_ << indent() << "def recv_" << (*f_iter)->get_name() + << "(self, iprot, mtype, rseqid):" << endl; + } else { + t_struct noargs(program_); + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + f_service_ << indent() << "def " << function_signature(&recv_function) << ":" << endl; + } + indent_up(); + + // TODO(mcslee): Validate message reply here, seq ids etc. + + if (gen_twisted_) { + f_service_ << indent() << "d = self._reqs.pop(rseqid)" << endl; + } else if (gen_tornado_) { + } else { + f_service_ << indent() << "iprot = self._iprot" << endl << indent() + << "(fname, mtype, rseqid) = iprot.readMessageBegin()" << endl; + } + + f_service_ << indent() << "if mtype == TMessageType.EXCEPTION:" << endl + << indent() << indent_str() << "x = TApplicationException()" << endl; + + if (gen_twisted_) { + f_service_ << indent() << indent_str() << "x.read(iprot)" << endl << indent() + << indent_str() << "iprot.readMessageEnd()" << endl << indent() << indent_str() << "return d.errback(x)" + << endl << indent() << "result = " << resultname << "()" << endl << indent() + << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl; + } else { + f_service_ << indent() << indent_str() << "x.read(iprot)" << endl << indent() + << indent_str() << "iprot.readMessageEnd()" << endl << indent() << indent_str() << "raise x" << endl + << indent() << "result = " << resultname << "()" << endl << indent() + << "result.read(iprot)" << endl << indent() << "iprot.readMessageEnd()" << endl; + } + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_ << indent() << "if result.success is not None:" << endl; + if (gen_twisted_) { + f_service_ << indent() << indent_str() << "return d.callback(result.success)" << endl; + } else { + f_service_ << indent() << indent_str() << "return result.success" << endl; + } + } + + 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) { + f_service_ << indent() << "if result." << (*x_iter)->get_name() << " is not None:" << endl; + if (gen_twisted_) { + f_service_ << indent() << indent_str() << "return d.errback(result." << (*x_iter)->get_name() << ")" + << endl; + } else { + f_service_ << indent() << indent_str() << "raise result." << (*x_iter)->get_name() << "" << endl; + } + } + + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + if (gen_twisted_) { + f_service_ << indent() << "return d.callback(None)" << endl; + } else { + f_service_ << indent() << "return" << endl; + } + } else { + if (gen_twisted_) { + f_service_ + << indent() + << "return d.errback(TApplicationException(TApplicationException.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\"))" << endl; + } else { + f_service_ << indent() + << "raise TApplicationException(TApplicationException.MISSING_RESULT, \"" + << (*f_iter)->get_name() << " failed: unknown result\")" << endl; + } + } + + // Close function + indent_down(); + } + } + + indent_down(); +} + +/** + * Generates a command line tool for making remote requests + * + * @param tservice The service to generate a remote for. + */ +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) { + vector p_functions = parent->get_functions(); + functions.insert(functions.end(), p_functions.begin(), p_functions.end()); + parent = parent->get_extends(); + } + vector::iterator f_iter; + + string f_remote_name = package_dir_ + "/" + service_name_ + "-remote"; + ofstream f_remote; + f_remote.open(f_remote_name.c_str()); + + f_remote << + "#!/usr/bin/env python" << endl << + py_autogen_comment() << endl << + "import sys" << endl << + "import pprint" << endl << + "if sys.version_info[0] > 2:" << endl << + indent_str() << "from urllib.parse import urlparse" << endl << + "else:" << endl << + indent_str() << "from urlparse import urlparse" << endl << + "from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient" << endl << + "from thrift.protocol.TBinaryProtocol import TBinaryProtocol" << endl << + endl; + + f_remote << + "from " << module_ << " import " << service_name_ << endl << + "from " << module_ << ".ttypes import *" << endl << + endl; + + f_remote << + "if len(sys.argv) <= 1 or sys.argv[1] == '--help':" << endl << + indent_str() << "print('')" << endl << + indent_str() << "print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]')" << endl << + indent_str() << "print('')" << endl << + indent_str() << "print('Functions:')" << endl; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_remote << indent_str() << "print(' " << (*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; + for (std::vector::size_type i = 0; i < num_args; ++i) { + if (first) { + first = false; + } else { + f_remote << ", "; + } + f_remote << args[i]->get_type()->get_name() << " " << args[i]->get_name(); + } + f_remote << ")')" << endl; + } + f_remote << indent_str() << "print('')" << endl << indent_str() << "sys.exit(0)" << endl << endl; + + f_remote << "pp = pprint.PrettyPrinter(indent=2)" << endl + << "host = 'localhost'" << endl + << "port = 9090" << endl + << "uri = ''" << endl + << "framed = False" << endl + << "ssl = False" << endl + << "validate = True" << endl + << "ca_certs = None" << endl + << "keyfile = None" << endl + << "certfile = None" << endl + << "http = False" << endl + << "argi = 1" << endl + << endl + << "if sys.argv[argi] == '-h':" << endl + << indent_str() << "parts = sys.argv[argi + 1].split(':')" << endl + << indent_str() << "host = parts[0]" << endl + << indent_str() << "if len(parts) > 1:" << endl + << indent_str() << indent_str() << "port = int(parts[1])" << endl + << indent_str() << "argi += 2" << endl + << endl + << "if sys.argv[argi] == '-u':" << endl + << indent_str() << "url = urlparse(sys.argv[argi + 1])" << endl + << indent_str() << "parts = url[1].split(':')" << endl + << indent_str() << "host = parts[0]" << endl + << indent_str() << "if len(parts) > 1:" << endl + << indent_str() << indent_str() << "port = int(parts[1])" << endl + << indent_str() << "else:" << endl + << indent_str() << indent_str() << "port = 80" << endl + << indent_str() << "uri = url[2]" << endl + << indent_str() << "if url[4]:" << endl + << indent_str() << indent_str() << "uri += '?%s' % url[4]" << endl + << indent_str() << "http = True" << endl + << indent_str() << "argi += 2" << endl + << endl + << "if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed':" << endl + << indent_str() << "framed = True" << endl + << indent_str() << "argi += 1" << endl + << endl + << "if sys.argv[argi] == '-s' or sys.argv[argi] == '-ssl':" << endl + << indent_str() << "ssl = True" << endl + << indent_str() << "argi += 1" << endl + << endl + << "if sys.argv[argi] == '-novalidate':" << endl + << indent_str() << "validate = False" << endl + << indent_str() << "argi += 1" << endl + << endl + << "if sys.argv[argi] == '-ca_certs':" << endl + << indent_str() << "ca_certs = sys.argv[argi+1]" << endl + << indent_str() << "argi += 2" << endl + << endl + << "if sys.argv[argi] == '-keyfile':" << endl + << indent_str() << "keyfile = sys.argv[argi+1]" << endl + << indent_str() << "argi += 2" << endl + << endl + << "if sys.argv[argi] == '-certfile':" << endl + << indent_str() << "certfile = sys.argv[argi+1]" << endl + << indent_str() << "argi += 2" << endl + << endl + << "cmd = sys.argv[argi]" << endl + << "args = sys.argv[argi + 1:]" << endl + << endl + << "if http:" << endl + << indent_str() << "transport = THttpClient.THttpClient(host, port, uri)" << endl + << "else:" << endl + << indent_str() << "if ssl:" << endl + << indent_str() << indent_str() << "socket = TSSLSocket.TSSLSocket(host, port, " + "validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile)" + << endl + << indent_str() << "else:" << endl + << indent_str() << indent_str() << "socket = TSocket.TSocket(host, port)" << endl + << indent_str() << "if framed:" << endl + << indent_str() << indent_str() << "transport = TTransport.TFramedTransport(socket)" << endl + << indent_str() << "else:" << endl + << indent_str() << indent_str() << "transport = TTransport.TBufferedTransport(socket)" << endl + << "protocol = TBinaryProtocol(transport)" << endl + << "client = " << service_name_ << ".Client(protocol)" << endl + << "transport.open()" << endl + << endl; + + // Generate the dispatch methods + bool first = true; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_remote << "el"; + } + + 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; + indent_up(); + f_remote << indent() << "if len(args) != " << num_args << ":" << endl + << indent() << indent_str() << "print('" << (*f_iter)->get_name() << " requires " << num_args + << " args')" << endl + << indent() << indent_str() << "sys.exit(1)" << endl + << indent() << "pp.pprint(client." << (*f_iter)->get_name() << "("; + indent_down(); + bool first_arg = true; + for (std::vector::size_type i = 0; i < num_args; ++i) { + if (first_arg) + first_arg = false; + else + f_remote << " "; + if (args[i]->get_type()->is_string()) { + f_remote << "args[" << i << "],"; + } else { + f_remote << "eval(args[" << i << "]),"; + } + } + f_remote << "))" << endl; + + f_remote << endl; + } + + if (functions.size() > 0) { + f_remote << "else:" << endl; + f_remote << indent_str() << "print('Unrecognized method %s' % cmd)" << endl; + f_remote << indent_str() << "sys.exit(1)" << endl; + f_remote << endl; + } + + f_remote << "transport.close()" << endl; + + // Close service file + f_remote.close(); + +#ifndef _MSC_VER + + // Make file executable, love that bitwise OR action + chmod(f_remote_name.c_str(), + S_IRUSR | S_IWUSR | S_IXUSR +#ifndef _WIN32 + | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH +#endif + ); + +#endif // _MSC_VER +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_py_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + 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, "; + } + + f_service_ << endl << endl; + + // Generate the header portion + if (gen_twisted_) { + f_service_ << "class Processor(" << extends_processor << "TProcessor):" << endl + << indent_str() << "implements(Iface)" << endl << endl; + } else { + f_service_ << "class Processor(" << extends_processor << "Iface, TProcessor):" << endl; + } + + indent_up(); + + indent(f_service_) << "def __init__(self, handler):" << endl; + indent_up(); + if (extends.empty()) { + if (gen_twisted_) { + f_service_ << indent() << "self._handler = Iface(handler)" << endl; + } else { + f_service_ << indent() << "self._handler = handler" << endl; + } + + f_service_ << indent() << "self._processMap = {}" << endl; + } else { + if (gen_twisted_) { + f_service_ << indent() << extends << ".Processor.__init__(self, Iface(handler))" << endl; + } else { + f_service_ << indent() << extends << ".Processor.__init__(self, handler)" << endl; + } + } + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << indent() << "self._processMap[\"" << (*f_iter)->get_name() + << "\"] = Processor.process_" << (*f_iter)->get_name() << endl; + } + indent_down(); + f_service_ << endl; + + // Generate the server implementation + f_service_ << indent() << "def process(self, iprot, oprot):" << endl; + indent_up(); + + f_service_ << indent() << "(name, type, seqid) = iprot.readMessageBegin()" << endl; + + // TODO(mcslee): validate message + + // HOT: dictionary function lookup + f_service_ << indent() << "if name not in self._processMap:" << endl; + indent_up(); + f_service_ << indent() << "iprot.skip(TType.STRUCT)" << endl + << indent() << "iprot.readMessageEnd()" << endl + << indent() + << "x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown " + "function %s' % (name))" + << endl + << indent() << "oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid)" << endl + << indent() << "x.write(oprot)" << endl + << indent() << "oprot.writeMessageEnd()" << endl + << indent() << "oprot.trans.flush()" << endl; + + if (gen_twisted_) { + f_service_ << indent() << "return defer.succeed(None)" << endl; + } else { + f_service_ << indent() << "return" << endl; + } + indent_down(); + + f_service_ << indent() << "else:" << endl; + + if (gen_twisted_ || gen_tornado_) { + f_service_ << indent() << indent_str() + << "return self._processMap[name](self, seqid, iprot, oprot)" << endl; + } else { + f_service_ << indent() << indent_str() << "self._processMap[name](self, seqid, iprot, oprot)" + << endl; + + // Read end of args field, the T_STOP, and the struct close + f_service_ << indent() << "return True" << endl; + } + + indent_down(); + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + f_service_ << endl; + generate_process_function(tservice, *f_iter); + } + + indent_down(); +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_py_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open function + if (gen_tornado_) { + f_service_ << indent() << "@gen.coroutine" << endl << indent() << "def process_" + << tfunction->get_name() << "(self, seqid, iprot, oprot):" << endl; + } else { + f_service_ << indent() << "def process_" << tfunction->get_name() + << "(self, seqid, iprot, oprot):" << endl; + } + + indent_up(); + + string argsname = tfunction->get_name() + "_args"; + string resultname = tfunction->get_name() + "_result"; + + f_service_ << indent() << "args = " << 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; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_ << indent() << "result = " << resultname << "()" << endl; + } + + if (gen_twisted_) { + // TODO: Propagate arbitrary exception raised by handler to client as does plain "py" + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + f_service_ << indent() << "d = defer.maybeDeferred(self._handler." << tfunction->get_name() + << ", "; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ")" << endl; + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_ << indent() << "return d" << endl; + indent_down(); + f_service_ << endl; + return; + } + + f_service_ << indent() << "d.addCallback(self.write_results_success_" << tfunction->get_name() + << ", result, seqid, oprot)" << endl; + + if (xceptions.size() > 0) { + f_service_ << indent() << "d.addErrback(self.write_results_exception_" + << tfunction->get_name() << ", result, seqid, oprot)" << endl; + } + + f_service_ << indent() << "return d" << endl; + + indent_down(); + f_service_ << endl; + + indent(f_service_) << "def write_results_success_" << tfunction->get_name() + << "(self, success, result, seqid, oprot):" << endl; + indent_up(); + f_service_ << indent() << "result.success = success" << endl << indent() + << "oprot.writeMessageBegin(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid)" << endl << indent() << "result.write(oprot)" + << endl << indent() << "oprot.writeMessageEnd()" << endl << indent() + << "oprot.trans.flush()" << endl; + indent_down(); + + // Try block for a function with exceptions + if (!tfunction->is_oneway() && xceptions.size() > 0) { + f_service_ << endl; + indent(f_service_) << "def write_results_exception_" << tfunction->get_name() + << "(self, error, result, seqid, oprot):" << endl; + indent_up(); + f_service_ << indent() << "try:" << endl; + + // Kinda absurd + f_service_ << indent() << indent_str() << "error.raiseException()" << endl; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << + indent() << "except " << type_name((*x_iter)->get_type()) << " as " << (*x_iter)->get_name() << ":" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << endl; + indent_down(); + } else { + f_service_ << indent() << "pass" << endl; + } + } + f_service_ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid)" << endl << indent() << "result.write(oprot)" + << endl << indent() << "oprot.writeMessageEnd()" << endl << indent() + << "oprot.trans.flush()" << endl; + indent_down(); + } + + } else if (gen_tornado_) { + // TODO: Propagate arbitrary exception raised by handler to client as does plain "py" + + // Generate the function call + t_struct* arg_struct = tfunction->get_arglist(); + const std::vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + if (xceptions.size() > 0) { + f_service_ << indent() << "try:" << endl; + indent_up(); + } + f_service_ << indent(); + if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { + f_service_ << "result.success = "; + } + f_service_ << "yield gen.maybe_future(self._handler." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << "))" << endl; + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + indent_down(); + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as " + << (*x_iter)->get_name() << ":" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << endl; + indent_down(); + } else { + f_service_ << indent() << "pass" << endl; + } + } + } + + if (!tfunction->is_oneway()) { + f_service_ << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name() + << "\", TMessageType.REPLY, seqid)" << endl << indent() << "result.write(oprot)" + << endl << indent() << "oprot.writeMessageEnd()" << endl << indent() + << "oprot.trans.flush()" << endl; + } + + // Close function + indent_down(); + + } else { // py + // Try block for a function with exceptions + // It also catches arbitrary exceptions raised by handler method to propagate them to the client + f_service_ << indent() << "try:" << endl; + indent_up(); + + // Generate the function call + 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_ << "self._handler." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ")" << endl; + if (!tfunction->is_oneway()) { + f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl; + } + + indent_down(); + f_service_ << indent() + << "except (TTransport.TTransportException, KeyboardInterrupt, SystemExit):" << endl + << indent() << indent_str() << "raise" << endl; + + if (!tfunction->is_oneway()) { + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_ << indent() << "except " << type_name((*x_iter)->get_type()) << " as " + << (*x_iter)->get_name() << ":" << endl; + if (!tfunction->is_oneway()) { + indent_up(); + f_service_ << indent() << "msg_type = TMessageType.REPLY" << endl; + f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " + << (*x_iter)->get_name() << endl; + indent_down(); + } else { + f_service_ << indent() << "pass" << endl; + } + } + + f_service_ << indent() << "except Exception as ex:" << endl + << indent() << indent_str() << "msg_type = TMessageType.EXCEPTION" << endl + << indent() << indent_str() << "logging.exception(ex)" << endl + << indent() + << indent_str() << "result = TApplicationException(TApplicationException.INTERNAL_ERROR, " + "'Internal error')" << endl + << indent() << "oprot.writeMessageBegin(\"" << tfunction->get_name() + << "\", msg_type, seqid)" << endl + << indent() << "result.write(oprot)" << endl + << indent() << "oprot.writeMessageEnd()" << endl + << indent() << "oprot.trans.flush()" << endl; + } else { + f_service_ << indent() << "except:" << endl + << indent() << indent_str() << "pass" << endl; + } + + // Close function + indent_down(); + } +} + +/** + * Deserializes a field of any type. + */ +void t_py_generator::generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix) { + 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(); + } + + string name = prefix + tfield->get_name(); + + 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 << " = 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; + case t_base_type::TYPE_STRING: + if (((t_base_type*)type)->is_binary()) { + out << "readBinary()"; + } else if(!gen_utf8strings_) { + out << "readString()"; + } else { + out << "readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.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 Python 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->get_name().c_str()); + } +} + +/** + * Generates an unserializer for a struct, calling read() + */ +void t_py_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + if (is_immutable(tstruct)) { + out << indent() << prefix << " = " << type_name(tstruct) << ".read(iprot)" << endl; + } else { + out << indent() << prefix << " = " << type_name(tstruct) << "()" << endl + << indent() << prefix << ".read(iprot)" << endl; + } +} + +/** + * Serialize a container by writing out the header followed by + * data and then a footer. + */ +void t_py_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { + string size = tmp("_size"); + string ktype = tmp("_ktype"); + string vtype = tmp("_vtype"); + string etype = tmp("_etype"); + + t_field fsize(g_type_i32, size); + t_field fktype(g_type_i8, ktype); + t_field fvtype(g_type_i8, vtype); + t_field fetype(g_type_i8, etype); + + // Declare variables, read header + if (ttype->is_map()) { + out << indent() << prefix << " = {}" << endl << indent() << "(" << ktype << ", " << vtype + << ", " << size << ") = iprot.readMapBegin()" << endl; + } else if (ttype->is_set()) { + out << indent() << prefix << " = set()" << endl << indent() << "(" << etype << ", " << size + << ") = iprot.readSetBegin()" << endl; + } else if (ttype->is_list()) { + out << indent() << prefix << " = []" << endl << indent() << "(" << etype << ", " << size + << ") = iprot.readListBegin()" << endl; + } + + // For loop iterates over elements + string i = tmp("_i"); + indent(out) << + "for " << i << " in range(" << size << "):" << endl; + + indent_up(); + + 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); + } + + indent_down(); + + // Read container end + if (ttype->is_map()) { + indent(out) << "iprot.readMapEnd()" << endl; + if (is_immutable(ttype)) { + indent(out) << prefix << " = TFrozenDict(" << prefix << ")" << endl; + } + } else if (ttype->is_set()) { + indent(out) << "iprot.readSetEnd()" << endl; + if (is_immutable(ttype)) { + indent(out) << prefix << " = frozenset(" << prefix << ")" << endl; + } + } else if (ttype->is_list()) { + if (is_immutable(ttype)) { + indent(out) << prefix << " = tuple(" << prefix << ")" << endl; + } + indent(out) << "iprot.readListEnd()" << endl; + } +} + +/** + * Generates code to deserialize a map + */ +void t_py_generator::generate_deserialize_map_element(ofstream& 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); + + generate_deserialize_field(out, &fkey); + generate_deserialize_field(out, &fval); + + indent(out) << prefix << "[" << key << "] = " << val << endl; +} + +/** + * Write a set element + */ +void t_py_generator::generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix) { + string elem = tmp("_elem"); + t_field felem(tset->get_elem_type(), elem); + + generate_deserialize_field(out, &felem); + + indent(out) << prefix << ".add(" << elem << ")" << endl; +} + +/** + * Write a list element + */ +void t_py_generator::generate_deserialize_list_element(ofstream& out, + t_list* tlist, + string prefix) { + string elem = tmp("_elem"); + t_field felem(tlist->get_elem_type(), elem); + + generate_deserialize_field(out, &felem); + + indent(out) << prefix << ".append(" << elem << ")" << endl; +} + +/** + * Serializes a field of any type. + * + * @param tfield The field to serialize + * @param prefix Name to prepend to field name + */ +void t_py_generator::generate_serialize_field(ofstream& out, t_field* tfield, string prefix) { + 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: " + prefix + tfield->get_name(); + } + + if (type->is_struct() || type->is_xception()) { + generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); + } else if (type->is_container()) { + generate_serialize_container(out, type, prefix + tfield->get_name()); + } else if (type->is_base_type() || type->is_enum()) { + + string name = prefix + tfield->get_name(); + + indent(out) << "oprot."; + + 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 (((t_base_type*)type)->is_binary()) { + out << "writeBinary(" << name << ")"; + } else if (!gen_utf8strings_) { + out << "writeString(" << name << ")"; + } else { + out << "writeString(" << name << ".encode('utf-8') if sys.version_info[0] == 2 else " << name << ")"; + } + break; + case t_base_type::TYPE_BOOL: + out << "writeBool(" << name << ")"; + break; + case t_base_type::TYPE_I8: + out << "writeByte(" << name << ")"; + break; + case t_base_type::TYPE_I16: + out << "writeI16(" << name << ")"; + break; + case t_base_type::TYPE_I32: + out << "writeI32(" << name << ")"; + break; + case t_base_type::TYPE_I64: + out << "writeI64(" << name << ")"; + break; + case t_base_type::TYPE_DOUBLE: + out << "writeDouble(" << name << ")"; + break; + default: + throw "compiler error: no Python name for base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << "writeI32(" << name << ")"; + } + out << endl; + } else { + printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", + prefix.c_str(), + tfield->get_name().c_str(), + type->get_name().c_str()); + } +} + +/** + * Serializes all the members of a struct. + * + * @param tstruct The struct to serialize + * @param prefix String prefix to attach to all fields + */ +void t_py_generator::generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix) { + (void)tstruct; + indent(out) << prefix << ".write(oprot)" << endl; +} + +void t_py_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { + if (ttype->is_map()) { + indent(out) << "oprot.writeMapBegin(" << type_to_enum(((t_map*)ttype)->get_key_type()) << ", " + << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " + << "len(" << prefix << "))" << endl; + } else if (ttype->is_set()) { + indent(out) << "oprot.writeSetBegin(" << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", " + << "len(" << prefix << "))" << endl; + } else if (ttype->is_list()) { + indent(out) << "oprot.writeListBegin(" << type_to_enum(((t_list*)ttype)->get_elem_type()) + << ", " + << "len(" << prefix << "))" << endl; + } + + if (ttype->is_map()) { + string kiter = tmp("kiter"); + string viter = tmp("viter"); + indent(out) << "for " << kiter << ", " << viter << " in " << prefix << ".items():" << endl; + indent_up(); + generate_serialize_map_element(out, (t_map*)ttype, kiter, viter); + indent_down(); + } else if (ttype->is_set()) { + string iter = tmp("iter"); + indent(out) << "for " << iter << " in " << prefix << ":" << endl; + indent_up(); + generate_serialize_set_element(out, (t_set*)ttype, iter); + indent_down(); + } else if (ttype->is_list()) { + string iter = tmp("iter"); + indent(out) << "for " << iter << " in " << prefix << ":" << endl; + indent_up(); + generate_serialize_list_element(out, (t_list*)ttype, iter); + indent_down(); + } + + 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; + } +} + +/** + * Serializes the members of a map. + * + */ +void t_py_generator::generate_serialize_map_element(ofstream& out, + t_map* tmap, + string kiter, + string viter) { + t_field kfield(tmap->get_key_type(), kiter); + generate_serialize_field(out, &kfield, ""); + + t_field vfield(tmap->get_val_type(), viter); + generate_serialize_field(out, &vfield, ""); +} + +/** + * Serializes the members of a set. + */ +void t_py_generator::generate_serialize_set_element(ofstream& out, t_set* tset, string iter) { + t_field efield(tset->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Serializes the members of a list. + */ +void t_py_generator::generate_serialize_list_element(ofstream& out, t_list* tlist, string iter) { + t_field efield(tlist->get_elem_type(), iter); + generate_serialize_field(out, &efield, ""); +} + +/** + * Generates the docstring for a given struct. + */ +void t_py_generator::generate_python_docstring(ofstream& out, t_struct* tstruct) { + generate_python_docstring(out, tstruct, tstruct, "Attributes"); +} + +/** + * Generates the docstring for a given function. + */ +void t_py_generator::generate_python_docstring(ofstream& out, t_function* tfunction) { + generate_python_docstring(out, tfunction, tfunction->get_arglist(), "Parameters"); +} + +/** + * Generates the docstring for a struct or function. + */ +void t_py_generator::generate_python_docstring(ofstream& out, + t_doc* tdoc, + t_struct* tstruct, + const char* subheader) { + bool has_doc = false; + stringstream ss; + if (tdoc->has_doc()) { + has_doc = true; + ss << tdoc->get_doc(); + } + + const vector& fields = tstruct->get_members(); + if (fields.size() > 0) { + if (has_doc) { + ss << endl; + } + has_doc = true; + ss << subheader << ":\n"; + vector::const_iterator p_iter; + for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { + t_field* p = *p_iter; + ss << " - " << p->get_name(); + if (p->has_doc()) { + ss << ": " << p->get_doc(); + } else { + ss << endl; + } + } + } + + if (has_doc) { + generate_docstring_comment(out, "\"\"\"\n", "", ss.str(), "\"\"\"\n"); + } +} + +/** + * Generates the docstring for a generic object. + */ +void t_py_generator::generate_python_docstring(ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + generate_docstring_comment(out, "\"\"\"\n", "", tdoc->get_doc(), "\"\"\"\n"); + } +} + +/** + * Declares an argument, which may include initialization as necessary. + * + * @param tfield The field + */ +string t_py_generator::declare_argument(t_field* tfield) { + std::ostringstream result; + result << tfield->get_name() << "="; + if (tfield->get_value() != NULL) { + result << "thrift_spec[" << tfield->get_key() << "][4]"; + } else { + result << "None"; + } + return result.str(); +} + +/** + * Renders a field default value, returns None otherwise. + * + * @param tfield The field + */ +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) { + return render_const_value(type, tfield->get_value()); + } else { + return "None"; + } +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_py_generator::function_signature(t_function* tfunction, bool interface) { + vector pre; + vector post; + string signature = tfunction->get_name() + "("; + + if (!(gen_twisted_ && interface)) { + pre.push_back("self"); + } + + signature += argument_list(tfunction->get_arglist(), &pre, &post) + ")"; + return signature; +} + +/** + * Renders a field list + */ +string t_py_generator::argument_list(t_struct* tstruct, vector* pre, vector* post) { + string result = ""; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + vector::const_iterator s_iter; + bool first = true; + if (pre) { + for (s_iter = pre->begin(); s_iter != pre->end(); ++s_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += *s_iter; + } + } + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += (*f_iter)->get_name(); + } + if (post) { + for (s_iter = post->begin(); s_iter != post->end(); ++s_iter) { + if (first) { + first = false; + } else { + result += ", "; + } + result += *s_iter; + } + } + return result; +} + +string t_py_generator::type_name(t_type* ttype) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + t_program* program = ttype->get_program(); + if (ttype->is_service()) { + return get_real_py_module(program, gen_twisted_, package_prefix_) + "." + ttype->get_name(); + } + if (program != NULL && program != program_) { + return get_real_py_module(program, gen_twisted_, package_prefix_) + ".ttypes." + ttype->get_name(); + } + return ttype->get_name(); +} + +/** + * Converts the parse type to a Python tyoe + */ +string t_py_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 "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(); +} + +/** See the comment inside generate_py_struct_definition for what this is. */ +string t_py_generator::type_to_spec_args(t_type* ttype) { + while (ttype->is_typedef()) { + ttype = ((t_typedef*)ttype)->get_type(); + } + + if (ttype->is_base_type() && reinterpret_cast(ttype)->is_binary()) { + return "'BINARY'"; + } else if (gen_utf8strings_ && ttype->is_base_type() + && reinterpret_cast(ttype)->is_string()) { + return "'UTF8'"; + } else if (ttype->is_base_type() || ttype->is_enum()) { + return "None"; + } else if (ttype->is_struct() || ttype->is_xception()) { + return "(" + type_name(ttype) + ", " + type_name(ttype) + ".thrift_spec)"; + } else if (ttype->is_map()) { + return "(" + type_to_enum(((t_map*)ttype)->get_key_type()) + ", " + + type_to_spec_args(((t_map*)ttype)->get_key_type()) + ", " + + type_to_enum(((t_map*)ttype)->get_val_type()) + ", " + + type_to_spec_args(((t_map*)ttype)->get_val_type()) + ", " + + (is_immutable(ttype) ? "True" : "False") + ")"; + + } else if (ttype->is_set()) { + return "(" + type_to_enum(((t_set*)ttype)->get_elem_type()) + ", " + + type_to_spec_args(((t_set*)ttype)->get_elem_type()) + ", " + + (is_immutable(ttype) ? "True" : "False") + ")"; + + } else if (ttype->is_list()) { + return "(" + type_to_enum(((t_list*)ttype)->get_elem_type()) + ", " + + type_to_spec_args(((t_list*)ttype)->get_elem_type()) + ", " + + (is_immutable(ttype) ? "True" : "False") + ")"; + } + + throw "INVALID TYPE IN type_to_spec_args: " + ttype->get_name(); +} + +THRIFT_REGISTER_GENERATOR( + py, + "Python", + " twisted: Generate Twisted-friendly RPC services.\n" + " tornado: Generate code for use with Tornado.\n" + " no_utf8strings: Do not Encode/decode strings using utf8 in the generated code. Basically no effect for Python 3.\n" + " coding=CODING: Add file encoding declare in generated file.\n" + " slots: Generate code using slots for instance members.\n" + " dynamic: Generate dynamic code, less code generated but slower.\n" + " 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" + " 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" + " Package prefix for generated files.\n" + " old_style: Deprecated. Generate old-style classes.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc new file mode 100644 index 00000000..924f6f6e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_rb_generator.cc @@ -0,0 +1,1263 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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/version.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * A subclass of std::ofstream that includes indenting functionality. + */ +class t_rb_ofstream : public std::ofstream { +private: + int indent_; + +public: + t_rb_ofstream() : std::ofstream(), indent_(0) {} + explicit t_rb_ofstream(const char* filename, + ios_base::openmode mode = ios_base::out, + int indent = 0) + : std::ofstream(filename, mode), indent_(indent) {} + + t_rb_ofstream& indent() { + for (int i = 0; i < indent_; ++i) { + *this << " "; + } + return *this; + } + + void indent_up() { indent_++; } + void indent_down() { indent_--; } +}; + +/** + * Ruby code generator. + * + */ +class t_rb_generator : public t_oop_generator { +public: + t_rb_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; + + require_rubygems_ = false; + namespaced_ = false; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("rubygems") == 0) { + require_rubygems_ = true; + } else if( iter->first.compare("namespaced") == 0) { + namespaced_ = true; + } else { + throw "unknown option ruby:" + iter->first; + } + } + + out_dir_base_ = "gen-rb"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_union(t_struct* tunion); + void generate_xception(t_struct* txception); + void generate_service(t_service* tservice); + + t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value); + + /** + * Struct generation code + */ + + void generate_rb_struct(t_rb_ofstream& out, t_struct* tstruct, bool is_exception); + void generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct); + void generate_rb_union(t_rb_ofstream& out, t_struct* tstruct, bool is_exception); + void generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct); + void generate_rb_function_helpers(t_function* tfunction); + void generate_rb_simple_constructor(t_rb_ofstream& out, t_struct* tstruct); + void generate_rb_simple_exception_constructor(t_rb_ofstream& out, t_struct* tstruct); + void generate_field_constants(t_rb_ofstream& out, t_struct* tstruct); + void generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct); + void generate_field_defns(t_rb_ofstream& out, t_struct* tstruct); + void generate_field_data(t_rb_ofstream& out, + t_type* field_type, + const std::string& field_name, + t_const_value* field_value, + bool optional); + + /** + * Service-level generation functions + */ + + void generate_service_helpers(t_service* tservice); + void generate_service_interface(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(t_rb_ofstream& out, + t_field* tfield, + std::string prefix = "", + bool inclass = false); + + void generate_deserialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_deserialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(t_rb_ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(t_rb_ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(t_rb_ofstream& out, + t_list* tlist, + std::string prefix = ""); + + void generate_serialize_field(t_rb_ofstream& out, t_field* tfield, std::string prefix = ""); + + void generate_serialize_struct(t_rb_ofstream& out, t_struct* tstruct, std::string prefix = ""); + + void generate_serialize_container(t_rb_ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(t_rb_ofstream& out, + t_map* tmap, + std::string kiter, + std::string viter); + + void generate_serialize_set_element(t_rb_ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(t_rb_ofstream& out, t_list* tlist, std::string iter); + + void generate_rdoc(t_rb_ofstream& out, t_doc* tdoc); + + /** + * Helper rendering functions + */ + + std::string rb_autogen_comment(); + std::string render_require_thrift(); + std::string render_includes(); + std::string declare_field(t_field* tfield); + std::string type_name(const t_type* ttype); + std::string full_type_name(const t_type* ttype); + 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 rb_namespace_to_path_prefix(std::string rb_namespace); + + std::vector ruby_modules(const t_program* p) { + std::string ns = p->get_namespace("rb"); + std::vector modules; + if (ns.empty()) { + return modules; + } + + std::string::iterator pos = ns.begin(); + while (true) { + std::string::iterator delim = std::find(pos, ns.end(), '.'); + modules.push_back(capitalize(std::string(pos, delim))); + pos = delim; + if (pos == ns.end()) { + break; + } + ++pos; + } + + return modules; + } + + void begin_namespace(t_rb_ofstream&, std::vector); + void end_namespace(t_rb_ofstream&, std::vector); + +private: + /** + * File streams + */ + + t_rb_ofstream f_types_; + t_rb_ofstream f_consts_; + t_rb_ofstream f_service_; + + std::string namespace_dir_; + std::string require_prefix_; + + /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */ + bool require_rubygems_; + + /** If true, generate files in idiomatic namespaced directories. */ + bool namespaced_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_rb_generator::init_generator() { + string subdir = get_out_dir(); + + // Make output directory + MKDIR(subdir.c_str()); + + if (namespaced_) { + require_prefix_ = rb_namespace_to_path_prefix(program_->get_namespace("rb")); + + string dir = require_prefix_; + 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); + } + } + + namespace_dir_ = subdir; + + // Make output file + string f_types_name = namespace_dir_ + underscore(program_name_) + "_types.rb"; + f_types_.open(f_types_name.c_str()); + + string f_consts_name = namespace_dir_ + underscore(program_name_) + "_constants.rb"; + f_consts_.open(f_consts_name.c_str()); + + // Print header + f_types_ << rb_autogen_comment() << endl << render_require_thrift() << render_includes() << endl; + begin_namespace(f_types_, ruby_modules(program_)); + + f_consts_ << rb_autogen_comment() << endl << render_require_thrift() << "require '" + << require_prefix_ << underscore(program_name_) << "_types'" << endl << endl; + begin_namespace(f_consts_, ruby_modules(program_)); +} + +/** + * Renders the require of thrift itself, and possibly of the rubygems dependency. + */ +string t_rb_generator::render_require_thrift() { + if (require_rubygems_) { + return "require 'rubygems'\nrequire 'thrift'\n"; + } else { + return "require 'thrift'\n"; + } +} + +/** + * Renders all the imports necessary for including another Thrift program + */ +string t_rb_generator::render_includes() { + const vector& includes = program_->get_includes(); + string result = ""; + for (size_t i = 0; i < includes.size(); ++i) { + if (namespaced_) { + t_program* included = includes[i]; + 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"; + } + } + if (includes.size() > 0) { + result += "\n"; + } + return result; +} + +/** + * Autogen'd comment + */ +string t_rb_generator::rb_autogen_comment() { + 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"; +} + +/** + * Closes the type files + */ +void t_rb_generator::close_generator() { + // Close types file + end_namespace(f_types_, ruby_modules(program_)); + end_namespace(f_consts_, ruby_modules(program_)); + f_types_.close(); + f_consts_.close(); +} + +/** + * Generates a typedef. This is not done in Ruby, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_rb_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_rb_generator::generate_enum(t_enum* tenum) { + f_types_.indent() << "module " << capitalize(tenum->get_name()) << endl; + f_types_.indent_up(); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + + // Ruby class constants have to be capitalized... omg i am so on the fence + // about languages strictly enforcing capitalization why can't we just all + // agree and play nice. + string name = capitalize((*c_iter)->get_name()); + + generate_rdoc(f_types_, *c_iter); + f_types_.indent() << name << " = " << value << endl; + } + + // Create a hash mapping values back to their names (as strings) since ruby has no native enum + // type + f_types_.indent() << "VALUE_MAP = {"; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + // Populate the hash + int value = (*c_iter)->get_value(); + if (c_iter != constants.begin()) + f_types_ << ", "; + f_types_ << value << " => \"" << capitalize((*c_iter)->get_name()) << "\""; + } + f_types_ << "}" << endl; + + // Create a set with valid values for this enum + f_types_.indent() << "VALID_VALUES = Set.new(["; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + // Populate the set + if (c_iter != constants.begin()) + f_types_ << ", "; + f_types_ << capitalize((*c_iter)->get_name()); + } + f_types_ << "]).freeze" << endl; + + f_types_.indent_down(); + f_types_.indent() << "end" << endl << endl; +} + +/** + * Generate a constant value + */ +void t_rb_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + name[0] = toupper(name[0]); + + f_consts_.indent() << name << " = "; + render_const_value(f_consts_, type, value) << endl << endl; +} + +/** + * 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 + */ +t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out, + t_type* type, + t_const_value* value) { + 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_STRING: + out << "%q\"" << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + out.indent() << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << full_type_name(type) << ".new({" << endl; + out.indent_up(); + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + out.indent(); + render_const_value(out, g_type_string, v_iter->first) << " => "; + render_const_value(out, field_type, v_iter->second) << "," << endl; + } + out.indent_down(); + out.indent() << "})"; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + out << "{" << endl; + out.indent_up(); + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out.indent(); + render_const_value(out, ktype, v_iter->first) << " => "; + render_const_value(out, vtype, v_iter->second) << "," << endl; + } + out.indent_down(); + out.indent() << "}"; + } 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(); + } + if (type->is_set()) { + out << "Set.new([" << endl; + } else { + out << "[" << endl; + } + out.indent_up(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out.indent(); + render_const_value(out, etype, *v_iter) << "," << endl; + } + out.indent_down(); + if (type->is_set()) { + out.indent() << "])"; + } else { + out.indent() << "]"; + } + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + return out; +} + +/** + * Generates a ruby struct + */ +void t_rb_generator::generate_struct(t_struct* tstruct) { + if (tstruct->is_union()) { + generate_rb_union(f_types_, tstruct, false); + } else { + generate_rb_struct(f_types_, tstruct, false); + } +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_rb_generator::generate_xception(t_struct* txception) { + generate_rb_struct(f_types_, txception, true); +} + +/** + * Generates a ruby struct + */ +void t_rb_generator::generate_rb_struct(t_rb_ofstream& out, + t_struct* tstruct, + bool is_exception = false) { + generate_rdoc(out, tstruct); + out.indent() << "class " << type_name(tstruct); + if (is_exception) { + out << " < ::Thrift::Exception"; + } + out << endl; + + out.indent_up(); + out.indent() << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl; + + if (is_exception) { + generate_rb_simple_exception_constructor(out, tstruct); + } + + generate_field_constants(out, tstruct); + generate_field_defns(out, tstruct); + generate_rb_struct_required_validator(out, tstruct); + + out.indent() << "::Thrift::Struct.generate_accessors self" << endl; + + out.indent_down(); + out.indent() << "end" << endl << endl; +} + +/** + * Generates a ruby union + */ +void t_rb_generator::generate_rb_union(t_rb_ofstream& out, + t_struct* tstruct, + bool is_exception = false) { + (void)is_exception; + generate_rdoc(out, tstruct); + out.indent() << "class " << type_name(tstruct) << " < ::Thrift::Union" << endl; + + out.indent_up(); + out.indent() << "include ::Thrift::Struct_Union" << endl; + + generate_field_constructors(out, tstruct); + + generate_field_constants(out, tstruct); + generate_field_defns(out, tstruct); + generate_rb_union_validator(out, tstruct); + + out.indent() << "::Thrift::Union.generate_accessors self" << endl; + + out.indent_down(); + out.indent() << "end" << endl << endl; +} + +void t_rb_generator::generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct) { + + out.indent() << "class << self" << endl; + out.indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (f_iter != fields.begin()) { + out << endl; + } + std::string field_name = (*f_iter)->get_name(); + + out.indent() << "def " << field_name << "(val)" << endl; + out.indent() << " " << tstruct->get_name() << ".new(:" << field_name << ", val)" << endl; + out.indent() << "end" << endl; + } + + out.indent_down(); + out.indent() << "end" << endl; + + out << endl; +} + +void t_rb_generator::generate_rb_simple_exception_constructor(t_rb_ofstream& out, + t_struct* tstruct) { + const vector& members = tstruct->get_members(); + + if (members.size() == 1) { + vector::const_iterator m_iter = members.begin(); + + if ((*m_iter)->get_type()->is_string()) { + string name = (*m_iter)->get_name(); + + out.indent() << "def initialize(message=nil)" << endl; + out.indent_up(); + out.indent() << "super()" << endl; + out.indent() << "self." << name << " = message" << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; + + if (name != "message") { + out.indent() << "def message; " << name << " end" << endl << endl; + } + } + } +} + +void t_rb_generator::generate_field_constants(t_rb_ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + std::string field_name = (*f_iter)->get_name(); + std::string cap_field_name = upcase_string(field_name); + + out.indent() << cap_field_name << " = " << (*f_iter)->get_key() << endl; + } + out << endl; +} + +void t_rb_generator::generate_field_defns(t_rb_ofstream& out, t_struct* tstruct) { + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + out.indent() << "FIELDS = {" << endl; + out.indent_up(); + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (f_iter != fields.begin()) { + out << "," << endl; + } + + // generate the field docstrings within the FIELDS constant. no real better place... + generate_rdoc(out, *f_iter); + + out.indent() << upcase_string((*f_iter)->get_name()) << " => "; + + generate_field_data(out, + (*f_iter)->get_type(), + (*f_iter)->get_name(), + (*f_iter)->get_value(), + (*f_iter)->get_req() == t_field::T_OPTIONAL); + } + out.indent_down(); + out << endl; + out.indent() << "}" << endl << endl; + + out.indent() << "def struct_fields; FIELDS; end" << endl << endl; +} + +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, + bool optional = false) { + field_type = get_true_type(field_type); + + // Begin this field's defn + out << "{:type => " << type_to_enum(field_type); + + if (!field_name.empty()) { + out << ", :name => '" << field_name << "'"; + } + + if (field_value != NULL) { + out << ", :default => "; + render_const_value(out, field_type, field_value); + } + + if (!field_type->is_base_type()) { + if (field_type->is_struct() || field_type->is_xception()) { + out << ", :class => " << full_type_name((t_struct*)field_type); + } else if (field_type->is_list()) { + out << ", :element => "; + generate_field_data(out, ((t_list*)field_type)->get_elem_type()); + } else if (field_type->is_map()) { + out << ", :key => "; + generate_field_data(out, ((t_map*)field_type)->get_key_type()); + out << ", :value => "; + generate_field_data(out, ((t_map*)field_type)->get_val_type()); + } else if (field_type->is_set()) { + out << ", :element => "; + generate_field_data(out, ((t_set*)field_type)->get_elem_type()); + } + } else { + if (((t_base_type*)field_type)->is_binary()) { + out << ", :binary => true"; + } + } + + if (optional) { + out << ", :optional => true"; + } + + if (field_type->is_enum()) { + out << ", :enum_class => " << full_type_name(field_type); + } + + // End of this field's defn + 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; + out.indent_up(); + } +} + +void t_rb_generator::end_namespace(t_rb_ofstream& out, vector modules) { + for (vector::reverse_iterator m_iter = modules.rbegin(); m_iter != modules.rend(); + ++m_iter) { + out.indent_down(); + out.indent() << "end" << endl; + } +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_rb_generator::generate_service(t_service* tservice) { + string f_service_name = namespace_dir_ + underscore(service_name_) + ".rb"; + f_service_.open(f_service_name.c_str()); + + f_service_ << rb_autogen_comment() << endl << render_require_thrift(); + + if (tservice->get_extends() != NULL) { + if (namespaced_) { + f_service_ << "require '" << rb_namespace_to_path_prefix( + tservice->get_extends()->get_program()->get_namespace("rb")) + << underscore(tservice->get_extends()->get_name()) << "'" << endl; + } else { + f_service_ << "require '" << require_prefix_ + << underscore(tservice->get_extends()->get_name()) << "'" << endl; + } + } + + f_service_ << "require '" << require_prefix_ << underscore(program_name_) << "_types'" << endl + << endl; + + begin_namespace(f_service_, ruby_modules(tservice->get_program())); + + f_service_.indent() << "module " << capitalize(tservice->get_name()) << endl; + f_service_.indent_up(); + + // Generate the three main parts of the service (well, two for now in PHP) + generate_service_client(tservice); + generate_service_server(tservice); + generate_service_helpers(tservice); + + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; + + end_namespace(f_service_, ruby_modules(tservice->get_program())); + + // Close service file + f_service_.close(); +} + +/** + * Generates helper functions for a service. + * + * @param tservice The service to generate a header definition for + */ +void t_rb_generator::generate_service_helpers(t_service* tservice) { + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + f_service_.indent() << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + generate_rb_struct(f_service_, ts); + generate_rb_function_helpers(*f_iter); + } +} + +/** + * Generates a struct and helpers for a function. + * + * @param tfunction The function + */ +void t_rb_generator::generate_rb_function_helpers(t_function* tfunction) { + 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_rb_struct(f_service_, &result); +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_rb_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + extends = full_type_name(tservice->get_extends()); + extends_client = " < " + extends + "::Client "; + } + + f_service_.indent() << "class Client" << extends_client << endl; + f_service_.indent_up(); + + f_service_.indent() << "include ::Thrift::Client" << endl << 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) { + t_struct* arg_struct = (*f_iter)->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + string funname = (*f_iter)->get_name(); + + // Open function + f_service_.indent() << "def " << function_signature(*f_iter) << endl; + f_service_.indent_up(); + f_service_.indent() << "send_" << funname << "("; + + bool 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; + + 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_.indent_down(); + f_service_.indent() << "end" << endl; + f_service_ << endl; + + f_service_.indent() << "def send_" << function_signature(*f_iter) << endl; + f_service_.indent_up(); + + std::string argsname = capitalize((*f_iter)->get_name() + "_args"); + std::string messageSendProc = (*f_iter)->is_oneway() ? "send_oneway_message" : "send_message"; + + f_service_.indent() << messageSendProc << "('" << funname << "', " << argsname; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name(); + } + + f_service_ << ")" << endl; + + f_service_.indent_down(); + f_service_.indent() << "end" << endl; + + if (!(*f_iter)->is_oneway()) { + std::string resultname = capitalize((*f_iter)->get_name() + "_result"); + t_struct noargs(program_); + + t_function recv_function((*f_iter)->get_returntype(), + string("recv_") + (*f_iter)->get_name(), + &noargs); + // Open function + f_service_ << endl; + f_service_.indent() << "def " << function_signature(&recv_function) << endl; + f_service_.indent_up(); + + // TODO(mcslee): Validate message reply here, seq ids etc. + + f_service_.indent() << "result = receive_message(" << resultname << ")" << endl; + + // Careful, only return _result if not a void function + if (!(*f_iter)->get_returntype()->is_void()) { + f_service_.indent() << "return result.success unless result.success.nil?" << endl; + } + + 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) { + f_service_.indent() << "raise result." << (*x_iter)->get_name() << " unless result." + << (*x_iter)->get_name() << ".nil?" << endl; + } + + // Careful, only return _result if not a void function + if ((*f_iter)->get_returntype()->is_void()) { + f_service_.indent() << "return" << endl; + } else { + f_service_.indent() << "raise " + "::Thrift::ApplicationException.new(::Thrift::ApplicationException::" + "MISSING_RESULT, '" << (*f_iter)->get_name() + << " failed: unknown result')" << endl; + } + + // Close function + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; + } + } + + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; +} + +/** + * Generates a service server definition. + * + * @param tservice The service to generate a server for. + */ +void t_rb_generator::generate_service_server(t_service* tservice) { + // Generate the dispatch methods + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + string extends = ""; + string extends_processor = ""; + if (tservice->get_extends() != NULL) { + extends = full_type_name(tservice->get_extends()); + extends_processor = " < " + extends + "::Processor "; + } + + // Generate the header portion + f_service_.indent() << "class Processor" << extends_processor << endl; + f_service_.indent_up(); + + f_service_.indent() << "include ::Thrift::Processor" << endl << endl; + + // Generate the process subfunctions + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + generate_process_function(tservice, *f_iter); + } + + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; +} + +/** + * Generates a process function definition. + * + * @param tfunction The function to write a dispatcher for + */ +void t_rb_generator::generate_process_function(t_service* tservice, t_function* tfunction) { + (void)tservice; + // Open function + f_service_.indent() << "def process_" << tfunction->get_name() << "(seqid, iprot, oprot)" << endl; + f_service_.indent_up(); + + string argsname = capitalize(tfunction->get_name()) + "_args"; + string resultname = capitalize(tfunction->get_name()) + "_result"; + + f_service_.indent() << "args = read_args(iprot, " << argsname << ")" << endl; + + t_struct* xs = tfunction->get_xceptions(); + const std::vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + // Declare result for non oneway function + if (!tfunction->is_oneway()) { + f_service_.indent() << "result = " << resultname << ".new()" << endl; + } + + // Try block for a function with exceptions + if (xceptions.size() > 0) { + f_service_.indent() << "begin" << endl; + f_service_.indent_up(); + } + + // Generate the function call + 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_ << "@handler." << tfunction->get_name() << "("; + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + f_service_ << ", "; + } + f_service_ << "args." << (*f_iter)->get_name(); + } + f_service_ << ")" << endl; + + if (!tfunction->is_oneway() && xceptions.size() > 0) { + f_service_.indent_down(); + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + f_service_.indent() << "rescue " << full_type_name((*x_iter)->get_type()) << " => " + << (*x_iter)->get_name() << endl; + if (!tfunction->is_oneway()) { + f_service_.indent_up(); + f_service_.indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() + << endl; + f_service_.indent_down(); + } + } + f_service_.indent() << "end" << endl; + } + + // Shortcut out here for oneway functions + if (tfunction->is_oneway()) { + f_service_.indent() << "return" << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; + return; + } + + f_service_.indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)" + << endl; + + // Close function + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_rb_generator::function_signature(t_function* tfunction, string prefix) { + // TODO(mcslee): Nitpicky, no ',' if argument_list is empty + return prefix + tfunction->get_name() + "(" + argument_list(tfunction->get_arglist()) + ")"; +} + +/** + * Renders a field list + */ +string t_rb_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 += (*f_iter)->get_name(); + } + return result; +} + +string t_rb_generator::type_name(const t_type* ttype) { + string prefix = ""; + + string name = ttype->get_name(); + if (ttype->is_struct() || ttype->is_xception() || ttype->is_enum()) { + name = capitalize(ttype->get_name()); + } + + return prefix + name; +} + +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 + "::"; + } + return prefix + type_name(ttype); +} + +/** + * Converts the parse type to a Ruby tyoe + */ +string t_rb_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 "::Thrift::Types::STRING"; + case t_base_type::TYPE_BOOL: + return "::Thrift::Types::BOOL"; + case t_base_type::TYPE_I8: + return "::Thrift::Types::BYTE"; + case t_base_type::TYPE_I16: + return "::Thrift::Types::I16"; + case t_base_type::TYPE_I32: + return "::Thrift::Types::I32"; + case t_base_type::TYPE_I64: + return "::Thrift::Types::I64"; + case t_base_type::TYPE_DOUBLE: + return "::Thrift::Types::DOUBLE"; + } + } else if (type->is_enum()) { + return "::Thrift::Types::I32"; + } else if (type->is_struct() || type->is_xception()) { + return "::Thrift::Types::STRUCT"; + } else if (type->is_map()) { + return "::Thrift::Types::MAP"; + } else if (type->is_set()) { + return "::Thrift::Types::SET"; + } else if (type->is_list()) { + return "::Thrift::Types::LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + +string t_rb_generator::rb_namespace_to_path_prefix(string rb_namespace) { + string namespaces_left = rb_namespace; + string::size_type loc; + + string path_prefix = ""; + + while ((loc = namespaces_left.find(".")) != string::npos) { + path_prefix = path_prefix + underscore(namespaces_left.substr(0, loc)) + "/"; + namespaces_left = namespaces_left.substr(loc + 1); + } + if (namespaces_left.size() > 0) { + path_prefix = path_prefix + underscore(namespaces_left) + "/"; + } + return path_prefix; +} + +void t_rb_generator::generate_rdoc(t_rb_ofstream& out, t_doc* tdoc) { + if (tdoc->has_doc()) { + out.indent(); + generate_docstring_comment(out, "", "# ", tdoc->get_doc(), ""); + } +} + +void t_rb_generator::generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct) { + out.indent() << "def validate" << endl; + out.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_field* field = (*f_iter); + if (field->get_req() == t_field::T_REQUIRED) { + out.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, " + "'Required field " << field->get_name() << " is unset!')"; + if (field->get_type()->is_bool()) { + out << " if @" << field->get_name() << ".nil?"; + } else { + out << " unless @" << field->get_name(); + } + out << endl; + } + } + + // if field is an enum, check that its value is valid + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* field = (*f_iter); + + if (field->get_type()->is_enum()) { + out.indent() << "unless @" << field->get_name() << ".nil? || " + << full_type_name(field->get_type()) << "::VALID_VALUES.include?(@" + << field->get_name() << ")" << endl; + out.indent_up(); + out.indent() << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, " + "'Invalid value of field " << field->get_name() << "!')" << endl; + out.indent_down(); + out.indent() << "end" << endl; + } + } + + out.indent_down(); + out.indent() << "end" << endl << endl; +} + +void t_rb_generator::generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct) { + out.indent() << "def validate" << endl; + out.indent_up(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + out.indent() + << "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?" + << endl; + + // if field is an enum, check that its value is valid + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + const t_field* field = (*f_iter); + + if (field->get_type()->is_enum()) { + out.indent() << "if get_set_field == :" << field->get_name() << endl; + out.indent() << " raise " + "::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, " + "'Invalid value of field " << field->get_name() << "!') unless " + << full_type_name(field->get_type()) << "::VALID_VALUES.include?(get_value)" + << endl; + out.indent() << "end" << endl; + } + } + + out.indent_down(); + out.indent() << "end" << endl << endl; +} + +THRIFT_REGISTER_GENERATOR( + rb, + "Ruby", + " rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n" + " namespaced: Generate files in idiomatic namespaced directories.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc new file mode 100644 index 00000000..ffd73182 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_st_generator.cc @@ -0,0 +1,1055 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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/version.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +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 + +/** + * Smalltalk code generator. + * + */ +class t_st_generator : public t_oop_generator { +public: + t_st_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; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option st:" + iter->first; + } + + out_dir_base_ = "gen-st"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_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_xception(t_struct* txception); + void generate_service(t_service* tservice); + void generate_class_side_definition(); + void generate_force_consts(); + + std::string render_const_value(t_type* type, t_const_value* value); + + /** + * Struct generation code + */ + + void generate_st_struct(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_accessors(std::ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + + void generate_service_client(t_service* tservice); + + void generate_send_method(t_function* tfunction); + void generate_recv_method(t_function* tfunction); + + std::string map_reader(t_map* tmap); + std::string list_reader(t_list* tlist); + std::string set_reader(t_set* tset); + std::string struct_reader(t_struct* tstruct, std::string clsName); + + std::string map_writer(t_map* tmap, std::string name); + std::string list_writer(t_list* tlist, std::string name); + std::string set_writer(t_set* tset, std::string name); + std::string struct_writer(t_struct* tstruct, std::string fname); + + std::string write_val(t_type* t, std::string fname); + std::string read_val(t_type* t); + + /** + * Helper rendering functions + */ + + std::string st_autogen_comment(); + + void st_class_def(std::ofstream& out, std::string name); + void st_method(std::ofstream& out, std::string cls, std::string name); + void st_method(std::ofstream& out, std::string cls, std::string name, std::string category); + void st_close_method(std::ofstream& out); + void st_class_method(std::ofstream& out, std::string cls, std::string name); + void st_class_method(std::ofstream& out, std::string cls, std::string name, std::string category); + void st_setter(std::ofstream& out, std::string cls, std::string name, std::string type); + void st_getter(std::ofstream& out, std::string cls, std::string name); + void st_accessors(std::ofstream& out, std::string cls, std::string name, std::string type); + + std::string class_name(); + static bool is_valid_namespace(const std::string& sub_namespace); + std::string client_class_name(); + std::string prefix(std::string name); + std::string declare_field(t_field* tfield); + std::string type_name(t_type* ttype); + + std::string function_signature(t_function* tfunction); + std::string argument_list(t_struct* tstruct); + std::string function_types_comment(t_function* fn); + + std::string type_to_enum(t_type* ttype); + std::string a_type(t_type* type); + bool is_vowel(char c); + std::string temp_name(); + std::string generated_category(); + +private: + /** + * File streams + */ + int temporary_var; + std::ofstream f_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + * + * @param tprogram The program to generate + */ +void t_st_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + temporary_var = 0; + + // Make output file + string f_name = get_out_dir() + "/" + program_name_ + ".st"; + f_.open(f_name.c_str()); + + // Print header + f_ << st_autogen_comment() << endl; + + st_class_def(f_, program_name_); + generate_class_side_definition(); + + // 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); + } +} + +string t_st_generator::class_name() { + return capitalize(program_name_); +} + +bool t_st_generator::is_valid_namespace(const std::string& sub_namespace) { + return sub_namespace == "prefix" || sub_namespace == "category"; +} + +string t_st_generator::prefix(string class_name) { + string prefix = program_->get_namespace("smalltalk.prefix"); + string name = capitalize(class_name); + name = prefix.empty() ? name : (prefix + name); + return name; +} + +string t_st_generator::client_class_name() { + return capitalize(service_name_) + "Client"; +} + +/** + * Autogen'd comment + */ +string t_st_generator::st_autogen_comment() { + return std::string("'") + "Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n" + "\n" + + "DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n" + "'!\n"; +} + +void t_st_generator::generate_force_consts() { + f_ << prefix(class_name()) << " enums keysAndValuesDo: [:k :v | " << prefix(class_name()) + << " enums at: k put: v value].!" << endl; + + f_ << prefix(class_name()) << " constants keysAndValuesDo: [:k :v | " << prefix(class_name()) + << " constants at: k put: v value].!" << endl; +} + +void t_st_generator::close_generator() { + generate_force_consts(); + f_.close(); +} + +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 = '-'; + } + } + return cat.size() ? cat : "Generated-" + class_name(); +} + +/** + * Generates a typedef. This is not done in Smalltalk, types are all implicit. + * + * @param ttypedef The type definition + */ +void t_st_generator::generate_typedef(t_typedef* ttypedef) { + (void)ttypedef; +} + +void t_st_generator::st_class_def(std::ofstream& out, string name) { + out << "Object subclass: #" << prefix(name) << endl; + indent_up(); + out << indent() << "instanceVariableNames: ''" << endl << indent() << "classVariableNames: ''" + << endl << indent() << "poolDictionaries: ''" << endl << indent() << "category: '" + << generated_category() << "'!" << endl << endl; +} + +void t_st_generator::st_method(std::ofstream& out, string cls, string name) { + st_method(out, cls, name, "as yet uncategorized"); +} + +void t_st_generator::st_class_method(std::ofstream& out, string cls, string name) { + st_method(out, cls + " class", name); +} + +void t_st_generator::st_class_method(std::ofstream& out, string cls, string name, string category) { + st_method(out, cls, name, category); +} + +void t_st_generator::st_method(std::ofstream& out, string cls, string name, string category) { + char timestr[50]; + time_t rawtime; + struct tm* tinfo; + + time(&rawtime); + tinfo = localtime(&rawtime); + strftime(timestr, 50, "%m/%d/%Y %H:%M", tinfo); + + out << "!" << prefix(cls) << " methodsFor: '" + category + "' stamp: 'thrift " << timestr + << "'!\n" << name << endl; + + indent_up(); + out << indent(); +} + +void t_st_generator::st_close_method(std::ofstream& out) { + out << "! !" << endl << endl; + indent_down(); +} + +void t_st_generator::st_setter(std::ofstream& out, + string cls, + string name, + string type = "anObject") { + st_method(out, cls, name + ": " + type); + out << name << " := " + type; + st_close_method(out); +} + +void t_st_generator::st_getter(std::ofstream& out, string cls, string name) { + st_method(out, cls, name + ""); + out << "^ " << name; + st_close_method(out); +} + +void t_st_generator::st_accessors(std::ofstream& out, + string cls, + string name, + string type = "anObject") { + st_setter(out, cls, name, type); + st_getter(out, cls, name); +} + +void t_st_generator::generate_class_side_definition() { + f_ << prefix(class_name()) << " class" << endl << "\tinstanceVariableNames: 'constants enums'!" + << endl << endl; + + st_accessors(f_, class_name() + " class", "enums"); + st_accessors(f_, class_name() + " class", "constants"); + + f_ << prefix(class_name()) << " enums: Dictionary new!" << endl; + f_ << prefix(class_name()) << " constants: Dictionary new!" << endl; + + f_ << endl; +} + +/** + * Generates code for an enumerated type. Done using a class to scope + * the values. + * + * @param tenum The enumeration + */ +void t_st_generator::generate_enum(t_enum* tenum) { + string cls_name = program_name_ + capitalize(tenum->get_name()); + + f_ << prefix(class_name()) << " enums at: '" << tenum->get_name() << "' put: [" + << "(Dictionary new " << endl; + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + int value = (*c_iter)->get_value(); + f_ << "\tat: '" << (*c_iter)->get_name() << "' put: " << value << ";" << endl; + } + + f_ << "\tyourself)]!" << endl << endl; +} + +/** + * Generate a constant value + */ +void t_st_generator::generate_const(t_const* tconst) { + t_type* type = tconst->get_type(); + string name = tconst->get_name(); + t_const_value* value = tconst->get_value(); + + f_ << prefix(class_name()) << " constants at: '" << name << "' put: [" + << render_const_value(type, value) << "]!" << endl << endl; +} + +/** + * 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 + */ +string t_st_generator::render_const_value(t_type* type, t_const_value* value) { + type = get_true_type(type); + std::ostringstream out; + 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: + out << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + out << (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: + out << value->get_integer(); + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << 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()) { + indent(out) << value->get_integer(); + } else if (type->is_struct() || type->is_xception()) { + out << "(" << capitalize(type->get_name()) << " new " << endl; + indent_up(); + + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + 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; + 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(); + } + + out << indent() << v_iter->first->get_string() << ": " + << render_const_value(field_type, v_iter->second) << ";" << endl; + } + out << indent() << "yourself)"; + + indent_down(); + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + out << "(Dictionary new" << endl; + indent_up(); + indent_up(); + const map& val = value->get_map(); + map::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent() << indent(); + out << "at: " << render_const_value(ktype, v_iter->first); + out << " put: "; + out << render_const_value(vtype, v_iter->second); + out << ";" << endl; + } + out << indent() << indent() << "yourself)"; + indent_down(); + indent_down(); + } 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(); + } + if (type->is_set()) { + out << "(Set new" << endl; + } else { + out << "(OrderedCollection new" << endl; + } + indent_up(); + indent_up(); + const vector& val = value->get_list(); + vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + out << indent() << indent(); + out << "add: " << render_const_value(etype, *v_iter); + out << ";" << endl; + } + out << indent() << indent() << "yourself)"; + indent_down(); + indent_down(); + } else { + throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); + } + return out.str(); +} + +/** + * Generates a Smalltalk struct + */ +void t_st_generator::generate_struct(t_struct* tstruct) { + generate_st_struct(f_, tstruct, false); +} + +/** + * Generates a struct definition for a thrift exception. Basically the same + * as a struct but extends the Exception class. + * + * @param txception The struct definition + */ +void t_st_generator::generate_xception(t_struct* txception) { + generate_st_struct(f_, txception, true); +} + +/** + * Generates a smalltalk class to represent a struct + */ +void t_st_generator::generate_st_struct(std::ofstream& out, + t_struct* tstruct, + bool is_exception = false) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + if (is_exception) + out << "Error"; + else + out << "Object"; + + out << " subclass: #" << prefix(type_name(tstruct)) << endl << "\tinstanceVariableNames: '"; + + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (m_iter != members.begin()) + out << " "; + out << camelcase((*m_iter)->get_name()); + } + } + + out << "'\n" + << "\tclassVariableNames: ''\n" + << "\tpoolDictionaries: ''\n" + << "\tcategory: '" << generated_category() << "'!\n\n"; + + generate_accessors(out, tstruct); +} + +bool t_st_generator::is_vowel(char c) { + switch (tolower(c)) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return true; + } + return false; +} + +string t_st_generator::a_type(t_type* type) { + string prefix; + + if (is_vowel(type_name(type)[0])) + prefix = "an"; + else + prefix = "a"; + + return prefix + capitalize(type_name(type)); +} + +void t_st_generator::generate_accessors(std::ofstream& out, t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + string type; + string prefix; + + if (members.size() > 0) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + st_accessors(out, + capitalize(type_name(tstruct)), + camelcase((*m_iter)->get_name()), + a_type((*m_iter)->get_type())); + } + out << endl; + } +} + +/** + * Generates a thrift service. + * + * @param tservice The service definition + */ +void t_st_generator::generate_service(t_service* tservice) { + generate_service_client(tservice); + // generate_service_server(tservice); +} + +string t_st_generator::temp_name() { + std::ostringstream out; + out << "temp" << temporary_var++; + return out.str(); +} + +string t_st_generator::map_writer(t_map* tmap, string fname) { + std::ostringstream out; + string key = temp_name(); + string val = temp_name(); + + out << "[oprot writeMapBegin: (TMap new keyType: " << type_to_enum(tmap->get_key_type()) + << "; valueType: " << type_to_enum(tmap->get_val_type()) << "; size: " << fname << " size)." + << endl; + indent_up(); + + out << indent() << fname << " keysAndValuesDo: [:" << key << " :" << val << " |" << endl; + indent_up(); + + out << indent() << write_val(tmap->get_key_type(), key) << "." << endl << indent() + << write_val(tmap->get_val_type(), val); + indent_down(); + + out << "]." << endl << indent() << "oprot writeMapEnd] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::map_reader(t_map* tmap) { + std::ostringstream out; + string desc = temp_name(); + string val = temp_name(); + + out << "[|" << desc << " " << val << "| " << endl; + indent_up(); + + out << indent() << desc << " := iprot readMapBegin." << endl << indent() << val + << " := Dictionary new." << endl << indent() << desc << " size timesRepeat: [" << endl; + + indent_up(); + out << indent() << val << " at: " << read_val(tmap->get_key_type()) + << " put: " << read_val(tmap->get_val_type()); + indent_down(); + + out << "]." << endl << indent() << "iprot readMapEnd." << endl << indent() << val << "] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::list_writer(t_list* tlist, string fname) { + std::ostringstream out; + string val = temp_name(); + + out << "[oprot writeListBegin: (TList new elemType: " << type_to_enum(tlist->get_elem_type()) + << "; size: " << fname << " size)." << endl; + indent_up(); + + out << indent() << fname << " do: [:" << val << "|" << endl; + indent_up(); + + out << indent() << write_val(tlist->get_elem_type(), val) << endl; + indent_down(); + + out << "]." << endl << indent() << "oprot writeListEnd] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::list_reader(t_list* tlist) { + std::ostringstream out; + string desc = temp_name(); + string val = temp_name(); + + out << "[|" << desc << " " << val << "| " << desc << " := iprot readListBegin." << endl; + indent_up(); + + out << indent() << val << " := OrderedCollection new." << endl << indent() << desc + << " size timesRepeat: [" << endl; + + indent_up(); + out << indent() << val << " add: " << read_val(tlist->get_elem_type()); + indent_down(); + + out << "]." << endl << indent() << "iprot readListEnd." << endl << indent() << val << "] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::set_writer(t_set* tset, string fname) { + std::ostringstream out; + string val = temp_name(); + + out << "[oprot writeSetBegin: (TSet new elemType: " << type_to_enum(tset->get_elem_type()) + << "; size: " << fname << " size)." << endl; + indent_up(); + + out << indent() << fname << " do: [:" << val << "|" << endl; + indent_up(); + + out << indent() << write_val(tset->get_elem_type(), val) << endl; + indent_down(); + + out << "]." << endl << indent() << "oprot writeSetEnd] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::set_reader(t_set* tset) { + std::ostringstream out; + string desc = temp_name(); + string val = temp_name(); + + out << "[|" << desc << " " << val << "| " << desc << " := iprot readSetBegin." << endl; + indent_up(); + + out << indent() << val << " := Set new." << endl << indent() << desc << " size timesRepeat: [" + << endl; + + indent_up(); + out << indent() << val << " add: " << read_val(tset->get_elem_type()); + indent_down(); + + out << "]." << endl << indent() << "iprot readSetEnd." << endl << indent() << val << "] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::struct_writer(t_struct* tstruct, string sname) { + std::ostringstream out; + const vector& fields = tstruct->get_sorted_members(); + vector::const_iterator fld_iter; + + out << "[oprot writeStructBegin: " + << "(TStruct new name: '" + tstruct->get_name() + "')." << endl; + indent_up(); + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + bool optional = (*fld_iter)->get_req() == t_field::T_OPTIONAL; + string fname = camelcase((*fld_iter)->get_name()); + string accessor = sname + " " + camelcase(fname); + + if (optional) { + out << indent() << accessor << " ifNotNil: [" << endl; + indent_up(); + } + + out << indent() << "oprot writeFieldBegin: (TField new name: '" << fname + << "'; type: " << type_to_enum((*fld_iter)->get_type()) + << "; id: " << (*fld_iter)->get_key() << ")." << endl; + + out << indent() << write_val((*fld_iter)->get_type(), accessor) << "." << endl << indent() + << "oprot writeFieldEnd"; + + if (optional) { + out << "]"; + indent_down(); + } + + out << "." << endl; + } + + out << indent() << "oprot writeFieldStop; writeStructEnd] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::struct_reader(t_struct* tstruct, string clsName = "") { + std::ostringstream out; + const vector& fields = tstruct->get_members(); + vector::const_iterator fld_iter; + string val = temp_name(); + string desc = temp_name(); + string found = temp_name(); + + if (clsName.size() == 0) { + clsName = tstruct->get_name(); + } + + out << "[|" << desc << " " << val << "|" << endl; + indent_up(); + + // This is nasty, but without it we'll break things by prefixing TResult. + string name = ((capitalize(clsName) == "TResult") ? capitalize(clsName) : prefix(clsName)); + out << indent() << val << " := " << name << " new." << endl; + + out << indent() << "iprot readStructBegin." << endl << indent() << "[" << desc + << " := iprot readFieldBegin." << endl << indent() << desc + << " type = TType stop] whileFalse: [|" << found << "|" << endl; + indent_up(); + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + out << indent() << desc << " id = " << (*fld_iter)->get_key() << " ifTrue: [" << endl; + indent_up(); + + out << indent() << found << " := true." << endl << indent() << val << " " + << camelcase((*fld_iter)->get_name()) << ": " << read_val((*fld_iter)->get_type()); + indent_down(); + + out << "]." << endl; + } + + out << indent() << found << " ifNil: [iprot skip: " << desc << " type]]." << endl; + indent_down(); + + out << indent() << "oprot readStructEnd." << endl << indent() << val << "] value"; + indent_down(); + + return out.str(); +} + +string t_st_generator::write_val(t_type* t, string fname) { + t = get_true_type(t); + + if (t->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)t)->get_base(); + switch (tbase) { + case t_base_type::TYPE_DOUBLE: + return "iprot writeDouble: " + fname + " asFloat"; + 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: + return "iprot write" + capitalize(type_name(t)) + ": " + fname + " asInteger"; + default: + return "iprot write" + capitalize(type_name(t)) + ": " + fname; + } + } else if (t->is_map()) { + return map_writer((t_map*)t, fname); + } else if (t->is_struct() || t->is_xception()) { + return struct_writer((t_struct*)t, fname); + } else if (t->is_list()) { + return list_writer((t_list*)t, fname); + } else if (t->is_set()) { + return set_writer((t_set*)t, fname); + } else if (t->is_enum()) { + return "iprot writeI32: " + fname; + } else { + throw "Sorry, I don't know how to write this: " + type_name(t); + } +} + +string t_st_generator::read_val(t_type* t) { + t = get_true_type(t); + + if (t->is_base_type()) { + return "iprot read" + capitalize(type_name(t)); + } else if (t->is_map()) { + return map_reader((t_map*)t); + } else if (t->is_struct() || t->is_xception()) { + return struct_reader((t_struct*)t); + } else if (t->is_list()) { + return list_reader((t_list*)t); + } else if (t->is_set()) { + return set_reader((t_set*)t); + } else if (t->is_enum()) { + return "iprot readI32"; + } else { + throw "Sorry, I don't know how to read this: " + type_name(t); + } +} + +void t_st_generator::generate_send_method(t_function* function) { + string funname = function->get_name(); + string signature = function_signature(function); + t_struct* arg_struct = function->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator fld_iter; + + st_method(f_, client_class_name(), "send" + capitalize(signature)); + f_ << "oprot writeMessageBegin:" << endl; + indent_up(); + + f_ << indent() << "(TCallMessage new" << endl; + indent_up(); + + f_ << indent() << "name: '" << funname << "'; " << endl << indent() << "seqid: self nextSeqid)." + << endl; + indent_down(); + indent_down(); + + f_ << indent() << "oprot writeStructBegin: " + << "(TStruct new name: '" + capitalize(camelcase(funname)) + "_args')." << endl; + + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + string fname = camelcase((*fld_iter)->get_name()); + + f_ << indent() << "oprot writeFieldBegin: (TField new name: '" << fname + << "'; type: " << type_to_enum((*fld_iter)->get_type()) << "; id: " << (*fld_iter)->get_key() + << ")." << endl; + + f_ << indent() << write_val((*fld_iter)->get_type(), fname) << "." << endl << indent() + << "oprot writeFieldEnd." << endl; + } + + f_ << indent() << "oprot writeFieldStop; writeStructEnd; writeMessageEnd." << endl; + f_ << indent() << "oprot transport flush"; + + st_close_method(f_); +} + +// We only support receiving TResult structures (so this won't work on the server side) +void t_st_generator::generate_recv_method(t_function* function) { + string funname = camelcase(function->get_name()); + string signature = function_signature(function); + + t_struct result(program_, "TResult"); + t_field success(function->get_returntype(), "success", 0); + result.append(&success); + + t_struct* xs = function->get_xceptions(); + const vector& fields = xs->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + // duplicate the field, but call it "exception"... we don't need a dynamic name + t_field* exception = new t_field((*f_iter)->get_type(), "exception", (*f_iter)->get_key()); + result.append(exception); + } + + st_method(f_, client_class_name(), "recv" + capitalize(funname)); + f_ << "| f msg res | " << endl << indent() << "msg := oprot readMessageBegin." << endl << indent() + << "self validateRemoteMessage: msg." << endl << indent() + << "res := " << struct_reader(&result) << "." << endl << indent() << "oprot readMessageEnd." + << endl << indent() << "oprot transport flush." << endl << indent() + << "res exception ifNotNil: [res exception signal]." << endl << indent() << "^ res"; + st_close_method(f_); +} + +string t_st_generator::function_types_comment(t_function* fn) { + std::ostringstream out; + const vector& fields = fn->get_arglist()->get_members(); + vector::const_iterator f_iter; + + out << "\""; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << camelcase((*f_iter)->get_name()) << ": " << type_name((*f_iter)->get_type()); + if ((f_iter + 1) != fields.end()) { + out << ", "; + } + } + + out << "\""; + + return out.str(); +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_st_generator::generate_service_client(t_service* tservice) { + string extends = ""; + string extends_client = "TClient"; + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_client = extends + "Client"; + } + + f_ << extends_client << " subclass: #" << prefix(client_class_name()) << endl + << "\tinstanceVariableNames: ''\n" + << "\tclassVariableNames: ''\n" + << "\tpoolDictionaries: ''\n" + << "\tcategory: '" << generated_category() << "'!\n\n"; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string funname = camelcase((*f_iter)->get_name()); + string signature = function_signature(*f_iter); + + st_method(f_, client_class_name(), signature); + f_ << function_types_comment(*f_iter) << endl << indent() << "self send" + << capitalize(signature) << "." << endl; + + if (!(*f_iter)->is_oneway()) { + f_ << indent() << "^ self recv" << capitalize(funname) << " success " << endl; + } + + st_close_method(f_); + + generate_send_method(*f_iter); + if (!(*f_iter)->is_oneway()) { + generate_recv_method(*f_iter); + } + } +} + +/** + * Renders a function signature of the form 'type name(args)' + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_st_generator::function_signature(t_function* tfunction) { + return camelcase(tfunction->get_name()) + capitalize(argument_list(tfunction->get_arglist())); +} + +/** + * Renders a field list + */ +string t_st_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 += " "; + } + string name = camelcase((*f_iter)->get_name()); + result += name + ": " + name; + } + return result; +} + +string t_st_generator::type_name(t_type* ttype) { + string prefix = ""; + t_program* program = ttype->get_program(); + if (program != NULL && program != program_) { + if (!ttype->is_service()) { + prefix = program->get_name() + "_types."; + } + } + + string name = ttype->get_name(); + if (ttype->is_struct() || ttype->is_xception()) { + name = capitalize(ttype->get_name()); + } + + return prefix + name; +} + +/* Convert t_type to Smalltalk type code */ +string t_st_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 "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(); +} + +THRIFT_REGISTER_GENERATOR(st, "Smalltalk", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc new file mode 100644 index 00000000..87dd2f02 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_swift_generator.cc @@ -0,0 +1,2209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift/platform.h" +#include "thrift/generate/t_oop_generator.h" + +using std::map; +using std::ostream; +using std::ofstream; +using std::ostringstream; +using std::set; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * Swift code generator. + * + * Designed from the Objective-C (aka Cocoa) generator. + */ +class t_swift_generator : public t_oop_generator { +public: + t_swift_generator(t_program* program, + const map& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)option_string; + map::const_iterator iter; + + log_unexpected_ = false; + async_clients_ = false; + promise_kit_ = false; + debug_descriptions_ = 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("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 { + throw "unknown option swift:" + iter->first; + } + } + + out_dir_base_ = "gen-swift"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + void generate_consts(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, + bool is_property = false); + void render_const_value(ostream& out, + t_type* type, + t_const_value* value); + + void generate_swift_struct(ofstream& out, + t_struct* tstruct, + bool is_private); + void generate_swift_struct_init(ofstream& out, + t_struct* tstruct, + bool all, + bool is_private); + + void generate_swift_struct_implementation(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private); + void generate_swift_struct_hashable_extension(ofstream& out, + t_struct* tstruct, + bool is_private); + void generate_swift_struct_equatable_extension(ofstream& out, + t_struct* tstruct, + bool is_private); + void generate_swift_struct_thrift_extension(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private); + void generate_swift_struct_reader(ofstream& out, t_struct* tstruct, bool is_private); + void generate_swift_struct_writer(ofstream& out,t_struct* tstruct, bool is_private); + void generate_swift_struct_result_writer(ofstream& out, t_struct* tstruct); + void generate_swift_struct_printable_extension(ofstream& out, t_struct* tstruct); + + string function_result_helper_struct_type(t_service *tservice, t_function* tfunction); + 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_swift_service_protocol(ofstream& out, t_service* tservice); + void generate_swift_service_protocol_async(ofstream& out, t_service* tservice); + + void generate_swift_service_client(ofstream& out, t_service* tservice); + void generate_swift_service_client_async(ofstream& out, t_service* tservice); + + void generate_swift_service_client_send_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_swift_service_client_send_function_invocation(ofstream& out, t_function* tfunction); + void generate_swift_service_client_send_async_function_invocation(ofstream& out, + t_function* tfunction); + void generate_swift_service_client_recv_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol); + void generate_swift_service_client_implementation(ofstream& out, t_service* tservice); + void generate_swift_service_client_async_implementation(ofstream& out, t_service* tservice); + + void generate_swift_service_server(ofstream& out, t_service* tservice); + void generate_swift_service_server_implementation(ofstream& out, t_service* tservice); + void generate_swift_service_helpers(t_service* tservice); + + /** + * Helper rendering functions + */ + + string swift_imports(); + string swift_thrift_imports(); + string type_name(t_type* ttype, bool is_optional=false, bool is_forced=false); + string base_type_name(t_base_type* tbase); + string declare_property(t_field* tfield, bool is_private); + string function_signature(t_function* tfunction); + string async_function_signature(t_function* tfunction); + string promise_function_signature(t_function* tfunction); + string function_name(t_function* tfunction); + string argument_list(t_struct* tstruct, string protocol_name, bool is_internal); + string type_to_enum(t_type* ttype, bool qualified=false); + string maybe_escape_identifier(const string& identifier); + void populate_reserved_words(); + +private: + + void block_open(ostream& out) { + out << " {" << endl; + indent_up(); + } + + void block_close(ostream& out, bool end_line=true) { + indent_down(); + indent(out) << "}"; + if (end_line) out << endl; + } + + + bool field_is_optional(t_field* tfield) { + return tfield->get_req() == t_field::T_OPTIONAL; + } + + bool struct_has_required_fields(t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!field_is_optional(*m_iter)) { + return true; + } + } + return false; + } + + bool struct_has_optional_fields(t_struct* tstruct) { + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (field_is_optional(*m_iter)) { + return true; + } + } + return false; + } + + string constants_declarations_; + + /** + * File streams + */ + + ofstream f_decl_; + ofstream f_impl_; + + bool log_unexpected_; + bool async_clients_; + bool promise_kit_; + bool debug_descriptions_; + + set swift_reserved_words_; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + */ +void t_swift_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + populate_reserved_words(); + + // we have a .swift declarations file... + string f_decl_name = capitalize(program_name_) + ".swift"; + string f_decl_fullname = get_out_dir() + f_decl_name; + f_decl_.open(f_decl_fullname.c_str()); + + f_decl_ << autogen_comment() << endl; + + f_decl_ << swift_imports() << swift_thrift_imports() << endl; + + // ...and a .swift implementation extensions file + string f_impl_name = capitalize(program_name_) + "+Exts.swift"; + 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_ << swift_imports() << swift_thrift_imports() << endl; + +} + +/** + * Prints standard Cocoa imports + * + * @return List of imports for Cocoa libraries + */ +string t_swift_generator::swift_imports() { + + vector includes_list; + includes_list.push_back("Foundation"); + + ostringstream includes; + + vector::const_iterator i_iter; + for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { + includes << "import " << *i_iter << endl; + } + + includes << endl; + + return includes.str(); +} + +/** + * Prints Thrift runtime imports + * + * @return List of imports necessary for Thrift runtime + */ +string t_swift_generator::swift_thrift_imports() { + + vector includes_list; + includes_list.push_back("Thrift"); + + if (promise_kit_) { + includes_list.push_back("PromiseKit"); + } + + ostringstream includes; + + vector::const_iterator i_iter; + for (i_iter=includes_list.begin(); i_iter!=includes_list.end(); ++i_iter) { + includes << "import " << *i_iter << endl; + } + + includes << endl; + + return includes.str(); +} + +/** + * Finish up generation. + */ +void t_swift_generator::close_generator() { + // stick our constants declarations at the end of the header file + // since they refer to things we are defining. + f_decl_ << constants_declarations_ << endl; +} + +/** + * Generates a typedef. This is just a simple 1-liner in Swift + * + * @param ttypedef The type definition + */ +void t_swift_generator::generate_typedef(t_typedef* ttypedef) { + f_decl_ << indent() << "public typealias " << ttypedef->get_symbolic() + << " = " << type_name(ttypedef->get_type()) << endl; + f_decl_ << endl; +} + +/** + * Generates code for an enumerated type. In Swift, this is + * essentially the same as the thrift definition itself, using + * Swift syntax. + * + * @param tenum The enumeration + */ +void t_swift_generator::generate_enum(t_enum* tenum) { + f_decl_ << indent() << "public enum " << tenum->get_name() << " : Int32"; + block_open(f_decl_); + + vector constants = tenum->get_constants(); + vector::iterator c_iter; + + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + f_decl_ << indent() << "case " << (*c_iter)->get_name() + << " = " << (*c_iter)->get_value() << endl; + } + + f_decl_ << endl; + f_decl_ << indent() << "public init() { self.init(rawValue: " << constants.front()->get_value() << ")! }" << endl; + + block_close(f_decl_); + f_decl_ << endl; + + f_impl_ << indent() << "extension " << tenum->get_name() << " : TEnum"; + block_open(f_impl_); + + f_impl_ << endl; + + f_impl_ << indent() << "public static func readValueFromProtocol(proto: TProtocol) throws -> " << tenum->get_name(); + block_open(f_impl_); + f_impl_ << indent() << "var raw = Int32()" << endl + << indent() << "try proto.readI32(&raw)" << endl + << indent() << "return " << tenum->get_name() << "(rawValue: raw)!" << endl; + block_close(f_impl_); + f_impl_ << endl; + + f_impl_ << indent() << "public static func writeValue(value: " << tenum->get_name() << ", toProtocol proto: TProtocol) throws"; + block_open(f_impl_); + f_impl_ << indent() << "try proto.writeI32(value.rawValue)" << endl; + block_close(f_impl_); + f_impl_ << endl; + + block_close(f_impl_); + f_impl_ << endl; +} + +/** + * Generates public constants for all Thrift constants. + * + * @param consts Constants to generate + */ +void t_swift_generator::generate_consts(vector consts) { + + ostringstream const_interface; + + // 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(); + const_interface << "public let " << capitalize((*c_iter)->get_name()) << " : " << type_name(type) << " = "; + render_const_value(const_interface, type, (*c_iter)->get_value()); + const_interface << endl << endl; + } + + // this gets spit into the header file in ::close_generator + constants_declarations_ = const_interface.str(); + +} + +/** + * Generates a struct definition for a thrift data type. This is a struct + * with public members. Optional types are used for optional properties to + * allow them to be tested for availability. Separate inits are included for + * required properties & all properties. + * + * Generates extensions to provide conformance to TStruct, TSerializable, + * Hashable & Equatable + * + * @param tstruct The struct definition + */ +void t_swift_generator::generate_struct(t_struct* tstruct) { + generate_swift_struct(f_decl_, tstruct, false); + generate_swift_struct_implementation(f_impl_, tstruct, false, false); +} + +/** + * Exceptions are structs, but they conform to ErrorType + * + * @param tstruct The struct definition + */ +void t_swift_generator::generate_xception(t_struct* txception) { + generate_swift_struct(f_decl_, txception, false); + generate_swift_struct_implementation(f_impl_, txception, false, false); +} + +/** + * Generate the interface for a struct. Only properties and + * init methods are included. + * + * @param tstruct The struct definition + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + out << indent() << visibility << " final class " << tstruct->get_name(); + + if (tstruct->is_xception()) { + out << " : ErrorType"; + } + + block_open(out); + + // properties + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << endl; + out << indent() << declare_property(*m_iter, is_private) << endl; + } + + out << endl; + + // init + + indent(out) << visibility << " init()"; + block_open(out); + block_close(out); + + out << endl; + + if (struct_has_required_fields(tstruct)) { + generate_swift_struct_init(out, tstruct, false, is_private); + } + if (struct_has_optional_fields(tstruct)) { + generate_swift_struct_init(out, tstruct, true, is_private); + } + + block_close(out); + + out << endl; +} + +/** + * Generate struct init for properties + * + * @param tstruct The structure definition + * @param all Generate init with all or just required properties + * @param is_private + * Is the initializer public or private + */ +void t_swift_generator::generate_swift_struct_init(ofstream& out, + t_struct* tstruct, + bool all, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + indent(out) << visibility << " init("; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + bool first=true; + for (m_iter = members.begin(); m_iter != members.end();) { + if (all || !field_is_optional(*m_iter)) { + if (first) { + first = false; + } + else { + out << ", "; + } + out << (*m_iter)->get_name() << ": " + << maybe_escape_identifier(type_name((*m_iter)->get_type(), field_is_optional(*m_iter))); + } + ++m_iter; + } + out << ")"; + + block_open(out); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (all || (*m_iter)->get_req() == t_field::T_REQUIRED || (*m_iter)->get_req() == t_field::T_OPT_IN_REQ_OUT) { + out << indent() << "self." << maybe_escape_identifier((*m_iter)->get_name()) << " = " + << maybe_escape_identifier((*m_iter)->get_name()) << endl; + } + } + + block_close(out); + + out << endl; +} + +/** + * Generate the hashable protocol implmentation + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_hashable_extension(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + indent(out) << "extension " << tstruct->get_name() << " : Hashable"; + + block_open(out); + + out << endl; + + indent(out) << visibility << " var hashValue : Int"; + + 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; + + 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) << "return result" << endl; + } + else { + indent(out) << "return 31" << endl; + } + + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generate the equatable protocol implementation + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_equatable_extension(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + indent(out) << visibility << " func ==(lhs: " << type_name(tstruct) << ", rhs: " << type_name(tstruct) << ") -> Bool"; + + block_open(out); + + indent(out) << "return"; + + const vector& members = tstruct->get_members(); + vector::const_iterator m_iter; + + if (members.size()) { + + out << endl; + + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end();) { + t_field* tfield = *m_iter; + indent(out) << "(lhs." << maybe_escape_identifier(tfield->get_name()) + << " ?== rhs." << maybe_escape_identifier(tfield->get_name()) << ")"; + if (++m_iter != members.end()) { + out << " &&"; + } + out << endl; + } + + indent_down(); + + } + else { + out << " true" << endl; + } + + block_close(out); + + out << endl; +} + +/** + * Generate struct implementation. Produces extensions that + * fulfill the requisite protocols to complete the value. + * + * @param tstruct The struct definition + * @param is_result + * If this is a result it needs a different writer + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_implementation(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private) { + + generate_swift_struct_equatable_extension(out, tstruct, is_private); + + if (!is_private && !is_result) { + generate_swift_struct_printable_extension(out, tstruct); + } + + generate_swift_struct_hashable_extension(out, tstruct, is_private); + generate_swift_struct_thrift_extension(out, tstruct, is_result, is_private); + + out << endl << endl; +} + +/** + * Generate the TStruct protocol implementation. + * + * @param tstruct The structure definition + * @param is_result + * Is the struct a result value + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_thrift_extension(ofstream& out, + t_struct* tstruct, + bool is_result, + bool is_private) { + + indent(out) << "extension " << tstruct->get_name() << " : TStruct"; + + block_open(out); + + out << endl; + + generate_swift_struct_reader(out, tstruct, is_private); + + if (is_result) { + generate_swift_struct_result_writer(out, tstruct); + } + else { + generate_swift_struct_writer(out, tstruct, is_private); + } + + block_close(out); + + out << endl; +} + +/** + * Generates a function to read a struct from + * from a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_reader(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + indent(out) << visibility << " static func readValueFromProtocol(__proto: TProtocol) throws -> " + << tstruct->get_name(); + + block_open(out); + + out << endl; + + indent(out) << "try __proto.readStructBegin()" << endl << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool optional = field_is_optional(*f_iter); + indent(out) << "var " << maybe_escape_identifier((*f_iter)->get_name()) << " : " + << type_name((*f_iter)->get_type(), optional, !optional) << endl; + } + + out << endl; + + // Loop over reading in fields + indent(out) << "fields: while true"; + + block_open(out); + + out << endl; + + indent(out) << "let (_, fieldType, fieldID) = try __proto.readFieldBegin()" << endl << endl; + indent(out) << "switch (fieldID, fieldType)"; + + block_open(out); + + indent(out) << "case (_, .STOP):" << endl; + indent_up(); + indent(out) << "break fields" << endl << endl; + indent_down(); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + + indent(out) << "case (" << (*f_iter)->get_key() << ", " << type_to_enum((*f_iter)->get_type()) << "):" << endl; + indent_up(); + indent(out) << maybe_escape_identifier((*f_iter)->get_name()) << " = try __proto.readValue() as " + << type_name((*f_iter)->get_type()) << endl << endl; + indent_down(); + + } + + indent(out) << "case let (_, unknownType):" << endl; + indent_up(); + indent(out) << "try __proto.skipType(unknownType)" << endl; + indent_down(); + + block_close(out); + + out << endl; + + // Read field end marker + indent(out) << "try __proto.readFieldEnd()" << endl; + + block_close(out); + + out << endl; + + indent(out) << "try __proto.readStructEnd()" << endl; + + out << endl; + + if (struct_has_required_fields(tstruct)) { + // performs various checks (e.g. check that all required fields are set) + indent(out) << "// Required fields" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (field_is_optional(*f_iter)) { + continue; + } + indent(out) << "try __proto.validateValue(" << (*f_iter)->get_name() << ", " + << "named: \"" << (*f_iter)->get_name() << "\")" << endl; + } + } + + out << endl; + + indent(out) << "return " << tstruct->get_name() << "("; + for (f_iter = fields.begin(); f_iter != fields.end();) { + out << (*f_iter)->get_name() << ": " << maybe_escape_identifier((*f_iter)->get_name()); + if (++f_iter != fields.end()) { + out << ", "; + } + } + out << ")" << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a function to write a struct to + * a protocol. (TStruct compliance) + * + * @param tstruct The structure definition + * @param is_private + * Is the struct public or private + */ +void t_swift_generator::generate_swift_struct_writer(ofstream& out, + t_struct* tstruct, + bool is_private) { + + string visibility = is_private ? "private" : "public"; + + indent(out) << visibility << " static func writeValue(__value: " << tstruct->get_name() << ", toProtocol __proto: TProtocol) throws"; + + block_open(out); + + out << endl; + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "try __proto.writeStructBeginWithName(\"" << name << "\")" << endl; + + out << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field *tfield = *f_iter; + + bool optional = field_is_optional(tfield); + if (optional) { + indent(out) << "if let " << maybe_escape_identifier(tfield->get_name()) + << " = __value." << maybe_escape_identifier(tfield->get_name()); + block_open(out); + } + + indent(out) << "try __proto.writeFieldValue(" + << (optional ? "" : "__value.") << maybe_escape_identifier(tfield->get_name()) << ", " + << "name: \"" << tfield->get_name() << "\", " + << "type: " << type_to_enum(tfield->get_type()) << ", " + << "id: " << tfield->get_key() << ")" << endl; + + if (optional) { + block_close(out); + } + + out << endl; + } + + indent(out) << "try __proto.writeFieldStop()" << endl << endl; + + indent(out) << "try __proto.writeStructEnd()" << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a function to read a struct from + * from a protocol. (TStruct compliance) + * + * This is specifically a function result. Only + * the first available field is written. + * + * @param tstruct The structure definition + */ +void t_swift_generator::generate_swift_struct_result_writer(ofstream& out, t_struct* tstruct) { + + indent(out) << "private static func writeValue(__value: " << tstruct->get_name() << ", toProtocol __proto: TProtocol) throws"; + + block_open(out); + + out << endl; + + string name = tstruct->get_name(); + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "try __proto.writeStructBeginWithName(\"" << name << "\")" << endl; + + out << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field *tfield = *f_iter; + + indent(out) << "if let result = __value." << (*f_iter)->get_name(); + + block_open(out); + + indent(out) << "try __proto.writeFieldValue(result, " + << "name: \"" << tfield->get_name() << "\", " + << "type: " << type_to_enum(tfield->get_type()) << ", " + << "id: " << tfield->get_key() << ")" << endl; + + block_close(out); + } + // Write the struct map + indent(out) << "try __proto.writeFieldStop()" << endl << endl; + + indent(out) << "try __proto.writeStructEnd()" << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a description method for the given struct + * + * @param tstruct The struct definition + */ +void t_swift_generator::generate_swift_struct_printable_extension(ofstream& out, t_struct* tstruct) { + + // Allow use of debugDescription so the app can add description via a cateogory/extension + + indent(out) << "extension " << tstruct->get_name() << " : " + << (debug_descriptions_ ? "CustomDebugStringConvertible" : "CustomStringConvertible"); + + block_open(out); + + out << endl; + + indent(out) << "public var description : String"; + + block_open(out); + + indent(out) << "var desc = \"" << tstruct->get_name() << "(\"" << endl; + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end();) { + indent(out) << "desc += \"" << (*f_iter)->get_name() + << "=\\(self." << maybe_escape_identifier((*f_iter)->get_name()) << ")"; + if (++f_iter != fields.end()) { + out << ", "; + } + out << "\"" << endl; + } + indent(out) << "desc += \")\"" << endl; + indent(out) << "return desc" << endl; + + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a thrift service. In Swift this consists of a + * protocol definition and a client (with it's implementation + * separated into exts file). + * + * @param tservice The service definition + */ +void t_swift_generator::generate_service(t_service* tservice) { + + generate_swift_service_protocol(f_decl_, tservice); + generate_swift_service_client(f_decl_, tservice); + if (async_clients_) { + generate_swift_service_protocol_async(f_decl_, tservice); + generate_swift_service_client_async(f_decl_, tservice); + } + generate_swift_service_server(f_decl_, tservice); + + generate_swift_service_helpers(tservice); + + generate_swift_service_client_implementation(f_impl_, tservice); + if (async_clients_) { + generate_swift_service_client_async_implementation(f_impl_, tservice); + } + generate_swift_service_server_implementation(f_impl_, tservice); +} + +/** + * Generates structs for all the service return types + * + * @param tservice The service + */ +void t_swift_generator::generate_swift_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_swift_struct(f_impl_, &qname_ts, true); + generate_swift_struct_implementation(f_impl_, &qname_ts, false, true); + generate_function_helpers(tservice, *f_iter); + } +} + +string t_swift_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_swift_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_swift_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)); + if (!tfunction->get_returntype()->is_void()) { + t_field* success = new t_field(tfunction->get_returntype(), "success", 0); + success->set_req(t_field::T_OPTIONAL); + 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) { + t_field *x = *f_iter; + t_field *ox = new t_field(x->get_type(), x->get_name(), x->get_key()); + ox->set_req(t_field::T_OPTIONAL); + result.append(ox); + } + + // generate the result struct + generate_swift_struct(f_impl_, &result, true); + generate_swift_struct_implementation(f_impl_, &result, true, true); + + for (f_iter = result.get_members().begin(); f_iter != result.get_members().end(); ++f_iter) { + delete *f_iter; + } +} + +/** + * Generates a service protocol definition. + * + * @param tservice The service to generate a protocol definition for + */ +void t_swift_generator::generate_swift_service_protocol(ofstream& out, t_service* tservice) { + + indent(out) << "public protocol " << tservice->get_name(); + + block_open(out); + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + out << endl; + indent(out) << function_signature(*f_iter) << " // exceptions: "; + t_struct* xs = (*f_iter)->get_xceptions(); + const 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 << endl; + } + + block_close(out); + + out << endl; +} + +/** + * Generates an asynchronous service protocol definition. + * + * @param tservice The service to generate a protocol definition for + */ +void t_swift_generator::generate_swift_service_protocol_async(ofstream& out, t_service* tservice) { + + indent(out) << "public protocol " << tservice->get_name() << "Async"; + + block_open(out); + + vector functions = tservice->get_functions(); + vector::iterator f_iter; + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + out << endl; + indent(out) << async_function_signature(*f_iter) << endl; + if (promise_kit_) { + indent(out) << promise_function_signature(*f_iter) << endl; + } + out << endl; + } + + block_close(out); + + out << endl; +} + +/** + * Generates a service client interface definition. + * + * @param tservice The service to generate a client interface definition for + */ +void t_swift_generator::generate_swift_service_client(ofstream& out, + t_service* tservice) { + + indent(out) << "public class " << tservice->get_name() << "Client /* : " << tservice->get_name() << " */"; + + block_open(out); + + out << endl; + + indent(out) << "let __inProtocol : TProtocol" << endl << endl; + + indent(out) << "let __outProtocol : TProtocol" << endl << endl; + + indent(out) << "public init(inoutProtocol: TProtocol)"; + + block_open(out); + + indent(out) << "__inProtocol = inoutProtocol" << endl; + + indent(out) << "__outProtocol = inoutProtocol" << endl; + + block_close(out); + + out << endl; + + indent(out) << "public init(inProtocol: TProtocol, outProtocol: TProtocol)"; + + block_open(out); + + indent(out) << "__inProtocol = inProtocol" << endl; + + indent(out) << "__outProtocol = outProtocol" << endl; + + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a service client interface definition. + * + * @param tservice The service to generate a client interface definition for + */ +void t_swift_generator::generate_swift_service_client_async(ofstream& out, + t_service* tservice) { + + indent(out) << "public class " << tservice->get_name() << "AsyncClient /* : " << tservice->get_name() << " */"; + + block_open(out); + + out << endl; + + indent(out) << "let __protocolFactory : TProtocolFactory" << endl << endl; + + indent(out) << "let __transportFactory : TAsyncTransportFactory" << endl << endl; + + indent(out) << "public init(protocolFactory: TProtocolFactory, transportFactory: TAsyncTransportFactory)"; + + block_open(out); + + indent(out) << "__protocolFactory = protocolFactory" << endl; + + indent(out) << "__transportFactory = transportFactory" << endl; + + block_close(out); + + out << endl; + + block_close(out); + + out << 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_swift_generator::generate_swift_service_server(ofstream& out, + t_service* tservice) { + + indent(out) << "public class " << tservice->get_name() << "Processor : NSObject /* " << tservice->get_name() << " */"; + + block_open(out); + + out << endl; + + out << indent() << "typealias ProcessorHandlerDictionary = " + << "[String: (Int, TProtocol, TProtocol, " << tservice->get_name() << ") throws -> Void]" << endl + << endl + << indent() << "let service : " << tservice->get_name() << endl + << endl + << indent() << "public init(service: " << tservice->get_name() << ")"; + block_open(out); + indent(out) << "self.service = service" << endl; + block_close(out); + + out << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a function that will send the arguments + * for a service function via a protocol. + * + * @param tservice The service to generate + * @param tfunction The function to generate + * @param needs_protocol + * Wether the first parameter must be a protocol or if + * the protocol is to be assumed + */ +void t_swift_generator::generate_swift_service_client_send_function_implementation(ofstream& out, + t_service *tservice, + t_function* tfunction, + bool needs_protocol) { + + string funname = tfunction->get_name(); + + t_function send_function(g_type_bool, + "send_" + tfunction->get_name(), + tfunction->get_arglist()); + + string argsname = function_args_helper_struct_type(tservice, tfunction); + t_struct* arg_struct = tfunction->get_arglist(); + + // Open function + indent(out) << "private func " << send_function.get_name() << "(" << argument_list(tfunction->get_arglist(), needs_protocol ? "__outProtocol" : "", true) << ") throws"; + block_open(out); + + out << endl; + + // Serialize the request + indent(out) << "try __outProtocol.writeMessageBeginWithName(\"" << funname << "\", " + << "type: " << (tfunction->is_oneway() ? ".ONEWAY" : ".CALL") << ", " + << "sequenceID: 0)" << endl; + + out << endl; + + indent(out) << "let __args = " << argsname << "("; + + // write out function parameters + + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end();) { + t_field *tfield = (*f_iter); + out << tfield->get_name() << ": " << tfield->get_name(); + if (++f_iter != fields.end()) { + out << ", "; + } + } + out << ")" << endl; + indent(out) << "try " << argsname << ".writeValue(__args, toProtocol: __outProtocol)" << endl << endl; + + indent(out) << "try __outProtocol.writeMessageEnd()" << endl; + + block_close(out); + + out << endl; +} + +/** + * Generates a function that will recv the result for a + * service function via a protocol. + * + * @param tservice The service to generate + * @param tfunction The function to generate + * @param needs_protocol + * Wether the first parameter must be a protocol or if + * the protocol is to be assumed + */ +void t_swift_generator::generate_swift_service_client_recv_function_implementation(ofstream& out, + t_service* tservice, + t_function* tfunction, + bool needs_protocol) { + + // Open function + indent(out) << "private func recv_" << tfunction->get_name() << "("; + + if (needs_protocol) { + out << "__inProtocol: TProtocol"; + } + + out << ") throws"; + + if (!tfunction->get_returntype()->is_void()) { + out << " -> " << type_name(tfunction->get_returntype()); + } + + block_open(out); + + // check for an exception + + out << endl; + + indent(out) << "try __inProtocol.readResultMessageBegin() " << endl << endl; + + string resultname = function_result_helper_struct_type(tservice, tfunction); + indent(out); + if (!tfunction->get_returntype()->is_void() || !tfunction->get_xceptions()->get_members().empty()) { + out << "let __result = "; + } + out << "try " << resultname << ".readValueFromProtocol(__inProtocol)" << endl << endl; + + indent(out) << "try __inProtocol.readMessageEnd()" << endl << endl; + + // Careful, only return _result if not a void function + if (!tfunction->get_returntype()->is_void()) { + indent(out) << "if let __success = __result.success"; + block_open(out); + indent(out) << "return __success" << endl; + block_close(out); + } + + t_struct* xs = tfunction->get_xceptions(); + const vector& xceptions = xs->get_members(); + vector::const_iterator x_iter; + + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + indent(out) << "if let " << (*x_iter)->get_name() << " = __result." << (*x_iter)->get_name(); + block_open(out); + indent(out) << "throw " << (*x_iter)->get_name() << endl; + block_close(out); + } + + // If you get here it's an exception, unless a void function + if (!tfunction->get_returntype()->is_void()) { + indent(out) << "throw NSError(" << endl; + indent_up(); + indent(out) << "domain: TApplicationErrorDomain, " << endl; + indent(out) << "code: Int(TApplicationError.MissingResult.rawValue)," << endl; + indent(out) << "userInfo: [TApplicationErrorMethodKey: \"" << tfunction->get_name() << "\"])" << endl; + indent_down(); + } + + // Close function + block_close(out); + + out << endl; +} + +/** + * Generates an invocation of a given the send function for the + * service function. + * + * @param tfunction The service to generate an implementation for + */ +void t_swift_generator::generate_swift_service_client_send_function_invocation(ofstream& out, + t_function* tfunction) { + + indent(out) << "try send_" << tfunction->get_name() << "("; + + t_struct* arg_struct = tfunction->get_arglist(); + + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end();) { + out << (*f_iter)->get_name() << ": " << (*f_iter)->get_name(); + if (++f_iter != fields.end()) { + out << ", "; + } + } + + out << ")" << endl; +} + +/** + * Generates an invocation of a given the send function for the + * service function. This is for asynchronous protocols. + * + * @param tfunction The service to generate an implementation for + */ +void t_swift_generator::generate_swift_service_client_send_async_function_invocation(ofstream& out, + t_function* tfunction) { + + t_struct* arg_struct = tfunction->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + indent(out) << "try send_" << tfunction->get_name() << "(__protocol"; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + out << ", " << (*f_iter)->get_name() << ": " << (*f_iter)->get_name(); + } + + out << ")" << endl; +} + +/** + * Generates a service client protocol implementation via extension. + * + * @param tservice The service to generate an implementation for + */ +void t_swift_generator::generate_swift_service_client_implementation(ofstream& out, + t_service* tservice) { + + string name = tservice->get_name() + "Client"; + + indent(out) << "extension " << name << " : " << tservice->get_name(); + + block_open(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_swift_service_client_send_function_implementation(out, tservice, *f_iter, false); + + if (!(*f_iter)->is_oneway()) { + generate_swift_service_client_recv_function_implementation(out, tservice, *f_iter, false); + } + + // Open function + indent(out) << "public " << function_signature(*f_iter); + + block_open(out); + + out << endl; + + generate_swift_service_client_send_function_invocation(out, *f_iter); + + out << endl; + + indent(out) << "try __outProtocol.transport().flush()" << endl << endl; + + if (!(*f_iter)->is_oneway()) { + if ((*f_iter)->get_returntype()->is_void()) { + indent(out) << "try recv_" << (*f_iter)->get_name() << "()" << endl; + } else { + indent(out) << "return try recv_" << (*f_iter)->get_name() << "()" << endl; + } + } + + block_close(out); + + out << endl; + } + + block_close(out); + + out << endl; +} + +/** + * Generates a service asynchronous client protocol implementation via extension. + * + * @param tservice The service to generate an implementation for + */ +void t_swift_generator::generate_swift_service_client_async_implementation(ofstream& out, + t_service* tservice) { + + string name = tservice->get_name() + "AsyncClient"; + string protocol_name = tservice->get_name() + "Async"; + + indent(out) << "extension " << name << " : " << protocol_name; + + block_open(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_swift_service_client_send_function_implementation(out, tservice, *f_iter, true); + + if (!(*f_iter)->is_oneway()) { + generate_swift_service_client_recv_function_implementation(out, tservice, *f_iter, true); + } + + indent(out) << "public " << async_function_signature(*f_iter); + block_open(out); + + out << endl; + + out << indent() << "let __transport = __transportFactory.newTransport()" << endl + << indent() << "let __protocol = __protocolFactory.newProtocolOnTransport(__transport)" << endl + << endl; + + generate_swift_service_client_send_async_function_invocation(out, *f_iter); + + out << endl; + + indent(out) << "__transport.flushWithCompletion("; + + if ((*f_iter)->is_oneway()) { + out << "success, failure: failure)" << endl; + } + else { + block_open(out); + indent(out) << "do"; + block_open(out); + + indent(out); + if (!(*f_iter)->get_returntype()->is_void()) { + out << "let result = "; + } + out << "try self.recv_" << (*f_iter)->get_name() << "(__protocol)" << endl; + + out << indent() << "success("; + if (!(*f_iter)->get_returntype()->is_void()) { + out << "result"; + } + out << ")" << endl; + + block_close(out); + indent(out) << "catch let error"; + block_open(out); + indent(out) << "failure(error as NSError)" << endl; + block_close(out); + block_close(out); + indent(out) << ", failure: failure)" << endl; + } + + + block_close(out); + + out << endl; + + // Promise function + if (promise_kit_) { + + indent(out) << "public " << promise_function_signature(*f_iter); + block_open(out); + + out << indent() << "let (__promise, __fulfill, __reject) = Promise<" << type_name((*f_iter)->get_returntype()) << ">.pendingPromise()" << endl << endl + << indent() << "let __transport = __transportFactory.newTransport()" << endl + << indent() << "let __protocol = __protocolFactory.newProtocolOnTransport(__transport)" << endl + << endl; + + generate_swift_service_client_send_async_function_invocation(out, *f_iter); + + out << endl; + + indent(out) << "__transport.flushWithCompletion("; + + if ((*f_iter)->is_oneway()) { + out << "{ __fulfill() }, failure: { __reject($0) })" << endl; + } + else { + block_open(out); + indent(out) << "do"; + block_open(out); + + indent(out); + if (!(*f_iter)->get_returntype()->is_void()) { + out << "let result = "; + } + out << "try self.recv_" << (*f_iter)->get_name() << "(__protocol)" << endl; + + out << indent() << "__fulfill("; + if (!(*f_iter)->get_returntype()->is_void()) { + out << "result"; + } + out << ")" << endl; + + block_close(out); + indent(out) << "catch let error"; + block_open(out); + indent(out) << "__reject(error)" << endl; + block_close(out); + block_close(out); + + indent(out) << ", failure: { error in " << endl; + indent_up(); + indent(out) << "__reject(error)" << endl; + indent_down(); + indent(out) << "})" << endl; + } + + indent(out) << "return __promise" << endl; + + block_close(out); + + out << endl; + + } + + } + + block_close(out); + + out << endl; +} + +/** + * Generates a service server implementation. + * + * Implemented by generating a block for each service function that + * handles the processing of that function. The blocks are stored in + * a map and looked up via function/message name. + * + * @param tservice The service to generate an implementation for + */ +void t_swift_generator::generate_swift_service_server_implementation(ofstream& out, + t_service* tservice) { + + string name = tservice->get_name() + "Processor"; + + indent(out) << "extension " << name << " : TProcessor"; + block_open(out); + + out << endl; + + indent(out) << "static let processorHandlers : ProcessorHandlerDictionary ="; + block_open(out); + + out << endl; + + out << indent() << "var processorHandlers = ProcessorHandlerDictionary()" << endl << 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) { + + t_function* tfunction = *f_iter; + + string args_type = function_args_helper_struct_type(tservice, *f_iter); + + out << indent() << "processorHandlers[\"" << tfunction->get_name() << "\"] = { sequenceID, inProtocol, outProtocol, handler in" << endl + << endl; + + indent_up(); + out << indent() << "let args = try " << args_type << ".readValueFromProtocol(inProtocol)" << endl + << endl + << indent() << "try inProtocol.readMessageEnd()" << endl + << endl; + + if (!tfunction->is_oneway() ) { + string result_type = function_result_helper_struct_type(tservice, tfunction); + indent(out) << "var result = " << result_type << "()" << endl; + + indent(out) << "do"; + block_open(out); + + indent(out); + if (!tfunction->get_returntype()->is_void()) { + out << "result.success = "; + } + out << "try handler." << function_name(tfunction) << "("; + + t_struct* arg_struct = tfunction->get_arglist(); + const vector& fields = arg_struct->get_members(); + vector::const_iterator f_iter; + + for (f_iter = fields.begin(); f_iter != fields.end();) { + string fieldName = (*f_iter)->get_name(); + if (f_iter != fields.begin()) { + out << fieldName << ": "; + } + out << "args." << fieldName; + if (++f_iter != fields.end()) { + out << ", "; + } + } + + out << ")" << endl; + + block_close(out); + + t_struct* xs = tfunction->get_xceptions(); + const vector& xfields = xs->get_members(); + vector::const_iterator x_iter; + + for (x_iter = xfields.begin(); x_iter != xfields.end(); ++x_iter) { + indent(out) << "catch let error as " << (*x_iter)->get_type()->get_name(); + block_open(out); + indent(out) << "result." << (*x_iter)->get_name() << " = error" << endl; + block_close(out); + } + + indent(out) << "catch let error"; + block_open(out); + out << indent() << "throw error" << endl; + block_close(out); + + out << endl; + + if (!tfunction->is_oneway()) { + out << indent() << "try outProtocol.writeMessageBeginWithName(\"" << tfunction->get_name() << "\", type: .REPLY, sequenceID: sequenceID)" << endl + << indent() << "try " << result_type << ".writeValue(result, toProtocol: outProtocol)" << endl + << indent() << "try outProtocol.writeMessageEnd()" << endl; + } + } + block_close(out); + + } + + indent(out) << "return processorHandlers" << endl; + + block_close(out,false); + out << "()" << endl; + + out << endl; + + indent(out) << "public func processOnInputProtocol(inProtocol: TProtocol, outputProtocol outProtocol: TProtocol) throws"; + block_open(out); + + out << endl; + + out << indent() << "let (messageName, _, sequenceID) = try inProtocol.readMessageBegin()" << endl + << endl + << indent() << "if let processorHandler = " << name << ".processorHandlers[messageName]"; + block_open(out); + out << indent() << "do"; + block_open(out); + out << indent() << "try processorHandler(sequenceID, inProtocol, outProtocol, service)" << endl; + block_close(out); + out << indent() << "catch let error as NSError"; + block_open(out); + out << indent() << "try outProtocol.writeExceptionForMessageName(messageName, sequenceID: sequenceID, ex: error)" << endl; + block_close(out); + block_close(out); + out << indent() << "else"; + block_open(out); + out << indent() << "try inProtocol.skipType(.STRUCT)" << endl + << indent() << "try inProtocol.readMessageEnd()" << endl + << indent() << "try outProtocol.writeExceptionForMessageName(messageName," << endl; + indent_up(); + out << indent() << "sequenceID: sequenceID," << endl + << indent() << "ex: NSError(" << endl; + indent_up(); + out << indent() << "domain: TApplicationErrorDomain, " << endl + << indent() << "code: Int(TApplicationError.UnknownMethod.rawValue), " << endl + << indent() << "userInfo: [TApplicationErrorMethodKey: messageName]))" << endl; + indent_down(); + indent_down(); + block_close(out); + + block_close(out); + + block_close(out); + out << endl; +} + +/** + * Returns an Swift name + * + * @param ttype The type + * @param class_ref Do we want a Class reference istead of a type reference? + * @return Swift type name, i.e. Dictionary + */ +string t_swift_generator::type_name(t_type* ttype, bool is_optional, bool is_forced) { + string result; + if (ttype->is_base_type()) { + result = base_type_name((t_base_type*)ttype); + } else if (ttype->is_map()) { + t_map *map = (t_map *)ttype; + result = "TMap<" + type_name(map->get_key_type()) + ", " + type_name(map->get_val_type()) + ">"; + } else if (ttype->is_set()) { + t_set *set = (t_set *)ttype; + result = "TSet<" + type_name(set->get_elem_type()) + ">"; + } else if (ttype->is_list()) { + t_list *list = (t_list *)ttype; + result = "TList<" + type_name(list->get_elem_type()) + ">"; + } + else { + result = ttype->get_name(); + } + + if (is_optional) { + result += "?"; + } + + if (is_forced) { + result += "!"; + } + + return result; +} + +/** + * Returns the Swift type that corresponds to the thrift type. + * + * @param tbase The base type + */ +string t_swift_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 "TBinary"; + } else { + return "String"; + } + case t_base_type::TYPE_BOOL: + return "Bool"; + case t_base_type::TYPE_I8: + return "Int8"; + case t_base_type::TYPE_I16: + return "Int16"; + case t_base_type::TYPE_I32: + return "Int32"; + case t_base_type::TYPE_I64: + return "Int64"; + case t_base_type::TYPE_DOUBLE: + return "Double"; + default: + throw "compiler error: no Swift name for base type " + t_base_type::t_base_name(tbase); + } +} + +/** + * Renders full constant value (as would be seen after an '=') + * + */ +void t_swift_generator::render_const_value(ostream& out, + t_type* type, + t_const_value* value) { + 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_STRING: + out << "\"" << get_escaped_string(value) << "\""; + break; + case t_base_type::TYPE_BOOL: + out << ((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: + out << type_name(type) << "(" << value->get_integer() << ")"; + break; + case t_base_type::TYPE_DOUBLE: + out << type_name(type) << "("; + if (value->get_type() == t_const_value::CV_INTEGER) { + out << value->get_integer(); + } else { + out << value->get_double(); + } + out << ")"; + break; + default: + throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + out << value->get_identifier(); + } else if (type->is_struct() || type->is_xception()) { + + out << type_name(type) << "("; + + const vector& fields = ((t_struct*)type)->get_members(); + vector::const_iterator f_iter; + + const map& val = value->get_map(); + map::const_iterator v_iter; + + for (f_iter = fields.begin(); f_iter != fields.end();) { + t_field* tfield = *f_iter; + t_const_value* value = NULL; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + if (tfield->get_name() == v_iter->first->get_string()) { + value = v_iter->second; + } + } + + if (value) { + out << tfield->get_name() << ": "; + render_const_value(out, tfield->get_type(), value); + } + else if (!field_is_optional(tfield)) { + throw "constant error: required field " + type->get_name() + "." + tfield->get_name() + " has no value"; + } + + if (++f_iter != fields.end()) { + out << ", "; + } + } + + out << ")"; + + } else if (type->is_map()) { + + out << "["; + + 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();) { + + render_const_value(out, ktype, v_iter->first); + out << ": "; + render_const_value(out, vtype, v_iter->second); + + if (++v_iter != val.end()) { + out << ", "; + } + } + + out << "]"; + + } else if (type->is_list()) { + + out << "["; + + t_type* etype = ((t_list*)type)->get_elem_type(); + + const map& val = value->get_map(); + map::const_iterator v_iter; + + for (v_iter = val.begin(); v_iter != val.end();) { + + render_const_value(out, etype, v_iter->first); + + if (++v_iter != val.end()) { + out << ", "; + } + } + + out << "]"; + + } else if (type->is_set()) { + + out << "["; + + t_type* etype = ((t_set*)type)->get_elem_type(); + + const map& val = value->get_map(); + map::const_iterator v_iter; + + for (v_iter = val.begin(); v_iter != val.end();) { + + render_const_value(out, etype, v_iter->first); + + if (++v_iter != val.end()) { + out << ", "; + } + } + + out << "]"; + + } else { + throw "compiler error: no const of type " + type->get_name(); + } + +} + +/** + * Declares an Swift property. + * + * @param tfield The field to declare a property for + */ +string t_swift_generator::declare_property(t_field* tfield, bool is_private) { + + string visibility = is_private ? "private" : "public"; + + ostringstream render; + + render << visibility << " var " << maybe_escape_identifier(tfield->get_name()); + + if (field_is_optional(tfield)) { + render << " : " << type_name(tfield->get_type(), true); + } + else { + render << " = " << type_name(tfield->get_type(), false) << "()"; + } + + return render.str(); +} + +/** + * Renders a function signature + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_swift_generator::function_signature(t_function* tfunction) { + + string result = "func " + function_name(tfunction); + + result += "(" + argument_list(tfunction->get_arglist(), "", false) + ") throws"; + + t_type* ttype = tfunction->get_returntype(); + if (!ttype->is_void()) { + result += " -> " + type_name(ttype); + } + + return result; +} + +/** + * Renders a function signature that returns asynchronously via blocks. + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_swift_generator::async_function_signature(t_function* tfunction) { + t_type* ttype = tfunction->get_returntype(); + t_struct* targlist = tfunction->get_arglist(); + string response_param = "(" + ((ttype->is_void()) ? "" : type_name(ttype)) + ") -> Void"; + string result = "func " + function_name(tfunction); + result += "(" + argument_list(tfunction->get_arglist(), "", false) + + (targlist->get_members().size() ? ", " : "") + + "success: " + response_param + ", " + + "failure: (NSError) -> Void) throws"; + return result; +} + +/** + * Renders a function signature that returns asynchronously via promises. + * + * @param tfunction Function definition + * @return String of rendered function definition + */ +string t_swift_generator::promise_function_signature(t_function* tfunction) { + return "func " + function_name(tfunction) + "(" + argument_list(tfunction->get_arglist(), "", false) + ") throws " + + "-> Promise<" + type_name(tfunction->get_returntype()) + ">"; +} + +/** + * Renders a verbose function name suitable for a Swift method + */ +string t_swift_generator::function_name(t_function* tfunction) { + string name = tfunction->get_name(); + if (!tfunction->get_arglist()->get_members().empty()) { + string first_arg = tfunction->get_arglist()->get_members().front()->get_name(); + if (name.size() < first_arg.size() || + lowercase(name.substr(name.size()-first_arg.size())) != lowercase(first_arg)) { + name += "With" + capitalize(tfunction->get_arglist()->get_members()[0]->get_name()); + } + } + return name; +} + +/** + * Renders a Swift method argument list + */ +string t_swift_generator::argument_list(t_struct* tstruct, string protocol_name, bool is_internal) { + string result = ""; + bool include_protocol = !protocol_name.empty(); + + const vector& fields = tstruct->get_members(); + vector::const_iterator f_iter; + + if (include_protocol) { + result += protocol_name + ": TProtocol"; + if (!fields.empty()) { + result += ", "; + } + } + else if (!fields.empty() && is_internal) { + // Force first argument to be named + result += fields.front()->get_name() + " "; + } + + for (f_iter = fields.begin(); f_iter != fields.end();) { + t_field* arg = *f_iter; + result += arg->get_name() + ": " + type_name(arg->get_type()); + + if (++f_iter != fields.end()) { + result += ", "; + } + } + return result; +} + +/** + * https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html + * + */ + +void t_swift_generator::populate_reserved_words() { + swift_reserved_words_.insert("Self"); + swift_reserved_words_.insert("associatedtype"); + swift_reserved_words_.insert("defer"); + swift_reserved_words_.insert("deinit"); + swift_reserved_words_.insert("dynamicType"); + swift_reserved_words_.insert("enum"); + swift_reserved_words_.insert("extension"); + swift_reserved_words_.insert("fallthrough"); + swift_reserved_words_.insert("false"); + swift_reserved_words_.insert("func"); + swift_reserved_words_.insert("guard"); + swift_reserved_words_.insert("init"); + swift_reserved_words_.insert("inout"); + swift_reserved_words_.insert("internal"); + swift_reserved_words_.insert("let"); + swift_reserved_words_.insert("operator"); + swift_reserved_words_.insert("protocol"); + swift_reserved_words_.insert("repeat"); + swift_reserved_words_.insert("rethrows"); + swift_reserved_words_.insert("struct"); + swift_reserved_words_.insert("subscript"); + swift_reserved_words_.insert("throws"); + swift_reserved_words_.insert("true"); + swift_reserved_words_.insert("typealias"); + swift_reserved_words_.insert("where"); +} + +string t_swift_generator::maybe_escape_identifier(const string& identifier) { + if (swift_reserved_words_.find(identifier) != swift_reserved_words_.end()) { + return "`" + identifier + "`"; + } + return identifier; +} + +/** + * Converts the parse type to a Swift TType enumeration. + */ +string t_swift_generator::type_to_enum(t_type* type, bool qualified) { + type = get_true_type(type); + + string result = qualified ? "TType." : "."; + + 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 result + "STRING"; + case t_base_type::TYPE_BOOL: + return result + "BOOL"; + case t_base_type::TYPE_I8: + return result + "BYTE"; + case t_base_type::TYPE_I16: + return result + "I16"; + case t_base_type::TYPE_I32: + return result + "I32"; + case t_base_type::TYPE_I64: + return result + "I64"; + case t_base_type::TYPE_DOUBLE: + return result + "DOUBLE"; + } + } else if (type->is_enum()) { + return result + "I32"; + } else if (type->is_struct() || type->is_xception()) { + return result + "STRUCT"; + } else if (type->is_map()) { + return result + "MAP"; + } else if (type->is_set()) { + return result + "SET"; + } else if (type->is_list()) { + return result + "LIST"; + } + + throw "INVALID TYPE IN type_to_enum: " + type->get_name(); +} + + +THRIFT_REGISTER_GENERATOR( + swift, + "Swift", + " 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" + " async_clients: Generate clients which invoke asynchronously via block syntax.\n" + " promise_kit: Generate clients which invoke asynchronously via promises.\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc new file mode 100644 index 00000000..b35f3510 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xml_generator.cc @@ -0,0 +1,687 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; +using std::stack; +using std::set; + +static const string endl = "\n"; +static const string quot = "\""; + +static const string default_ns_prefix = "http://thrift.apache.org/xml/ns/"; + +/** + * This generator creates an XML model of the parsed IDL tree, and is designed + * to make it easy to use this file as the input for other template engines, + * such as XSLT. To this end, the generated XML is slightly more verbose than + * you might expect... for example, references to "id" types (such as structs, + * unions, etc) always specify the name of the IDL document, even if the type + * is defined in the same document as the reference. + */ +class t_xml_generator : public t_generator { +public: + t_xml_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; + + should_merge_includes_ = false; + should_use_default_ns_ = true; + should_use_namespaces_ = true; + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + if( iter->first.compare("merge") == 0) { + should_merge_includes_ = true; + } else if( iter->first.compare("no_default_ns") == 0) { + should_use_default_ns_ = false; + } else if( iter->first.compare("no_namespaces") == 0) { + should_use_namespaces_ = false; + } else { + throw "unknown option xml:" + iter->first; + } + } + + out_dir_base_ = "gen-xml"; + } + + virtual ~t_xml_generator() {} + + void init_generator(); + void close_generator(); + void generate_program(); + + void iterate_program(t_program* program); + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + 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_annotations(std::map annotations); + +private: + bool should_merge_includes_; + bool should_use_default_ns_; + bool should_use_namespaces_; + + std::ofstream f_xml_; + + std::set programs_; + std::stack elements_; + bool top_element_is_empty; + bool top_element_is_open; + + string target_namespace(t_program* program); + void write_element_start(const string name); + void close_top_element(); + void write_element_end(); + void write_attribute(string key, string val); + void write_int_attribute(string key, int val); + string escape_xml_string(const string& input); + + void write_xml_comment(string msg); + + void write_type(t_type* ttype); + void write_doc(t_doc* tdoc); + + template + string number_to_string(T t) { + std::ostringstream out; + out.imbue(std::locale::classic()); + out.precision(std::numeric_limits::digits10); + out << t; + return out.str(); + } + + template + void write_number(T n) { + f_xml_ << number_to_string(n); + } + + template + void write_element_number(string name, T n) { + write_element_string(name, number_to_string(n)); + } + + string get_type_name(t_type* ttype); + + void generate_constant(t_const* con); + + void write_element_string(string name, string value); + void write_value(t_type* tvalue); + void write_const_value(t_const_value* value); + virtual std::string xml_autogen_comment() { + 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"; + } +}; + +void t_xml_generator::init_generator() { + MKDIR(get_out_dir().c_str()); + + string f_xml_name = get_out_dir() + program_->get_name() + ".xml"; + f_xml_.open(f_xml_name.c_str()); + + top_element_is_open = false; +} + +string t_xml_generator::target_namespace(t_program* program) { + std::map map; + std::map::iterator iter; + map = program->get_namespace_annotations("xml"); + if ((iter = map.find("targetNamespace")) != map.end()) { + return iter->second; + } + map = program->get_namespaces(); + if ((iter = map.find("xml")) != map.end()) { + return default_ns_prefix + iter->second; + } + map = program->get_namespace_annotations("*"); + if ((iter = map.find("xml.targetNamespace")) != map.end()) { + return iter->second; + } + map = program->get_namespaces(); + if ((iter = map.find("*")) != map.end()) { + return default_ns_prefix + iter->second; + } + return default_ns_prefix + program->get_name(); +} + +void t_xml_generator::write_xml_comment(string msg) { + close_top_element(); + // TODO: indent any EOLs that may occur with msg + // TODO: proper msg escaping needed? + f_xml_ << indent() << "" << endl; + top_element_is_empty = false; +} + +void t_xml_generator::close_top_element() { + if( top_element_is_open) { + top_element_is_open = false; + if (elements_.size() > 0 && top_element_is_empty) { + f_xml_ << ">" << endl; + } + } +} + +void t_xml_generator::write_element_start(string name) { + if (should_use_namespaces_ && !should_use_default_ns_) { + name = "idl:" + name; + } + close_top_element(); + f_xml_ << indent() << "<" << name; + elements_.push(name); + top_element_is_empty = true; + top_element_is_open = true; + indent_up(); +} + +void t_xml_generator::write_element_end() { + indent_down(); + if (top_element_is_empty && top_element_is_open) { + f_xml_ << " />" << endl; + } else { + f_xml_ << indent() << "" << endl; + } + top_element_is_empty = false; + elements_.pop(); +} + +void t_xml_generator::write_attribute(string key, string val) { + f_xml_ << " " << key << "=\"" << escape_xml_string(val) << "\""; +} + +void t_xml_generator::write_int_attribute(string key, int val) { + write_attribute(key, number_to_string(val)); +} + +void t_xml_generator::write_element_string(string name, string val) { + if (should_use_namespaces_ && !should_use_default_ns_) { + name = "idl:" + name; + } + close_top_element(); + top_element_is_empty = false; + f_xml_ << indent() + << "<" << name << ">" << escape_xml_string(val) << "" + << endl; +} + +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) { + case '&': + ss << "&"; + break; + case '"': + ss << """; + break; + case '\'': + ss << "'"; + break; + case '<': + ss << "<"; + break; + case '>': + ss << ">"; + break; + default: + ss << *iter; + break; + } + } + return ss.str(); +} + +void t_xml_generator::close_generator() { + f_xml_.close(); +} + +void t_xml_generator::generate_program() { + + init_generator(); + + write_element_start("idl"); + if (should_use_namespaces_) { + if (should_use_default_ns_) { + write_attribute("xmlns", "http://thrift.apache.org/xml/idl"); + } + write_attribute("xmlns:idl", "http://thrift.apache.org/xml/idl"); + } + + write_xml_comment( xml_autogen_comment()); + + iterate_program(program_); + + write_element_end(); + + close_generator(); + +} + +void t_xml_generator::iterate_program(t_program* program) { + + write_element_start("document"); + write_attribute("name", program->get_name()); + if (should_use_namespaces_) { + const string targetNamespace = target_namespace(program); + write_attribute("targetNamespace", targetNamespace); + write_attribute("xmlns:" + program->get_name(), targetNamespace); + } + write_doc(program); + + const vector includes = program->get_includes(); + vector::const_iterator inc_it; + for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) { + write_element_start("include"); + write_attribute("name", (*inc_it)->get_name()); + write_element_end(); + } + + const map& namespaces = program->get_namespaces(); + map::const_iterator ns_it; + for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) { + write_element_start("namespace"); + write_attribute("name", ns_it->first); + write_attribute("value", ns_it->second); + generate_annotations(program->get_namespace_annotations(ns_it->first)); + write_element_end(); + } + + // TODO: can constants have annotations? + vector consts = program->get_consts(); + vector::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + generate_constant(*c_iter); + } + + vector typedefs = program->get_typedefs(); + vector::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + generate_typedef(*td_iter); + } + + vector enums = program->get_enums(); + vector::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + generate_enum(*en_iter); + } + + 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); + } + } + + vector services = program->get_services(); + vector::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + generate_service(*sv_iter); + } + + write_element_end(); + + if (should_merge_includes_) { + programs_.insert(program->get_name()); + const vector programs = program->get_includes(); + vector::const_iterator prog_it; + for (prog_it = programs.begin(); prog_it != programs.end(); ++prog_it) { + if (!programs_.count((*prog_it)->get_name())) { + iterate_program(*prog_it); + } + } + } + +} + +void t_xml_generator::generate_typedef(t_typedef* ttypedef) { + write_element_start("typedef"); + write_attribute("name", ttypedef->get_name()); + write_doc(ttypedef); + write_type(ttypedef->get_true_type()); + generate_annotations(ttypedef->annotations_); + write_element_end(); + return; +} + +void t_xml_generator::write_type(t_type* ttype) { + const string type = get_type_name(ttype); + write_attribute("type", type); + if (type == "id") { + write_attribute("type-module", ttype->get_program()->get_name()); + write_attribute("type-id", ttype->get_name()); + } else if (type == "list" || type == "set") { + t_type* etype = ((t_list*)ttype)->get_elem_type(); + write_element_start("elemType"); + write_type(etype); + write_element_end(); + } else if (type == "map") { + t_type* ktype = ((t_map*)ttype)->get_key_type(); + write_element_start("keyType"); + write_type(ktype); + write_element_end(); + t_type* vtype = ((t_map*)ttype)->get_val_type(); + write_element_start("valueType"); + write_type(vtype); + write_element_end(); + } +} + +void t_xml_generator::write_doc(t_doc* tdoc) { + if (tdoc->has_doc()) { + string doc = tdoc->get_doc(); + // for some reason there always seems to be a trailing newline on doc + // comments; loop below naively tries to strip off trailing cr/lf + int n = 0; + for (string::reverse_iterator i = doc.rbegin(); i != doc.rend(); i++,n++) { + if (*i != '\n' || *i == '\r') { + if (n > 0) { + doc.erase(doc.length() - n); + } + break; + } + } + write_attribute("doc", doc); + } +} + +void t_xml_generator::generate_annotations( + std::map annotations) { + std::map::iterator iter; + for (iter = annotations.begin(); iter != annotations.end(); ++iter) { + write_element_start("annotation"); + write_attribute("key", iter->first); + write_attribute("value", iter->second); + write_element_end(); + } +} + +void t_xml_generator::generate_constant(t_const* con) { + write_element_start("const"); + write_attribute("name", con->get_name()); + write_doc(con); + write_type(con->get_type()); + write_const_value(con->get_value()); + write_element_end(); +} + +void t_xml_generator::write_const_value(t_const_value* value) { + + switch (value->get_type()) { + + case t_const_value::CV_IDENTIFIER: + case t_const_value::CV_INTEGER: + write_element_number("int", value->get_integer()); + break; + + case t_const_value::CV_DOUBLE: + write_element_number("double", value->get_double()); + break; + + case t_const_value::CV_STRING: + write_element_string("string", value->get_string()); + break; + + case t_const_value::CV_LIST: { + write_element_start("list"); + std::vector list = value->get_list(); + std::vector::iterator lit; + for (lit = list.begin(); lit != list.end(); ++lit) { + write_element_start("entry"); + write_const_value(*lit); + write_element_end(); + } + write_element_end(); + break; + } + + case t_const_value::CV_MAP: { + write_element_start("map"); + std::map map = value->get_map(); + std::map::iterator mit; + for (mit = map.begin(); mit != map.end(); ++mit) { + write_element_start("entry"); + write_element_start("key"); + write_const_value(mit->first); + write_element_end(); + write_element_start("value"); + write_const_value(mit->second); + write_element_end(); + write_element_end(); + } + write_element_end(); + break; + } + + default: + indent_up(); + f_xml_ << indent() << "" << endl; + indent_down(); + break; + } + +} + +void t_xml_generator::generate_enum(t_enum* tenum) { + + write_element_start("enum"); + write_attribute("name", tenum->get_name()); + write_doc(tenum); + + vector values = tenum->get_constants(); + vector::iterator val_iter; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + t_enum_value* val = (*val_iter); + write_element_start("member"); + write_attribute("name", val->get_name()); + write_int_attribute("value", val->get_value()); + write_doc(val); + generate_annotations(val->annotations_); + write_element_end(); + } + + generate_annotations(tenum->annotations_); + + write_element_end(); + +} + +void t_xml_generator::generate_struct(t_struct* tstruct) { + + string tagname = "struct"; + if (tstruct->is_union()) { + tagname = "union"; + } else if (tstruct->is_xception()) { + tagname = "exception"; + } + + write_element_start(tagname); + write_attribute("name", tstruct->get_name()); + write_doc(tstruct); + vector members = tstruct->get_members(); + vector::iterator mem_iter; + for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) { + write_element_start("field"); + generate_field(*mem_iter); + write_element_end(); + } + + generate_annotations(tstruct->annotations_); + + write_element_end(); + +} + +void t_xml_generator::generate_field(t_field* field) { + write_attribute("name", field->get_name()); + write_int_attribute("field-id", field->get_key()); + write_doc(field); + string requiredness; + switch (field->get_req()) { + case t_field::T_REQUIRED: + requiredness = "required"; + break; + case t_field::T_OPTIONAL: + requiredness = "optional"; + break; + default: + requiredness = ""; + break; + } + if (requiredness != "") { + write_attribute("required", requiredness); + } + write_type(field->get_type()); + if (field->get_value()) { + write_element_start("default"); + write_const_value(field->get_value()); + write_element_end(); + } + generate_annotations(field->annotations_); +} + +void t_xml_generator::generate_service(t_service* tservice) { + + write_element_start("service"); + write_attribute("name", tservice->get_name()); + + if (should_use_namespaces_) { + string prog_ns = target_namespace(tservice->get_program()); + if (*prog_ns.rbegin() != '/') { + prog_ns.push_back('/'); + } + const string tns = prog_ns + tservice->get_name(); + write_attribute("targetNamespace", tns); + write_attribute("xmlns:tns", tns); + } + + if (tservice->get_extends()) { + const t_service* extends = tservice->get_extends(); + write_attribute("parent-module", extends->get_program()->get_name()); + write_attribute("parent-id", extends->get_name()); + } + + write_doc(tservice); + + vector functions = tservice->get_functions(); + vector::iterator fn_iter = functions.begin(); + for (; fn_iter != functions.end(); fn_iter++) { + generate_function(*fn_iter); + } + + generate_annotations(tservice->annotations_); + + write_element_end(); + +} + +void t_xml_generator::generate_function(t_function* tfunc) { + + write_element_start("method"); + + write_attribute("name", tfunc->get_name()); + if (tfunc->is_oneway()) { + write_attribute("oneway", "true"); + } + + write_doc(tfunc); + + write_element_start("returns"); + write_type(tfunc->get_returntype()); + write_element_end(); + + vector members = tfunc->get_arglist()->get_members(); + vector::iterator mem_iter = members.begin(); + for (; mem_iter != members.end(); mem_iter++) { + write_element_start("arg"); + generate_field(*mem_iter); + write_element_end(); + } + + vector excepts = tfunc->get_xceptions()->get_members(); + vector::iterator ex_iter = excepts.begin(); + for (; ex_iter != excepts.end(); ex_iter++) { + write_element_start("throws"); + generate_field(*ex_iter); + write_element_end(); + } + + generate_annotations(tfunc->annotations_); + + write_element_end(); + +} + +string t_xml_generator::get_type_name(t_type* ttype) { + if (ttype->is_list()) { + return "list"; + } + if (ttype->is_set()) { + return "set"; + } + if (ttype->is_map()) { + return "map"; + } + if ((ttype->is_enum() )|| + (ttype->is_struct() )|| + (ttype->is_typedef() )|| + (ttype->is_xception())){ + return "id"; + } + if (ttype->is_base_type()) { + t_base_type* tbasetype = (t_base_type*)ttype; + if (tbasetype->is_binary() ) { + return "binary"; + } + return t_base_type::t_base_name(tbasetype->get_base()); + } + return "(unknown)"; +} + +THRIFT_REGISTER_GENERATOR( + xml, + "XML", + " merge: Generate output with included files merged\n" + " no_default_ns: Omit default xmlns and add idl: prefix to all elements\n" + " no_namespaces: Do not add namespace definitions to the XML model\n") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc new file mode 100644 index 00000000..fa51ba0a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/generate/t_xsd_generator.cc @@ -0,0 +1,376 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include "thrift/version.h" +#include "thrift/platform.h" +#include "thrift/generate/t_generator.h" + +using std::map; +using std::ofstream; +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 + +/** + * XSD generator, creates an XSD for the base types etc. + * + */ +class t_xsd_generator : public t_generator { +public: + t_xsd_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; + + /* no options yet */ + for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { + throw "unknown option xsd:" + iter->first; + } + + out_dir_base_ = "gen-xsd"; + } + + virtual ~t_xsd_generator() {} + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + /** + * Program-level generation functions + */ + + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum) { (void)tenum; } + + void generate_service(t_service* tservice); + void generate_struct(t_struct* tstruct); + +private: + void generate_element(std::ostream& out, + std::string name, + t_type* ttype, + t_struct* attrs = NULL, + bool optional = false, + bool nillable = false, + bool list_element = false); + + std::string ns(std::string in, std::string ns) { return ns + ":" + in; } + + std::string xsd(std::string in) { return ns(in, "xsd"); } + + std::string type_name(t_type* ttype); + std::string base_type_name(t_base_type::t_base tbase); + + virtual std::string xml_autogen_comment() { + return std::string("\n"; + } + + /** + * Output xsd/php file + */ + std::ofstream f_xsd_; + std::ofstream f_php_; + + /** + * Output string stream + */ + std::ostringstream s_xsd_types_; +}; + +void t_xsd_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Make output file + string f_php_name = get_out_dir() + program_->get_name() + "_xsd.php"; + f_php_.open(f_php_name.c_str()); + + f_php_ << "" << endl; + f_php_.close(); +} + +void t_xsd_generator::generate_typedef(t_typedef* ttypedef) { + indent(s_xsd_types_) << "get_name() << "\">" << endl; + indent_up(); + if (ttypedef->get_type()->is_string() && ((t_base_type*)ttypedef->get_type())->is_string_enum()) { + indent(s_xsd_types_) << "get_type()) << "\">" + << endl; + indent_up(); + const vector& values = ((t_base_type*)ttypedef->get_type())->get_string_enum_vals(); + vector::const_iterator v_iter; + for (v_iter = values.begin(); v_iter != values.end(); ++v_iter) { + indent(s_xsd_types_) << "" << endl; + } + indent_down(); + indent(s_xsd_types_) << "" << endl; + } else { + indent(s_xsd_types_) << "get_type()) << "\" />" + << endl; + } + indent_down(); + indent(s_xsd_types_) << "" << endl << endl; +} + +void t_xsd_generator::generate_struct(t_struct* tstruct) { + vector::const_iterator m_iter; + const vector& members = tstruct->get_members(); + bool xsd_all = tstruct->get_xsd_all(); + + indent(s_xsd_types_) << "get_name() << "\">" << endl; + indent_up(); + if (xsd_all) { + indent(s_xsd_types_) << "" << endl; + } else { + indent(s_xsd_types_) << "" << endl; + } + indent_up(); + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_element(s_xsd_types_, + (*m_iter)->get_name(), + (*m_iter)->get_type(), + (*m_iter)->get_xsd_attrs(), + (*m_iter)->get_xsd_optional() || xsd_all, + (*m_iter)->get_xsd_nillable()); + } + + indent_down(); + if (xsd_all) { + indent(s_xsd_types_) << "" << endl; + } else { + indent(s_xsd_types_) << "" << endl; + } + indent_down(); + indent(s_xsd_types_) << "" << endl << endl; +} + +void t_xsd_generator::generate_element(ostream& out, + string name, + t_type* ttype, + t_struct* attrs, + bool optional, + bool nillable, + bool list_element) { + string sminOccurs = (optional || list_element) ? " minOccurs=\"0\"" : ""; + string smaxOccurs = list_element ? " maxOccurs=\"unbounded\"" : ""; + string soptional = sminOccurs + smaxOccurs; + string snillable = nillable ? " nillable=\"true\"" : ""; + + if (ttype->is_void() || ttype->is_list()) { + indent(out) << "" << endl; + indent_up(); + if (attrs == NULL && ttype->is_void()) { + indent(out) << "" << endl; + } else { + indent(out) << "" << endl; + indent_up(); + if (ttype->is_list()) { + indent(out) << "" << endl; + indent_up(); + string subname; + t_type* subtype = ((t_list*)ttype)->get_elem_type(); + if (subtype->is_base_type() || subtype->is_container()) { + subname = name + "_elt"; + } else { + subname = type_name(subtype); + } + f_php_ << "$GLOBALS['" << program_->get_name() << "_xsd_elt_" << name << "'] = '" << subname + << "';" << endl; + generate_element(out, subname, subtype, NULL, false, false, true); + indent_down(); + indent(out) << "" << endl; + indent(out) << "" << endl; + } + if (attrs != NULL) { + const vector& members = attrs->get_members(); + vector::const_iterator a_iter; + for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) { + indent(out) << "get_name() << "\" type=\"" + << type_name((*a_iter)->get_type()) << "\" />" << endl; + } + } + indent_down(); + indent(out) << "" << endl; + } + indent_down(); + indent(out) << "" << endl; + } else { + if (attrs == NULL) { + indent(out) << "" + << endl; + } else { + // Wow, all this work for a SIMPLE TYPE with attributes?!?!?! + indent(out) << "" + << endl; + indent_up(); + indent(out) << "" << endl; + indent_up(); + indent(out) << "" << endl; + indent_up(); + indent(out) << "" << endl; + indent_up(); + const vector& members = attrs->get_members(); + vector::const_iterator a_iter; + for (a_iter = members.begin(); a_iter != members.end(); ++a_iter) { + indent(out) << "get_name() << "\" type=\"" + << type_name((*a_iter)->get_type()) << "\" />" << endl; + } + indent_down(); + indent(out) << "" << endl; + indent_down(); + indent(out) << "" << endl; + indent_down(); + indent(out) << "" << endl; + indent_down(); + indent(out) << "" << endl; + } + } +} + + +void t_xsd_generator::generate_service(t_service* tservice) { + // Make output file + string f_xsd_name = get_out_dir() + tservice->get_name() + ".xsd"; + f_xsd_.open(f_xsd_name.c_str()); + + string ns = program_->get_namespace("xsd"); + const std::map annot = program_->get_namespace_annotations("xsd"); + const std::map::const_iterator uri = annot.find("uri"); + if (uri != annot.end()) { + ns = uri->second; + } + if (ns.size() > 0) { + ns = " targetNamespace=\"" + ns + "\" xmlns=\"" + ns + "\" " + + "elementFormDefault=\"qualified\""; + } + + // Print the XSD header + f_xsd_ << "" << endl + << "" << endl + << xml_autogen_comment() + << endl; + + // Print out the type definitions + indent(f_xsd_) << s_xsd_types_.str(); + + // Keep a list of all the possible exceptions that might get thrown + map all_xceptions; + + // List the elements that you might actually get + vector functions = tservice->get_functions(); + vector::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + string elemname = (*f_iter)->get_name() + "_response"; + t_type* returntype = (*f_iter)->get_returntype(); + generate_element(f_xsd_, elemname, returntype); + f_xsd_ << endl; + + 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) { + all_xceptions[(*x_iter)->get_name()] = (t_struct*)((*x_iter)->get_type()); + } + } + + map::iterator ax_iter; + for (ax_iter = all_xceptions.begin(); ax_iter != all_xceptions.end(); ++ax_iter) { + generate_element(f_xsd_, ax_iter->first, ax_iter->second); + } + + // Close the XSD document + f_xsd_ << endl << "" << endl; + f_xsd_.close(); +} + +string t_xsd_generator::type_name(t_type* ttype) { + if (ttype->is_typedef()) { + return ttype->get_name(); + } + + if (ttype->is_base_type()) { + return xsd(base_type_name(((t_base_type*)ttype)->get_base())); + } + + if (ttype->is_enum()) { + return xsd("int"); + } + + if (ttype->is_struct() || ttype->is_xception()) { + return ttype->get_name(); + } + + return "container"; +} + +/** + * Returns the XSD type that corresponds to the thrift type. + * + * @param tbase The base type + * @return Explicit XSD type, i.e. xsd:string + */ +string t_xsd_generator::base_type_name(t_base_type::t_base tbase) { + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + return "string"; + case t_base_type::TYPE_BOOL: + return "boolean"; + case t_base_type::TYPE_I8: + return "byte"; + case t_base_type::TYPE_I16: + return "short"; + case t_base_type::TYPE_I32: + return "int"; + case t_base_type::TYPE_I64: + return "long"; + case t_base_type::TYPE_DOUBLE: + return "decimal"; + default: + throw "compiler error: no XSD base type name for base type " + t_base_type::t_base_name(tbase); + } +} + +THRIFT_REGISTER_GENERATOR(xsd, "XSD", "") diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/globals.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/globals.h new file mode 100644 index 00000000..961c6ef8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/globals.h @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_GLOBALS_H +#define T_GLOBALS_H + +#include +#include +#include +#include +#include + +/** + * This module contains all the global variables (slap on the wrist) that are + * shared throughout the program. The reason for this is to facilitate simple + * interaction between the parser and the rest of the program. Before calling + * yyparse(), the main.cc program will make necessary adjustments to these + * global variables such that the parser does the right thing and puts entries + * into the right containers, etc. + * + */ + +/** + * Hooray for forward declaration of types! + */ + +class t_program; +class t_scope; +class t_type; + +/** + * Parsing mode, two passes up in this gin rummy! + */ + +enum PARSE_MODE { INCLUDES = 1, PROGRAM = 2 }; + +/** + * Strictness level + */ +extern int g_strict; + +/** + * The master program parse tree. This is accessed from within the parser code + * to build up the program elements. + */ +extern t_program* g_program; + +/** + * The scope that we are currently parsing into + */ +extern t_scope* g_scope; + +/** + * The parent scope to also load symbols into + */ +extern t_scope* g_parent_scope; + +/** + * The prefix for the parent scope entries + */ +extern std::string g_parent_prefix; + +/** + * The parsing pass that we are on. We do different things on each pass. + */ +extern PARSE_MODE g_parse_mode; + +/** + * Global time string, used in formatting error messages etc. + */ +extern char* g_time_str; + +/** + * The last parsed doctext comment. + */ +extern char* g_doctext; + +/** + * The location of the last parsed doctext comment. + */ +extern int g_doctext_lineno; + +/** + * Status of program level doctext candidate + */ +enum PROGDOCTEXT_STATUS { + INVALID = 0, + STILL_CANDIDATE = 1, // the text may or may not be the program doctext + ALREADY_PROCESSED = 2, // doctext has been used and is no longer available + ABSOLUTELY_SURE = 3, // this is the program doctext + NO_PROGRAM_DOCTEXT = 4 // there is no program doctext +}; + +/** + * The program level doctext. Stored separately to make parsing easier. + */ +extern char* g_program_doctext_candidate; +extern int g_program_doctext_lineno; +extern PROGDOCTEXT_STATUS g_program_doctext_status; + +/** + * Whether or not negative field keys are accepted. + * + * When a field does not have a user-specified key, thrift automatically + * assigns a negative value. However, this is fragile since changes to the + * file may unintentionally change the key numbering, resulting in a new + * protocol that is not backwards compatible. + * + * When g_allow_neg_field_keys is enabled, users can explicitly specify + * negative keys. This way they can write a .thrift file with explicitly + * specified keys that is still backwards compatible with older .thrift files + * that did not specify key values. + */ +extern int g_allow_neg_field_keys; + +/** + * Whether or not 64-bit constants will generate a warning. + * + * Some languages don't support 64-bit constants, but many do, so we can + * suppress this warning for projects that don't use any non-64-bit-safe + * languages. + */ +extern int g_allow_64bit_consts; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.cc new file mode 100644 index 00000000..f821f5fc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.cc @@ -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. + */ + +/** + * 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; + +void pdebug(const char* fmt, ...) { + if (g_debug == 0) { + return; + } + va_list args; + // printf("[PARSE:%d] ", yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +void pverbose(const char* fmt, ...) { + if (g_verbose == 0) { + return; + } + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +void pwarning(int level, const char* fmt, ...) { + if (g_warn < level) { + return; + } + va_list args; + // printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +void failure(const char* fmt, ...) { + va_list args; + // fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + printf("\n"); + exit(1); +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.h new file mode 100644 index 00000000..ebefbf22 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/logging.h @@ -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. + */ + +#ifndef T_LOGGING_H +#define T_LOGGING_H + +extern int g_debug; +extern int g_warn; +extern int g_verbose; + +/** + * Parse debugging output, used to print helpful info + */ +void pdebug(const char* fmt, ...); + +/** + * Parser warning + */ +void pwarning(int level, const char* fmt, ...); + +/** + * Print verbose output message + */ +void pverbose(const char* fmt, ...); + +/** + * Failure! + */ +void failure(const char* fmt, ...); + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.cc new file mode 100644 index 00000000..84218405 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.cc @@ -0,0 +1,1307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 - a lightweight cross-language rpc/serialization tool + * + * This file contains the main compiler engine for Thrift, which invokes the + * scanner/parser to build the thrift object tree. The interface generation + * code for each language lives in a file by the language name under the + * generate/ folder, and all parse structures live in parse/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include /* for GetFullPathName */ +#endif + +// Careful: must include globals first for extern definitions +#include "thrift/common.h" +#include "thrift/globals.h" + +#include "thrift/platform.h" +#include "thrift/main.h" +#include "thrift/parse/t_program.h" +#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" + +using namespace std; + +/** + * Global program tree + */ +t_program* g_program; + +/** + * Global scope + */ +t_scope* g_scope; + +/** + * Parent scope to also parse types + */ +t_scope* g_parent_scope; + +/** + * Prefix for putting types in parent scope + */ +string g_parent_prefix; + +/** + * Parsing pass + */ +PARSE_MODE g_parse_mode; + +/** + * Current directory of file being parsed + */ +string g_curdir; + +/** + * Current file being parsed + */ +string g_curpath; + +/** + * Search path for inclusions + */ +vector g_incl_searchpath; + +/** + * Global debug state + */ +int g_debug = 0; + +/** + * Strictness level + */ +int g_strict = 127; + +/** + * Warning level + */ +int g_warn = 1; + +/** + * Verbose output + */ +int g_verbose = 0; + +/** + * Global time string + */ +char* g_time_str; + +/** + * The last parsed doctext comment. + */ +char* g_doctext; + +/** + * The First doctext comment + */ +char* g_program_doctext_candidate; + +/** + * Whether or not negative field keys are accepted. + */ +int g_allow_neg_field_keys; + +/** + * Whether or not 64-bit constants will generate a warning. + */ +int g_allow_64bit_consts = 0; + +/** + * Flags to control code generation + */ +bool gen_recurse = false; + +/** + * Flags to control thrift audit + */ +bool g_audit = false; + +/** + * Flag to control return status + */ +bool g_return_failure = false; +bool g_audit_fatal = true; +bool g_generator_failure = false; + +/** + * Win32 doesn't have realpath, so use fallback implementation in that case, + * otherwise this just calls through to realpath + */ +char* saferealpath(const char* path, char* resolved_path) { +#ifdef _WIN32 + char buf[MAX_PATH]; + char* basename; + DWORD len = GetFullPathName(path, MAX_PATH, buf, &basename); + if (len == 0 || len > MAX_PATH - 1) { + strcpy(resolved_path, path); + } else { + strcpy(resolved_path, buf); + } + + // Replace backslashes with forward slashes so the + // rest of the code behaves correctly. + size_t resolved_len = strlen(resolved_path); + for (size_t i = 0; i < resolved_len; i++) { + if (resolved_path[i] == '\\') { + resolved_path[i] = '/'; + } + } + return resolved_path; +#else + return realpath(path, resolved_path); +#endif +} + +bool check_is_directory(const char* dir_name) { +#ifdef _WIN32 + DWORD attributes = ::GetFileAttributesA(dir_name); + if (attributes == INVALID_FILE_ATTRIBUTES) { + fprintf(stderr, + "Output directory %s is unusable: GetLastError() = %ld\n", + dir_name, + GetLastError()); + return false; + } + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) { + fprintf(stderr, "Output directory %s exists but is not a directory\n", dir_name); + return false; + } + return true; +#else + struct stat sb; + if (stat(dir_name, &sb) < 0) { + fprintf(stderr, "Output directory %s is unusable: %s\n", dir_name, strerror(errno)); + return false; + } + if (!S_ISDIR(sb.st_mode)) { + fprintf(stderr, "Output directory %s exists but is not a directory\n", dir_name); + return false; + } + return true; +#endif +} + +/** + * Report an error to the user. This is called yyerror for historical + * reasons (lex and yacc expect the error reporting routine to be called + * this). Call this function to report any errors to the user. + * yyerror takes printf style arguments. + * + * @param fmt C format string followed by additional arguments + */ +void yyerror(const char* fmt, ...) { + va_list args; + fprintf(stderr, "[ERROR:%s:%d] (last token was '%s')\n", g_curpath.c_str(), yylineno, yytext); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +/** + * Prints a debug message from the parser. + * + * @param fmt C format string followed by additional arguments + */ +void pdebug(const char* fmt, ...) { + if (g_debug == 0) { + return; + } + va_list args; + printf("[PARSE:%d] ", yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +/** + * Prints a verbose output mode message + * + * @param fmt C format string followed by additional arguments + */ +void pverbose(const char* fmt, ...) { + if (g_verbose == 0) { + return; + } + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +/** + * Prints a warning message + * + * @param fmt C format string followed by additional arguments + */ +void pwarning(int level, const char* fmt, ...) { + if (g_warn < level) { + return; + } + va_list args; + printf("[WARNING:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +/** + * Prints a failure message and exits + * + * @param fmt C format string followed by additional arguments + */ +void failure(const char* fmt, ...) { + va_list args; + fprintf(stderr, "[FAILURE:%s:%d] ", g_curpath.c_str(), yylineno); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + printf("\n"); + exit(1); +} + +/** + * Converts a string filename into a thrift program name + */ +string program_name(string filename) { + string::size_type slash = filename.rfind("/"); + if (slash != string::npos) { + filename = filename.substr(slash + 1); + } + string::size_type dot = filename.rfind("."); + if (dot != string::npos) { + filename = filename.substr(0, dot); + } + return filename; +} + +/** + * Gets the directory path of a filename + */ +string directory_name(string filename) { + string::size_type slash = filename.rfind("/"); + // No slash, just use the current directory + if (slash == string::npos) { + return "."; + } + return filename.substr(0, slash); +} + +/** + * Finds the appropriate file path for the given filename + */ +string include_file(string filename) { + // Absolute path? Just try that + if (filename[0] == '/') { + // Realpath! + char rp[THRIFT_PATH_MAX]; + // cppcheck-suppress uninitvar + if (saferealpath(filename.c_str(), rp) == NULL) { + pwarning(0, "Cannot open include file %s\n", filename.c_str()); + return std::string(); + } + + // Stat this file + struct stat finfo; + if (stat(rp, &finfo) == 0) { + return rp; + } + } else { // relative path, start searching + // new search path with current dir global + vector sp = g_incl_searchpath; + sp.insert(sp.begin(), g_curdir); + + // iterate through paths + vector::iterator it; + for (it = sp.begin(); it != sp.end(); it++) { + string sfilename = *(it) + "/" + filename; + + // Realpath! + char rp[THRIFT_PATH_MAX]; + // cppcheck-suppress uninitvar + if (saferealpath(sfilename.c_str(), rp) == NULL) { + continue; + } + + // Stat this files + struct stat finfo; + if (stat(rp, &finfo) == 0) { + return rp; + } + } + } + + // Uh oh + pwarning(0, "Could not find include file %s\n", filename.c_str()); + return std::string(); +} + +/** + * Clears any previously stored doctext string. + * Also prints a warning if we are discarding information. + */ +void clear_doctext() { + if (g_doctext != NULL) { + pwarning(2, "Uncaptured doctext at on line %d.", g_doctext_lineno); + } + free(g_doctext); + g_doctext = NULL; +} + +/** + * Reset program doctext information after processing a file + */ +void reset_program_doctext_info() { + if (g_program_doctext_candidate != NULL) { + free(g_program_doctext_candidate); + g_program_doctext_candidate = NULL; + } + g_program_doctext_lineno = 0; + g_program_doctext_status = INVALID; + pdebug("%s", "program doctext set to INVALID"); +} + +/** + * 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)) { + g_program_doctext_status = ABSOLUTELY_SURE; + pdebug("%s", "program doctext set to ABSOLUTELY_SURE"); + } else { + g_program_doctext_status = NO_PROGRAM_DOCTEXT; + pdebug("%s", "program doctext set to NO_PROGRAM_DOCTEXT"); + } +} + +/** + * Cleans up text commonly found in doxygen-like comments + * + * Warning: if you mix tabs and spaces in a non-uniform way, + * you will get what you deserve. + */ +char* clean_up_doctext(char* doctext) { + // Convert to C++ string, and remove Windows's carriage returns. + string docstring = doctext; + docstring.erase(remove(docstring.begin(), docstring.end(), '\r'), docstring.end()); + + // Separate into lines. + vector lines; + string::size_type pos = string::npos; + string::size_type last; + while (true) { + last = (pos == string::npos) ? 0 : pos + 1; + pos = docstring.find('\n', last); + if (pos == string::npos) { + // First bit of cleaning. If the last line is only whitespace, drop it. + string::size_type nonwhite = docstring.find_first_not_of(" \t", last); + if (nonwhite != string::npos) { + lines.push_back(docstring.substr(last)); + } + break; + } + lines.push_back(docstring.substr(last, pos - last)); + } + + // A very profound docstring. + if (lines.empty()) { + return NULL; + } + + // Clear leading whitespace from the first line. + pos = lines.front().find_first_not_of(" \t"); + lines.front().erase(0, pos); + + // If every nonblank line after the first has the same number of spaces/tabs, + // then a star, remove them. + bool have_prefix = true; + bool found_prefix = false; + string::size_type prefix_len = 0; + vector::iterator l_iter; + for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) { + if (l_iter->empty()) { + continue; + } + + pos = l_iter->find_first_not_of(" \t"); + if (!found_prefix) { + if (pos != string::npos) { + if (l_iter->at(pos) == '*') { + found_prefix = true; + prefix_len = pos; + } else { + have_prefix = false; + break; + } + } else { + // Whitespace-only line. Truncate it. + l_iter->clear(); + } + } else if (l_iter->size() > pos && l_iter->at(pos) == '*' && pos == prefix_len) { + // Business as usual. + } else if (pos == string::npos) { + // Whitespace-only line. Let's truncate it for them. + l_iter->clear(); + } else { + // The pattern has been broken. + have_prefix = false; + break; + } + } + + // If our prefix survived, delete it from every line. + if (have_prefix) { + // Get the star too. + prefix_len++; + for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) { + l_iter->erase(0, prefix_len); + } + } + + // Now delete the minimum amount of leading whitespace from each line. + prefix_len = string::npos; + for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) { + if (l_iter->empty()) { + continue; + } + pos = l_iter->find_first_not_of(" \t"); + if (pos != string::npos && (prefix_len == string::npos || pos < prefix_len)) { + prefix_len = pos; + } + } + + // If our prefix survived, delete it from every line. + if (prefix_len != string::npos) { + for (l_iter = lines.begin() + 1; l_iter != lines.end(); ++l_iter) { + l_iter->erase(0, prefix_len); + } + } + + // Remove trailing whitespace from every line. + for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) { + pos = l_iter->find_last_not_of(" \t"); + if (pos != string::npos && pos != l_iter->length() - 1) { + l_iter->erase(pos + 1); + } + } + + // If the first line is empty, remove it. + // Don't do this earlier because a lot of steps skip the first line. + if (lines.front().empty()) { + lines.erase(lines.begin()); + } + + // Now rejoin the lines and copy them back into doctext. + docstring.clear(); + for (l_iter = lines.begin(); l_iter != lines.end(); ++l_iter) { + docstring += *l_iter; + docstring += '\n'; + } + + // assert(docstring.length() <= strlen(doctext)); may happen, see THRIFT-1755 + if (docstring.length() <= strlen(doctext)) { + strcpy(doctext, docstring.c_str()); + } else { + free(doctext); // too short + doctext = strdup(docstring.c_str()); + } + return doctext; +} + +/** Set to true to debug docstring parsing */ +static bool dump_docs = false; + +/** + * Dumps docstrings to stdout + * Only works for top-level definitions and the whole program doc + * (i.e., not enum constants, struct fields, or functions. + */ +void dump_docstrings(t_program* program) { + string progdoc = program->get_doc(); + if (!progdoc.empty()) { + printf("Whole program doc:\n%s\n", progdoc.c_str()); + } + const vector& typedefs = program->get_typedefs(); + vector::const_iterator t_iter; + for (t_iter = typedefs.begin(); t_iter != typedefs.end(); ++t_iter) { + t_typedef* td = *t_iter; + if (td->has_doc()) { + printf("typedef %s:\n%s\n", td->get_name().c_str(), td->get_doc().c_str()); + } + } + const vector& enums = program->get_enums(); + vector::const_iterator e_iter; + for (e_iter = enums.begin(); e_iter != enums.end(); ++e_iter) { + t_enum* en = *e_iter; + if (en->has_doc()) { + printf("enum %s:\n%s\n", en->get_name().c_str(), en->get_doc().c_str()); + } + } + const vector& consts = program->get_consts(); + vector::const_iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + t_const* co = *c_iter; + if (co->has_doc()) { + printf("const %s:\n%s\n", co->get_name().c_str(), co->get_doc().c_str()); + } + } + const vector& structs = program->get_structs(); + vector::const_iterator s_iter; + for (s_iter = structs.begin(); s_iter != structs.end(); ++s_iter) { + t_struct* st = *s_iter; + if (st->has_doc()) { + printf("struct %s:\n%s\n", st->get_name().c_str(), st->get_doc().c_str()); + } + } + const vector& xceptions = program->get_xceptions(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + t_struct* xn = *x_iter; + if (xn->has_doc()) { + printf("xception %s:\n%s\n", xn->get_name().c_str(), xn->get_doc().c_str()); + } + } + const vector& services = program->get_services(); + vector::const_iterator v_iter; + for (v_iter = services.begin(); v_iter != services.end(); ++v_iter) { + t_service* sv = *v_iter; + if (sv->has_doc()) { + printf("service %s:\n%s\n", sv->get_name().c_str(), sv->get_doc().c_str()); + } + } +} + +/** + * 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()) { + 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\"."); + } + } +} + +static bool g_byte_warning_emitted = false; + +/** + * Emits a one-time warning on byte type, promoting the new i8 type instead + */ +void emit_byte_type_warning() { + if (!g_byte_warning_emitted) { + pwarning(1, + "The \"byte\" type is a compatibility alias for \"i8\". Use \"i8\" to emphasize the " + "signedness of this type.\n"); + g_byte_warning_emitted = true; + } +} + +/** + * 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 + */ +void error_unsupported_namespace_decl(const char* old_form, const char* new_form) { + const char* remainder = ""; + if( new_form == NULL) { + new_form = old_form; + remainder = "_namespace"; + } + failure("Unsupported declaration '%s%s'. Use 'namespace %s' instead.", old_form, remainder, new_form); +} + +/** + * Prints the version number + */ +void version() { + printf("Thrift version %s\n", THRIFT_VERSION); +} + +/** + * Display the usage message and then exit with an error code. + */ +void usage() { + fprintf(stderr, "Usage: thrift [options] file\n\n"); + fprintf(stderr, "Use thrift -help for a list of options\n"); + exit(1); +} + +/** + * Diplays the help message and then exits with an error code. + */ +void help() { + fprintf(stderr, "Usage: thrift [options] file\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -version Print the compiler version\n"); + fprintf(stderr, " -o dir Set the output directory for gen-* packages\n"); + fprintf(stderr, " (default: current directory)\n"); + fprintf(stderr, " -out dir Set the ouput location for generated files.\n"); + fprintf(stderr, " (no gen-* folder will be created)\n"); + fprintf(stderr, " -I dir Add a directory to the list of directories\n"); + fprintf(stderr, " searched for include directives\n"); + fprintf(stderr, " -nowarn Suppress all compiler warnings (BAD!)\n"); + fprintf(stderr, " -strict Strict compiler warnings on\n"); + fprintf(stderr, " -v[erbose] Verbose mode\n"); + fprintf(stderr, " -r[ecurse] Also generate included files\n"); + fprintf(stderr, " -debug Parse debug trace to stdout\n"); + fprintf(stderr, + " --allow-neg-keys Allow negative field keys (Used to " + "preserve protocol\n"); + fprintf(stderr, " compatibility with older .thrift files)\n"); + fprintf(stderr, " --allow-64bit-consts Do not print warnings about using 64-bit constants\n"); + fprintf(stderr, " --gen STR Generate code with a dynamically-registered generator.\n"); + fprintf(stderr, " STR has the form language[:key1=val1[,key2[,key3=val3]]].\n"); + fprintf(stderr, " Keys and values are options passed to the generator.\n"); + fprintf(stderr, " Many options will not require values.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Options related to audit operation\n"); + fprintf(stderr, " --audit OldFile Old Thrift file to be audited with 'file'\n"); + fprintf(stderr, " -Iold dir Add a directory to the list of directories\n"); + fprintf(stderr, " searched for include directives for old thrift file\n"); + fprintf(stderr, " -Inew dir Add a directory to the list of directories\n"); + fprintf(stderr, " searched for include directives for new thrift file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Available generators (and options):\n"); + + t_generator_registry::gen_map_t gen_map = t_generator_registry::get_generator_map(); + t_generator_registry::gen_map_t::iterator iter; + for (iter = gen_map.begin(); iter != gen_map.end(); ++iter) { + fprintf(stderr, + " %s (%s):\n", + iter->second->get_short_name().c_str(), + iter->second->get_long_name().c_str()); + fprintf(stderr, "%s", iter->second->get_documentation().c_str()); + } + exit(1); +} + +/** + * You know, when I started working on Thrift I really thought it wasn't going + * to become a programming language because it was just a generator and it + * wouldn't need runtime type information and all that jazz. But then we + * decided to add constants, and all of a sudden that means runtime type + * validation and inference, except the "runtime" is the code generator + * runtime. + */ +void validate_const_rec(std::string name, t_type* type, t_const_value* value) { + if (type->is_void()) { + throw "type error: cannot declare a void const: " + 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_STRING: + if (value->get_type() != t_const_value::CV_STRING) { + throw "type error: const \"" + name + "\" was declared as string"; + } + break; + case t_base_type::TYPE_BOOL: + if (value->get_type() != t_const_value::CV_INTEGER) { + throw "type error: const \"" + name + "\" was declared as bool"; + } + break; + case t_base_type::TYPE_I8: + if (value->get_type() != t_const_value::CV_INTEGER) { + throw "type error: const \"" + name + "\" was declared as byte"; + } + break; + case t_base_type::TYPE_I16: + if (value->get_type() != t_const_value::CV_INTEGER) { + throw "type error: const \"" + name + "\" was declared as i16"; + } + break; + case t_base_type::TYPE_I32: + if (value->get_type() != t_const_value::CV_INTEGER) { + throw "type error: const \"" + name + "\" was declared as i32"; + } + break; + case t_base_type::TYPE_I64: + if (value->get_type() != t_const_value::CV_INTEGER) { + throw "type error: const \"" + name + "\" was declared as i64"; + } + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() != t_const_value::CV_INTEGER + && value->get_type() != t_const_value::CV_DOUBLE) { + throw "type error: const \"" + name + "\" was declared as double"; + } + break; + default: + throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase) + name; + } + } else if (type->is_enum()) { + if (value->get_type() != t_const_value::CV_IDENTIFIER) { + throw "type error: const \"" + name + "\" was declared as enum"; + } + + // see if there's a dot in the identifier + std::string name_portion = value->get_identifier_name(); + + const vector& enum_values = ((t_enum*)type)->get_constants(); + vector::const_iterator c_iter; + bool found = false; + + for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { + if ((*c_iter)->get_name() == name_portion) { + found = true; + break; + } + } + if (!found) { + throw "type error: const " + name + " was declared as type " + type->get_name() + + " which is an enum, but " + value->get_identifier() + + " is not a valid value for that enum"; + } + } else if (type->is_struct() || type->is_xception()) { + if (value->get_type() != t_const_value::CV_MAP) { + throw "type error: const \"" + name + "\" was declared as struct/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; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + 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; + 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(); + } + + validate_const_rec(name + "." + v_iter->first->get_string(), field_type, v_iter->second); + } + } else if (type->is_map()) { + t_type* k_type = ((t_map*)type)->get_key_type(); + t_type* v_type = ((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) { + validate_const_rec(name + "", k_type, v_iter->first); + validate_const_rec(name + "", v_type, v_iter->second); + } + } else if (type->is_list() || type->is_set()) { + t_type* e_type; + if (type->is_list()) { + e_type = ((t_list*)type)->get_elem_type(); + } else { + e_type = ((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) { + validate_const_rec(name + "", e_type, *v_iter); + } + } +} + +/** + * Check simple identifier names + * It's easier to do it this way instead of rewriting the whole grammar etc. + */ +void validate_simple_identifier(const char* identifier) { + string name(identifier); + if (name.find(".") != string::npos) { + yyerror("Identifier %s can't have a dot.", identifier); + exit(1); + } +} + +/** + * Check the type of the parsed const information against its declared type + */ +void validate_const_type(t_const* c) { + validate_const_rec(c->get_name(), c->get_type(), c->get_value()); +} + +/** + * Check the type of a default value assigned to a field. + */ +void validate_field_value(t_field* field, t_const_value* cv) { + validate_const_rec(field->get_name(), field->get_type(), cv); +} + +/** + * Check that all the elements of a throws block are actually exceptions. + */ +bool validate_throws(t_struct* throws) { + const vector& members = throws->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (!t_generator::get_true_type((*m_iter)->get_type())->is_xception()) { + return false; + } + } + return true; +} + +/** + * Skips UTF-8 BOM if there is one + */ +bool skip_utf8_bom(FILE* f) { + + // pretty straightforward, but works + if (fgetc(f) == 0xEF) { + if (fgetc(f) == 0xBB) { + if (fgetc(f) == 0xBF) { + return true; + } + } + } + + rewind(f); + return false; +} + +/** + * Parses a program + */ +void parse(t_program* program, t_program* parent_program) { + // Get scope file path + string path = program->get_path(); + + // Set current dir global, which is used in the include_file function + g_curdir = directory_name(path); + g_curpath = path; + + // Open the file + // skip UTF-8 BOM if there is one + yyin = fopen(path.c_str(), "r"); + if (yyin == 0) { + failure("Could not open input file: \"%s\"", path.c_str()); + } + if (skip_utf8_bom(yyin)) + pverbose("Skipped UTF-8 BOM at %s\n", path.c_str()); + + // Create new scope and scan for includes + pverbose("Scanning %s for includes\n", path.c_str()); + g_parse_mode = INCLUDES; + g_program = program; + g_scope = program->scope(); + try { + yylineno = 1; + if (yyparse() != 0) { + failure("Parser error during include pass."); + } + } catch (string x) { + failure(x.c_str()); + } + fclose(yyin); + + // Recursively parse all the include programs + vector& includes = program->get_includes(); + vector::iterator iter; + for (iter = includes.begin(); iter != includes.end(); ++iter) { + parse(*iter, program); + } + + // reset program doctext status before parsing a new file + reset_program_doctext_info(); + + // Parse the program file + g_parse_mode = PROGRAM; + g_program = program; + g_scope = program->scope(); + g_parent_scope = (parent_program != NULL) ? parent_program->scope() : NULL; + g_parent_prefix = program->get_name() + "."; + g_curpath = path; + + // Open the file + // skip UTF-8 BOM if there is one + yyin = fopen(path.c_str(), "r"); + if (yyin == 0) { + failure("Could not open input file: \"%s\"", path.c_str()); + } + if (skip_utf8_bom(yyin)) + pverbose("Skipped UTF-8 BOM at %s\n", path.c_str()); + + pverbose("Parsing %s for types\n", path.c_str()); + yylineno = 1; + try { + if (yyparse() != 0) { + failure("Parser error during types pass."); + } + } catch (string x) { + failure(x.c_str()); + } + fclose(yyin); +} + +/** + * Generate code + */ +void generate(t_program* program, const vector& generator_strings) { + // Oooohh, recursive code generation, hot!! + if (gen_recurse) { + const vector& includes = program->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + // Propagate output path from parent to child programs + includes[i]->set_out_path(program->get_out_path(), program->is_out_path_absolute()); + + generate(includes[i], generator_strings); + } + } + + // Generate code! + try { + pverbose("Program: %s\n", program->get_path().c_str()); + + if (dump_docs) { + dump_docstrings(program); + } + + vector::const_iterator iter; + 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 + pwarning(1, "Unable to get a generator for \"%s\".\n", iter->c_str()); + g_generator_failure = true; +#endif + } else if (generator) { + pverbose("Generating \"%s\"\n", iter->c_str()); + generator->generate_program(); + delete generator; + } + } + } catch (string s) { + failure("Error: %s\n", s.c_str()); + } catch (const char* exc) { + failure("Error: %s\n", exc); + } +} + +void audit(t_program* new_program, + t_program* old_program, + string new_thrift_include_path, + string old_thrift_include_path) { + vector temp_incl_searchpath = g_incl_searchpath; + if (!old_thrift_include_path.empty()) { + g_incl_searchpath.push_back(old_thrift_include_path); + } + + parse(old_program, NULL); + + 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); + + compare_namespace(new_program, old_program); + compare_services(new_program->get_services(), old_program->get_services()); + compare_enums(new_program->get_enums(), old_program->get_enums()); + compare_structs(new_program->get_structs(), old_program->get_structs()); + compare_structs(new_program->get_xceptions(), old_program->get_xceptions()); + compare_consts(new_program->get_consts(), old_program->get_consts()); +} + +/** + * Parse it up.. then spit it back out, in pretty much every language. Alright + * not that many languages, but the cool ones that we care about. + */ +int main(int argc, char** argv) { + int i; + std::string out_path; + bool out_path_is_absolute = false; + + // Setup time string + time_t now = time(NULL); + g_time_str = ctime(&now); + + // Check for necessary arguments, you gotta have at least a filename and + // an output language flag + if (argc < 2) { + usage(); + } + + vector generator_strings; + string old_thrift_include_path; + string new_thrift_include_path; + string old_input_file; + + // Set the current path to a dummy value to make warning messages clearer. + g_curpath = "arguments"; + + // Hacky parameter handling... I didn't feel like using a library sorry! + for (i = 1; i < argc - 1; i++) { + char* arg; + + arg = strtok(argv[i], " "); + while (arg != NULL) { + // Treat double dashes as single dashes + if (arg[0] == '-' && arg[1] == '-') { + ++arg; + } + + if (strcmp(arg, "-help") == 0) { + help(); + } else if (strcmp(arg, "-version") == 0) { + version(); + exit(0); + } else if (strcmp(arg, "-debug") == 0) { + g_debug = 1; + } else if (strcmp(arg, "-nowarn") == 0) { + g_warn = 0; + } else if (strcmp(arg, "-strict") == 0) { + g_strict = 255; + g_warn = 2; + } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "-verbose") == 0) { + g_verbose = 1; + } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0) { + gen_recurse = true; + } else if (strcmp(arg, "-allow-neg-keys") == 0) { + g_allow_neg_field_keys = true; + } else if (strcmp(arg, "-allow-64bit-consts") == 0) { + g_allow_64bit_consts = true; + } else if (strcmp(arg, "-gen") == 0) { + arg = argv[++i]; + if (arg == NULL) { + fprintf(stderr, "Missing generator specification\n"); + usage(); + } + generator_strings.push_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) { + fprintf(stderr, "Missing Include directory\n"); + usage(); + } + g_incl_searchpath.push_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) { + fprintf(stderr, "-o: missing output directory\n"); + usage(); + } + out_path = arg; + +#ifdef _WIN32 + // strip out trailing \ on Windows + std::string::size_type last = out_path.length() - 1; + if (out_path[last] == '\\') { + out_path.erase(last); + } +#endif + if (!check_is_directory(out_path.c_str())) + return -1; + } else if (strcmp(arg, "-audit") == 0) { + g_audit = true; + arg = argv[++i]; + if (arg == NULL) { + 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) { + failure("Could not open input file with realpath: %s", arg); + } + old_input_file = string(old_thrift_file_rp); + } else if (strcmp(arg, "-audit-nofatal") == 0) { + g_audit_fatal = false; + } else if (strcmp(arg, "-Iold") == 0) { + arg = argv[++i]; + if (arg == NULL) { + 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) { + fprintf(stderr, "Missing Include directory for new thrift file\n"); + usage(); + } + new_thrift_include_path = string(arg); + } else { + fprintf(stderr, "Unrecognized option: %s\n", arg); + usage(); + } + + // Tokenize more + arg = strtok(NULL, " "); + } + } + + // display help + if ((strcmp(argv[argc - 1], "-help") == 0) || (strcmp(argv[argc - 1], "--help") == 0)) { + help(); + } + + // if you're asking for version, you have a right not to pass a file + if ((strcmp(argv[argc - 1], "-version") == 0) || (strcmp(argv[argc - 1], "--version") == 0)) { + version(); + exit(0); + } + + // Initialize global types + initGlobals(); + + if (g_audit) { + // Audit operation + + if (old_input_file.empty()) { + fprintf(stderr, "Missing file name of old thrift file for audit\n"); + usage(); + } + + char new_thrift_file_rp[THRIFT_PATH_MAX]; + if (argv[i] == NULL) { + 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) { + failure("Could not open input file with realpath: %s", argv[i]); + } + string new_input_file(new_thrift_file_rp); + + t_program new_program(new_input_file); + t_program old_program(old_input_file); + + audit(&new_program, &old_program, new_thrift_include_path, old_thrift_include_path); + + } else { + // Generate options + + // You gotta generate something! + if (generator_strings.empty()) { + fprintf(stderr, "No output language(s) specified\n"); + usage(); + } + + // Real-pathify it + char rp[THRIFT_PATH_MAX]; + if (argv[i] == NULL) { + fprintf(stderr, "Missing file name\n"); + usage(); + } + // cppcheck-suppress uninitvar + if (saferealpath(argv[i], rp) == NULL) { + failure("Could not open input file with realpath: %s", argv[i]); + } + string input_file(rp); + + // Instance of the global parse tree + t_program* program = new t_program(input_file); + if (out_path.size()) { + program->set_out_path(out_path, out_path_is_absolute); + } + + // Compute the cpp include prefix. + // infer this from the filename passed in + string input_filename = argv[i]; + string include_prefix; + + string::size_type last_slash = string::npos; + if ((last_slash = input_filename.rfind("/")) != string::npos) { + include_prefix = input_filename.substr(0, last_slash); + } + + program->set_include_prefix(include_prefix); + + // Parse it! + parse(program, NULL); + + // The current path is not really relevant when we are doing generation. + // Reset the variable to make warning messages clearer. + g_curpath = "generation"; + // Reset yylineno for the heck of it. Use 1 instead of 0 because + // That is what shows up during argument parsing. + yylineno = 1; + + // Generate it! + generate(program, generator_strings); + delete program; + } + + // Clean up. Who am I kidding... this program probably orphans heap memory + // all over the place, but who cares because it is about to exit and it is + // all referenced and used by this wacky parse tree up until now anyways. + clearGlobals(); + + // Finished + if (g_return_failure && g_audit_fatal) { + exit(2); + } + if (g_generator_failure) { + exit(3); + } + // Finished + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.h new file mode 100644 index 00000000..54abb03c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/main.h @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_MAIN_H +#define T_MAIN_H + +#include +#include + +#include "thrift/logging.h" + +#include "thrift/parse/t_const.h" +#include "thrift/parse/t_field.h" + +/** + * Defined in the flex library + */ + +extern "C" { int yylex(void); } + +int yyparse(void); + +/** + * Expected to be defined by Flex/Bison + */ +void yyerror(const char* fmt, ...); + +/** + * Check simple identifier names + */ +void validate_simple_identifier(const char* identifier); + +/** + * Check constant types + */ +void validate_const_type(t_const* c); + +/** + * Check constant types + */ +void validate_field_value(t_field* field, t_const_value* cv); + +/** + * Check members of a throws block + */ +bool validate_throws(t_struct* throws); + +/** + * Converts a string filename into a thrift program name + */ +std::string program_name(std::string filename); + +/** + * Gets the directory path of a filename + */ +std::string directory_name(std::string filename); + +/** + * Get the absolute path for an include file + */ +std::string include_file(std::string filename); + +/** + * Clears any previously stored doctext string. + */ +void clear_doctext(); + +/** + * Cleans up text commonly found in doxygen-like comments + */ +char* clean_up_doctext(char* doctext); + +/** + * We are sure the program doctext candidate is really the program doctext. + */ +void declare_valid_program_doctext(); + +/** + * Emits a warning on list, binary type is typically a much better choice. + */ +void check_for_list_of_bytes(t_type* list_elem_type); + +/** + * Emits a one-time warning on byte type, promoting the new i8 type instead + */ +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 + */ +void error_unsupported_namespace_decl(const char* old_form, const char* new_form = NULL); + +/** + * Flex utilities + */ + +extern int yylineno; +extern char yytext[]; +extern std::FILE* yyin; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/parse.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/parse.cc new file mode 100644 index 00000000..01f76375 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/parse.cc @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "thrift/parse/t_type.h" +#include "thrift/parse/t_typedef.h" + +#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; +} + +const t_type* t_type::get_true_type() const { + const t_type* type = this; + while (type->is_typedef()) { + type = ((t_typedef*)type)->get_type(); + } + return type; +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_base_type.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_base_type.h new file mode 100644 index 00000000..32523cb9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_base_type.h @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_BASE_TYPE_H +#define T_BASE_TYPE_H + +#include +#include "thrift/parse/t_type.h" + +/** + * A thrift base type, which must be one of the defined enumerated types inside + * this definition. + * + */ +class t_base_type : public t_type { +public: + /** + * Enumeration of thrift base types + */ + enum t_base { + TYPE_VOID, + TYPE_STRING, + TYPE_BOOL, + TYPE_I8, + TYPE_I16, + TYPE_I32, + TYPE_I64, + TYPE_DOUBLE + }; + + t_base_type(std::string name, t_base base) + : t_type(name), base_(base), string_list_(false), binary_(false), string_enum_(false) {} + + t_base get_base() const { return base_; } + + bool is_void() const { return base_ == TYPE_VOID; } + + bool is_string() const { return base_ == TYPE_STRING; } + + bool is_bool() const { return base_ == TYPE_BOOL; } + + void set_string_list(bool val) { string_list_ = val; } + + bool is_string_list() const { return (base_ == TYPE_STRING) && string_list_; } + + void set_binary(bool val) { binary_ = val; } + + bool is_binary() const { return (base_ == TYPE_STRING) && binary_; } + + void set_string_enum(bool val) { string_enum_ = val; } + + bool is_string_enum() const { return base_ == TYPE_STRING && string_enum_; } + + void add_string_enum_val(std::string val) { string_enum_vals_.push_back(val); } + + const std::vector& get_string_enum_vals() const { return string_enum_vals_; } + + bool is_base_type() const { return true; } + + static std::string t_base_name(t_base tbase) { + switch (tbase) { + case TYPE_VOID: + return "void"; + break; + case TYPE_STRING: + return "string"; + break; + case TYPE_BOOL: + return "bool"; + break; + case TYPE_I8: + return "i8"; + break; + case TYPE_I16: + return "i16"; + break; + case TYPE_I32: + return "i32"; + break; + case TYPE_I64: + return "i64"; + break; + case TYPE_DOUBLE: + return "double"; + break; + default: + return "(unknown)"; + break; + } + } + +private: + t_base base_; + + bool string_list_; + bool binary_; + bool string_enum_; + std::vector string_enum_vals_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const.h new file mode 100644 index 00000000..3c08e0de --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_CONST_H +#define T_CONST_H + +#include "thrift/parse/t_type.h" +#include "thrift/parse/t_const_value.h" + +/** + * A const is a constant value defined across languages that has a type and + * a value. The trick here is that the declared type might not match the type + * of the value object, since that is not determined until after parsing the + * whole thing out. + * + */ +class t_const : public t_doc { +public: + t_const(t_type* type, std::string name, t_const_value* value) + : type_(type), name_(name), value_(value) {} + + t_type* get_type() const { return type_; } + + std::string get_name() const { return name_; } + + t_const_value* get_value() const { return value_; } + +private: + t_type* type_; + std::string name_; + t_const_value* value_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const_value.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const_value.h new file mode 100644 index 00000000..55078032 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_const_value.h @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_CONST_VALUE_H +#define T_CONST_VALUE_H + +#include "thrift/parse/t_enum.h" +#include +#include +#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. + * + */ +class t_const_value { +public: + enum t_const_value_type { CV_INTEGER, CV_DOUBLE, CV_STRING, CV_MAP, CV_LIST, CV_IDENTIFIER }; + + t_const_value() {} + + t_const_value(int64_t val) { set_integer(val); } + + t_const_value(std::string val) { set_string(val); } + + void set_string(std::string val) { + valType_ = CV_STRING; + stringVal_ = val; + } + + std::string get_string() const { return stringVal_; } + + void set_integer(int64_t val) { + valType_ = CV_INTEGER; + intVal_ = val; + } + + int64_t get_integer() const { + if (valType_ == CV_IDENTIFIER) { + if (enum_ == NULL) { + throw "have identifier \"" + get_identifier() + "\", but unset enum on line!"; + } + std::string identifier = get_identifier(); + std::string::size_type dot = identifier.rfind('.'); + if (dot != std::string::npos) { + identifier = identifier.substr(dot + 1); + } + t_enum_value* val = enum_->get_constant_by_name(identifier); + if (val == NULL) { + throw "Unable to find enum value \"" + identifier + "\" in enum \"" + enum_->get_name() + + "\""; + } + return val->get_value(); + } else { + return intVal_; + } + } + + void set_double(double val) { + valType_ = CV_DOUBLE; + doubleVal_ = val; + } + + double get_double() const { return doubleVal_; } + + void set_map() { valType_ = CV_MAP; } + + void add_map(t_const_value* key, t_const_value* val) { mapVal_[key] = val; } + + const std::map& get_map() const { return mapVal_; } + + void set_list() { valType_ = CV_LIST; } + + void add_list(t_const_value* val) { listVal_.push_back(val); } + + const std::vector& get_list() const { return listVal_; } + + void set_identifier(std::string val) { + valType_ = CV_IDENTIFIER; + identifierVal_ = val; + } + + std::string get_identifier() const { return identifierVal_; } + + std::string get_identifier_name() const { + std::string ret = get_identifier(); + size_t s = ret.find('.'); + if (s == std::string::npos) { + throw "error: identifier " + ret + " is unqualified!"; + } + ret = ret.substr(s + 1); + s = ret.find('.'); + if (s != std::string::npos) { + ret = ret.substr(s + 1); + } + return ret; + } + + std::string get_identifier_with_parent() const { + std::string ret = get_identifier(); + size_t s = ret.find('.'); + if (s == std::string::npos) { + throw "error: identifier " + ret + " is unqualified!"; + } + size_t s2 = ret.find('.', s + 1); + if (s2 != std::string::npos) { + ret = ret.substr(s + 1); + } + return ret; + } + + void set_enum(t_enum* tenum) { enum_ = tenum; } + + t_const_value_type get_type() const { return valType_; } + +private: + std::map mapVal_; + std::vector listVal_; + std::string stringVal_; + int64_t intVal_; + double doubleVal_; + std::string identifierVal_; + t_enum* enum_; + + t_const_value_type valType_; + + // to read enum_ + template + friend void plugin_output::convert(From*, To&); +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_container.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_container.h new file mode 100644 index 00000000..5bdab70a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_container.h @@ -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. + */ + +#ifndef T_CONTAINER_H +#define T_CONTAINER_H + +#include "thrift/parse/t_type.h" + +class t_container : public t_type { +public: + t_container() : cpp_name_(), has_cpp_name_(false) {} + + virtual ~t_container() {} + + void set_cpp_name(std::string cpp_name) { + cpp_name_ = cpp_name; + has_cpp_name_ = true; + } + + bool has_cpp_name() const { return has_cpp_name_; } + + std::string get_cpp_name() const { return cpp_name_; } + + bool is_container() const { return true; } + +private: + std::string cpp_name_; + bool has_cpp_name_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_doc.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_doc.h new file mode 100644 index 00000000..7bcb8f5e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_doc.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 T_DOC_H +#define T_DOC_H + +#include "thrift/globals.h" +#include "thrift/logging.h" + +/** + * Documentation stubs + * + */ +class t_doc { + +public: + t_doc() : has_doc_(false) {} + virtual ~t_doc() {} + + void set_doc(const std::string& doc) { + doc_ = doc; + has_doc_ = true; + if ((g_program_doctext_lineno == g_doctext_lineno) + && (g_program_doctext_status == STILL_CANDIDATE)) { + g_program_doctext_status = ALREADY_PROCESSED; + pdebug("%s", "program doctext set to ALREADY_PROCESSED"); + } + } + + const std::string& get_doc() const { return doc_; } + + bool has_doc() { return has_doc_; } + +private: + std::string doc_; + bool has_doc_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum.h new file mode 100644 index 00000000..9e23780c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum.h @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_ENUM_H +#define T_ENUM_H + +#include + +#include "thrift/parse/t_enum_value.h" +#include "thrift/parse/t_type.h" + +/** + * An enumerated type. A list of constant objects with a name for the type. + * + */ +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 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) { + 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) { + if ((*c_iter)->get_name() == name) { + return *c_iter; + } + } + return NULL; + } + + t_enum_value* get_constant_by_value(int64_t value) { + 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) { + if ((*c_iter)->get_value() == value) { + return *c_iter; + } + } + return NULL; + } + + t_enum_value* get_min_value() { + 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; + } else { + int min_value_value; + min_value = enum_values.front(); + min_value_value = min_value->get_value(); + for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { + if ((*c_iter)->get_value() < min_value_value) { + min_value = (*c_iter); + min_value_value = min_value->get_value(); + } + } + } + return min_value; + } + + t_enum_value* get_max_value() { + 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; + } else { + int max_value_value; + max_value = enum_values.back(); + max_value_value = max_value->get_value(); + for (c_iter = enum_values.begin(); c_iter != enum_values.end(); ++c_iter) { + if ((*c_iter)->get_value() > max_value_value) { + max_value = (*c_iter); + max_value_value = max_value->get_value(); + } + } + } + return max_value; + } + + bool is_enum() const { return true; } + +private: + std::vector constants_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h new file mode 100644 index 00000000..c0bf3adf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_enum_value.h @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_ENUM_VALUE_H +#define T_ENUM_VALUE_H + +#include +#include +#include "thrift/parse/t_doc.h" + +/** + * A constant. These are used inside of enum definitions. Constants are just + * symbol identifiers that may or may not have an explicit value associated + * with them. + * + */ +class t_enum_value : public t_doc { +public: + t_enum_value(std::string name, int value) : name_(name), value_(value) {} + + ~t_enum_value() {} + + const std::string& get_name() const { return name_; } + + int get_value() const { return value_; } + + std::map annotations_; + +private: + std::string name_; + int value_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_field.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_field.h new file mode 100644 index 00000000..c5f1f800 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_field.h @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_FIELD_H +#define T_FIELD_H + +#include +#include +#include + +#include "thrift/parse/t_doc.h" +#include "thrift/parse/t_type.h" + +// Forward declare for xsd_attrs +class t_struct; + +/** + * Class to represent a field in a thrift structure. A field has a data type, + * a symbolic name, and a numeric identifier. + * + */ +class t_field : public t_doc { +public: + t_field(t_type* type, std::string name) + : type_(type), + name_(name), + key_(0), + value_(NULL), + xsd_optional_(false), + xsd_nillable_(false), + xsd_attrs_(NULL), + reference_(false) {} + + t_field(t_type* type, std::string name, int32_t key) + : type_(type), + name_(name), + key_(key), + req_(T_OPT_IN_REQ_OUT), + value_(NULL), + xsd_optional_(false), + xsd_nillable_(false), + xsd_attrs_(NULL), + reference_(false) {} + + ~t_field() {} + + t_type* get_type() { return type_; } + + const t_type* get_type() const { return type_; } + + const std::string& get_name() const { return name_; } + + int32_t get_key() const { return key_; } + + enum e_req { T_REQUIRED, T_OPTIONAL, T_OPT_IN_REQ_OUT }; + + void set_req(e_req req) { req_ = req; } + + e_req get_req() const { return req_; } + + void set_value(t_const_value* value) { value_ = value; } + + t_const_value* get_value() { return value_; } + + const t_const_value* get_value() const { return value_; } + + void set_xsd_optional(bool xsd_optional) { xsd_optional_ = xsd_optional; } + + bool get_xsd_optional() const { return xsd_optional_; } + + void set_xsd_nillable(bool xsd_nillable) { xsd_nillable_ = xsd_nillable; } + + bool get_xsd_nillable() const { return xsd_nillable_; } + + void set_xsd_attrs(t_struct* xsd_attrs) { xsd_attrs_ = xsd_attrs; } + + t_struct* get_xsd_attrs() { 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. + * The arguments are (const) references to const pointers to const t_fields. + */ + struct key_compare { + bool operator()(t_field const* const& a, t_field const* const& b) { + return a->get_key() < b->get_key(); + } + }; + + std::map annotations_; + + bool get_reference() { return reference_; } + + void set_reference(bool reference) { reference_ = reference; } + +private: + t_type* type_; + std::string name_; + int32_t key_; + e_req req_; + t_const_value* value_; + + bool xsd_optional_; + bool xsd_nillable_; + t_struct* xsd_attrs_; + bool reference_; +}; + +/** + * A simple struct for the parser to use to store a field ID, and whether or + * not it was specified by the user or automatically chosen. + */ +struct t_field_id { + int32_t value; + bool auto_assigned; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_function.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_function.h new file mode 100644 index 00000000..05dc930f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_function.h @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_FUNCTION_H +#define T_FUNCTION_H + +#include +#include "thrift/parse/t_type.h" +#include "thrift/parse/t_struct.h" +#include "thrift/parse/t_doc.h" + +/** + * Representation of a function. Key parts are return type, function name, + * optional modifiers, and an argument list, which is implemented as a thrift + * struct. + * + */ +class t_function : public t_doc { +public: + t_function(t_type* returntype, std::string name, t_struct* arglist, bool oneway = false) + : returntype_(returntype), name_(name), arglist_(arglist), oneway_(oneway) { + xceptions_ = new t_struct(NULL); + if (oneway_ && (!returntype_->is_void())) { + pwarning(1, "Oneway methods should return void.\n"); + } + } + + t_function(t_type* returntype, + std::string name, + t_struct* arglist, + t_struct* xceptions, + bool oneway = false) + : returntype_(returntype), + name_(name), + arglist_(arglist), + xceptions_(xceptions), + oneway_(oneway) { + if (oneway_ && !xceptions_->get_members().empty()) { + throw std::string("Oneway methods can't throw exceptions."); + } + if (oneway_ && (!returntype_->is_void())) { + pwarning(1, "Oneway methods should return void.\n"); + } + } + + ~t_function() {} + + t_type* get_returntype() const { return returntype_; } + + const std::string& get_name() const { return name_; } + + t_struct* get_arglist() const { return arglist_; } + + t_struct* get_xceptions() const { return xceptions_; } + + bool is_oneway() const { return oneway_; } + + std::map annotations_; + +private: + t_type* returntype_; + std::string name_; + t_struct* arglist_; + t_struct* xceptions_; + bool oneway_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_list.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_list.h new file mode 100644 index 00000000..acf68653 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_list.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_LIST_H +#define T_LIST_H + +#include "thrift/parse/t_container.h" + +/** + * A list is a lightweight container type that just wraps another data type. + * + */ +class t_list : public t_container { +public: + t_list(t_type* elem_type) : elem_type_(elem_type) {} + + t_type* get_elem_type() const { return elem_type_; } + + bool is_list() const { return true; } + +private: + t_type* elem_type_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_map.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_map.h new file mode 100644 index 00000000..dd3f089c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_map.h @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_MAP_H +#define T_MAP_H + +#include "thrift/parse/t_container.h" + +/** + * A map is a lightweight container type that just wraps another two data + * types. + * + */ +class t_map : public t_container { +public: + t_map(t_type* key_type, t_type* val_type) : key_type_(key_type), val_type_(val_type) {} + + t_type* get_key_type() const { return key_type_; } + + t_type* get_val_type() const { return val_type_; } + + bool is_map() const { return true; } + +private: + t_type* key_type_; + t_type* val_type_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_program.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_program.h new file mode 100644 index 00000000..43dd45a6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_program.h @@ -0,0 +1,395 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_PROGRAM_H +#define T_PROGRAM_H + +#include +#include +#include + +// For program_name() +#include "thrift/main.h" + +#include "thrift/parse/t_doc.h" +#include "thrift/parse/t_scope.h" +#include "thrift/parse/t_base_type.h" +#include "thrift/parse/t_typedef.h" +#include "thrift/parse/t_enum.h" +#include "thrift/parse/t_const.h" +#include "thrift/parse/t_struct.h" +#include "thrift/parse/t_service.h" +#include "thrift/parse/t_list.h" +#include "thrift/parse/t_map.h" +#include "thrift/parse/t_set.h" +#include "thrift/generate/t_generator_registry.h" +//#include "thrift/parse/t_doc.h" + +/** + * Top level class representing an entire thrift program. A program consists + * fundamentally of the following: + * + * Typedefs + * Enumerations + * Constants + * Structs + * Exceptions + * Services + * + * The program module also contains the definitions of the base types. + * + */ +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) {} + + t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) { + name_ = program_name(path); + scope_ = new t_scope(); + } + + ~t_program() { + if (scope_) { + delete scope_; + scope_ = NULL; + } + } + + // Path accessor + const std::string& get_path() const { return path_; } + + // Output path accessor + const std::string& get_out_path() const { return out_path_; } + + // Create gen-* dir accessor + bool is_out_path_absolute() const { return out_path_is_absolute_; } + + // Name accessor + const std::string& get_name() const { return name_; } + + // Namespace + const std::string& get_namespace() const { return namespace_; } + + // Include prefix accessor + const std::string& get_include_prefix() const { return include_prefix_; } + + // Accessors for program elements + const std::vector& get_typedefs() const { return typedefs_; } + const std::vector& get_enums() const { return enums_; } + const std::vector& get_consts() const { return consts_; } + const std::vector& get_structs() const { return structs_; } + const std::vector& get_xceptions() const { return xceptions_; } + const std::vector& get_objects() const { return objects_; } + const std::vector& get_services() const { return services_; } + const std::map& get_namespaces() const { return namespaces_; } + + // Program elements + void add_typedef(t_typedef* td) { typedefs_.push_back(td); } + void add_enum(t_enum* te) { enums_.push_back(te); } + void add_const(t_const* tc) { consts_.push_back(tc); } + void add_struct(t_struct* ts) { + objects_.push_back(ts); + structs_.push_back(ts); + } + void add_xception(t_struct* tx) { + objects_.push_back(tx); + xceptions_.push_back(tx); + } + void add_service(t_service* ts) { services_.push_back(ts); } + + // Programs to include + const std::vector& get_includes() const { return includes_; } + + void set_out_path(std::string out_path, bool out_path_is_absolute) { + out_path_ = out_path; + out_path_is_absolute_ = out_path_is_absolute; + // Ensure that it ends with a trailing '/' (or '\' for windows machines) + char c = out_path_.at(out_path_.size() - 1); + if (!(c == '/' || c == '\\')) { + out_path_.push_back('/'); + } + } + + // Typename collision detection + /** + * Search for typename collisions + * @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) { + int occurrences = program_typename_count(this, t); + for (std::vector::iterator it = includes_.begin(); it != includes_.end(); ++it) { + occurrences += program_typename_count(*it, t); + } + return 0 == occurrences; + } + + /** + * Search all type collections for duplicate typenames + * @param prog the program to search + * @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 occurrences = 0; + occurrences += collection_typename_count(prog, prog->typedefs_, t); + occurrences += collection_typename_count(prog, prog->enums_, t); + occurrences += collection_typename_count(prog, prog->objects_, t); + occurrences += collection_typename_count(prog, prog->services_, t); + return occurrences; + } + + /** + * Search a type collection for duplicate typenames + * @param prog the program to search + * @param type_collection the type collection to search + * @param t the type to test for collisions + * @return the number of certain typename collisions + */ + template + int collection_typename_count(t_program* prog, T type_collection, t_type* t) { + int occurrences = 0; + for (typename T::iterator it = type_collection.begin(); it != type_collection.end(); ++it) + if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t)) + ++occurrences; + return occurrences; + } + + /** + * Determine whether identical typenames will collide based on namespaces. + * + * Because we do not know which languages the user will generate code for, + * collisions within programs (IDL files) having namespace declarations can be + * difficult to determine. Only guaranteed collisions return true (cause an error). + * Possible collisions involving explicit namespace declarations produce a warning. + * Other possible collisions go unreported. + * @param prog the program containing the preexisting typename + * @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) { + // Case 1: Typenames are in the same program [collision] + if (prog == t->get_program()) { + pwarning(1, + "Duplicate typename %s found in %s", + t->get_name().c_str(), + t->get_program()->get_name().c_str()); + return true; + } + + // 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(); + ++it) { + if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) { + pwarning(1, + "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]", + t->get_name().c_str(), + t->get_program()->get_name().c_str(), + it->first.c_str(), + it->second.c_str(), + prog->get_name().c_str(), + it->first.c_str(), + it->second.c_str()); + } else { + match = false; + } + } + for (std::map::iterator it = t->get_program()->namespaces_.begin(); + it != t->get_program()->namespaces_.end(); + ++it) { + if (0 == it->second.compare(prog->get_namespace(it->first))) { + pwarning(1, + "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]", + t->get_name().c_str(), + t->get_program()->get_name().c_str(), + it->first.c_str(), + it->second.c_str(), + prog->get_name().c_str(), + it->first.c_str(), + it->second.c_str()); + } else { + match = false; + } + } + if (0 == prog->namespaces_.size() && 0 == t->get_program()->namespaces_.size()) { + pwarning(1, + "Duplicate typename %s found in %s and %s", + t->get_name().c_str(), + t->get_program()->get_name().c_str(), + prog->get_name().c_str()); + } + return match; + } + + // Scoping and namespacing + void set_namespace(std::string name) { namespace_ = name; } + + // Scope accessor + t_scope* scope() const { return scope_; } + + // Includes + + void add_include(t_program* program) { + includes_.push_back(program); + } + + void add_include(std::string path, std::string include_site) { + t_program* program = new t_program(path); + + // include prefix for this program is the site at which it was included + // (minus the filename) + std::string include_prefix; + std::string::size_type last_slash = std::string::npos; + if ((last_slash = include_site.rfind("/")) != std::string::npos) { + include_prefix = include_site.substr(0, last_slash); + } + + program->set_include_prefix(include_prefix); + includes_.push_back(program); + } + + std::vector& get_includes() { return includes_; } + + void set_include_prefix(std::string include_prefix) { + include_prefix_ = include_prefix; + + // this is intended to be a directory; add a trailing slash if necessary + std::string::size_type len = include_prefix_.size(); + if (len > 0 && include_prefix_[len - 1] != '/') { + include_prefix_ += '/'; + } + } + + // Language neutral namespace / packaging + void set_namespace(std::string language, std::string name_space) { + 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"); + base_language = "st"; + } + + t_generator_registry::gen_map_t my_copy = t_generator_registry::get_generator_map(); + + t_generator_registry::gen_map_t::iterator it; + it = my_copy.find(base_language); + + if (it == my_copy.end()) { + std::string warning = "No generator named '" + base_language + "' could be found!"; + pwarning(1, warning.c_str()); + } else { + if (sub_index != std::string::npos) { + std::string sub_namespace = language.substr(sub_index + 1); + if (!it->second->is_valid_namespace(sub_namespace)) { + std::string warning = base_language + " generator does not accept '" + sub_namespace + + "' as sub-namespace!"; + pwarning(1, warning.c_str()); + } + } + } + } + + namespaces_[language] = name_space; + } + + std::string get_namespace(std::string language) const { + std::map::const_iterator iter; + if ((iter = namespaces_.find(language)) != namespaces_.end() + || (iter = namespaces_.find("*")) != namespaces_.end()) { + return iter->second; + } + return std::string(); + } + + const std::map& get_all_namespaces(){ + return namespaces_; + } + + void set_namespace_annotations(std::string language, std::map annotations) { + namespace_annotations_[language] = annotations; + } + + const std::map& get_namespace_annotations(std::string language) { + return namespace_annotations_[language]; + } + + // Language specific namespace / packaging + + void add_cpp_include(std::string path) { cpp_includes_.push_back(path); } + + const std::vector& get_cpp_includes() { return cpp_includes_; } + + void add_c_include(std::string path) { c_includes_.push_back(path); } + + const std::vector& get_c_includes() { return c_includes_; } + +private: + // File path + std::string path_; + + // Name + std::string name_; + + // Output directory + std::string out_path_; + + // Output directory is absolute location for generated source (no gen-*) + bool out_path_is_absolute_; + + // Namespace + std::string namespace_; + + // Included programs + std::vector includes_; + + // Include prefix for this program, if any + std::string include_prefix_; + + // Identifier lookup scope + t_scope* scope_; + + // Components to generate code for + std::vector typedefs_; + std::vector enums_; + std::vector consts_; + std::vector objects_; + std::vector structs_; + std::vector xceptions_; + std::vector services_; + + // Dynamic namespaces + std::map namespaces_; + + // Annotations for dynamic namespaces + std::map > namespace_annotations_; + + // C++ extra includes + std::vector cpp_includes_; + + // C extra includes + std::vector c_includes_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_scope.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_scope.h new file mode 100644 index 00000000..e1962002 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_scope.h @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_SCOPE_H +#define T_SCOPE_H + +#include +#include +#include + +#include "thrift/parse/t_type.h" +#include "thrift/parse/t_service.h" +#include "thrift/parse/t_const.h" +#include "thrift/parse/t_const_value.h" +#include "thrift/parse/t_base_type.h" +#include "thrift/parse/t_map.h" +#include "thrift/parse/t_list.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 + * used to determine code generation, but rather to resolve identifiers at + * parse time. + * + */ +class t_scope { +public: + t_scope() {} + + void add_type(std::string name, t_type* type) { types_[name] = type; } + + t_type* get_type(std::string name) { return types_[name]; } + + void add_service(std::string name, t_service* service) { services_[name] = service; } + + t_service* get_service(std::string name) { return services_[name]; } + + void add_constant(std::string name, t_const* constant) { + if (constants_.find(name) != constants_.end()) { + throw "Enum " + name + " is already defined!"; + } else { + constants_[name] = constant; + } + } + + t_const* get_constant(std::string name) { return constants_[name]; } + + void print() { + std::map::iterator iter; + for (iter = types_.begin(); iter != types_.end(); ++iter) { + printf("%s => %s\n", iter->first.c_str(), iter->second->get_name().c_str()); + } + } + + void resolve_const_value(t_const_value* const_val, t_type* ttype) { + if (ttype->is_map()) { + 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) { + resolve_const_value(v_iter->first, ((t_map*)ttype)->get_key_type()); + resolve_const_value(v_iter->second, ((t_map*)ttype)->get_val_type()); + } + } else if (ttype->is_list() || ttype->is_set()) { + const std::vector& val = const_val->get_list(); + std::vector::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + resolve_const_value((*v_iter), ((t_list*)ttype)->get_elem_type()); + } + } else if (ttype->is_struct()) { + t_struct* 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) { + throw "No field named \"" + v_iter->first->get_string() + + "\" was found in struct of type \"" + tstruct->get_name() + "\""; + } + resolve_const_value(v_iter->second, field->get_type()); + } + } else if (const_val->get_type() == t_const_value::CV_IDENTIFIER) { + if (ttype->is_enum()) { + const_val->set_enum((t_enum*)ttype); + } else { + t_const* constant = get_constant(const_val->get_identifier()); + if (constant == NULL) { + throw "No enum value or constant found named \"" + const_val->get_identifier() + "\"!"; + } + + // Resolve typedefs to the underlying type + t_type* const_type = constant->get_type()->get_true_type(); + + if (const_type->is_base_type()) { + switch (((t_base_type*)const_type)->get_base()) { + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_I8: + const_val->set_integer(constant->get_value()->get_integer()); + break; + case t_base_type::TYPE_STRING: + const_val->set_string(constant->get_value()->get_string()); + break; + case t_base_type::TYPE_DOUBLE: + const_val->set_double(constant->get_value()->get_double()); + break; + case t_base_type::TYPE_VOID: + throw "Constants cannot be of type VOID"; + } + } else if (const_type->is_map()) { + const std::map& map = constant->get_value()->get_map(); + std::map::const_iterator v_iter; + + const_val->set_map(); + for (v_iter = map.begin(); v_iter != map.end(); ++v_iter) { + const_val->add_map(v_iter->first, v_iter->second); + } + } else if (const_type->is_list()) { + const std::vector& val = constant->get_value()->get_list(); + std::vector::const_iterator v_iter; + + const_val->set_list(); + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + const_val->add_list(*v_iter); + } + } + } + } 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; + t_enum_value* enum_value = tenum->get_constant_by_value(const_val->get_integer()); + if (enum_value == NULL) { + std::ostringstream valstm; + valstm << const_val->get_integer(); + throw "Couldn't find a named value in enum " + tenum->get_name() + " for value " + + valstm.str(); + } + const_val->set_identifier(tenum->get_name() + "." + enum_value->get_name()); + const_val->set_enum(tenum); + } + } + +private: + // Map of names to types + std::map types_; + + // Map of names to constants + std::map constants_; + + // Map of names to services + std::map services_; + + // to list map entries + template + friend void plugin_output::convert(From*, To&); +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_service.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_service.h new file mode 100644 index 00000000..e2204cae --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_service.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 T_SERVICE_H +#define T_SERVICE_H + +#include "thrift/parse/t_function.h" +#include + +class t_program; + +/** + * A service consists of a set of functions. + * + */ +class t_service : public t_type { +public: + t_service(t_program* program) : t_type(program), extends_(NULL) {} + + bool is_service() const { return true; } + + void set_extends(t_service* extends) { extends_ = extends; } + + void add_function(t_function* func) { + 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"; + } + } + functions_.push_back(func); + } + + const std::vector& get_functions() const { return functions_; } + + t_service* get_extends() { return extends_; } + + const t_service* get_extends() const { return extends_; } + +private: + std::vector functions_; + t_service* extends_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_set.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_set.h new file mode 100644 index 00000000..f913be4f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_set.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_SET_H +#define T_SET_H + +#include "thrift/parse/t_container.h" + +/** + * A set is a lightweight container type that just wraps another data type. + * + */ +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_; } + + bool is_set() const { return true; } + +private: + t_type* elem_type_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_struct.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_struct.h new file mode 100644 index 00000000..4102da7b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_struct.h @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_STRUCT_H +#define T_STRUCT_H + +#include +#include +#include +#include + +#include "thrift/parse/t_type.h" +#include "thrift/parse/t_field.h" + +// Forward declare that puppy +class t_program; + +/** + * A struct is a container for a set of member fields that has a name. Structs + * are also used to implement exception types. + * + */ +class t_struct : public t_type { +public: + typedef std::vector members_type; + + t_struct(t_program* program) + : t_type(program), + is_xception_(false), + is_union_(false), + members_validated(false), + members_with_value(0), + xsd_all_(false) {} + + t_struct(t_program* program, const std::string& name) + : t_type(program, name), + is_xception_(false), + is_union_(false), + members_validated(false), + members_with_value(0), + xsd_all_(false) {} + + void set_name(const std::string& name) { + name_ = name; + validate_union_members(); + } + + void set_xception(bool is_xception) { is_xception_ = is_xception; } + + void validate_union_member(t_field* field) { + if (is_union_ && (!name_.empty())) { + + // 1) unions can't have required fields + // 2) union members are implicitly optional, otherwise bugs like THRIFT-3650 wait to happen + if (field->get_req() != t_field::T_OPTIONAL) { + // no warning on default requiredness, but do warn on anything else that is explicitly asked for + if(field->get_req() != t_field::T_OPT_IN_REQ_OUT) { + pwarning(1, + "Union %s field %s: union members must be optional, ignoring specified requiredness.\n", + name_.c_str(), + field->get_name().c_str()); + } + field->set_req(t_field::T_OPTIONAL); + } + + // unions may have up to one member defaulted, but not more + if (field->get_value() != NULL) { + if (1 < ++members_with_value) { + throw "Error: Field " + field->get_name() + " provides another default value for union " + + name_; + } + } + } + } + + void validate_union_members() { + if (is_union_ && (!name_.empty()) && (!members_validated)) { + members_type::const_iterator m_iter; + for (m_iter = members_in_id_order_.begin(); m_iter != members_in_id_order_.end(); ++m_iter) { + validate_union_member(*m_iter); + } + members_validated = true; + } + } + + void set_union(bool is_union) { + is_union_ = is_union; + validate_union_members(); + } + + void set_xsd_all(bool xsd_all) { xsd_all_ = xsd_all; } + + bool get_xsd_all() const { return xsd_all_; } + + bool append(t_field* elem) { + typedef members_type::iterator iter_type; + std::pair bounds = std::equal_range(members_in_id_order_.begin(), + members_in_id_order_.end(), + elem, + t_field::key_compare()); + if (bounds.first != bounds.second) { + return false; + } + // returns false when there is a conflict of field names + if (get_field_by_name(elem->get_name()) != NULL) { + return false; + } + members_.push_back(elem); + members_in_id_order_.insert(bounds.second, elem); + validate_union_member(elem); + return true; + } + + const members_type& get_members() const { return members_; } + + const members_type& get_sorted_members() { return members_in_id_order_; } + + bool is_struct() const { return !is_xception_; } + + bool is_xception() const { 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; + } + + const t_field* get_field_by_name(std::string field_name) const { + 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; + } + +private: + members_type members_; + members_type members_in_id_order_; + bool is_xception_; + bool is_union_; + bool members_validated; + int members_with_value; + + bool xsd_all_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_type.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_type.h new file mode 100644 index 00000000..30f8c1f7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_type.h @@ -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. + */ + +#ifndef T_TYPE_H +#define T_TYPE_H + +#include +#include +#include +#include +#include "thrift/parse/t_doc.h" + +class t_program; + +/** + * Generic representation of a thrift type. These objects are used by the + * parser module to build up a tree of object that are all explicitly typed. + * The generic t_type class exports a variety of useful methods that are + * used by the code generator to branch based upon different handling for the + * various types. + * + */ +class t_type : public t_doc { +public: + virtual ~t_type() {} + + virtual void set_name(const std::string& name) { name_ = name; } + + virtual const std::string& get_name() const { return name_; } + + virtual bool is_void() const { return false; } + virtual bool is_base_type() const { return false; } + virtual bool is_string() const { return false; } + virtual bool is_bool() const { return false; } + virtual bool is_typedef() const { return false; } + virtual bool is_enum() const { return false; } + virtual bool is_struct() const { return false; } + virtual bool is_xception() const { return false; } + virtual bool is_container() const { return false; } + virtual bool is_list() const { return false; } + virtual bool is_set() const { return false; } + virtual bool is_map() const { return false; } + virtual bool is_service() const { return false; } + + t_program* get_program() { return program_; } + + const t_program* get_program() const { return program_; } + + t_type* get_true_type(); + const t_type* get_true_type() const; + + // This function will break (maybe badly) unless 0 <= num <= 16. + static char nybble_to_xdigit(int num) { + if (num < 10) { + return '0' + num; + } else { + return 'A' + num - 10; + } + } + + static std::string byte_to_hex(uint8_t byte) { + std::string rv; + rv += nybble_to_xdigit(byte >> 4); + rv += nybble_to_xdigit(byte & 0x0f); + return rv; + } + + std::map annotations_; + +protected: + t_type() : program_(NULL) { ; } + + 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_program* program_; + std::string name_; +}; + +/** + * Placeholder struct for returning the key and value of an annotation + * during parsing. + */ +struct t_annotation { + std::string key; + std::string val; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc new file mode 100644 index 00000000..99ffdb8b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.cc @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include + +#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) { + printf("Type \"%s\" not defined\n", symbolic_.c_str()); + exit(1); + } + return type; + } + return type_; +} diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.h new file mode 100644 index 00000000..0cccc265 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/parse/t_typedef.h @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_TYPEDEF_H +#define T_TYPEDEF_H + +#include +#include "thrift/parse/t_type.h" + +/** + * A typedef is a mapping from a symbolic name to another type. In dymanically + * typed languages (i.e. php/python) the code generator can actually usually + * ignore typedefs and just use the underlying type directly, though in C++ + * the symbolic naming can be quite useful for code clarity. + * + */ +class t_typedef : public t_type { +public: + t_typedef(t_program* program, t_type* type, const std::string& symbolic) + : t_type(program, symbolic), type_(type), symbolic_(symbolic), forward_(false), seen_(false) {} + + /** + * This constructor is used to refer to a type that is lazily + * resolved at a later time, like for forward declarations or + * recursive types. + */ + t_typedef(t_program* program, const std::string& symbolic, bool forward) + : t_type(program, symbolic), + type_(NULL), + symbolic_(symbolic), + forward_(forward), + seen_(false) {} + + ~t_typedef() {} + + 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; } + +private: + t_type* type_; + std::string symbolic_; + bool forward_; + mutable bool seen_; +}; + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/platform.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/platform.h new file mode 100644 index 00000000..7a8edaea --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/platform.h @@ -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. + */ + +/** + * define for mkdir,since the method signature + * is different for the non-POSIX MinGW + */ + +#ifdef _MSC_VER +#include "thrift/windows/config.h" +#endif + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +#ifdef _WIN32 +#define MKDIR(x) mkdir(x) +#else +#define MKDIR(x) mkdir(x, S_IRWXU | S_IRWXG | S_IRWXO) +#endif + +#ifdef PATH_MAX +#define THRIFT_PATH_MAX PATH_MAX +#else +#define THRIFT_PATH_MAX MAX_PATH +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/Makefile.am b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/Makefile.am new file mode 100644 index 00000000..e84cdbd9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/Makefile.am @@ -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. +# +# +# 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 + +if WITH_PLUGIN +plugin_gen = plugin_types.h \ + plugin_types.cpp \ + plugin_constants.h \ + plugin_constants.cpp + +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 $@ + +$(plugin_gen): gen.stamp + @if test -f $@; then :; else \ + $(RM) -f gen.stamp; \ + $(MAKE) $(AM_MAKEFLAGS) gen.stamp; \ + fi + +clean-local: + $(RM) version.h windows/version.h $(plugin_gen) +endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.cc new file mode 100644 index 00000000..1d45d89c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.cc @@ -0,0 +1,503 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#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/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, + boost::bind(&TypeCache::compile, this, _1)); + } + + std::map const* source; + +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 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), + boost::bind(&::t_enum::append, to, _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), + boost::bind(&::t_const_value::add_list, to, _1)); + } else + T_CONST_VALUE_CASE(string); + else T_CONST_VALUE_CASE(integer); + else T_CONST_VALUE_CASE(double); + else { + T_CONST_VALUE_CASE(identifier); + if (from.__isset.enum_val) + to->set_enum(resolve_type< ::t_enum>(from.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), + boost::bind(&::t_struct::append, to, _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), + boost::bind(&::t_service::add_function, to, _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>), + boost::bind(&::t_program::add_typedef, to, _1)); + boost::for_each(from.enums | boost::adaptors::transformed(&resolve_type< ::t_enum>), + boost::bind(&::t_program::add_enum, to, _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), + boost::bind(&::t_program::add_const, to, _1)); + boost::for_each(from.services | boost::adaptors::transformed(&resolve_service), + boost::bind(&::t_program::add_service, to, _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(), + boost::bind(&::t_program::add_c_include, to, _1)); + std::for_each(from.cpp_includes.begin(), from.cpp_includes.end(), + boost::bind(&::t_program::add_cpp_include, to, _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 + boost::shared_ptr transport( + new TFramedTransport(boost::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.identifier_val) + return ::t_const_value::CV_IDENTIFIER; + if (v.__isset.enum_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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.h new file mode 100644 index 00000000..705cd41f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_PLUGIN_PLUGIN_H +#define T_PLUGIN_PLUGIN_H + +#include "thrift/Thrift.h" + +class t_program; + +namespace apache { +namespace thrift { +namespace plugin { + +struct ThriftPluginError : public apache::thrift::TException { + ThriftPluginError(const std::string& msg) : apache::thrift::TException(msg) {} +}; + +class GeneratorPlugin { +public: + int exec(int argc, char* argv[]); + virtual int generate(::t_program*, const std::map&) = 0; +}; +} +} +} + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.thrift b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.thrift new file mode 100644 index 00000000..a93873da --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin.thrift @@ -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. + */ + +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 +} + +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 + 6: optional string identifier_val + 7: optional t_type_id enum_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_struct { + 1: required TypeMetadata metadata + 2: required list members + 3: required bool is_union + 4: required bool is_xception +} +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_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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.cc new file mode 100644 index 00000000..168a4a66 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.cc @@ -0,0 +1,410 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 + +#include "thrift/generate/t_generator.h" +#include "thrift/plugin/plugin.h" +#include "thrift/plugin/type_util.h" +#include "thrift/protocol/TBinaryProtocol.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::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) + +// 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; + std::map cache; + + template + int64_t store(T2* t) { + intptr_t id = reinterpret_cast(t); + if (id) { + typename std::map::iterator it = cache.find(id); + if (it == cache.end()) { + // HACK: fake resolve for recursive type + cache.insert(std::make_pair(id, to_type())); + // overwrite with true value + cache[id] = convert(t); + } + } + return static_cast(id); + } + + void clear() { cache.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 + +#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 = 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: + THRIFT_ASSIGN_ID_N(t_type, enum_, enum_val); + THRIFT_ASSIGN_N(get_identifier(), identifier_val, ); + 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(); +} + +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 = 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 + boost::shared_ptr transport( + new TFramedTransport(boost::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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.h new file mode 100644 index 00000000..eab2d1bf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/plugin_output.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef T_PLUGIN_PLUGIN_OUTPUT_H +#define T_PLUGIN_PLUGIN_OUTPUT_H + +#include + +class t_program; + +namespace plugin_output { + +enum PluginDelegateResult { + PLUGIN_NOT_FOUND, + PLUGIN_FAILURE, + PLUGIN_SUCCEESS, +}; + +PluginDelegateResult delegateToPlugin(t_program* program, const std::string& options); +} + +#endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/type_util.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/type_util.h new file mode 100644 index 00000000..508b7418 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/plugin/type_util.h @@ -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. + */ + +#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&); +} +} +} + +// 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/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thriftl.ll b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thriftl.ll new file mode 100644 index 00000000..bf7e8a56 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thriftl.ll @@ -0,0 +1,463 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 scanner. + * + * Tokenizes a thrift definition file. + */ + +%{ + +/* This is redundant with some of the flags in Makefile.am, but it works + * when people override CXXFLAGS without being careful. The pragmas are + * the 'right' way to do it, but don't work on old-enough GCC (in particular + * the GCC that ship on Mac OS X 10.6.5, *counter* to what the GNU docs say) + * + * We should revert the Makefile.am changes once Apple ships a reasonable + * GCC. + */ +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-label" +#endif + +#ifdef _MSC_VER +//warning C4102: 'find_rule' : unreferenced label +#pragma warning(disable:4102) +//avoid isatty redefinition +#define YY_NEVER_INTERACTIVE 1 + +#define YY_NO_UNISTD_H 1 +#endif + +#include +#include +#include +#include + +#ifdef _MSC_VER +#include "thrift/windows/config.h" +#endif +#include "thrift/main.h" +#include "thrift/common.h" +#include "thrift/globals.h" +#include "thrift/parse/t_program.h" + +/** + * Must be included AFTER parse/t_program.h, but I can't remember why anymore + * because I wrote this a while ago. + */ +#if defined(BISON_USE_PARSER_H_EXTENSION) +#include "thrift/thrifty.h" +#else +#include "thrift/thrifty.hh" +#endif + +void thrift_reserved_keyword(char* keyword) { + yyerror("Cannot use reserved language keyword: \"%s\"\n", keyword); + exit(1); +} + +void integer_overflow(char* text) { + yyerror("This integer is too big: \"%s\"\n", text); + exit(1); +} + +void unexpected_token(char* text) { + yyerror("Unexpected token in input: \"%s\"\n", text); + exit(1); +} + +%} + +/** + * Provides the yylineno global, useful for debugging output + */ +%option lex-compat + +/** + * Our inputs are all single files, so no need for yywrap + */ +%option noyywrap + +/** + * We don't use it, and it fires up warnings at -Wall + */ +%option nounput + +/** + * Helper definitions, comments, constants, and whatnot + */ + +intconstant ([+-]?[0-9]+) +hexconstant ([+-]?"0x"[0-9A-Fa-f]+) +dubconstant ([+-]?[0-9]*(\.[0-9]+)?([eE][+-]?[0-9]+)?) +identifier ([a-zA-Z_](\.[a-zA-Z_0-9]|[a-zA-Z_0-9])*) +whitespace ([ \t\r\n]*) +sillycomm ("/*""*"*"*/") +multicm_begin ("/*") +doctext_begin ("/**") +comment ("//"[^\n]*) +unixcomment ("#"[^\n]*) +symbol ([:;\,\{\}\(\)\=<>\[\]]) +literal_begin (['\"]) + +%% + +{whitespace} { /* do nothing */ } +{sillycomm} { /* do nothing */ } + +{doctext_begin} { + std::string parsed("/**"); + int state = 0; // 0 = normal, 1 = "*" seen, "*/" seen + while(state < 2) + { + int ch = yyinput(); + parsed.push_back(ch); + switch (ch) { + case EOF: + yyerror("Unexpected end of file in doc-comment at %d\n", yylineno); + exit(1); + case '*': + state = 1; + break; + case '/': + state = (state == 1) ? 2 : 0; + break; + default: + state = 0; + break; + } + } + pdebug("doctext = \"%s\"\n",parsed.c_str()); + + /* This does not show up in the parse tree. */ + /* Rather, the parser will grab it out of the global. */ + if (g_parse_mode == PROGRAM) { + clear_doctext(); + g_doctext = strdup(parsed.c_str() + 3); + assert(strlen(g_doctext) >= 2); + g_doctext[strlen(g_doctext) - 2] = ' '; + 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)){ + g_program_doctext_candidate = strdup(g_doctext); + g_program_doctext_lineno = g_doctext_lineno; + g_program_doctext_status = STILL_CANDIDATE; + pdebug("%s","program doctext set to STILL_CANDIDATE"); + } + } +} + +{multicm_begin} { /* parsed, but thrown away */ + std::string parsed("/*"); + int state = 0; // 0 = normal, 1 = "*" seen, "*/" seen + while(state < 2) + { + int ch = yyinput(); + parsed.push_back(ch); + switch (ch) { + case EOF: + yyerror("Unexpected end of file in multiline comment at %d\n", yylineno); + exit(1); + case '*': + state = 1; + break; + case '/': + state = (state == 1) ? 2 : 0; + break; + default: + state = 0; + break; + } + } + pdebug("multi_comm = \"%s\"\n",parsed.c_str()); +} + +{comment} { /* do nothing */ } +{unixcomment} { /* do nothing */ } + +{symbol} { return yytext[0]; } +"*" { return yytext[0]; } + +"false" { yylval.iconst=0; return tok_int_constant; } +"true" { yylval.iconst=1; return tok_int_constant; } + +"namespace" { return tok_namespace; } +"cpp_namespace" { error_unsupported_namespace_decl("cpp"); /* do nothing */ } +"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 */ } +"xsd_all" { return tok_xsd_all; } +"xsd_optional" { return tok_xsd_optional; } +"xsd_nillable" { return tok_xsd_nillable; } +"xsd_namespace" { error_unsupported_namespace_decl("xsd"); /* do nothing */ } +"xsd_attrs" { return tok_xsd_attrs; } +"include" { return tok_include; } +"void" { return tok_void; } +"bool" { return tok_bool; } +"byte" { + emit_byte_type_warning(); + return tok_i8; +} +"i8" { return tok_i8; } +"i16" { return tok_i16; } +"i32" { return tok_i32; } +"i64" { return tok_i64; } +"double" { return tok_double; } +"string" { return tok_string; } +"binary" { return tok_binary; } +"slist" { + pwarning(0, "\"slist\" is deprecated and will be removed in a future compiler version. This type should be replaced with \"string\".\n"); + return tok_slist; +} +"senum" { + pwarning(0, "\"senum\" is deprecated and will be removed in a future compiler version. This type should be replaced with \"string\".\n"); + return tok_senum; +} +"map" { return tok_map; } +"list" { return tok_list; } +"set" { return tok_set; } +"oneway" { return tok_oneway; } +"typedef" { return tok_typedef; } +"struct" { return tok_struct; } +"union" { return tok_union; } +"exception" { return tok_xception; } +"extends" { return tok_extends; } +"throws" { return tok_throws; } +"service" { return tok_service; } +"enum" { return tok_enum; } +"const" { return tok_const; } +"required" { return tok_required; } +"optional" { return tok_optional; } +"async" { + pwarning(0, "\"async\" is deprecated. It is called \"oneway\" now.\n"); + return tok_oneway; +} +"&" { return tok_reference; } + + +"BEGIN" { thrift_reserved_keyword(yytext); } +"END" { thrift_reserved_keyword(yytext); } +"__CLASS__" { thrift_reserved_keyword(yytext); } +"__DIR__" { thrift_reserved_keyword(yytext); } +"__FILE__" { thrift_reserved_keyword(yytext); } +"__FUNCTION__" { thrift_reserved_keyword(yytext); } +"__LINE__" { thrift_reserved_keyword(yytext); } +"__METHOD__" { thrift_reserved_keyword(yytext); } +"__NAMESPACE__" { thrift_reserved_keyword(yytext); } +"abstract" { thrift_reserved_keyword(yytext); } +"alias" { thrift_reserved_keyword(yytext); } +"and" { thrift_reserved_keyword(yytext); } +"args" { thrift_reserved_keyword(yytext); } +"as" { thrift_reserved_keyword(yytext); } +"assert" { thrift_reserved_keyword(yytext); } +"begin" { thrift_reserved_keyword(yytext); } +"break" { thrift_reserved_keyword(yytext); } +"case" { thrift_reserved_keyword(yytext); } +"catch" { thrift_reserved_keyword(yytext); } +"class" { thrift_reserved_keyword(yytext); } +"clone" { thrift_reserved_keyword(yytext); } +"continue" { thrift_reserved_keyword(yytext); } +"declare" { thrift_reserved_keyword(yytext); } +"def" { thrift_reserved_keyword(yytext); } +"default" { thrift_reserved_keyword(yytext); } +"del" { thrift_reserved_keyword(yytext); } +"delete" { thrift_reserved_keyword(yytext); } +"do" { thrift_reserved_keyword(yytext); } +"dynamic" { thrift_reserved_keyword(yytext); } +"elif" { thrift_reserved_keyword(yytext); } +"else" { thrift_reserved_keyword(yytext); } +"elseif" { thrift_reserved_keyword(yytext); } +"elsif" { thrift_reserved_keyword(yytext); } +"end" { thrift_reserved_keyword(yytext); } +"enddeclare" { thrift_reserved_keyword(yytext); } +"endfor" { thrift_reserved_keyword(yytext); } +"endforeach" { thrift_reserved_keyword(yytext); } +"endif" { thrift_reserved_keyword(yytext); } +"endswitch" { thrift_reserved_keyword(yytext); } +"endwhile" { thrift_reserved_keyword(yytext); } +"ensure" { thrift_reserved_keyword(yytext); } +"except" { thrift_reserved_keyword(yytext); } +"exec" { thrift_reserved_keyword(yytext); } +"finally" { thrift_reserved_keyword(yytext); } +"float" { thrift_reserved_keyword(yytext); } +"for" { thrift_reserved_keyword(yytext); } +"foreach" { thrift_reserved_keyword(yytext); } +"from" { thrift_reserved_keyword(yytext); } +"function" { thrift_reserved_keyword(yytext); } +"global" { thrift_reserved_keyword(yytext); } +"goto" { thrift_reserved_keyword(yytext); } +"if" { thrift_reserved_keyword(yytext); } +"implements" { thrift_reserved_keyword(yytext); } +"import" { thrift_reserved_keyword(yytext); } +"in" { thrift_reserved_keyword(yytext); } +"inline" { thrift_reserved_keyword(yytext); } +"instanceof" { thrift_reserved_keyword(yytext); } +"interface" { thrift_reserved_keyword(yytext); } +"is" { thrift_reserved_keyword(yytext); } +"lambda" { thrift_reserved_keyword(yytext); } +"module" { thrift_reserved_keyword(yytext); } +"native" { thrift_reserved_keyword(yytext); } +"new" { thrift_reserved_keyword(yytext); } +"next" { thrift_reserved_keyword(yytext); } +"nil" { thrift_reserved_keyword(yytext); } +"not" { thrift_reserved_keyword(yytext); } +"or" { thrift_reserved_keyword(yytext); } +"package" { thrift_reserved_keyword(yytext); } +"pass" { thrift_reserved_keyword(yytext); } +"public" { thrift_reserved_keyword(yytext); } +"print" { thrift_reserved_keyword(yytext); } +"private" { thrift_reserved_keyword(yytext); } +"protected" { thrift_reserved_keyword(yytext); } +"raise" { thrift_reserved_keyword(yytext); } +"redo" { thrift_reserved_keyword(yytext); } +"rescue" { thrift_reserved_keyword(yytext); } +"retry" { thrift_reserved_keyword(yytext); } +"register" { thrift_reserved_keyword(yytext); } +"return" { thrift_reserved_keyword(yytext); } +"self" { thrift_reserved_keyword(yytext); } +"sizeof" { thrift_reserved_keyword(yytext); } +"static" { thrift_reserved_keyword(yytext); } +"super" { thrift_reserved_keyword(yytext); } +"switch" { thrift_reserved_keyword(yytext); } +"synchronized" { thrift_reserved_keyword(yytext); } +"then" { thrift_reserved_keyword(yytext); } +"this" { thrift_reserved_keyword(yytext); } +"throw" { thrift_reserved_keyword(yytext); } +"transient" { thrift_reserved_keyword(yytext); } +"try" { thrift_reserved_keyword(yytext); } +"undef" { thrift_reserved_keyword(yytext); } +"unless" { thrift_reserved_keyword(yytext); } +"unsigned" { thrift_reserved_keyword(yytext); } +"until" { thrift_reserved_keyword(yytext); } +"use" { thrift_reserved_keyword(yytext); } +"var" { thrift_reserved_keyword(yytext); } +"virtual" { thrift_reserved_keyword(yytext); } +"volatile" { thrift_reserved_keyword(yytext); } +"when" { thrift_reserved_keyword(yytext); } +"while" { thrift_reserved_keyword(yytext); } +"with" { thrift_reserved_keyword(yytext); } +"xor" { thrift_reserved_keyword(yytext); } +"yield" { thrift_reserved_keyword(yytext); } + +{intconstant} { + errno = 0; + yylval.iconst = strtoll(yytext, NULL, 10); + if (errno == ERANGE) { + integer_overflow(yytext); + } + return tok_int_constant; +} + +{hexconstant} { + errno = 0; + char sign = yytext[0]; + int shift = sign == '0' ? 2 : 3; + yylval.iconst = strtoll(yytext+shift, NULL, 16); + if (sign == '-') { + yylval.iconst = -yylval.iconst; + } + if (errno == ERANGE) { + integer_overflow(yytext); + } + return tok_int_constant; +} + +{identifier} { + yylval.id = strdup(yytext); + return tok_identifier; +} + +{dubconstant} { + /* Deliberately placed after identifier, since "e10" is NOT a double literal (THRIFT-3477) */ + yylval.dconst = atof(yytext); + return tok_dub_constant; +} + +{literal_begin} { + char mark = yytext[0]; + std::string result; + for(;;) + { + int ch = yyinput(); + switch (ch) { + case EOF: + yyerror("End of file while read string at %d\n", yylineno); + exit(1); + case '\n': + yyerror("End of line while read string at %d\n", yylineno - 1); + exit(1); + case '\\': + ch = yyinput(); + switch (ch) { + case 'r': + result.push_back('\r'); + continue; + case 'n': + result.push_back('\n'); + continue; + case 't': + result.push_back('\t'); + continue; + case '"': + result.push_back('"'); + continue; + case '\'': + result.push_back('\''); + continue; + case '\\': + result.push_back('\\'); + continue; + default: + yyerror("Bad escape character\n"); + return -1; + } + break; + default: + if (ch == mark) { + yylval.id = strdup(result.c_str()); + return tok_literal; + } else { + result.push_back(ch); + } + } + } +} + + +. { + unexpected_token(yytext); +} + +%% + +/* vim: filetype=lex +*/ diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thrifty.yy b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thrifty.yy new file mode 100644 index 00000000..b7a7f72e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/thrifty.yy @@ -0,0 +1,1193 @@ +%{ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 parser. + * + * This parser is used on a thrift definition file. + * + */ + +#define __STDC_LIMIT_MACROS +#define __STDC_FORMAT_MACROS +#include +#ifndef _MSC_VER +#include +#else +#include +#endif +#include +#ifdef _MSC_VER +#include "thrift/windows/config.h" +#endif +#include "thrift/main.h" +#include "thrift/common.h" +#include "thrift/globals.h" +#include "thrift/parse/t_program.h" +#include "thrift/parse/t_scope.h" + +#ifdef _MSC_VER +//warning C4065: switch statement contains 'default' but no 'case' labels +#pragma warning(disable:4065) +#endif + +/** + * This global variable is used for automatic numbering of field indices etc. + * when parsing the members of a struct. Field values are automatically + * assigned starting from -1 and working their way down. + */ +int y_field_val = -1; +/** + * This global variable is used for automatic numbering of enum values. + * y_enum_val is the last value assigned; the next auto-assigned value will be + * y_enum_val+1, and then it continues working upwards. Explicitly specified + * enum values reset y_enum_val to that value. + */ +int32_t y_enum_val = -1; +int g_arglist = 0; +const int struct_is_struct = 0; +const int struct_is_union = 1; + +%} + +/** + * This structure is used by the parser to hold the data types associated with + * various parse nodes. + */ +%union { + char* id; + int64_t iconst; + double dconst; + bool tbool; + t_doc* tdoc; + t_type* ttype; + t_base_type* tbase; + t_typedef* ttypedef; + t_enum* tenum; + t_enum_value* tenumv; + t_const* tconst; + t_const_value* tconstv; + t_struct* tstruct; + t_service* tservice; + t_function* tfunction; + t_field* tfield; + char* dtext; + t_field::e_req ereq; + t_annotation* tannot; + t_field_id tfieldid; +} + +/** + * Strings identifier + */ +%token tok_identifier +%token tok_literal +%token tok_doctext + +/** + * Constant values + */ +%token tok_int_constant +%token tok_dub_constant + +/** + * Header keywords + */ +%token tok_include +%token tok_namespace +%token tok_cpp_include +%token tok_cpp_type +%token tok_xsd_all +%token tok_xsd_optional +%token tok_xsd_nillable +%token tok_xsd_attrs + +/** + * Base datatype keywords + */ +%token tok_void +%token tok_bool +%token tok_string +%token tok_binary +%token tok_slist +%token tok_senum +%token tok_i8 +%token tok_i16 +%token tok_i32 +%token tok_i64 +%token tok_double + +/** + * Complex type keywords + */ +%token tok_map +%token tok_list +%token tok_set + +/** + * Function modifiers + */ +%token tok_oneway + +/** + * Thrift language keywords + */ +%token tok_typedef +%token tok_struct +%token tok_xception +%token tok_throws +%token tok_extends +%token tok_service +%token tok_enum +%token tok_const +%token tok_required +%token tok_optional +%token tok_union +%token tok_reference + +/** + * Grammar nodes + */ + +%type BaseType +%type SimpleBaseType +%type ContainerType +%type SimpleContainerType +%type MapType +%type SetType +%type ListType + +%type Definition +%type TypeDefinition + +%type Typedef + +%type TypeAnnotations +%type TypeAnnotationList +%type TypeAnnotation +%type TypeAnnotationValue + +%type Field +%type FieldIdentifier +%type FieldRequiredness +%type FieldType +%type FieldValue +%type FieldList +%type FieldReference + +%type Enum +%type EnumDefList +%type EnumDef +%type EnumValue + +%type Senum +%type SenumDefList +%type SenumDef + +%type Const +%type ConstValue +%type ConstList +%type ConstListContents +%type ConstMap +%type ConstMapContents + +%type StructHead +%type Struct +%type Xception +%type Service + +%type Function +%type FunctionType +%type FunctionList + +%type Throws +%type Extends +%type Oneway +%type XsdAll +%type XsdOptional +%type XsdNillable +%type XsdAttributes +%type CppType + +%type CaptureDocText + +%% + +/** + * Thrift Grammar Implementation. + * + * For the most part this source file works its way top down from what you + * might expect to find in a typical .thrift file, i.e. type definitions and + * namespaces up top followed by service definitions using those types. + */ + +Program: + HeaderList DefinitionList + { + pdebug("Program -> Headers DefinitionList"); + if((g_program_doctext_candidate != NULL) && (g_program_doctext_status != ALREADY_PROCESSED)) + { + g_program->set_doc(g_program_doctext_candidate); + g_program_doctext_status = ALREADY_PROCESSED; + } + clear_doctext(); + } + +CaptureDocText: + { + if (g_parse_mode == PROGRAM) { + $$ = g_doctext; + g_doctext = NULL; + } else { + $$ = NULL; + } + } + +/* TODO(dreiss): Try to DestroyDocText in all sorts or random places. */ +DestroyDocText: + { + if (g_parse_mode == PROGRAM) { + clear_doctext(); + } + } + +/* We have to DestroyDocText here, otherwise it catches the doctext + on the first real element. */ +HeaderList: + HeaderList DestroyDocText Header + { + pdebug("HeaderList -> HeaderList Header"); + } +| + { + pdebug("HeaderList -> "); + } + +Header: + Include + { + pdebug("Header -> Include"); + } +| tok_namespace tok_identifier tok_identifier TypeAnnotations + { + pdebug("Header -> tok_namespace tok_identifier tok_identifier"); + declare_valid_program_doctext(); + if (g_parse_mode == PROGRAM) { + g_program->set_namespace($2, $3); + } + if ($4 != NULL) { + g_program->set_namespace_annotations($2, $4->annotations_); + delete $4; + } + } +| tok_namespace '*' tok_identifier + { + pdebug("Header -> tok_namespace * tok_identifier"); + declare_valid_program_doctext(); + if (g_parse_mode == PROGRAM) { + g_program->set_namespace("*", $3); + } + } +| tok_cpp_include tok_literal + { + pdebug("Header -> tok_cpp_include tok_literal"); + declare_valid_program_doctext(); + if (g_parse_mode == PROGRAM) { + g_program->add_cpp_include($2); + } + } + +Include: + tok_include tok_literal + { + pdebug("Include -> tok_include tok_literal"); + declare_valid_program_doctext(); + if (g_parse_mode == INCLUDES) { + std::string path = include_file(std::string($2)); + if (!path.empty()) { + g_program->add_include(path, std::string($2)); + } + } + } + +DefinitionList: + DefinitionList CaptureDocText Definition + { + pdebug("DefinitionList -> DefinitionList Definition"); + if ($2 != NULL && $3 != NULL) { + $3->set_doc($2); + } + } +| + { + pdebug("DefinitionList -> "); + } + +Definition: + Const + { + pdebug("Definition -> Const"); + if (g_parse_mode == PROGRAM) { + g_program->add_const($1); + } + $$ = $1; + } +| TypeDefinition + { + pdebug("Definition -> TypeDefinition"); + if (g_parse_mode == PROGRAM) { + g_scope->add_type($1->get_name(), $1); + if (g_parent_scope != NULL) { + g_parent_scope->add_type(g_parent_prefix + $1->get_name(), $1); + } + if (! g_program->is_unique_typename($1)) { + yyerror("Type \"%s\" is already defined.", $1->get_name().c_str()); + exit(1); + } + } + $$ = $1; + } +| Service + { + pdebug("Definition -> Service"); + if (g_parse_mode == PROGRAM) { + g_scope->add_service($1->get_name(), $1); + if (g_parent_scope != NULL) { + g_parent_scope->add_service(g_parent_prefix + $1->get_name(), $1); + } + g_program->add_service($1); + if (! g_program->is_unique_typename($1)) { + yyerror("Type \"%s\" is already defined.", $1->get_name().c_str()); + exit(1); + } + } + $$ = $1; + } + +TypeDefinition: + Typedef + { + pdebug("TypeDefinition -> Typedef"); + if (g_parse_mode == PROGRAM) { + g_program->add_typedef($1); + } + } +| Enum + { + pdebug("TypeDefinition -> Enum"); + if (g_parse_mode == PROGRAM) { + g_program->add_enum($1); + } + } +| Senum + { + pdebug("TypeDefinition -> Senum"); + if (g_parse_mode == PROGRAM) { + g_program->add_typedef($1); + } + } +| Struct + { + pdebug("TypeDefinition -> Struct"); + if (g_parse_mode == PROGRAM) { + g_program->add_struct($1); + } + } +| Xception + { + pdebug("TypeDefinition -> Xception"); + if (g_parse_mode == PROGRAM) { + g_program->add_xception($1); + } + } + +CommaOrSemicolonOptional: + ',' + {} +| ';' + {} +| + {} + +Typedef: + tok_typedef FieldType tok_identifier TypeAnnotations CommaOrSemicolonOptional + { + pdebug("TypeDef -> tok_typedef FieldType tok_identifier"); + validate_simple_identifier( $3); + t_typedef *td = new t_typedef(g_program, $2, $3); + $$ = td; + if ($4 != NULL) { + $$->annotations_ = $4->annotations_; + delete $4; + } + } + +Enum: + tok_enum tok_identifier '{' EnumDefList '}' TypeAnnotations + { + pdebug("Enum -> tok_enum tok_identifier { EnumDefList }"); + $$ = $4; + validate_simple_identifier( $2); + $$->set_name($2); + if ($6 != NULL) { + $$->annotations_ = $6->annotations_; + delete $6; + } + + // make constants for all the enum values + if (g_parse_mode == PROGRAM) { + 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) { + std::string const_name = $$->get_name() + "." + (*c_iter)->get_name(); + 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) { + g_parent_scope->add_constant(g_parent_prefix + const_name, new t_const(g_type_i32, (*c_iter)->get_name(), const_val)); + } + } + } + } + +EnumDefList: + EnumDefList EnumDef + { + pdebug("EnumDefList -> EnumDefList EnumDef"); + $$ = $1; + $$->append($2); + } +| + { + pdebug("EnumDefList -> "); + $$ = new t_enum(g_program); + y_enum_val = -1; + } + +EnumDef: + CaptureDocText EnumValue TypeAnnotations CommaOrSemicolonOptional + { + pdebug("EnumDef -> EnumValue"); + $$ = $2; + if ($1 != NULL) { + $$->set_doc($1); + } + if ($3 != NULL) { + $$->annotations_ = $3->annotations_; + delete $3; + } + } + +EnumValue: + tok_identifier '=' tok_int_constant + { + pdebug("EnumValue -> tok_identifier = tok_int_constant"); + if ($3 < INT32_MIN || $3 > INT32_MAX) { + // Note: this used to be just a warning. However, since thrift always + // treats enums as i32 values, I'm changing it to a fatal error. + // I doubt this will affect many people, but users who run into this + // will have to update their thrift files to manually specify the + // truncated i32 value that thrift has always been using anyway. + failure("64-bit value supplied for enum %s will be truncated.", $1); + } + y_enum_val = static_cast($3); + $$ = new t_enum_value($1, y_enum_val); + } + | + tok_identifier + { + pdebug("EnumValue -> tok_identifier"); + validate_simple_identifier( $1); + if (y_enum_val == INT32_MAX) { + failure("enum value overflow at enum %s", $1); + } + ++y_enum_val; + $$ = new t_enum_value($1, y_enum_val); + } + +Senum: + tok_senum tok_identifier '{' SenumDefList '}' TypeAnnotations + { + pdebug("Senum -> tok_senum tok_identifier { SenumDefList }"); + validate_simple_identifier( $2); + $$ = new t_typedef(g_program, $4, $2); + if ($6 != NULL) { + $$->annotations_ = $6->annotations_; + delete $6; + } + } + +SenumDefList: + SenumDefList SenumDef + { + pdebug("SenumDefList -> SenumDefList SenumDef"); + $$ = $1; + $$->add_string_enum_val($2); + } +| + { + pdebug("SenumDefList -> "); + $$ = new t_base_type("string", t_base_type::TYPE_STRING); + $$->set_string_enum(true); + } + +SenumDef: + tok_literal CommaOrSemicolonOptional + { + pdebug("SenumDef -> tok_literal"); + $$ = $1; + } + +Const: + tok_const FieldType tok_identifier '=' ConstValue CommaOrSemicolonOptional + { + pdebug("Const -> tok_const FieldType tok_identifier = ConstValue"); + if (g_parse_mode == PROGRAM) { + validate_simple_identifier( $3); + g_scope->resolve_const_value($5, $2); + $$ = new t_const($2, $3, $5); + validate_const_type($$); + + g_scope->add_constant($3, $$); + if (g_parent_scope != NULL) { + g_parent_scope->add_constant(g_parent_prefix + $3, $$); + } + } else { + $$ = NULL; + } + } + +ConstValue: + tok_int_constant + { + pdebug("ConstValue => tok_int_constant"); + $$ = new t_const_value(); + $$->set_integer($1); + if (!g_allow_64bit_consts && ($1 < INT32_MIN || $1 > INT32_MAX)) { + pwarning(1, "64-bit constant \"%" PRIi64"\" may not work in all languages.\n", $1); + } + } +| tok_dub_constant + { + pdebug("ConstValue => tok_dub_constant"); + $$ = new t_const_value(); + $$->set_double($1); + } +| tok_literal + { + pdebug("ConstValue => tok_literal"); + $$ = new t_const_value($1); + } +| tok_identifier + { + pdebug("ConstValue => tok_identifier"); + $$ = new t_const_value(); + $$->set_identifier($1); + } +| ConstList + { + pdebug("ConstValue => ConstList"); + $$ = $1; + } +| ConstMap + { + pdebug("ConstValue => ConstMap"); + $$ = $1; + } + +ConstList: + '[' ConstListContents ']' + { + pdebug("ConstList => [ ConstListContents ]"); + $$ = $2; + } + +ConstListContents: + ConstListContents ConstValue CommaOrSemicolonOptional + { + pdebug("ConstListContents => ConstListContents ConstValue CommaOrSemicolonOptional"); + $$ = $1; + $$->add_list($2); + } +| + { + pdebug("ConstListContents =>"); + $$ = new t_const_value(); + $$->set_list(); + } + +ConstMap: + '{' ConstMapContents '}' + { + pdebug("ConstMap => { ConstMapContents }"); + $$ = $2; + } + +ConstMapContents: + ConstMapContents ConstValue ':' ConstValue CommaOrSemicolonOptional + { + pdebug("ConstMapContents => ConstMapContents ConstValue CommaOrSemicolonOptional"); + $$ = $1; + $$->add_map($2, $4); + } +| + { + pdebug("ConstMapContents =>"); + $$ = new t_const_value(); + $$->set_map(); + } + +StructHead: + tok_struct + { + $$ = struct_is_struct; + } +| tok_union + { + $$ = struct_is_union; + } + +Struct: + StructHead tok_identifier XsdAll '{' FieldList '}' TypeAnnotations + { + pdebug("Struct -> tok_struct tok_identifier { FieldList }"); + validate_simple_identifier( $2); + $5->set_xsd_all($3); + $5->set_union($1 == struct_is_union); + $$ = $5; + $$->set_name($2); + if ($7 != NULL) { + $$->annotations_ = $7->annotations_; + delete $7; + } + } + +XsdAll: + tok_xsd_all + { + $$ = true; + } +| + { + $$ = false; + } + +XsdOptional: + tok_xsd_optional + { + $$ = true; + } +| + { + $$ = false; + } + +XsdNillable: + tok_xsd_nillable + { + $$ = true; + } +| + { + $$ = false; + } + +XsdAttributes: + tok_xsd_attrs '{' FieldList '}' + { + $$ = $3; + } +| + { + $$ = NULL; + } + +Xception: + tok_xception tok_identifier '{' FieldList '}' TypeAnnotations + { + pdebug("Xception -> tok_xception tok_identifier { FieldList }"); + validate_simple_identifier( $2); + $4->set_name($2); + $4->set_xception(true); + $$ = $4; + if ($6 != NULL) { + $$->annotations_ = $6->annotations_; + delete $6; + } + } + +Service: + tok_service tok_identifier Extends '{' FlagArgs FunctionList UnflagArgs '}' TypeAnnotations + { + pdebug("Service -> tok_service tok_identifier { FunctionList }"); + validate_simple_identifier( $2); + $$ = $6; + $$->set_name($2); + $$->set_extends($3); + if ($9 != NULL) { + $$->annotations_ = $9->annotations_; + delete $9; + } + } + +FlagArgs: + { + g_arglist = 1; + } + +UnflagArgs: + { + g_arglist = 0; + } + +Extends: + tok_extends tok_identifier + { + pdebug("Extends -> tok_extends tok_identifier"); + $$ = NULL; + if (g_parse_mode == PROGRAM) { + $$ = g_scope->get_service($2); + if ($$ == NULL) { + yyerror("Service \"%s\" has not been defined.", $2); + exit(1); + } + } + } +| + { + $$ = NULL; + } + +FunctionList: + FunctionList Function + { + pdebug("FunctionList -> FunctionList Function"); + $$ = $1; + $1->add_function($2); + } +| + { + pdebug("FunctionList -> "); + $$ = new t_service(g_program); + } + +Function: + CaptureDocText Oneway FunctionType tok_identifier '(' FieldList ')' Throws TypeAnnotations CommaOrSemicolonOptional + { + validate_simple_identifier( $4); + $6->set_name(std::string($4) + "_args"); + $$ = new t_function($3, $4, $6, $8, $2); + if ($1 != NULL) { + $$->set_doc($1); + } + if ($9 != NULL) { + $$->annotations_ = $9->annotations_; + delete $9; + } + } + +Oneway: + tok_oneway + { + $$ = true; + } +| + { + $$ = false; + } + +Throws: + tok_throws '(' FieldList ')' + { + pdebug("Throws -> tok_throws ( FieldList )"); + $$ = $3; + if (g_parse_mode == PROGRAM && !validate_throws($$)) { + yyerror("Throws clause may not contain non-exception types"); + exit(1); + } + } +| + { + $$ = new t_struct(g_program); + } + +FieldList: + FieldList Field + { + pdebug("FieldList -> FieldList , Field"); + $$ = $1; + if (!($$->append($2))) { + yyerror("\"%d: %s\" - field identifier/name has already been used", $2->get_key(), $2->get_name().c_str()); + exit(1); + } + } +| + { + pdebug("FieldList -> "); + y_field_val = -1; + $$ = new t_struct(g_program); + } + +Field: + CaptureDocText FieldIdentifier FieldRequiredness FieldType FieldReference tok_identifier FieldValue XsdOptional XsdNillable XsdAttributes TypeAnnotations CommaOrSemicolonOptional + { + pdebug("tok_int_constant : Field -> FieldType tok_identifier"); + if ($2.auto_assigned) { + pwarning(1, "No field key specified for %s, resulting protocol may have conflicts or not be backwards compatible!\n", $6); + if (g_strict >= 192) { + yyerror("Implicit field keys are deprecated and not allowed with -strict"); + exit(1); + } + } + validate_simple_identifier($6); + $$ = new t_field($4, $6, $2.value); + $$->set_reference($5); + $$->set_req($3); + if ($7 != NULL) { + 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) { + $$->set_doc($1); + } + if ($10 != NULL) { + $$->set_xsd_attrs($10); + } + if ($11 != NULL) { + $$->annotations_ = $11->annotations_; + delete $11; + } + } + +FieldIdentifier: + tok_int_constant ':' + { + if ($1 <= 0) { + if (g_allow_neg_field_keys) { + /* + * g_allow_neg_field_keys exists to allow users to add explicitly + * specified key values to old .thrift files without breaking + * protocol compatibility. + */ + if ($1 != y_field_val) { + /* + * warn if the user-specified negative value isn't what + * thrift would have auto-assigned. + */ + pwarning(1, "Nonpositive field key (%" PRIi64") differs from what would be " + "auto-assigned by thrift (%d).\n", $1, y_field_val); + } + /* + * Leave $1 as-is, and update y_field_val to be one less than $1. + * The FieldList parsing will catch any duplicate key values. + */ + y_field_val = static_cast($1 - 1); + $$.value = static_cast($1); + $$.auto_assigned = false; + } else { + pwarning(1, "Nonpositive value (%d) not allowed as a field key.\n", + $1); + $$.value = y_field_val--; + $$.auto_assigned = true; + } + } else { + $$.value = static_cast($1); + $$.auto_assigned = false; + } + if( (SHRT_MIN > $$.value) || ($$.value > SHRT_MAX)) { + pwarning(1, "Field key (%d) exceeds allowed range (%d..%d).\n", + $$.value, SHRT_MIN, SHRT_MAX); + } + } +| + { + $$.value = y_field_val--; + $$.auto_assigned = true; + if( (SHRT_MIN > $$.value) || ($$.value > SHRT_MAX)) { + pwarning(1, "Field key (%d) exceeds allowed range (%d..%d).\n", + $$.value, SHRT_MIN, SHRT_MAX); + } + } + +FieldReference: + tok_reference + { + $$ = true; + } +| + { + $$ = false; + } + +FieldRequiredness: + tok_required + { + $$ = t_field::T_REQUIRED; + } +| tok_optional + { + if (g_arglist) { + if (g_parse_mode == PROGRAM) { + pwarning(1, "optional keyword is ignored in argument lists.\n"); + } + $$ = t_field::T_OPT_IN_REQ_OUT; + } else { + $$ = t_field::T_OPTIONAL; + } + } +| + { + $$ = t_field::T_OPT_IN_REQ_OUT; + } + +FieldValue: + '=' ConstValue + { + if (g_parse_mode == PROGRAM) { + $$ = $2; + } else { + $$ = NULL; + } + } +| + { + $$ = NULL; + } + +FunctionType: + FieldType + { + pdebug("FunctionType -> FieldType"); + $$ = $1; + } +| tok_void + { + pdebug("FunctionType -> tok_void"); + $$ = g_type_void; + } + +FieldType: + tok_identifier + { + pdebug("FieldType -> tok_identifier"); + if (g_parse_mode == INCLUDES) { + // Ignore identifiers in include mode + $$ = NULL; + } else { + // Lookup the identifier in the current scope + $$ = g_scope->get_type($1); + if ($$ == NULL) { + /* + * Either this type isn't yet declared, or it's never + declared. Either way allow it and we'll figure it out + during generation. + */ + $$ = new t_typedef(g_program, $1, true); + } + } + } +| BaseType + { + pdebug("FieldType -> BaseType"); + $$ = $1; + } +| ContainerType + { + pdebug("FieldType -> ContainerType"); + $$ = $1; + } + +BaseType: SimpleBaseType TypeAnnotations + { + pdebug("BaseType -> SimpleBaseType TypeAnnotations"); + if ($2 != NULL) { + $$ = new t_base_type(*static_cast($1)); + $$->annotations_ = $2->annotations_; + delete $2; + } else { + $$ = $1; + } + } + +SimpleBaseType: + tok_string + { + pdebug("BaseType -> tok_string"); + $$ = g_type_string; + } +| tok_binary + { + pdebug("BaseType -> tok_binary"); + $$ = g_type_binary; + } +| tok_slist + { + pdebug("BaseType -> tok_slist"); + $$ = g_type_slist; + } +| tok_bool + { + pdebug("BaseType -> tok_bool"); + $$ = g_type_bool; + } +| tok_i8 + { + pdebug("BaseType -> tok_i8"); + $$ = g_type_i8; + } +| tok_i16 + { + pdebug("BaseType -> tok_i16"); + $$ = g_type_i16; + } +| tok_i32 + { + pdebug("BaseType -> tok_i32"); + $$ = g_type_i32; + } +| tok_i64 + { + pdebug("BaseType -> tok_i64"); + $$ = g_type_i64; + } +| tok_double + { + pdebug("BaseType -> tok_double"); + $$ = g_type_double; + } + +ContainerType: SimpleContainerType TypeAnnotations + { + pdebug("ContainerType -> SimpleContainerType TypeAnnotations"); + $$ = $1; + if ($2 != NULL) { + $$->annotations_ = $2->annotations_; + delete $2; + } + } + +SimpleContainerType: + MapType + { + pdebug("SimpleContainerType -> MapType"); + $$ = $1; + } +| SetType + { + pdebug("SimpleContainerType -> SetType"); + $$ = $1; + } +| ListType + { + pdebug("SimpleContainerType -> ListType"); + $$ = $1; + } + +MapType: + tok_map CppType '<' FieldType ',' FieldType '>' + { + pdebug("MapType -> tok_map "); + $$ = new t_map($4, $6); + if ($2 != NULL) { + ((t_container*)$$)->set_cpp_name(std::string($2)); + } + } + +SetType: + tok_set CppType '<' FieldType '>' + { + pdebug("SetType -> tok_set"); + $$ = new t_set($4); + if ($2 != NULL) { + ((t_container*)$$)->set_cpp_name(std::string($2)); + } + } + +ListType: + tok_list '<' FieldType '>' CppType + { + pdebug("ListType -> tok_list"); + check_for_list_of_bytes($3); + $$ = new t_list($3); + if ($5 != NULL) { + ((t_container*)$$)->set_cpp_name(std::string($5)); + } + } + +CppType: + tok_cpp_type tok_literal + { + $$ = $2; + } +| + { + $$ = NULL; + } + +TypeAnnotations: + '(' TypeAnnotationList ')' + { + pdebug("TypeAnnotations -> ( TypeAnnotationList )"); + $$ = $2; + } +| + { + $$ = NULL; + } + +TypeAnnotationList: + TypeAnnotationList TypeAnnotation + { + pdebug("TypeAnnotationList -> TypeAnnotationList , TypeAnnotation"); + $$ = $1; + $$->annotations_[$2->key] = $2->val; + delete $2; + } +| + { + /* Just use a dummy structure to hold the annotations. */ + $$ = new t_struct(g_program); + } + +TypeAnnotation: + tok_identifier TypeAnnotationValue CommaOrSemicolonOptional + { + pdebug("TypeAnnotation -> TypeAnnotationValue"); + $$ = new t_annotation; + $$->key = $1; + $$->val = $2; + } + +TypeAnnotationValue: + '=' tok_literal + { + pdebug("TypeAnnotationValue -> = tok_literal"); + $$ = $2; + } +| + { + pdebug("TypeAnnotationValue ->"); + $$ = strdup("1"); + } + +%% diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/version.h.in b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/version.h.in new file mode 100644 index 00000000..aef076f7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/version.h.in @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _THRIFT_VERSION_H_ +#define _THRIFT_VERSION_H_ 1 + +#if defined(_MSC_VER) && (_MSC_VER > 1200) +#pragma once +#endif // _MSC_VER + +#define THRIFT_VERSION "@PACKAGE_VERSION@" + +#endif // _THRIFT_VERSION_H_ diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/windows/config.h b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/windows/config.h new file mode 100644 index 00000000..5f057ca8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/src/thrift/windows/config.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_WINDOWS_CONFIG_H_ +#define _THRIFT_WINDOWS_CONFIG_H_ 1 + +#if defined(_MSC_VER) && (_MSC_VER > 1200) +#pragma once +#endif // _MSC_VER + +#ifndef _WIN32 +#error "This is a Windows header only" +#endif + +#include +#include +#include + +#define strtoll(begin_ptr, end_ptr, length) _strtoi64(begin_ptr, end_ptr, length) + +#define PRIu64 "I64d" +#define PRIi64 "I64d" + +// squelch deprecation warnings +#pragma warning(disable : 4996) +// 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 + +#endif // _THRIFT_WINDOWS_CONFIG_H_ diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/test/CMakeLists.txt b/vendor/src/github.com/apache/thrift/compiler/cpp/test/CMakeLists.txt new file mode 100644 index 00000000..c1fe914c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/CMakeLists.txt @@ -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. +# + + +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() diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/test/Makefile.am b/vendor/src/github.com/apache/thrift/compiler/cpp/test/Makefile.am new file mode 100644 index 00000000..5a232029 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/Makefile.am @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# +# 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 serial-tests + +AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/compiler/cpp/src +AM_LDFLAGS = $(BOOST_LDFLAGS) +AM_CXXFLAGS = -Wall -Wextra -pedantic + +if WITH_PLUGIN +check_PROGRAMS = plugintest + +noinst_PROGRAMS = thrift-gen-mycpp + +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 +TESTS = $(check_PROGRAMS) cpp_plugin_test.sh + +clean-local: + $(RM) -rf gen-cpp gen-mycpp + +endif diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.cmake b/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.cmake new file mode 100644 index 00000000..fd182818 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.cmake @@ -0,0 +1,45 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +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/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.sh b/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.sh new file mode 100644 index 00000000..ddb2e0a0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/cpp_plugin_test.sh @@ -0,0 +1,27 @@ +#!/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. +# + +# 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 diff --git a/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/conversion_test.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/conversion_test.cc new file mode 100644 index 00000000..5159ba48 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/conversion_test.cc @@ -0,0 +1,496 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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_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/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/cpp_plugin.cc b/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/cpp_plugin.cc new file mode 100644 index 00000000..6cc19f2a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/compiler/cpp/test/plugin/cpp_plugin.cc @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "thrift/plugin/plugin.h" +#include "thrift/generate/t_generator.h" + +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; + } +}; +} +} +} + +int main(int argc, char* argv[]) { + apache::thrift::plugin::MyCppGenerator p; + return p.exec(argc, argv); +} diff --git a/vendor/src/github.com/apache/thrift/composer.json b/vendor/src/github.com/apache/thrift/composer.json new file mode 100644 index 00000000..d937bc7a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/composer.json @@ -0,0 +1,30 @@ +{ + "name": "apache/thrift", + "description": "Apache Thrift RPC system", + "homepage": "http://thrift.apache.org/", + "type": "library", + "license": "Apache-2.0", + "authors": [ + { + "name": "Apache Thrift Developers", + "email": "dev@thrift.apache.org", + "homepage": "http://thrift.apache.org" + } + ], + "support": { + "email": "dev@thrift.apache.org", + "issues": "https://issues.apache.org/jira/browse/THRIFT" + }, + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": {"Thrift": "lib/php/lib/"} + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "0.10.0" + } + } +} diff --git a/vendor/src/github.com/apache/thrift/configure.ac b/vendor/src/github.com/apache/thrift/configure.ac new file mode 100644 index 00000000..0972abfc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/configure.ac @@ -0,0 +1,959 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +AC_PREREQ(2.65) +AC_CONFIG_MACRO_DIR([./aclocal]) + +AC_INIT([thrift], [0.10.0]) + +AC_CONFIG_AUX_DIR([.]) + +AM_INIT_AUTOMAKE([1.13 subdir-objects tar-ustar]) +PKG_PROG_PKG_CONFIG + +AC_ARG_VAR([PY_PREFIX], [Prefix for installing Python modules. + (Normal --prefix is ignored for Python because + Python has different conventions.) + Default = "/usr"]) +AS_IF([test "x$PY_PREFIX" = x], [PY_PREFIX="/usr"]) + +AC_ARG_VAR([JAVA_PREFIX], [Prefix for installing the Java lib jar. + Default = "/usr/local/lib"]) +AS_IF([test "x$JAVA_PREFIX" != x], [JAVA_PREFIX="$JAVA_PREFIX/usr/local/lib"], + [test "x$PREFIX" != x], [JAVA_PREFIX="$PREFIX/usr/local/lib"], + [JAVA_PREFIX="/usr/local/lib"]) + +AC_ARG_VAR([RUBY_PREFIX], [Prefix for installing Ruby modules. + (Normal --prefix is ignored for Ruby because + Ruby has different conventions.) + Default = none, let ruby setup decide]) + +AC_ARG_VAR([PHP_PREFIX], [Prefix for installing PHP modules. + (Normal --prefix is ignored for PHP because + PHP has different conventions.) + Default = "/usr/lib/php"]) +AS_IF([test "x$PHP_PREFIX" = x], [PHP_PREFIX="/usr/lib/php"]) + +AC_ARG_VAR([PHP_CONFIG_PREFIX], + [Prefix for installing PHP extension module .ini file. + (Normal --prefix is ignored for PHP because PHP has + different conventions.) + Default = "/etc/php.d"]) +AS_IF([test "x$PHP_CONFIG_PREFIX" = x], [PHP_CONFIG_PREFIX="/etc/php.d"]) + +AC_ARG_VAR([INSTALLDIRS], [When installing Perl modules, specifies which + of the sets of installation directories + to choose: perl, site or vendor. + Default = "vendor"]) +AS_IF([test "x$INSTALLDIRS" = x], [INSTALLDIRS="vendor"]) + +AC_ARG_VAR([PERL_PREFIX], [Prefix for installing Perl modules. + (Normal --prefix is ignored for Perl because + Perl has different conventions.) + Ignored, when INSTALLDIRS set to site or vendor. + Default = "/usr/local/lib"]) +AS_IF([test "x$PERL_PREFIX" = x], [PERL_PREFIX="/usr/local"]) + +AC_ARG_VAR([CABAL_CONFIGURE_FLAGS], + [Extra flags to pass to cabal: "cabal Setup.lhs configure $CABAL_CONFIGURE_FLAGS". + (Typically used to set --user or force --global.)]) + +AC_SUBST(CABAL_CONFIGURE_FLAGS) + +AC_ARG_VAR([D_IMPORT_PREFIX], [Prefix for installing D modules. + [INCLUDEDIR/d2]]) +AS_IF([test "x$D_IMPORT_PREFIX" = x], [D_IMPORT_PREFIX="${includedir}/d2"]) + +AC_ARG_VAR([DMD_LIBEVENT_FLAGS], [DMD flags for linking libevent (auto-detected if not set).]) +AC_ARG_VAR([DMD_OPENSSL_FLAGS], [DMD flags for linking OpenSSL (auto-detected if not set).]) + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_LIBTOOL +AC_PROG_MAKE_SET +AC_PROG_BISON(2.5) +AC_PROG_YACC +AC_PROG_LEX +AM_PROG_LEX +AC_PROG_LN_S +AC_PROG_MKDIR_P +AC_PROG_AWK +AC_PROG_RANLIB + +AC_LANG([C++]) +AX_CXX_COMPILE_STDCXX_11([noext], [optional]) + +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 {} \;') + +AC_ARG_ENABLE([libs], + AS_HELP_STRING([--enable-libs], [build the Apache Thrift libraries [default=yes]]), + [], enable_libs=yes +) +have_libs=yes +if test "$enable_libs" = "no"; then + have_libs="no" + with_cpp="no" + with_c_glib="no" + with_java="no" + with_csharp="no" + with_python="no" + with_ruby="no" + with_haskell="no" + with_haxe="no" + with_perl="no" + with_php="no" + with_php_extension="no" + with_dart="no" + with_erlang="no" + with_go="no" + with_d="no" + with_nodejs="no" + with_lua="no" +fi + + +AX_THRIFT_LIB(cpp, [C++], yes) +have_cpp=no +if test "$with_cpp" = "yes"; then + AX_BOOST_BASE([1.53.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")]) + AC_SUBST([BOOST_FILESYSTEM_LDADD], [$(echo "$BOOST_LIB_DIR/libboost_filesystem.a")]) + AC_SUBST([BOOST_SYSTEM_LDADD], [$(echo "$BOOST_LIB_DIR/libboost_system.a")]) + AC_SUBST([BOOST_TEST_LDADD], [$(echo "$BOOST_LIB_DIR/libboost_unit_test_framework.a")]) + AC_SUBST([BOOST_THREAD_LDADD], [$(echo "$BOOST_LIB_DIR/libboost_thread.a")]) + have_cpp="yes" + fi + + AX_CHECK_OPENSSL() + + AX_LIB_EVENT([1.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="" + if test "$with_qt5" = "yes"; then + PKG_CHECK_MODULES([QT5], [Qt5Core >= 5.0, Qt5Network >= 5.0], + [have_qt5=yes;qt_reduce_reloc=`$PKG_CONFIG --variable=qt_config Qt5Core | grep "reduce_relocations"`], + [have_qt5=no]) + fi + if test "$have_qt5" = "yes"; then + AC_PATH_PROGS([QT5_MOC], [moc-qt5 moc], "fail") + if test "$QT5_MOC" = "fail"; then + have_qt5=no + fi + fi +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"]) + +AX_THRIFT_LIB(c_glib, [C (GLib)], yes) +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 + have_c_glib="yes" + fi +fi +AM_CONDITIONAL(WITH_C_GLIB, [test "$have_glib2" = "yes" -a "$have_gobject2" = "yes"]) + +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 + AX_JAVAC_AND_JAVA + AC_PATH_PROG([ANT], [ant]) + AX_CHECK_ANT_VERSION($ANT, 1.7) + AC_SUBST(CLASSPATH) + AC_SUBST(ANT_FLAGS) + if test "x$JAVA" != "x" && test "x$JAVAC" != "x" && test "x$ANT" != "x" ; then + have_java="yes" + fi +fi +AM_CONDITIONAL([WITH_JAVA], [test "$have_java" = "yes"]) + +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]) + if test -n "$ERLC" ; then + AC_ERLANG_SUBST_LIB_DIR + # Install into the detected Erlang directory instead of $libdir/erlang/lib + ERLANG_INSTALL_LIB_DIR="$ERLANG_LIB_DIR" + AC_ERLANG_SUBST_INSTALL_LIB_SUBDIR(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) + fi + if test -n "$ERL" -a -n "$ERLC" && test "x$REBAR" != "x" ; then + have_erlang="yes" + + # otp_release is simply a number (like "17") for OTP17+ while "R16..." for OTP16 or less. + # OTP version is currently only used for running tests. + if $ERL -eval 'erlang:display(erlang:system_info(otp_release)),halt().' -noshell | grep "^\"R" >/dev/null; then + erlang_otp16_or_less="yes" + fi + fi +fi +AM_CONDITIONAL(WITH_ERLANG, [test "$have_erlang" = "yes"]) +AM_CONDITIONAL(ERLANG_OTP16, [test "$erlang_otp16_or_less" = "yes"]) + +AX_THRIFT_LIB(nodejs, [Nodejs], yes) +have_nodejs=no +if test "$with_nodejs" = "yes"; then + AC_PATH_PROGS([NODEJS], [nodejs node]) + AC_PATH_PROG([NPM], [npm]) + if test "x$NODEJS" != "x" -a "x$NPM" != "x"; then + have_nodejs="yes" + fi +fi +AM_CONDITIONAL(WITH_NODEJS, [test "$have_nodejs" = "yes"]) +AM_CONDITIONAL(HAVE_NPM, [test "x$NPM" != "x"]) + +AX_THRIFT_LIB(lua, [Lua], yes) +have_lua=no +if test "$with_lua" = "yes"; then + AX_PROG_LUA(5.2,, have_lua="yes", have_lua="no") + if test "$have_lua" = "yes"; then + AX_LUA_HEADERS(, have_lua="no") + AX_LUA_LIBS(, have_lua="no") + fi +fi +AM_CONDITIONAL(WITH_LUA, [test "$have_lua" = "yes"]) + +# Find python regardless of with_python value, because it's needed by make cross +AM_PATH_PYTHON(2.6,, :) +AX_THRIFT_LIB(python, [Python], yes) +if test "$with_python" = "yes"; then + if test -n "$PYTHON"; then + have_python="yes" + fi + AC_PATH_PROG([TRIAL], [trial]) + if test -n "$TRIAL"; then + have_trial="yes" + fi +fi +AM_CONDITIONAL(WITH_PYTHON, [test "$have_python" = "yes"]) +AM_CONDITIONAL(WITH_TWISTED_TEST, [test "$have_trial" = "yes"]) + +# Find "python3" executable. +# 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 +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 + have_py3="yes" + fi +fi +AM_CONDITIONAL(WITH_PY3, [test "$have_py3" = "yes"]) + +AX_THRIFT_LIB(perl, [Perl], yes) +if test "$with_perl" = "yes"; then + AC_PATH_PROG([PERL], [perl]) + if test -n "$PERL" ; then + AC_PROG_PERL_MODULES([Bit::Vector], success="yes", success="no") + have_perl_bit_vector="$success" + AC_PROG_PERL_MODULES([Class::Accessor], success="yes", success="no") + have_perl_class_accessor="$success" + fi + if test -n "$PERL" -a "$have_perl_bit_vector" = "yes" ; then + if test -n "$PERL" -a "$have_perl_class_accessor" = "yes" ; then + have_perl="yes" + fi + fi +fi +AM_CONDITIONAL(WITH_PERL, [test "$have_perl" = "yes"]) + +AX_THRIFT_LIB(php, [PHP], yes) +if test "$with_php" = "yes"; then + AC_PATH_PROG([PHP], [php]) + if test -n "$PHP" ; then + have_php="yes" + fi +fi +AM_CONDITIONAL(WITH_PHP, [test "$have_php" = "yes"]) + +AX_THRIFT_LIB(php_extension, [PHP_EXTENSION], yes) +if test "$with_php_extension" = "yes"; then + if test -f "lib/php/src/ext/thrift_protocol/configure"; then + AC_PATH_PROG([PHP_CONFIG], [php-config]) + if test -n "$PHP_CONFIG" ; then + AC_CONFIG_SUBDIRS([lib/php/src/ext/thrift_protocol]) + have_php_extension="yes" + fi + fi +fi +AM_CONDITIONAL(WITH_PHP_EXTENSION, [test "$have_php_extension" = "yes"]) + +AC_PATH_PROG([PHPUNIT], [phpunit]) +AM_CONDITIONAL(HAVE_PHPUNIT, [test "x$PHPUNIT" != "x"]) + +AX_THRIFT_LIB(dart, [DART], yes) +if test "$with_dart" = "yes"; then + AC_PATH_PROG([DART], [dart]) + AC_PATH_PROG([DARTPUB], [pub]) + if test "x$DART" != "x" -a "x$DARTPUB" != "x"; then + have_dart="yes" + fi +fi +AM_CONDITIONAL(WITH_DART, [test "$have_dart" = "yes"]) + +AX_THRIFT_LIB(ruby, [Ruby], yes) +have_ruby=no +if test "$with_ruby" = "yes"; then + AC_PATH_PROG([RUBY], [ruby]) + AC_PATH_PROG([BUNDLER], [bundle]) + if test "x$RUBY" != "x" -a "x$BUNDLER" != "x"; then + have_ruby="yes" + fi +fi +AM_CONDITIONAL(WITH_RUBY, [test "$have_ruby" = "yes"]) +AM_CONDITIONAL(HAVE_BUNDLER, [test "x$BUNDLER" != "x"]) + +AX_THRIFT_LIB(haskell, [Haskell], yes) +have_haskell=no +RUNHASKELL=true +CABAL=true +if test "$with_haskell" = "yes"; then + AC_PATH_PROG([CABAL], [cabal]) + AC_PATH_PROG([RUNHASKELL], [runhaskell]) + if test "x$CABAL" != "x" -a "x$RUNHASKELL" != "x"; then + have_haskell="yes" + else + RUNHASKELL=true + CABAL=true + fi +fi +AC_SUBST(CABAL) +AC_SUBST(RUNHASKELL) +AM_CONDITIONAL(WITH_HASKELL, [test "$have_haskell" = "yes"]) + +AX_THRIFT_LIB(go, [Go], yes) +if test "$with_go" = "yes"; then + AC_PATH_PROG([GO], [go]) + if [[ -x "$GO" ]] ; then + AS_IF([test -n "$GO"],[ + ax_go_version="1.4" + + AC_MSG_CHECKING([for Go version]) + golang_version=`$GO version 2>&1 | $SED -e 's/\(go \)\(version \)\(go\)\(@<:@0-9@:>@.@<:@0-9@:>@.@<:@0-9@:>@\)\(@<:@\*@:>@*\).*/\4/'` + AC_MSG_RESULT($golang_version) + AC_SUBST([golang_version],[$golang_version]) + AX_COMPARE_VERSION([$ax_go_version],[le],[$golang_version],[ + : + have_go="yes" + ],[ + : + have_go="no" + ]) + ],[ + AC_MSG_WARN([could not find Go ]) + have_go="no" + ]) + fi +fi +AM_CONDITIONAL(WITH_GO, [test "$have_go" = "yes"]) + + +AX_THRIFT_LIB(haxe, [Haxe], yes) +if test "$with_haxe" = "yes"; then + AC_PATH_PROG([HAXE], [haxe]) + if [[ -x "$HAXE" ]] ; then + AX_PROG_HAXE_VERSION( [3.1.3], have_haxe="yes", have_haxe="no") + fi +fi +AM_CONDITIONAL(WITH_HAXE, [test "$have_haxe" = "yes"]) + + +AX_THRIFT_LIB(d, [D], yes) +if test "$with_d" = "yes"; then + AX_DMD + AC_SUBST(DMD) + if test "x$DMD" != "x"; then + have_d="yes" + fi +fi + +# Determine actual name of the generated D library for use in the command line +# when compiling tests. This is needed because the -l syntax doesn't work +# with OPTLINK (Windows). +lib_prefix=lib +lib_suffix=a +case "$host_os" in + cygwin* | mingw* | pw32* | cegcc*) + lib_prefix="" + lib_suffix=lib + ;; +esac +D_LIB_NAME="${lib_prefix}thriftd.${lib_suffix}" +AC_SUBST(D_LIB_NAME) +D_EVENT_LIB_NAME="${lib_prefix}thriftd-event.${lib_suffix}" +AC_SUBST(D_EVENT_LIB_NAME) +D_SSL_LIB_NAME="${lib_prefix}thriftd-ssl.${lib_suffix}" +AC_SUBST(D_SSL_LIB_NAME) + +if test "$have_d" = "yes"; then + AX_CHECK_D_MODULE(deimos.event2.event) + have_deimos_event2=$success + + with_d_event_tests="no" + if test "$have_deimos_event2" = "yes"; then + if test "x$DMD_LIBEVENT_FLAGS" = "x"; then + if test "$dmd_optlink" = "yes"; then + AC_MSG_WARN([D libevent interface found, but cannot auto-detect \ +linker flags for OPTLINK. Please set DMD_LIBEVENT_FLAGS manually.]) + else + AX_LIB_EVENT([2.0]) + if test "$success" = "yes"; then + DMD_LIBEVENT_FLAGS=$(echo "$LIBEVENT_LDFLAGS $LIBEVENT_LIBS" | \ + sed -e 's/^ *//g;s/ *$//g;s/^\(.\)/-L\1/g;s/ */ -L/g') + with_d_event_tests="yes" + else + AC_MSG_WARN([D libevent interface present, but libevent library not found.]) + fi + fi + else + with_d_event_tests="yes" + fi + fi + + AX_CHECK_D_MODULE(deimos.openssl.ssl) + have_deimos_openssl=$success + + with_d_ssl_tests="no" + if test "$have_deimos_openssl" = "yes"; then + if test "x$DMD_OPENSSL_FLAGS" = "x"; then + if test "$dmd_optlink" = "yes"; then + AC_MSG_WARN([D OpenSSL interface found, but cannot auto-detect \ +linker flags for OPTLINK. Please set DMD_OPENSSL_FLAGS manually.]) + else + AX_CHECK_OPENSSL([with_d_ssl_tests="yes"]) + if test "$with_d_ssl_tests" = "yes"; then + DMD_OPENSSL_FLAGS=$(echo "$OPENSSL_LDFLAGS $OPENSSL_LIBS" | \ + sed -e 's/^ *//g;s/ *$//g;s/^\(.\)/-L\1/g;s/ */ -L/g') + else + AC_MSG_WARN([D OpenSSL interface present, but OpenSSL library not found.]) + fi + fi + else + with_d_ssl_tests="yes" + fi + fi +fi + +AM_CONDITIONAL(WITH_D, [test "$have_d" = "yes"]) +AM_CONDITIONAL(DMD_OPTLINK, [test "$dmd_optlink" = "yes"]) +AC_SUBST(DMD_OF_DIRSEP, "$dmd_of_dirsep") +AM_CONDITIONAL(HAVE_DEIMOS_EVENT2, [test "$have_deimos_event2" = "yes"]) +AM_CONDITIONAL(WITH_D_EVENT_TESTS, [test "$with_d_event_tests" = "yes"]) +AC_SUBST(DMD_LIBEVENT_FLAGS) +AM_CONDITIONAL(HAVE_DEIMOS_OPENSSL, [test "$have_deimos_openssl" = "yes"]) +AM_CONDITIONAL(WITH_D_SSL_TESTS, [test "$with_d_ssl_tests" = "yes"]) +AC_SUBST(DMD_OPENSSL_FLAGS) + +AC_ARG_ENABLE([tests], + AS_HELP_STRING([--enable-tests], [build tests [default=yes]]), + [], enable_tests=yes +) +have_tests=yes +if test "$enable_tests" = "no"; then + have_tests="no" +fi +AM_CONDITIONAL(WITH_TESTS, [test "$have_tests" = "yes"]) + +AC_ARG_ENABLE([plugin], + AS_HELP_STRING([--enable-plugin], [build compiler plugin support [default=yes]]), + [], enable_plugin=yes +) +have_plugin=yes +if test "$have_cpp" = "no" ; then + have_plugin="no" +fi +if test "$enable_plugin" = "no"; then + have_plugin="no" +fi +if test "$have_plugin" = "yes" ; then + AC_CONFIG_LINKS([compiler/cpp/test/plugin/t_cpp_generator.cc:compiler/cpp/src/thrift/generate/t_cpp_generator.cc]) +fi +AM_CONDITIONAL(WITH_PLUGIN, [test "$have_plugin" = "yes"]) + +AC_ARG_ENABLE([tutorial], + AS_HELP_STRING([--enable-tutorial], [build tutorial [default=yes]]), + [], enable_tutorial=yes +) +have_tutorial=yes +if test "$enable_tutorial" = "no"; then + have_tutorial="no" +fi +AM_CONDITIONAL(WITH_TUTORIAL, [test "$have_tutorial" = "yes"]) + +AM_CONDITIONAL(MINGW, false) +case "${host_os}" in +*mingw*) + mingw32_support="yes" + AC_CHECK_HEADER(windows.h) + AM_CONDITIONAL(MINGW, true) + ;; +*) + AC_ISC_POSIX + ;; +esac + +AC_C_CONST +AC_C_INLINE +AC_C_VOLATILE + +AC_HEADER_STDBOOL +AC_HEADER_STDC +AC_HEADER_TIME +AC_HEADER_SYS_WAIT +AC_TYPE_SIGNAL +AC_CHECK_HEADERS([arpa/inet.h]) +AC_CHECK_HEADERS([sys/param.h]) +AC_CHECK_HEADERS([fcntl.h]) +AC_CHECK_HEADERS([inttypes.h]) +AC_CHECK_HEADERS([limits.h]) +AC_CHECK_HEADERS([netdb.h]) +AC_CHECK_HEADERS([netinet/in.h]) +AC_CHECK_HEADERS([pthread.h]) +AC_CHECK_HEADERS([stddef.h]) +AC_CHECK_HEADERS([stdlib.h]) +AC_CHECK_HEADERS([sys/socket.h]) +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_HEADERS([sys/un.h]) +AC_CHECK_HEADERS([sys/poll.h]) +AC_CHECK_HEADERS([sys/resource.h]) +AC_CHECK_HEADERS([unistd.h]) +AC_CHECK_HEADERS([libintl.h]) +AC_CHECK_HEADERS([malloc.h]) +AC_CHECK_HEADERS([openssl/ssl.h]) +AC_CHECK_HEADERS([openssl/rand.h]) +AC_CHECK_HEADERS([openssl/x509v3.h]) +AC_CHECK_HEADERS([sched.h]) +AC_CHECK_HEADERS([wchar.h]) + +AC_CHECK_LIB(pthread, pthread_create) +dnl NOTE(dreiss): I haven't been able to find any really solid docs +dnl on what librt is and how it fits into various Unix systems. +dnl My best guess is that it is where glibc stashes its implementation +dnl of the POSIX Real-Time Extensions. This seems necessary on Linux, +dnl and we haven't yet found a system where this is a problem. +AC_CHECK_LIB(rt, clock_gettime) +AC_CHECK_LIB(socket, setsockopt) + +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_INT8_T +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T +AC_CHECK_TYPES([ptrdiff_t], [], [exit 1]) + +AC_STRUCT_TM + +dnl NOTE(dreiss): AI_ADDRCONFIG is not defined on OpenBSD. +AC_CHECK_DECL([AI_ADDRCONFIG], [], + [AC_DEFINE([AI_ADDRCONFIG], 0, + [Define if the AI_ADDRCONFIG symbol is unavailable])], + [ + #include + #include + #include +]) + +AC_FUNC_ALLOCA +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_REALLOC +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_STAT +AC_FUNC_STRERROR_R +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([strtoul]) +AC_CHECK_FUNCS([bzero]) +AC_CHECK_FUNCS([ftruncate]) +AC_CHECK_FUNCS([gethostbyname]) +AC_CHECK_FUNCS([gethostbyname_r]) +AC_CHECK_FUNCS([gettimeofday]) +AC_CHECK_FUNCS([memmove]) +AC_CHECK_FUNCS([memset]) +AC_CHECK_FUNCS([mkdir]) +AC_CHECK_FUNCS([realpath]) +AC_CHECK_FUNCS([select]) +AC_CHECK_FUNCS([setlocale]) +AC_CHECK_FUNCS([socket]) +AC_CHECK_FUNCS([strchr]) +AC_CHECK_FUNCS([strdup]) +AC_CHECK_FUNCS([strerror]) +AC_CHECK_FUNCS([strstr]) +AC_CHECK_FUNCS([strtol]) +AC_CHECK_FUNCS([sqrt]) +dnl The following functions are optional. +AC_CHECK_FUNCS([alarm]) +AC_CHECK_FUNCS([clock_gettime]) +AC_CHECK_FUNCS([sched_get_priority_min]) +AC_CHECK_FUNCS([sched_get_priority_max]) +AC_CHECK_FUNCS([inet_ntoa]) +AC_CHECK_FUNCS([pow]) + +if test "$cross_compiling" = "no" ; then + AX_SIGNED_RIGHT_SHIFT +fi + +dnl autoscan thinks we need this macro because we have a member function +dnl called "error". Invoke the macro but don't run the check so autoscan +dnl thinks we are in the clear. It's highly unlikely that we will ever +dnl actually use the function that this checks for. +if false ; then + AC_FUNC_ERROR_AT_LINE +fi + +# --- Coverage hooks --- + +AC_ARG_ENABLE(coverage, + [ --enable-coverage turn on -fprofile-arcs -ftest-coverage], + [case "${enableval}" in + yes) ENABLE_COVERAGE=1 ;; + no) ENABLE_COVERAGE=0 ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-cov) ;; + esac], + [ENABLE_COVERAGE=2]) + +if test "x[$]ENABLE_COVERAGE" = "x1"; then + AC_MSG_WARN(enable coverage) + GCOV_CFLAGS="`echo \"[$]CFLAGS\" | perl -pe 's/-O\d+//g;'` -fprofile-arcs -ftest-coverage" + GCOV_CXXFLAGS="`echo \"[$]CXXFLAGS\" | perl -pe 's/-O\d+//g;'` -fprofile-arcs -ftest-coverage" + GCOV_LDFLAGS="-XCClinker -fprofile-arcs -XCClinker -ftest-coverage" +fi + +AC_SUBST(ENABLE_COVERAGE) +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 +AH_TOP([ +#ifndef CONFIG_H +#define CONFIG_H +]) +AH_BOTTOM([ +#endif +]) + + +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/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 + lib/go/Makefile + lib/go/test/Makefile + lib/haxe/test/Makefile + lib/hs/Makefile + lib/java/Makefile + lib/js/Makefile + lib/js/test/Makefile + lib/json/Makefile + lib/json/test/Makefile + lib/nodejs/Makefile + lib/perl/Makefile + lib/perl/test/Makefile + lib/php/Makefile + lib/php/test/Makefile + lib/dart/Makefile + lib/py/Makefile + lib/rb/Makefile + lib/lua/Makefile + lib/xml/Makefile + lib/xml/test/Makefile + test/Makefile + test/features/Makefile + test/c_glib/Makefile + test/cpp/Makefile + test/csharp/Makefile + test/erl/Makefile + test/go/Makefile + test/haxe/Makefile + test/hs/Makefile + test/lua/Makefile + test/php/Makefile + test/dart/Makefile + test/perl/Makefile + test/py/Makefile + test/py.twisted/Makefile + test/py.tornado/Makefile + test/rb/Makefile + tutorial/Makefile + tutorial/c_glib/Makefile + tutorial/cpp/Makefile + tutorial/d/Makefile + tutorial/go/Makefile + tutorial/haxe/Makefile + tutorial/hs/Makefile + tutorial/java/Makefile + tutorial/js/Makefile + tutorial/nodejs/Makefile + tutorial/dart/Makefile + tutorial/py/Makefile + tutorial/py.twisted/Makefile + tutorial/py.tornado/Makefile + tutorial/rb/Makefile +]) + +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 +AC_SUBST([MAYBE_C_GLIB]) +if test "$have_d" = "yes" -a "$have_deimos_event2" = "yes" -a "$have_deimos_openssl" = "yes"; then MAYBE_D="d" ; else MAYBE_D="" ; fi +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 +AC_SUBST([MAYBE_PY3]) +if test "$have_ruby" = "yes" ; then MAYBE_RUBY="rb" ; else MAYBE_RUBY="" ; fi +AC_SUBST([MAYBE_RUBY]) +if test "$have_haskell" = "yes" ; then MAYBE_HASKELL="hs" ; else MAYBE_HASKELL="" ; fi +AC_SUBST([MAYBE_HASKELL]) +if test "$have_perl" = "yes" ; then MAYBE_PERL="perl" ; else MAYBE_PERL="" ; fi +AC_SUBST([MAYBE_PERL]) +if test "$have_php" = "yes" ; then MAYBE_PHP="php" ; else MAYBE_PHP="" ; fi +AC_SUBST([MAYBE_PHP]) +if test "$have_dart" = "yes" ; then MAYBE_DART="dart" ; else MAYBE_DART="" ; fi +AC_SUBST([MAYBE_DART]) +if test "$have_go" = "yes" ; then MAYBE_GO="go" ; else MAYBE_GO="" ; fi +AC_SUBST([MAYBE_GO]) +if test "$have_nodejs" = "yes" ; then MAYBE_NODEJS="nodejs" ; else MAYBE_NODEJS="" ; fi +AC_SUBST([MAYBE_NODEJS]) +if test "$have_erlang" = "yes" ; then MAYBE_ERLANG="erl" ; else MAYBE_ERLANG="" ; fi +AC_SUBST([MAYBE_ERLANG]) +if test "$have_lua" = "yes" ; then MAYBE_LUA="lua" ; else MAYBE_LUA="" ; fi +AC_SUBST([MAYBE_LUA]) + +AC_OUTPUT + + +echo +echo "$PACKAGE $VERSION" +echo +echo "Building Plugin Support ...... : $have_plugin" +echo "Building C++ Library ......... : $have_cpp" +echo "Building C (GLib) Library .... : $have_c_glib" +echo "Building Java Library ........ : $have_java" +echo "Building C# Library .......... : $have_csharp" +echo "Building Python Library ...... : $have_python" +echo "Building Ruby Library ........ : $have_ruby" +echo "Building Haxe Library ........ : $have_haxe" +echo "Building Haskell Library ..... : $have_haskell" +echo "Building Perl Library ........ : $have_perl" +echo "Building PHP Library ......... : $have_php" +echo "Building Dart Library ........ : $have_dart" +echo "Building Erlang Library ...... : $have_erlang" +echo "Building Go Library .......... : $have_go" +echo "Building D Library ........... : $have_d" +echo "Building NodeJS Library ...... : $have_nodejs" +echo "Building Lua Library ......... : $have_lua" + +if test "$have_cpp" = "yes" ; then + echo + echo "C++ Library:" + echo " Build TZlibTransport ...... : $have_zlib" + echo " Build TNonblockingServer .. : $have_libevent" + echo " Build TQTcpServer (Qt4) .... : $have_qt" + echo " Build TQTcpServer (Qt5) .... : $have_qt5" +fi +if test "$have_java" = "yes" ; then + echo + echo "Java Library:" + echo " Using javac ............... : $JAVAC" + echo " Using java ................ : $JAVA" + echo " Using ant ................. : $ANT" +fi +if test "$have_csharp" = "yes" ; then + echo + echo "C# Library:" + echo " Using .NET 3.5 ............ : $net_3_5" +fi +if test "$have_python" = "yes" ; then + echo + echo "Python Library:" + echo " Using Python .............. : $PYTHON" + if test "$have_py3" = "yes" ; then + echo " Using Python3 ............. : $PYTHON3" + fi + if test "$have_trial" = "yes"; then + echo " Using trial ............... : $TRIAL" + fi +fi +if test "$have_php" = "yes" ; then + echo + echo "PHP Library:" + echo " Using php-config .......... : $PHP_CONFIG" +fi +if test "$have_dart" = "yes" ; then + echo + echo "Dart Library:" + echo " Using Dart ................ : $DART" + echo " Using Pub ................. : $DARTPUB" +fi +if test "$have_ruby" = "yes" ; then + echo + echo "Ruby Library:" + echo " Using Ruby ................ : $RUBY" +fi +if test "$have_haskell" = "yes" ; then + echo + echo "Haskell Library:" + echo " Using Haskell ............. : $RUNHASKELL" + echo " Using Cabal ............... : $CABAL" +fi +if test "$have_haxe" = "yes" ; then + echo + echo "Haxe Library:" + echo " Using Haxe ................ : $HAXE" + echo " Using Haxe version ........ : $HAXE_VERSION" +fi +if test "$have_perl" = "yes" ; then + echo + echo "Perl Library:" + echo " Using Perl ................ : $PERL" +fi +if test "$have_erlang" = "yes" ; then + echo + echo "Erlang Library:" + echo " Using erlc ................ : $ERLC" + echo " Using rebar ............... : $REBAR" +fi +if test "$have_go" = "yes" ; then + echo + echo "Go Library:" + echo " Using Go................... : $GO" + echo " Using Go version........... : $($GO version)" +fi +if test "$have_d" = "yes" ; then + echo + echo "D Library:" + echo " Using D Compiler .......... : $DMD" + echo " Building D libevent tests . : $with_d_event_tests" + echo " Building D SSL tests ...... : $with_d_ssl_tests" +fi +if test "$have_nodejs" = "yes" ; then + echo + echo "NodeJS Library:" + echo " Using NodeJS .............. : $NODEJS" + echo " Using NodeJS version....... : $($NODEJS --version)" +fi +if test "$have_lua" = "yes" ; then + echo + echo "Lua Library:" + echo " Using Lua .............. : $LUA" +fi +echo +echo "If something is missing that you think should be present," +echo "please skim the output of configure to find the missing" +echo "component. Details are present in config.log." diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/App.config b/vendor/src/github.com/apache/thrift/contrib/Rebus/App.config new file mode 100644 index 00000000..4208af6b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/App.config @@ -0,0 +1,33 @@ + + + + + +
+ + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/Program.cs b/vendor/src/github.com/apache/thrift/contrib/Rebus/Program.cs new file mode 100644 index 00000000..563c62ad --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/Program.cs @@ -0,0 +1,81 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Rebus.Configuration; +using Rebus.RabbitMQ; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RebusSample.Client; +using RebusSample.Server; + +namespace RebusSample +{ + class Program + { + static BuiltinContainerAdapter StartRequestServer(string server) + { + // client Rebus configuration + var adapter = new BuiltinContainerAdapter(); + Configure.With(adapter) + .Transport(t => t.UseRabbitMq("amqp://" + server, "MathRequests", "MathRequestErrors")) + .MessageOwnership(o => o.FromRebusConfigurationSection()) + .CreateBus().Start(); + + // register all relevant message handlers + adapter.Register(typeof(MathRequestCallHandler)); + return adapter; + } + + + static BuiltinContainerAdapter StartResponseServer(string server) + { + // client Rebus configuration + var adapter = new BuiltinContainerAdapter(); + Configure.With(adapter) + .Transport(t => t.UseRabbitMq("amqp://" + server, "MathResponses", "MathResponseErrors")) + .MessageOwnership(o => o.FromRebusConfigurationSection()) + .CreateBus().Start(); + + // register all relevant message handlers + adapter.Register(typeof(MathResponseCallHandler)); + return adapter; + } + + static void Main(string[] args) + { + string server = "localhost"; + + // start all servers + var req = StartRequestServer(server); + var rsp = StartResponseServer(server); + + // send the first message + var random = new Random(); + var client = new MathRequestClient(server); + client.DoTheMath(random.Next(), random.Next()); + + // now what? + Console.Write("Hit to stop ... "); + Console.ReadLine(); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/Properties/AssemblyInfo.cs b/vendor/src/github.com/apache/thrift/contrib/Rebus/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e476eab7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("RebusSample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RebusSample")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("0af10984-40d3-453d-b1e5-421529e8c7e2")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/README.md b/vendor/src/github.com/apache/thrift/contrib/Rebus/README.md new file mode 100644 index 00000000..bbb9c496 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/README.md @@ -0,0 +1,21 @@ +Sample code for the combination of Thrift with Rebus. + +Rebus is a .NET service bus, similar to NServiceBus, but more lightweight. +It ihas been mainly written by Mogens Heller Grabe and is currently hosted +on GitHub (https://github.com/rebus-org/Rebus) + +As with all ServiceBus or MQ scenarios, due to the highly asynchronous +operations it is recommended to do all calls as "oneway void" calls. + +The configuration can be done via App.Config, via code or even mixed from +both locations. Refer to the Rebus documentation for further details. For +this example, since we are effectively implementing two queue listeners in +only one single process, we do configuration of incoming and error queues +in the code. + +If you want to communicate with non-NET languages, you may need a customized +serializer as well, in order to override Rebus' default wire format. Please +refer to the Rebus docs on how to do that (it's not that hard, really). + +Additional requirements: +- RabbitMQ .NET client (see nuget) diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.csproj b/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.csproj new file mode 100644 index 00000000..4058a6da --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.csproj @@ -0,0 +1,102 @@ + + + + + + Debug + AnyCPU + {264E2126-EDE0-4B47-89C1-B397B25BB13D} + Exe + Properties + RebusSample + RebusSample + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\..\..\Toolbox\ServiceBus\3rdparty\rabbitmq-dotnet-client-3.2.1-dotnet-3.0\bin\RabbitMQ.Client.dll + + + ..\..\..\..\..\Toolbox\ServiceBus\3rdparty\Rebus-master\deploy\NET40\Rebus.dll + + + ..\..\..\..\..\Toolbox\ServiceBus\3rdparty\Rebus-master\deploy\NET40\Rebus.RabbitMQ.dll + + + + + + + + + + + + + + + + + + + + + + + + + {499eb63c-d74c-47e8-ae48-a2fc94538e9d} + Thrift + + + + + cd $(ProjectDir) +if not exist gen-csharp\*.cs thrift -gen csharp sample.thrift + + + + \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.sln b/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.sln new file mode 100644 index 00000000..284ef36a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/RebusSample.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30110.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RebusSample", "RebusSample.csproj", "{264E2126-EDE0-4B47-89C1-B397B25BB13D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Thrift", "..\..\lib\csharp\src\Thrift.csproj", "{499EB63C-D74C-47E8-AE48-A2FC94538E9D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {264E2126-EDE0-4B47-89C1-B397B25BB13D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {264E2126-EDE0-4B47-89C1-B397B25BB13D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {264E2126-EDE0-4B47-89C1-B397B25BB13D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {264E2126-EDE0-4B47-89C1-B397B25BB13D}.Release|Any CPU.Build.0 = Release|Any CPU + {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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Both.cs b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Both.cs new file mode 100644 index 00000000..fba67ec1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Both.cs @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using System; + + +namespace RebusSample +{ + // generic data container for serialized Thrift calls + public class GenericThriftServiceCall + { + public byte[] rawBytes; + } + + // specific containers (one per Thrift service) to leverage Rebus' handler routing + public class MathRequestCall : GenericThriftServiceCall { } + public class MathResponseCall : GenericThriftServiceCall { } + +} \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Client.cs b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Client.cs new file mode 100644 index 00000000..2408041a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Client.cs @@ -0,0 +1,157 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Rebus; +using Rebus.Configuration; +using Rebus.Messages; +using Rebus.RabbitMQ; +using System; +using System.Collections.Generic; +using System.IO; +using Thrift.Protocol; +using Thrift.Transport; + +/* + * The client emits calls to BasicMathServers + * + * The client implements the BasicMathClient service. + * If the server has processed our request, we get the results back through this service + */ + +namespace RebusSample.Client +{ + + // handler to be registered with Rebus + class MathResponseCallHandler : IHandleMessages + { + public void Handle(MathResponseCall message) + { + // Thrift protocol/transport stack + var stm = new MemoryStream(message.rawBytes); + var trns = new TStreamTransport(stm, null); + var prot = new TBinaryProtocol(trns); + + // create a processor and let him handle the call + var hndl = new MathResponsesHandler(); + var proc = new BasicMathClient.Processor(hndl); + proc.Process(prot, null); // oneway only + } + } + + + // serves incoming responses with calculation results + internal class MathResponsesHandler : BasicMathClient.Iface + { + public void FourResults(int added, int multiplied, int subtracted, int divided) + { + Console.WriteLine("added = {0}", added); + Console.WriteLine("multiplied= {0}", multiplied); + Console.WriteLine("subtracted = {0}", subtracted); + Console.WriteLine("divided = {0}", divided); + + PingAndDoAnotherCalculation(); + } + + + public void ThreeResults(int added, int multiplied, int subtracted) + { + Console.WriteLine("added = {0}", added); + Console.WriteLine("multiplied= {0}", multiplied); + Console.WriteLine("subtracted = {0}", subtracted); + Console.WriteLine("DIV/0 error during division"); + + PingAndDoAnotherCalculation(); + } + + + public void Pong(long value) + { + var latency = DateTime.Now.Ticks - value; + Console.WriteLine("Ping took {0} ms", new DateTime(latency).Millisecond); + } + + + private void PingAndDoAnotherCalculation() + { + var random = new Random(); + var client = new MathRequestClient("localhost"); + client.Ping(DateTime.Now.Ticks); + client.DoTheMath(random.Next(), random.Next()); + } + } + + + // provides the client-side interface for calculation requests + internal class MathRequestClient : BasicMathServer.Iface + { + private BuiltinContainerAdapter MQAdapter; + + + public MathRequestClient(string server) + { + MQAdapter = new BuiltinContainerAdapter(); + Configure.With(MQAdapter) + .Transport(t => t.UseRabbitMqInOneWayMode("amqp://" + server)) // we need send only + .MessageOwnership(o => o.FromRebusConfigurationSection()) + .CreateBus().Start(); + } + + + public void SerializeThriftCall(Action action) + { + // Thrift protocol/transport stack + var stm = new MemoryStream(); + var trns = new TStreamTransport(null, stm); + var prot = new TBinaryProtocol(trns); + + // serialize the call into a bunch of bytes + var client = new BasicMathServer.Client(prot); + if( action != null) + action(client); + else + throw new ArgumentException("action must not be null"); + + // make sure everything is written to the MemoryStream + trns.Flush(); + + // send the message + var msg = new MathRequestCall() { rawBytes = stm.ToArray() }; + MQAdapter.Bus.Send(msg); + } + + + public void Ping(long value) + { + SerializeThriftCall(client => + { + client.Ping(value); + }); + } + + + public void DoTheMath( int arg1, int arg2) + { + SerializeThriftCall(client => + { + client.DoTheMath(arg1, arg2); + }); + } + } +} + diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Server.cs b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Server.cs new file mode 100644 index 00000000..149d513c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/ServiceImpl/Server.cs @@ -0,0 +1,143 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using Rebus; +using Rebus.Configuration; +using Rebus.Messages; +using Rebus.RabbitMQ; +using System; +using System.Collections.Generic; +using System.IO; +using Thrift.Protocol; +using Thrift.Transport; + +/* + * The server implements the BasicMathServer service . + * All results are sent back to the client via the BasicMathClient service + */ + + +namespace RebusSample.Server +{ + // handler to be registered with Rebus + class MathRequestCallHandler : IHandleMessages + { + public void Handle(MathRequestCall message) + { + // Thrift protocol/transport stack + var stm = new MemoryStream(message.rawBytes); + var trns = new TStreamTransport(stm, null); + var prot = new TBinaryProtocol(trns); + + // create a processor and let him handle the call + var hndl = new MathRequestsHandler(); + var proc = new BasicMathServer.Processor(hndl); + proc.Process(prot, null); // oneway only + } + } + + + // serves incoming calculation requests + internal class MathRequestsHandler : BasicMathServer.Iface + { + public void Ping(long value) + { + var client = new MathResponseClient("localhost"); + client.Pong(value); + } + + + public void DoTheMath(int arg1, int arg2) + { + var client = new MathResponseClient("localhost"); + if( arg2 != 0) + client.FourResults( arg1+arg2, arg1*arg2, arg1-arg2, arg1/arg2); + else + client.ThreeResults( arg1+arg2, arg1*arg2, arg1-arg2); + } + } + + + // provides the client-side interface for calculation responses + internal class MathResponseClient : BasicMathClient.Iface + { + private BuiltinContainerAdapter MQAdapter; + + + public MathResponseClient(string server) + { + MQAdapter = new BuiltinContainerAdapter(); + Configure.With(MQAdapter) + .Transport(t => t.UseRabbitMqInOneWayMode("amqp://" + server)) // we need send only + .MessageOwnership(o => o.FromRebusConfigurationSection()) + .CreateBus().Start(); + } + + + public void SerializeThriftCall(Action action) + { + // Thrift protocol/transport stack + var stm = new MemoryStream(); + var trns = new TStreamTransport(null, stm); + var prot = new TBinaryProtocol(trns); + + // serialize the call into a bunch of bytes + var client = new BasicMathClient.Client(prot); + if (action != null) + action(client); + else + throw new ArgumentException("action must not be null"); + + // make sure everything is written to the MemoryStream + trns.Flush(); + + // send the message + var msg = new MathResponseCall() { rawBytes = stm.ToArray() }; + MQAdapter.Bus.Send(msg); + } + + + public void Pong(long value) + { + SerializeThriftCall(client => + { + client.Pong(value); + }); + } + + + public void ThreeResults(int added, int multiplied, int suctracted) + { + SerializeThriftCall(client => + { + client.ThreeResults(added, multiplied, suctracted); + }); + } + + + public void FourResults(int added, int multiplied, int suctracted, int divided) + { + SerializeThriftCall(client => + { + client.FourResults(added, multiplied, suctracted, divided); + }); + } + } +} + diff --git a/vendor/src/github.com/apache/thrift/contrib/Rebus/sample.thrift b/vendor/src/github.com/apache/thrift/contrib/Rebus/sample.thrift new file mode 100644 index 00000000..785e2d38 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Rebus/sample.thrift @@ -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. + */ + + +service BasicMathServer { + oneway void DoTheMath( 1: i32 arg1, 2: i32 arg2) + oneway void Ping(1: i64 value) +} + +service BasicMathClient { + oneway void ThreeResults( 1 : i32 added, 2 : i32 multiplied, 3 : i32 subtracted); + oneway void FourResults( 1 : i32 added, 2 : i32 multiplied, 3 : i32 subtracted, 4 : i32 divided); + oneway void Pong(1: i64 value) +} diff --git a/vendor/src/github.com/apache/thrift/contrib/Stomp/README.md b/vendor/src/github.com/apache/thrift/contrib/Stomp/README.md new file mode 100644 index 00000000..2e5f21cb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Stomp/README.md @@ -0,0 +1,18 @@ +Sample code for STOMP-based Thrift clients and/or servers. + +Although the sample Thrift STOMP Transport is written in +Delphi/Pascal, it can easily serve as a starting point for +similar implementations in other languages. + +STOMP is a protocol widely supported by many messaging systems, +such as Apache ActiveMQ, RabbitMQ and many others. In particular, +it can be used to communicate with Service-Bus products like Rebus +or NServiceBus, when running against a STOMP-capable MQ system. + +A prerequisite for this sample is the Delphi STOMP Adapter written +by Daniele Teti (http://www.danieleteti.it/stomp-client), currently +hosted at Google Code (http://code.google.com/p/delphistompclient). + +At the time of writing, the STOMP adapter does not fully support +binary data. Please check whether this has been fixed, otherwise +you have to use the JSON protocol (or to fix it on your own). diff --git a/vendor/src/github.com/apache/thrift/contrib/Stomp/Thrift.Transport.STOMP.pas b/vendor/src/github.com/apache/thrift/contrib/Stomp/Thrift.Transport.STOMP.pas new file mode 100644 index 00000000..7dfb3763 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Stomp/Thrift.Transport.STOMP.pas @@ -0,0 +1,200 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +unit Thrift.Transport.STOMP; + +interface + +uses + Classes,Windows, SysUtils, + Thrift, + Thrift.Transport, + Thrift.Protocol, + Thrift.Stream, + StompClient, + StompTypes; + +type + TStompTransportImpl = class( TStreamTransportImpl) + strict private + FData : TStringStream; + FServer : string; + FOutQueue : string; + FStompCli : IStompClient; + protected + function GetIsOpen: Boolean; override; + function Peek: Boolean; override; + public + constructor Create( const aServerAndPort, aOutQueue : string); + destructor Destroy; override; + + procedure Open(); override; + procedure Close(); override; + procedure Flush; override; + end; + + + TStompServerTransportImpl = class( TServerTransportImpl) + strict private + FServer : string; + FInQueue : string; + FClient : IStompClient; + protected + procedure Listen; override; + procedure Close; override; + function Accept( const fnAccepting: TProc): ITransport; override; + public + constructor Create( const aServerAndPort, aInQueue : string); + destructor Destroy; override; + end; + + +const + QUEUE_PREFIX = '/queue/'; + TOPIC_PREFIX = '/topic/'; + EXCHANGE_PREFIX = '/exchange/'; + + +implementation + + + +constructor TStompTransportImpl.Create( const aServerAndPort, aOutQueue : string); +var adapter : IThriftStream; +begin + FData := TStringStream.Create; + FServer := aServerAndPort; + FOutQueue := aOutQueue; + + adapter := TThriftStreamAdapterDelphi.Create( FData, FALSE); + inherited Create( nil, adapter); // output only +end; + + +destructor TStompTransportImpl.Destroy; +begin + inherited Destroy; + FreeAndNil( FData); + FStompCli := nil; +end; + + +function TStompTransportImpl.GetIsOpen: Boolean; +begin + result := (FStompCli <> nil); +end; + + +function TStompTransportImpl.Peek: Boolean; +begin + result := FALSE; // output only +end; + + +procedure TStompTransportImpl.Open; +begin + if FStompCli <> nil + then raise TTransportException.Create( TTransportException.TExceptionType.AlreadyOpen, 'already open') + else FStompCli := StompUtils.NewStomp( FServer); +end; + + +procedure TStompTransportImpl.Close; +begin + FStompCli := nil; + FData.Clear; +end; + + +procedure TStompTransportImpl.Flush; +begin + if FStompCli = nil + then raise TTransportException.Create( TTransportException.TExceptionType.NotOpen, 'not open'); + + FStompCli.Send( FOutQueue, FData.DataString); + FData.Clear; +end; + + +//--- TStompServerTransportImpl -------------------------------------------- + + +constructor TStompServerTransportImpl.Create( const aServerAndPort, aInQueue : string); +begin + inherited Create; + FServer := aServerAndPort; + FInQueue := aInQueue; +end; + + +destructor TStompServerTransportImpl.Destroy; +begin + try + Close; + finally + inherited Destroy; + end; +end; + + +procedure TStompServerTransportImpl.Listen; +begin + FClient := StompUtils.NewStomp(FServer); + FClient.Subscribe( FInQueue); +end; + + +procedure TStompServerTransportImpl.Close; +begin + if FClient <> nil then begin + FClient.Unsubscribe( FInQueue); + FClient := nil; + end; +end; + + +function TStompServerTransportImpl.Accept( const fnAccepting: TProc): ITransport; +var frame : IStompFrame; + adapter : IThriftStream; + stream : TStringStream; +begin + if FClient = nil + then raise TTransportException.Create( TTransportException.TExceptionType.NotOpen, + 'Not connected.'); + + if Assigned(fnAccepting) + then fnAccepting(); + + try + frame := FClient.Receive(MAXINT); + if frame = nil then Exit(nil); + + stream := TStringStream.Create( frame.GetBody); + adapter := TThriftStreamAdapterDelphi.Create( stream, TRUE); + result := TStreamTransportImpl.Create( adapter, nil); + + except + on E: Exception + do raise TTransportException.Create( E.ToString ); + end; +end; + + +end. + diff --git a/vendor/src/github.com/apache/thrift/contrib/Vagrantfile b/vendor/src/github.com/apache/thrift/contrib/Vagrantfile new file mode 100644 index 00000000..3bcc46a9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/contrib/Vagrantfile @@ -0,0 +1,133 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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_and_test = < + + + + + +### hello.js - Node Server + var thrift = require('thrift'); + var hello_svc = require('./gen-nodejs/hello_svc.js'); + + var hello_handler = { + get_message: function(name, result) { + var msg = "Hello " + name + "!"; + result(null, msg); + } + } + + var hello_svc_opt = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: hello_svc, + handler: hello_handler + }; + + var server_opt = { + staticFilePath: ".", + services: { + "/hello": hello_svc_opt + } + } + + var server = Thrift.createWebServer(server_opt); + var port = 9099; + server.listen(port); + console.log("Http/Thrift Server running on port: " + port); + + +TypeScript +------------------------------------ +TypeScript definition files can also be generated by running: + + thrift --gen js:ts file.thrift + diff --git a/vendor/src/github.com/apache/thrift/lib/js/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/js/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/js/package.json b/vendor/src/github.com/apache/thrift/lib/js/package.json new file mode 100644 index 00000000..9491970c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/package.json @@ -0,0 +1,15 @@ +{ + "name": "thrift", + "version": "0.10.0", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-cli": "^1.2.0", + "grunt-contrib-uglify": "^1.0.1", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-qunit": "^1.2.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-jsdoc": "^2.0.0", + "grunt-external-daemon": "^1.1.0", + "grunt-shell": "^1.3.0" + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/js/src/thrift.js b/vendor/src/github.com/apache/thrift/lib/js/src/thrift.js new file mode 100644 index 00000000..41935c40 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/src/thrift.js @@ -0,0 +1,1555 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/*jshint evil:true*/ + +/** + * The Thrift namespace houses the Apache Thrift JavaScript library + * elements providing JavaScript bindings for the Apache Thrift RPC + * system. End users will typically only directly make use of the + * Transport (TXHRTransport/TWebSocketTransport) and Protocol + * (TJSONPRotocol/TBinaryProtocol) constructors. + * + * Object methods beginning with a __ (e.g. __onOpen()) are internal + * and should not be called outside of the object's own methods. + * + * This library creates one global object: Thrift + * Code in this library must never create additional global identifiers, + * all features must be scoped within the Thrift namespace. + * @namespace + * @example + * var transport = new Thrift.Transport("http://localhost:8585"); + * var protocol = new Thrift.Protocol(transport); + * var client = new MyThriftSvcClient(protocol); + * var result = client.MyMethod(); + */ +var Thrift = { + /** + * Thrift JavaScript library version. + * @readonly + * @const {string} Version + * @memberof Thrift + */ + Version: '0.10.0', + + /** + * Thrift IDL type string to Id mapping. + * @readonly + * @property {number} STOP - End of a set of fields. + * @property {number} VOID - No value (only legal for return types). + * @property {number} BOOL - True/False integer. + * @property {number} BYTE - Signed 8 bit integer. + * @property {number} I08 - Signed 8 bit integer. + * @property {number} DOUBLE - 64 bit IEEE 854 floating point. + * @property {number} I16 - Signed 16 bit integer. + * @property {number} I32 - Signed 32 bit integer. + * @property {number} I64 - Signed 64 bit integer. + * @property {number} STRING - Array of bytes representing a string of characters. + * @property {number} UTF7 - Array of bytes representing a string of UTF7 encoded characters. + * @property {number} STRUCT - A multifield type. + * @property {number} MAP - A collection type (map/associative-array/dictionary). + * @property {number} SET - A collection type (unordered and without repeated values). + * @property {number} LIST - A collection type (unordered). + * @property {number} UTF8 - Array of bytes representing a string of UTF8 encoded characters. + * @property {number} UTF16 - Array of bytes representing a string of UTF16 encoded characters. + */ + Type: { + 'STOP' : 0, + 'VOID' : 1, + 'BOOL' : 2, + 'BYTE' : 3, + 'I08' : 3, + 'DOUBLE' : 4, + 'I16' : 6, + 'I32' : 8, + 'I64' : 10, + 'STRING' : 11, + 'UTF7' : 11, + 'STRUCT' : 12, + 'MAP' : 13, + 'SET' : 14, + 'LIST' : 15, + 'UTF8' : 16, + 'UTF16' : 17 + }, + + /** + * Thrift RPC message type string to Id mapping. + * @readonly + * @property {number} CALL - RPC call sent from client to server. + * @property {number} REPLY - RPC call normal response from server to client. + * @property {number} EXCEPTION - RPC call exception response from server to client. + * @property {number} ONEWAY - Oneway RPC call from client to server with no response. + */ + MessageType: { + 'CALL' : 1, + 'REPLY' : 2, + 'EXCEPTION' : 3, + 'ONEWAY' : 4 + }, + + /** + * Utility function returning the count of an object's own properties. + * @param {object} obj - Object to test. + * @returns {number} number of object's own properties + */ + objectLength: function(obj) { + var length = 0; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + length++; + } + } + return length; + }, + + /** + * Utility function to establish prototype inheritance. + * @see {@link http://javascript.crockford.com/prototypal.html|Prototypal Inheritance} + * @param {function} constructor - Contstructor function to set as derived. + * @param {function} superConstructor - Contstructor function to set as base. + * @param {string} [name] - Type name to set as name property in derived prototype. + */ + inherits: function(constructor, superConstructor, name) { + function F() {} + F.prototype = superConstructor.prototype; + constructor.prototype = new F(); + constructor.prototype.name = name || ""; + } +}; + +/** + * Initializes a Thrift TException instance. + * @constructor + * @augments Error + * @param {string} message - The TException message (distinct from the Error message). + * @classdesc TException is the base class for all Thrift exceptions types. + */ +Thrift.TException = function(message) { + this.message = message; +}; +Thrift.inherits(Thrift.TException, Error, 'TException'); + +/** + * Returns the message set on the exception. + * @readonly + * @returns {string} exception message + */ +Thrift.TException.prototype.getMessage = function() { + return this.message; +}; + +/** + * Thrift Application Exception type string to Id mapping. + * @readonly + * @property {number} UNKNOWN - Unknown/undefined. + * @property {number} UNKNOWN_METHOD - Client attempted to call a method unknown to the server. + * @property {number} INVALID_MESSAGE_TYPE - Client passed an unknown/unsupported MessageType. + * @property {number} WRONG_METHOD_NAME - Unused. + * @property {number} BAD_SEQUENCE_ID - Unused in Thrift RPC, used to flag proprietary sequence number errors. + * @property {number} MISSING_RESULT - Raised by a server processor if a handler fails to supply the required return result. + * @property {number} INTERNAL_ERROR - Something bad happened. + * @property {number} PROTOCOL_ERROR - The protocol layer failed to serialize or deserialize data. + * @property {number} INVALID_TRANSFORM - Unused. + * @property {number} INVALID_PROTOCOL - The protocol (or version) is not supported. + * @property {number} UNSUPPORTED_CLIENT_TYPE - Unused. + */ +Thrift.TApplicationExceptionType = { + 'UNKNOWN' : 0, + 'UNKNOWN_METHOD' : 1, + 'INVALID_MESSAGE_TYPE' : 2, + 'WRONG_METHOD_NAME' : 3, + 'BAD_SEQUENCE_ID' : 4, + 'MISSING_RESULT' : 5, + 'INTERNAL_ERROR' : 6, + 'PROTOCOL_ERROR' : 7, + 'INVALID_TRANSFORM' : 8, + 'INVALID_PROTOCOL' : 9, + 'UNSUPPORTED_CLIENT_TYPE' : 10 +}; + +/** + * Initializes a Thrift TApplicationException instance. + * @constructor + * @augments Thrift.TException + * @param {string} message - The TApplicationException message (distinct from the Error message). + * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code. + * @classdesc TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client. +*/ +Thrift.TApplicationException = function(message, code) { + this.message = message; + this.code = typeof code === "number" ? code : 0; +}; +Thrift.inherits(Thrift.TApplicationException, Thrift.TException, 'TApplicationException'); + +/** + * Read a TApplicationException from the supplied protocol. + * @param {object} input - The input protocol to read from. + */ +Thrift.TApplicationException.prototype.read = function(input) { + while (1) { + var ret = input.readFieldBegin(); + + if (ret.ftype == Thrift.Type.STOP) { + break; + } + + var fid = ret.fid; + + switch (fid) { + case 1: + if (ret.ftype == Thrift.Type.STRING) { + ret = input.readString(); + this.message = ret.value; + } else { + ret = input.skip(ret.ftype); + } + break; + case 2: + if (ret.ftype == Thrift.Type.I32) { + ret = input.readI32(); + this.code = ret.value; + } else { + ret = input.skip(ret.ftype); + } + break; + default: + ret = input.skip(ret.ftype); + break; + } + + input.readFieldEnd(); + } + + input.readStructEnd(); +}; + +/** + * Wite a TApplicationException to the supplied protocol. + * @param {object} output - The output protocol to write to. + */ +Thrift.TApplicationException.prototype.write = function(output) { + output.writeStructBegin('TApplicationException'); + + if (this.message) { + output.writeFieldBegin('message', Thrift.Type.STRING, 1); + output.writeString(this.getMessage()); + output.writeFieldEnd(); + } + + if (this.code) { + output.writeFieldBegin('type', Thrift.Type.I32, 2); + output.writeI32(this.code); + output.writeFieldEnd(); + } + + output.writeFieldStop(); + output.writeStructEnd(); +}; + +/** + * Returns the application exception code set on the exception. + * @readonly + * @returns {Thrift.TApplicationExceptionType} exception code + */ +Thrift.TApplicationException.prototype.getCode = function() { + return this.code; +}; + +Thrift.TProtocolExceptionType = { + UNKNOWN: 0, + INVALID_DATA: 1, + NEGATIVE_SIZE: 2, + SIZE_LIMIT: 3, + BAD_VERSION: 4, + NOT_IMPLEMENTED: 5, + DEPTH_LIMIT: 6 +}; + +Thrift.TProtocolException = function TProtocolException(type, message) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.type = type; + this.message = message; +}; +Thrift.inherits(Thrift.TProtocolException, Thrift.TException, 'TProtocolException'); + +/** + * Constructor Function for the XHR transport. + * If you do not specify a url then you must handle XHR operations on + * your own. This type can also be constructed using the Transport alias + * for backward compatibility. + * @constructor + * @param {string} [url] - The URL to connect to. + * @classdesc The Apache Thrift Transport layer performs byte level I/O + * between RPC clients and servers. The JavaScript TXHRTransport object + * uses Http[s]/XHR. Target servers must implement the http[s] transport + * (see: node.js example server_http.js). + * @example + * var transport = new Thrift.TXHRTransport("http://localhost:8585"); + */ +Thrift.Transport = Thrift.TXHRTransport = function(url, options) { + this.url = url; + this.wpos = 0; + this.rpos = 0; + this.useCORS = (options && options.useCORS); + this.customHeaders = options ? (options.customHeaders ? options.customHeaders : {}): {}; + this.send_buf = ''; + this.recv_buf = ''; +}; + +Thrift.TXHRTransport.prototype = { + /** + * Gets the browser specific XmlHttpRequest Object. + * @returns {object} the browser XHR interface object + */ + getXmlHttpRequestObject: function() { + try { return new XMLHttpRequest(); } catch (e1) { } + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { } + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { } + + throw "Your browser doesn't support XHR."; + }, + + /** + * Sends the current XRH request if the transport was created with a URL + * and the async parameter is false. If the transport was not created with + * a URL, or the async parameter is True and no callback is provided, or + * the URL is an empty string, the current send buffer is returned. + * @param {object} async - If true the current send buffer is returned. + * @param {object} callback - Optional async completion callback + * @returns {undefined|string} Nothing or the current send buffer. + * @throws {string} If XHR fails. + */ + flush: function(async, callback) { + var self = this; + if ((async && !callback) || this.url === undefined || this.url === '') { + return this.send_buf; + } + + var xreq = this.getXmlHttpRequestObject(); + + if (xreq.overrideMimeType) { + xreq.overrideMimeType('application/vnd.apache.thrift.json; charset=utf-8'); + } + + if (callback) { + //Ignore XHR callbacks until the data arrives, then call the + // client's callback + xreq.onreadystatechange = + (function() { + var clientCallback = callback; + return function() { + if (this.readyState == 4 && this.status == 200) { + self.setRecvBuffer(this.responseText); + clientCallback(); + } + }; + }()); + + // detect net::ERR_CONNECTION_REFUSED and call the callback. + xreq.onerror = + (function() { + var clientCallback = callback; + return function() { + clientCallback(); + }; + }()); + + } + + xreq.open('POST', this.url, !!async); + + // add custom headers + Object.keys(self.customHeaders).forEach(function(prop) { + xreq.setRequestHeader(prop, self.customHeaders[prop]); + }); + + if (xreq.setRequestHeader) { + xreq.setRequestHeader('Accept', 'application/vnd.apache.thrift.json; charset=utf-8'); + xreq.setRequestHeader('Content-Type', 'application/vnd.apache.thrift.json; charset=utf-8'); + } + + xreq.send(this.send_buf); + if (async && callback) { + return; + } + + if (xreq.readyState != 4) { + throw 'encountered an unknown ajax ready state: ' + xreq.readyState; + } + + if (xreq.status != 200) { + throw 'encountered a unknown request status: ' + xreq.status; + } + + this.recv_buf = xreq.responseText; + this.recv_buf_sz = this.recv_buf.length; + this.wpos = this.recv_buf.length; + this.rpos = 0; + }, + + /** + * Creates a jQuery XHR object to be used for a Thrift server call. + * @param {object} client - The Thrift Service client object generated by the IDL compiler. + * @param {object} postData - The message to send to the server. + * @param {function} args - The original call arguments with the success call back at the end. + * @param {function} recv_method - The Thrift Service Client receive method for the call. + * @returns {object} A new jQuery XHR object. + * @throws {string} If the jQuery version is prior to 1.5 or if jQuery is not found. + */ + jqRequest: function(client, postData, args, recv_method) { + if (typeof jQuery === 'undefined' || + typeof jQuery.Deferred === 'undefined') { + throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests'; + } + + var thriftTransport = this; + + var jqXHR = jQuery.ajax({ + url: this.url, + data: postData, + type: 'POST', + cache: false, + contentType: 'application/vnd.apache.thrift.json; charset=utf-8', + dataType: 'text thrift', + converters: { + 'text thrift' : function(responseData) { + thriftTransport.setRecvBuffer(responseData); + var value = recv_method.call(client); + return value; + } + }, + context: client, + success: jQuery.makeArray(args).pop() + }); + + return jqXHR; + }, + + /** + * Sets the buffer to provide the protocol when deserializing. + * @param {string} buf - The buffer to supply the protocol. + */ + setRecvBuffer: function(buf) { + this.recv_buf = buf; + this.recv_buf_sz = this.recv_buf.length; + this.wpos = this.recv_buf.length; + this.rpos = 0; + }, + + /** + * Returns true if the transport is open, XHR always returns true. + * @readonly + * @returns {boolean} Always True. + */ + isOpen: function() { + return true; + }, + + /** + * Opens the transport connection, with XHR this is a nop. + */ + open: function() {}, + + /** + * Closes the transport connection, with XHR this is a nop. + */ + close: function() {}, + + /** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ + read: function(len) { + var avail = this.wpos - this.rpos; + + if (avail === 0) { + return ''; + } + + var give = len; + + if (avail < len) { + give = avail; + } + + var ret = this.read_buf.substr(this.rpos, give); + this.rpos += give; + + //clear buf when complete? + return ret; + }, + + /** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ + readAll: function() { + return this.recv_buf; + }, + + /** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ + write: function(buf) { + this.send_buf = buf; + }, + + /** + * Returns the send buffer. + * @readonly + * @returns {string} The send buffer. + */ + getSendBuffer: function() { + return this.send_buf; + } + +}; + + +/** + * Constructor Function for the WebSocket transport. + * @constructor + * @param {string} [url] - The URL to connect to. + * @classdesc The Apache Thrift Transport layer performs byte level I/O + * between RPC clients and servers. The JavaScript TWebSocketTransport object + * uses the WebSocket protocol. Target servers must implement WebSocket. + * (see: node.js example server_http.js). + * @example + * var transport = new Thrift.TWebSocketTransport("http://localhost:8585"); + */ +Thrift.TWebSocketTransport = function(url) { + this.__reset(url); +}; + +Thrift.TWebSocketTransport.prototype = { + __reset: function(url) { + this.url = url; //Where to connect + this.socket = null; //The web socket + this.callbacks = []; //Pending callbacks + this.send_pending = []; //Buffers/Callback pairs waiting to be sent + this.send_buf = ''; //Outbound data, immutable until sent + this.recv_buf = ''; //Inbound data + this.rb_wpos = 0; //Network write position in receive buffer + this.rb_rpos = 0; //Client read position in receive buffer + }, + + /** + * Sends the current WS request and registers callback. The async + * parameter is ignored (WS flush is always async) and the callback + * function parameter is required. + * @param {object} async - Ignored. + * @param {object} callback - The client completion callback. + * @returns {undefined|string} Nothing (undefined) + */ + flush: function(async, callback) { + var self = this; + if (this.isOpen()) { + //Send data and register a callback to invoke the client callback + this.socket.send(this.send_buf); + this.callbacks.push((function() { + var clientCallback = callback; + return function(msg) { + self.setRecvBuffer(msg); + clientCallback(); + }; + }())); + } else { + //Queue the send to go out __onOpen + this.send_pending.push({ + buf: this.send_buf, + cb: callback + }); + } + }, + + __onOpen: function() { + var self = this; + if (this.send_pending.length > 0) { + //If the user made calls before the connection was fully + //open, send them now + this.send_pending.forEach(function(elem) { + this.socket.send(elem.buf); + this.callbacks.push((function() { + var clientCallback = elem.cb; + return function(msg) { + self.setRecvBuffer(msg); + clientCallback(); + }; + }())); + }); + this.send_pending = []; + } + }, + + __onClose: function(evt) { + this.__reset(this.url); + }, + + __onMessage: function(evt) { + if (this.callbacks.length) { + this.callbacks.shift()(evt.data); + } + }, + + __onError: function(evt) { + console.log("Thrift WebSocket Error: " + evt.toString()); + this.socket.close(); + }, + + /** + * Sets the buffer to use when receiving server responses. + * @param {string} buf - The buffer to receive server responses. + */ + setRecvBuffer: function(buf) { + this.recv_buf = buf; + this.recv_buf_sz = this.recv_buf.length; + this.wpos = this.recv_buf.length; + this.rpos = 0; + }, + + /** + * Returns true if the transport is open + * @readonly + * @returns {boolean} + */ + isOpen: function() { + return this.socket && this.socket.readyState == this.socket.OPEN; + }, + + /** + * Opens the transport connection + */ + open: function() { + //If OPEN/CONNECTING/CLOSING ignore additional opens + if (this.socket && this.socket.readyState != this.socket.CLOSED) { + return; + } + //If there is no socket or the socket is closed: + this.socket = new WebSocket(this.url); + this.socket.onopen = this.__onOpen.bind(this); + this.socket.onmessage = this.__onMessage.bind(this); + this.socket.onerror = this.__onError.bind(this); + this.socket.onclose = this.__onClose.bind(this); + }, + + /** + * Closes the transport connection + */ + close: function() { + this.socket.close(); + }, + + /** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ + read: function(len) { + var avail = this.wpos - this.rpos; + + if (avail === 0) { + return ''; + } + + var give = len; + + if (avail < len) { + give = avail; + } + + var ret = this.read_buf.substr(this.rpos, give); + this.rpos += give; + + //clear buf when complete? + return ret; + }, + + /** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ + readAll: function() { + return this.recv_buf; + }, + + /** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ + write: function(buf) { + this.send_buf = buf; + }, + + /** + * Returns the send buffer. + * @readonly + * @returns {string} The send buffer. + */ + getSendBuffer: function() { + return this.send_buf; + } + +}; + +/** + * Initializes a Thrift JSON protocol instance. + * @constructor + * @param {Thrift.Transport} transport - The transport to serialize to/from. + * @classdesc Apache Thrift Protocols perform serialization which enables cross + * language RPC. The Protocol type is the JavaScript browser implementation + * of the Apache Thrift TJSONProtocol. + * @example + * var protocol = new Thrift.Protocol(transport); + */ +Thrift.TJSONProtocol = Thrift.Protocol = function(transport) { + this.tstack = []; + this.tpos = []; + this.transport = transport; +}; + +/** + * Thrift IDL type Id to string mapping. + * @readonly + * @see {@link Thrift.Type} + */ +Thrift.Protocol.Type = {}; +Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"'; +Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"'; +Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"'; +Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"'; +Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"'; +Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"'; +Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"'; +Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"'; +Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"'; +Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"'; +Thrift.Protocol.Type[Thrift.Type.SET] = '"set"'; + +/** + * Thrift IDL type string to Id mapping. + * @readonly + * @see {@link Thrift.Type} + */ +Thrift.Protocol.RType = {}; +Thrift.Protocol.RType.tf = Thrift.Type.BOOL; +Thrift.Protocol.RType.i8 = Thrift.Type.BYTE; +Thrift.Protocol.RType.i16 = Thrift.Type.I16; +Thrift.Protocol.RType.i32 = Thrift.Type.I32; +Thrift.Protocol.RType.i64 = Thrift.Type.I64; +Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE; +Thrift.Protocol.RType.rec = Thrift.Type.STRUCT; +Thrift.Protocol.RType.str = Thrift.Type.STRING; +Thrift.Protocol.RType.map = Thrift.Type.MAP; +Thrift.Protocol.RType.lst = Thrift.Type.LIST; +Thrift.Protocol.RType.set = Thrift.Type.SET; + +/** + * The TJSONProtocol version number. + * @readonly + * @const {number} Version + * @memberof Thrift.Protocol + */ + Thrift.Protocol.Version = 1; + +Thrift.Protocol.prototype = { + /** + * Returns the underlying transport. + * @readonly + * @returns {Thrift.Transport} The underlying transport. + */ + getTransport: function() { + return this.transport; + }, + + /** + * Serializes the beginning of a Thrift RPC message. + * @param {string} name - The service method to call. + * @param {Thrift.MessageType} messageType - The type of method call. + * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift). + */ + writeMessageBegin: function(name, messageType, seqid) { + this.tstack = []; + this.tpos = []; + + this.tstack.push([Thrift.Protocol.Version, '"' + + name + '"', messageType, seqid]); + }, + + /** + * Serializes the end of a Thrift RPC message. + */ + writeMessageEnd: function() { + var obj = this.tstack.pop(); + + this.wobj = this.tstack.pop(); + this.wobj.push(obj); + + this.wbuf = '[' + this.wobj.join(',') + ']'; + + this.transport.write(this.wbuf); + }, + + + /** + * Serializes the beginning of a struct. + * @param {string} name - The name of the struct. + */ + writeStructBegin: function(name) { + this.tpos.push(this.tstack.length); + this.tstack.push({}); + }, + + /** + * Serializes the end of a struct. + */ + writeStructEnd: function() { + + var p = this.tpos.pop(); + var struct = this.tstack[p]; + var str = '{'; + var first = true; + for (var key in struct) { + if (first) { + first = false; + } else { + str += ','; + } + + str += key + ':' + struct[key]; + } + + str += '}'; + this.tstack[p] = str; + }, + + /** + * Serializes the beginning of a struct field. + * @param {string} name - The name of the field. + * @param {Thrift.Protocol.Type} fieldType - The data type of the field. + * @param {number} fieldId - The field's unique identifier. + */ + writeFieldBegin: function(name, fieldType, fieldId) { + this.tpos.push(this.tstack.length); + this.tstack.push({ 'fieldId': '"' + + fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType] + }); + + }, + + /** + * Serializes the end of a field. + */ + writeFieldEnd: function() { + var value = this.tstack.pop(); + var fieldInfo = this.tstack.pop(); + + this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' + + fieldInfo.fieldType + ':' + value + '}'; + this.tpos.pop(); + }, + + /** + * Serializes the end of the set of fields for a struct. + */ + writeFieldStop: function() { + //na + }, + + /** + * Serializes the beginning of a map collection. + * @param {Thrift.Type} keyType - The data type of the key. + * @param {Thrift.Type} valType - The data type of the value. + * @param {number} [size] - The number of elements in the map (ignored). + */ + writeMapBegin: function(keyType, valType, size) { + this.tpos.push(this.tstack.length); + this.tstack.push([Thrift.Protocol.Type[keyType], + Thrift.Protocol.Type[valType], 0]); + }, + + /** + * Serializes the end of a map. + */ + writeMapEnd: function() { + var p = this.tpos.pop(); + + if (p == this.tstack.length) { + return; + } + + if ((this.tstack.length - p - 1) % 2 !== 0) { + this.tstack.push(''); + } + + var size = (this.tstack.length - p - 1) / 2; + + this.tstack[p][this.tstack[p].length - 1] = size; + + var map = '}'; + var first = true; + while (this.tstack.length > p + 1) { + var v = this.tstack.pop(); + var k = this.tstack.pop(); + if (first) { + first = false; + } else { + map = ',' + map; + } + + if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings + map = k + ':' + v + map; + } + map = '{' + map; + + this.tstack[p].push(map); + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + }, + + /** + * Serializes the beginning of a list collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ + writeListBegin: function(elemType, size) { + this.tpos.push(this.tstack.length); + this.tstack.push([Thrift.Protocol.Type[elemType], size]); + }, + + /** + * Serializes the end of a list. + */ + writeListEnd: function() { + var p = this.tpos.pop(); + + while (this.tstack.length > p + 1) { + var tmpVal = this.tstack[p + 1]; + this.tstack.splice(p + 1, 1); + this.tstack[p].push(tmpVal); + } + + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + }, + + /** + * Serializes the beginning of a set collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ + writeSetBegin: function(elemType, size) { + this.tpos.push(this.tstack.length); + this.tstack.push([Thrift.Protocol.Type[elemType], size]); + }, + + /** + * Serializes the end of a set. + */ + writeSetEnd: function() { + var p = this.tpos.pop(); + + while (this.tstack.length > p + 1) { + var tmpVal = this.tstack[p + 1]; + this.tstack.splice(p + 1, 1); + this.tstack[p].push(tmpVal); + } + + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + }, + + /** Serializes a boolean */ + writeBool: function(value) { + this.tstack.push(value ? 1 : 0); + }, + + /** Serializes a number */ + writeByte: function(i8) { + this.tstack.push(i8); + }, + + /** Serializes a number */ + writeI16: function(i16) { + this.tstack.push(i16); + }, + + /** Serializes a number */ + writeI32: function(i32) { + this.tstack.push(i32); + }, + + /** Serializes a number */ + writeI64: function(i64) { + this.tstack.push(i64); + }, + + /** Serializes a number */ + writeDouble: function(dbl) { + this.tstack.push(dbl); + }, + + /** Serializes a string */ + writeString: function(str) { + // We do not encode uri components for wire transfer: + if (str === null) { + this.tstack.push(null); + } else { + // concat may be slower than building a byte buffer + var escapedString = ''; + for (var i = 0; i < str.length; i++) { + var ch = str.charAt(i); // a single double quote: " + if (ch === '\"') { + escapedString += '\\\"'; // write out as: \" + } else if (ch === '\\') { // a single backslash + escapedString += '\\\\'; // write out as double backslash + } else if (ch === '\b') { // a single backspace: invisible + escapedString += '\\b'; // write out as: \b" + } else if (ch === '\f') { // a single formfeed: invisible + escapedString += '\\f'; // write out as: \f" + } else if (ch === '\n') { // a single newline: invisible + escapedString += '\\n'; // write out as: \n" + } else if (ch === '\r') { // a single return: invisible + escapedString += '\\r'; // write out as: \r" + } else if (ch === '\t') { // a single tab: invisible + escapedString += '\\t'; // write out as: \t" + } else { + escapedString += ch; // Else it need not be escaped + } + } + this.tstack.push('"' + escapedString + '"'); + } + }, + + /** Serializes a string */ + writeBinary: function(binary) { + var str = ''; + if (typeof binary == 'string') { + str = binary; + } else if (binary instanceof Uint8Array) { + var arr = binary; + for (var i = 0; i < arr.length; ++i) { + str += String.fromCharCode(arr[i]); + } + } else { + throw new TypeError('writeBinary only accepts String or Uint8Array.'); + } + this.tstack.push('"' + btoa(str) + '"'); + }, + + /** + @class + @name AnonReadMessageBeginReturn + @property {string} fname - The name of the service method. + @property {Thrift.MessageType} mtype - The type of message call. + @property {number} rseqid - The sequence number of the message (0 in Thrift RPC). + */ + /** + * Deserializes the beginning of a message. + * @returns {AnonReadMessageBeginReturn} + */ + readMessageBegin: function() { + this.rstack = []; + this.rpos = []; + + if (typeof JSON !== 'undefined' && typeof JSON.parse === 'function') { + this.robj = JSON.parse(this.transport.readAll()); + } else if (typeof jQuery !== 'undefined') { + this.robj = jQuery.parseJSON(this.transport.readAll()); + } else { + this.robj = eval(this.transport.readAll()); + } + + var r = {}; + var version = this.robj.shift(); + + if (version != Thrift.Protocol.Version) { + throw 'Wrong thrift protocol version: ' + version; + } + + r.fname = this.robj.shift(); + r.mtype = this.robj.shift(); + r.rseqid = this.robj.shift(); + + + //get to the main obj + this.rstack.push(this.robj.shift()); + + return r; + }, + + /** Deserializes the end of a message. */ + readMessageEnd: function() { + }, + + /** + * Deserializes the beginning of a struct. + * @param {string} [name] - The name of the struct (ignored) + * @returns {object} - An object with an empty string fname property + */ + readStructBegin: function(name) { + var r = {}; + r.fname = ''; + + //incase this is an array of structs + if (this.rstack[this.rstack.length - 1] instanceof Array) { + this.rstack.push(this.rstack[this.rstack.length - 1].shift()); + } + + return r; + }, + + /** Deserializes the end of a struct. */ + readStructEnd: function() { + if (this.rstack[this.rstack.length - 2] instanceof Array) { + this.rstack.pop(); + } + }, + + /** + @class + @name AnonReadFieldBeginReturn + @property {string} fname - The name of the field (always ''). + @property {Thrift.Type} ftype - The data type of the field. + @property {number} fid - The unique identifier of the field. + */ + /** + * Deserializes the beginning of a field. + * @returns {AnonReadFieldBeginReturn} + */ + readFieldBegin: function() { + var r = {}; + + var fid = -1; + var ftype = Thrift.Type.STOP; + + //get a fieldId + for (var f in (this.rstack[this.rstack.length - 1])) { + if (f === null) { + continue; + } + + fid = parseInt(f, 10); + this.rpos.push(this.rstack.length); + + var field = this.rstack[this.rstack.length - 1][fid]; + + //remove so we don't see it again + delete this.rstack[this.rstack.length - 1][fid]; + + this.rstack.push(field); + + break; + } + + if (fid != -1) { + + //should only be 1 of these but this is the only + //way to match a key + for (var i in (this.rstack[this.rstack.length - 1])) { + if (Thrift.Protocol.RType[i] === null) { + continue; + } + + ftype = Thrift.Protocol.RType[i]; + this.rstack[this.rstack.length - 1] = + this.rstack[this.rstack.length - 1][i]; + } + } + + r.fname = ''; + r.ftype = ftype; + r.fid = fid; + + return r; + }, + + /** Deserializes the end of a field. */ + readFieldEnd: function() { + var pos = this.rpos.pop(); + + //get back to the right place in the stack + while (this.rstack.length > pos) { + this.rstack.pop(); + } + + }, + + /** + @class + @name AnonReadMapBeginReturn + @property {Thrift.Type} ktype - The data type of the key. + @property {Thrift.Type} vtype - The data type of the value. + @property {number} size - The number of elements in the map. + */ + /** + * Deserializes the beginning of a map. + * @returns {AnonReadMapBeginReturn} + */ + readMapBegin: function() { + var map = this.rstack.pop(); + var first = map.shift(); + if (first instanceof Array) { + this.rstack.push(map); + map = first; + first = map.shift(); + } + + var r = {}; + r.ktype = Thrift.Protocol.RType[first]; + r.vtype = Thrift.Protocol.RType[map.shift()]; + r.size = map.shift(); + + + this.rpos.push(this.rstack.length); + this.rstack.push(map.shift()); + + return r; + }, + + /** Deserializes the end of a map. */ + readMapEnd: function() { + this.readFieldEnd(); + }, + + /** + @class + @name AnonReadColBeginReturn + @property {Thrift.Type} etype - The data type of the element. + @property {number} size - The number of elements in the collection. + */ + /** + * Deserializes the beginning of a list. + * @returns {AnonReadColBeginReturn} + */ + readListBegin: function() { + var list = this.rstack[this.rstack.length - 1]; + + var r = {}; + r.etype = Thrift.Protocol.RType[list.shift()]; + r.size = list.shift(); + + this.rpos.push(this.rstack.length); + this.rstack.push(list.shift()); + + return r; + }, + + /** Deserializes the end of a list. */ + readListEnd: function() { + this.readFieldEnd(); + }, + + /** + * Deserializes the beginning of a set. + * @returns {AnonReadColBeginReturn} + */ + readSetBegin: function(elemType, size) { + return this.readListBegin(elemType, size); + }, + + /** Deserializes the end of a set. */ + readSetEnd: function() { + return this.readListEnd(); + }, + + /** Returns an object with a value property set to + * False unless the next number in the protocol buffer + * is 1, in which case the value property is True */ + readBool: function() { + var r = this.readI32(); + + if (r !== null && r.value == '1') { + r.value = true; + } else { + r.value = false; + } + + return r; + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readByte: function() { + return this.readI32(); + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readI16: function() { + return this.readI32(); + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readI32: function(f) { + if (f === undefined) { + f = this.rstack[this.rstack.length - 1]; + } + + var r = {}; + + if (f instanceof Array) { + if (f.length === 0) { + r.value = undefined; + } else { + r.value = f.shift(); + } + } else if (f instanceof Object) { + for (var i in f) { + if (i === null) { + continue; + } + this.rstack.push(f[i]); + delete f[i]; + + r.value = i; + break; + } + } else { + r.value = f; + this.rstack.pop(); + } + + return r; + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readI64: function() { + return this.readI32(); + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readDouble: function() { + return this.readI32(); + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readString: function() { + var r = this.readI32(); + return r; + }, + + /** Returns the an object with a value property set to the + next value found in the protocol buffer */ + readBinary: function() { + var r = this.readI32(); + r.value = atob(r.value); + return r; + }, + + /** + * Method to arbitrarily skip over data */ + skip: function(type) { + var ret, i; + switch (type) { + case Thrift.Type.STOP: + return null; + + case Thrift.Type.BOOL: + return this.readBool(); + + case Thrift.Type.BYTE: + return this.readByte(); + + case Thrift.Type.I16: + return this.readI16(); + + case Thrift.Type.I32: + return this.readI32(); + + case Thrift.Type.I64: + return this.readI64(); + + case Thrift.Type.DOUBLE: + return this.readDouble(); + + case Thrift.Type.STRING: + return this.readString(); + + case Thrift.Type.STRUCT: + this.readStructBegin(); + while (true) { + ret = this.readFieldBegin(); + if (ret.ftype == Thrift.Type.STOP) { + break; + } + this.skip(ret.ftype); + this.readFieldEnd(); + } + this.readStructEnd(); + return null; + + case Thrift.Type.MAP: + ret = this.readMapBegin(); + for (i = 0; i < ret.size; i++) { + if (i > 0) { + if (this.rstack.length > this.rpos[this.rpos.length - 1] + 1) { + this.rstack.pop(); + } + } + this.skip(ret.ktype); + this.skip(ret.vtype); + } + this.readMapEnd(); + return null; + + case Thrift.Type.SET: + ret = this.readSetBegin(); + for (i = 0; i < ret.size; i++) { + this.skip(ret.etype); + } + this.readSetEnd(); + return null; + + case Thrift.Type.LIST: + ret = this.readListBegin(); + for (i = 0; i < ret.size; i++) { + this.skip(ret.etype); + } + this.readListEnd(); + return null; + } + } +}; + + +/** + * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol + * @constructor + */ +Thrift.MultiplexProtocol = function (srvName, trans, strictRead, strictWrite) { + Thrift.Protocol.call(this, trans, strictRead, strictWrite); + this.serviceName = srvName; +}; +Thrift.inherits(Thrift.MultiplexProtocol, Thrift.Protocol, 'multiplexProtocol'); + +/** Override writeMessageBegin method of prototype*/ +Thrift.MultiplexProtocol.prototype.writeMessageBegin = function (name, type, seqid) { + + if (type === Thrift.MessageType.CALL || type === Thrift.MessageType.ONEWAY) { + Thrift.Protocol.prototype.writeMessageBegin.call(this, this.serviceName + ":" + name, type, seqid); + } else { + Thrift.Protocol.prototype.writeMessageBegin.call(this, name, type, seqid); + } +}; + +Thrift.Multiplexer = function () { + this.seqid = 0; +}; + +/** Instantiates a multiplexed client for a specific service + * @constructor + * @param {String} serviceName - The transport to serialize to/from. + * @param {Thrift.ServiceClient} SCl - The Service Client Class + * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port + * @example + * var mp = new Thrift.Multiplexer(); + * var transport = new Thrift.Transport("http://localhost:9090/foo.thrift"); + * var protocol = new Thrift.Protocol(transport); + * var client = mp.createClient('AuthService', AuthServiceClient, transport); +*/ +Thrift.Multiplexer.prototype.createClient = function (serviceName, SCl, transport) { + if (SCl.Client) { + SCl = SCl.Client; + } + var self = this; + SCl.prototype.new_seqid = function () { + self.seqid += 1; + return self.seqid; + }; + var client = new SCl(new Thrift.MultiplexProtocol(serviceName, transport)); + + return client; +}; + + + +var copyList, copyMap; + +copyList = function(lst, types) { + + if (!lst) {return lst; } + + var type; + + if (types.shift === undefined) { + type = types; + } + else { + type = types[0]; + } + var Type = type; + + var len = lst.length, result = [], i, val; + for (i = 0; i < len; i++) { + val = lst[i]; + if (type === null) { + result.push(val); + } + else if (type === copyMap || type === copyList) { + result.push(type(val, types.slice(1))); + } + else { + result.push(new Type(val)); + } + } + return result; +}; + +copyMap = function(obj, types){ + + if (!obj) {return obj; } + + var type; + + if (types.shift === undefined) { + type = types; + } + else { + type = types[0]; + } + var Type = type; + + var result = {}, val; + for(var prop in obj) { + if(obj.hasOwnProperty(prop)) { + val = obj[prop]; + if (type === null) { + result[prop] = val; + } + else if (type === copyMap || type === copyList) { + result[prop] = type(val, types.slice(1)); + } + else { + result[prop] = new Type(val); + } + } + } + return result; +}; + +Thrift.copyMap = copyMap; +Thrift.copyList = copyList; diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/Makefile.am b/vendor/src/github.com/apache/thrift/lib/js/test/Makefile.am new file mode 100644 index 00000000..14927c40 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/Makefile.am @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export CLASSPATH + +# Make sure this doesn't fail if ant is not configured. +clean-local: + ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ + $$ANT $(ANT_FLAGS) clean + +check-local: all + $(ANT) $(ANT_FLAGS) test + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/README.md b/vendor/src/github.com/apache/thrift/lib/js/test/README.md new file mode 100644 index 00000000..9ad140ed --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/README.md @@ -0,0 +1,68 @@ +Thrift Javascript Library +========================= +This browser based Apache Thrift implementation supports +RPC clients using the JSON protocol over Http[s] with XHR +and WebSocket. + +License +------- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Test Servers +------------ +drwxr-xr-x 2 randy randy 4096 Feb 8 15:44 sec +-rw-r--r-- 1 randy randy 2183 Feb 9 04:01 server_http.js +-rw-r--r-- 1 randy randy 2386 Feb 9 05:39 server_https.js + +server_http.js is a Node.js web server which support the +standard Apache Thrift test suite (thrift/test/ThriftTest.thrift). +The server supports Apache Thrift XHR and WebSocket clients. + +server_https.js is the same but uses SSL/TLS. The server key +and cert are pulled from the thrift/test/keys folder. + +Both of these servers support WebSocket (the http: supports ws:, +and the https: support wss:). + +To run the client test with the Java test server use: +$ make check (requires the Apache Thrift Java branch +and make check must have been run in thrift/lib/java +previously). + +To run the client tests with the Node servers run the grunt + build in the parent js directory (see README there). + +Test Clients +------------ +-rw-r--r-- 1 randy randy 13558 Feb 9 07:18 test-async.js +-rw-r--r-- 1 randy randy 5724 Feb 9 03:45 test_handler.js +-rwxr-xr-x 1 randy randy 2719 Feb 9 06:04 test.html +-rw-r--r-- 1 randy randy 4611 Feb 9 06:05 test-jq.js +-rwxr-xr-x 1 randy randy 12153 Feb 9 06:04 test.js +-rw-r--r-- 1 randy randy 2593 Feb 9 06:16 test-nojq.html +-rw-r--r-- 1 randy randy 1450 Feb 9 06:14 test-nojq.js +-rw-r--r-- 1 randy randy 2847 Feb 9 06:31 testws.html + +There are three html test driver files, all of which are +QUnit based. test.html tests the Apache Thrift jQuery +generated code (thrift -gen js:jquery). The test-nojq.html +runs almost identical tests against normal JavaScript builds +(thrift -gen js). Both of the previous tests use the XHR +transport. The testws.html runs similar tests using the +WebSocket transport. The test*.js files are loaded by the +html drivers and contain the actual Apache Thrift tests. diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/build.xml b/vendor/src/github.com/apache/thrift/lib/js/test/build.xml new file mode 100644 index 00000000..b3a6b3e0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/build.xml @@ -0,0 +1,227 @@ + + + + Java Script Test based on Thrift Java Library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + You need libthrift*.jar and libthrift*test.jar located at + ${thrift.java.dir}/build + 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/vendor/src/github.com/apache/thrift/lib/js/test/deep-constructor.test.js b/vendor/src/github.com/apache/thrift/lib/js/test/deep-constructor.test.js new file mode 100644 index 00000000..9a198094 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/deep-constructor.test.js @@ -0,0 +1,195 @@ +function serialize(data) { + var transport = new Thrift.Transport("/service"); + var protocol = new Thrift.Protocol(transport); + protocol.writeMessageBegin("", 0, 0); + data.write(protocol); + protocol.writeMessageEnd(); + return transport.send_buf; +} + +function deserialize(serialized, type) { + var transport = new Thrift.Transport("/service"); + transport.setRecvBuffer(serialized); + var protocol = new Thrift.Protocol(transport); + protocol.readMessageBegin(); + var data = new type(); + data.read(protocol); + protocol.readMessageEnd(); + return data; +} + + +function createThriftObj() { + + return new Complex({ + + struct_field: new Simple({value: 'a'}), + + struct_list_field: [ + new Simple({value: 'b'}), + new Simple({value: 'c'}), + ], + + struct_set_field: [ + new Simple({value: 'd'}), + new Simple({value: 'e'}), + ], + + struct_map_field: { + A: new Simple({value: 'f'}), + B: new Simple({value: 'g'}) + }, + + struct_nested_containers_field: [ + [ + { + C: [ + new Simple({value: 'h'}), + new Simple({value: 'i'}) + ] + } + ] + ], + + + struct_nested_containers_field2: { + D: [ + { + DA: new Simple({value: 'j'}) + }, + { + DB: new Simple({value: 'k'}) + } + ] + } + } + ); +} + + +function createJsObj() { + + return { + + struct_field: {value: 'a'}, + + struct_list_field: [ + {value: 'b'}, + {value: 'c'}, + ], + + struct_set_field: [ + {value: 'd'}, + {value: 'e'}, + ], + + struct_map_field: { + A: {value: 'f'}, + B: {value: 'g'} + }, + + struct_nested_containers_field: [ + [ + { + C: [ + {value: 'h'}, + {value: 'i'} + ] + } + ] + ], + + struct_nested_containers_field2: { + D: [ + { + DA: {value: 'j'} + }, + { + DB: {value: 'k'} + } + ] + } + }; +} + + +function assertValues(obj, assert) { + assert.equal(obj.struct_field.value, 'a'); + assert.equal(obj.struct_list_field[0].value, 'b'); + assert.equal(obj.struct_list_field[1].value, 'c'); + assert.equal(obj.struct_set_field[0].value, 'd'); + assert.equal(obj.struct_set_field[1].value, 'e'); + assert.equal(obj.struct_map_field.A.value, 'f'); + assert.equal(obj.struct_map_field.B.value, 'g'); + assert.equal(obj.struct_nested_containers_field[0][0].C[0].value, 'h'); + assert.equal(obj.struct_nested_containers_field[0][0].C[1].value, 'i'); + assert.equal(obj.struct_nested_containers_field2.D[0].DA.value, 'j'); + assert.equal(obj.struct_nested_containers_field2.D[1].DB.value, 'k'); +} + +var cases = { + + "Serialize/deserialize simple struct should return equal object": function(assert){ + var tObj = new Simple({value: 'a'}); + var received = deserialize(serialize(tObj), Simple); + assert.ok(tObj !== received); + assert.deepEqual(received, tObj); + }, + + + "Serialize/deserialize should return equal object": function(assert){ + var tObj = createThriftObj(); + var received = deserialize(serialize(tObj), Complex); + assert.ok(tObj !== received); + assert.deepEqual(received, tObj); + }, + + "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) { + var tObj1 = createThriftObj(); + var tObj2 = new Complex(createJsObj()); + assertValues(tObj2, assert); + assert.equal(serialize(tObj2), serialize(tObj1)); + }, + + "Modifications to args object should not affect constructed Thrift object": function (assert) { + + var args = createJsObj(); + assertValues(args, assert); + + var tObj = new Complex(args); + assertValues(tObj, assert); + + args.struct_field.value = 'ZZZ'; + args.struct_list_field[0].value = 'ZZZ'; + args.struct_list_field[1].value = 'ZZZ'; + args.struct_set_field[0].value = 'ZZZ'; + args.struct_set_field[1].value = 'ZZZ'; + args.struct_map_field.A.value = 'ZZZ'; + args.struct_map_field.B.value = 'ZZZ'; + args.struct_nested_containers_field[0][0].C[0] = 'ZZZ'; + args.struct_nested_containers_field[0][0].C[1] = 'ZZZ'; + args.struct_nested_containers_field2.D[0].DA = 'ZZZ'; + args.struct_nested_containers_field2.D[0].DB = 'ZZZ'; + + assertValues(tObj, assert); + }, + + "nulls are ok": function(assert) { + var tObj = new Complex({ + struct_field: null, + struct_list_field: null, + struct_set_field: null, + struct_map_field: null, + struct_nested_containers_field: null, + struct_nested_containers_field2: null + }); + var received = deserialize(serialize(tObj), Complex); + assert.ok(tObj !== received); + assert.deepEqual(tObj, received); + } + +}; + +Object.keys(cases).forEach(function(caseName) { + test(caseName, cases[caseName]); +}); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/jsTestDriver.conf b/vendor/src/github.com/apache/thrift/lib/js/test/jsTestDriver.conf new file mode 100644 index 00000000..b9702cd3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/jsTestDriver.conf @@ -0,0 +1,17 @@ +server: http://localhost:9876 + +load: +# Qunit adapter + - build/js/lib/equiv.js + - build/js/lib/QUnitAdapter.js +# dependencies + - build/js/lib/jquery.js + - build/js/thrift.js + - gen-js/ThriftTest_types.js + - gen-js/ThriftTest.js +# the test suite + - test.js + +# redirect to the Java based Thrift testserver +proxy: + - {matcher: "*", server: " http://localhost:8088"} diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/phantom-client.js b/vendor/src/github.com/apache/thrift/lib/js/test/phantom-client.js new file mode 100644 index 00000000..f75b2561 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/phantom-client.js @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +(function() { + 'use strict'; + + // Rudimentary test helper functions + // TODO: Return error code based on kind of errors rather than throw + var ok = function(t, msg) { + if (!t) { + console.log('*** FAILED ***'); + throw new Error(msg); + } + }; + var equal = function(a, b) { + if (a !== b) { + console.log('*** FAILED ***'); + throw new Error(); + } + }; + var test = function(name, f) { + console.log('TEST : ' + name); + f(); + console.log('OK\n'); + }; + + var parseArgs = function(args) { + var skips = [ + '--transport=http', + '--protocol=json', + ]; + var opts = { + port: '9090', + // protocol: 'json', + }; + var keys = {}; + for (var key in opts) { + keys['--' + key + '='] = key; + } + for (var i in args) { + var arg = args[i]; + if (skips.indexOf(arg) != -1) { + continue; + } + var hit = false; + for (var k in keys) { + if (arg.slice(0, k.length) === k) { + opts[keys[k]] = arg.slice(k.length); + hit = true; + break; + } + } + if (!hit) { + throw new Error('Unknown argument: ' + arg); + } + } + opts.port = parseInt(opts.port, 10); + if (!opts.port || opts.port < 1 || opts.port > 65535) { + throw new Error('Invalid port number'); + } + return opts; + }; + + var execute = function() { + console.log('### Apache Thrift Javascript standalone test client'); + console.log('------------------------------------------------------------'); + + phantom.page.injectJs('src/thrift.js'); + phantom.page.injectJs('test/gen-js/ThriftTest_types.js'); + phantom.page.injectJs('test/gen-js/ThriftTest.js'); + + var system = require('system'); + var opts = parseArgs(system.args.slice(1)); + var port = opts.port; + var transport = new Thrift.Transport('http://localhost:' + port + '/service'); + var protocol = new Thrift.Protocol(transport); + var client = new ThriftTest.ThriftTestClient(protocol); + + + // TODO: Remove duplicate code with test.js. + // all Languages in UTF-8 + var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語"; + + function checkRecursively(map1, map2) { + if (typeof map1 !== 'function' && typeof map2 !== 'function') { + if (!map1 || typeof map1 !== 'object') { + equal(map1, map2); + } else { + for (var key in map1) { + checkRecursively(map1[key], map2[key]); + } + } + } + } + + test("Void", function() { + equal(client.testVoid(), undefined); + }); + test("Binary (String)", function() { + var binary = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + equal(client.testBinary(binary), binary); + }); + test("Binary (Uint8Array)", function() { + var binary = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + var arr = new Uint8Array(binary.length); + for (var i = 0; i < binary.length; ++i) { + arr[i] = binary[i].charCodeAt(); + } + equal(client.testBinary(arr), binary); + }); + test("String", function() { + equal(client.testString(''), ''); + equal(client.testString(stringTest), stringTest); + + var specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; + equal(client.testString(specialCharacters),specialCharacters); + }); + test("Double", function() { + equal(client.testDouble(0), 0); + equal(client.testDouble(-1), -1); + equal(client.testDouble(3.14), 3.14); + equal(client.testDouble(Math.pow(2,60)), Math.pow(2,60)); + }); + test("Bool", function() { + equal(client.testBool(true), true); + equal(client.testBool(false), false); + }); + test("I8", function() { + equal(client.testByte(0), 0); + equal(client.testByte(0x01), 0x01); + }); + test("I32", function() { + equal(client.testI32(0), 0); + equal(client.testI32(Math.pow(2,30)), Math.pow(2,30)); + equal(client.testI32(-Math.pow(2,30)), -Math.pow(2,30)); + }); + test("I64", function() { + equal(client.testI64(0), 0); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + equal(client.testI64(Math.pow(2,52)), Math.pow(2,52)); + equal(client.testI64(-Math.pow(2,52)), -Math.pow(2,52)); + }); + + test("Struct", function() { + var structTestInput = new ThriftTest.Xtruct(); + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + structTestInput.i64_thing = Math.pow(2,52); + + var structTestOutput = client.testStruct(structTestInput); + + equal(structTestOutput.string_thing, structTestInput.string_thing); + equal(structTestOutput.byte_thing, structTestInput.byte_thing); + equal(structTestOutput.i32_thing, structTestInput.i32_thing); + equal(structTestOutput.i64_thing, structTestInput.i64_thing); + + equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput)); + }); + + test("Nest", function() { + var xtrTestInput = new ThriftTest.Xtruct(); + xtrTestInput.string_thing = 'worked'; + xtrTestInput.byte_thing = 0x01; + xtrTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + xtrTestInput.i64_thing = Math.pow(2,52); + + var nestTestInput = new ThriftTest.Xtruct2(); + nestTestInput.byte_thing = 0x02; + nestTestInput.struct_thing = xtrTestInput; + nestTestInput.i32_thing = Math.pow(2,15); + + var nestTestOutput = client.testNest(nestTestInput); + + equal(nestTestOutput.byte_thing, nestTestInput.byte_thing); + equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); + equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); + equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); + equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing); + equal(nestTestOutput.i32_thing, nestTestInput.i32_thing); + + equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput)); + }); + + test("Map", function() { + var mapTestInput = {7:77, 8:88, 9:99}; + + var mapTestOutput = client.testMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test("StringMap", function() { + var mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" + }; + + var mapTestOutput = client.testStringMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test("Set", function() { + var setTestInput = [1,2,3]; + ok(client.testSet(setTestInput), setTestInput); + }); + + test("List", function() { + var listTestInput = [1,2,3]; + ok(client.testList(listTestInput), listTestInput); + }); + + test("Enum", function() { + equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE); + }); + + test("TypeDef", function() { + equal(client.testTypedef(69), 69); + }); + + test("Skip", function() { + var structTestInput = new ThriftTest.Xtruct(); + var modifiedClient = new ThriftTest.ThriftTestClient(protocol); + + modifiedClient.recv_testStruct = function() { + var input = modifiedClient.input; + var xtruct3 = new ThriftTest.Xtruct3(); + + input.readMessageBegin(); + input.readStructBegin(); + + // read Xtruct data with Xtruct3 + input.readFieldBegin(); + xtruct3.read(input); + input.readFieldEnd(); + // read Thrift.Type.STOP message + input.readFieldBegin(); + input.readFieldEnd(); + + input.readStructEnd(); + input.readMessageEnd(); + + return xtruct3; + }; + + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2,30); + structTestInput.i64_thing = Math.pow(2,52); + + var structTestOutput = modifiedClient.testStruct(structTestInput); + + equal(structTestOutput instanceof ThriftTest.Xtruct3, true); + equal(structTestOutput.string_thing, structTestInput.string_thing); + equal(structTestOutput.changed, null); + equal(structTestOutput.i32_thing, structTestInput.i32_thing); + equal(structTestOutput.i64_thing, structTestInput.i64_thing); + }); + + test("MapMap", function() { + var mapMapTestExpectedResult = { + "4":{"1":1,"2":2,"3":3,"4":4}, + "-4":{"-4":-4, "-3":-3, "-2":-2, "-1":-1} + }; + + var mapMapTestOutput = client.testMapMap(1); + + + for (var key in mapMapTestOutput) { + for (var key2 in mapMapTestOutput[key]) { + equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]); + } + } + + checkRecursively(mapMapTestOutput, mapMapTestExpectedResult); + }); + + test("Xception", function() { + try { + client.testException("Xception"); + ok(false); + } catch (e) { + equal(e.errorCode, 1001); + equal(e.message, "Xception"); + } + }); + + test("no Exception", function() { + try { + client.testException("no Exception"); + } catch (e) { + ok(false); + } + }); + + test("TException", function() { + try { + client.testException("TException"); + ok(false); + } catch(e) { + ok(ok); + } + }); + + var crazy = { + "userMap":{ "5":5, "8":8 }, + "xtructs":[{ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }, + { + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + }] + }; + test("Insanity", function() { + var insanity = { + "1":{ + "2":crazy, + "3":crazy + }, + "2":{ "6":{ "userMap":null, "xtructs":null } } + }; + var res = client.testInsanity(new ThriftTest.Insanity(crazy)); + ok(res, JSON.stringify(res)); + ok(insanity, JSON.stringify(insanity)); + + checkRecursively(res, insanity); + }); + + console.log('------------------------------------------------------------'); + console.log('### All tests succeeded.'); + return 0; + }; + + try { + var ret = execute(); + phantom.exit(ret); + } catch (err) { + // Catch all and exit to avoid hang. + console.error(err); + phantom.exit(1); + } +})(); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/phantomjs-qunit.js b/vendor/src/github.com/apache/thrift/lib/js/test/phantomjs-qunit.js new file mode 100644 index 00000000..a840db61 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/phantomjs-qunit.js @@ -0,0 +1,91 @@ +/*jshint evil:true*/ + +/* This file is only used by the test suite. + * + * Origin: https://github.com/ariya/phantomjs/blob/master/examples/run-qunit.js + * License: https://github.com/ariya/phantomjs/blob/master/LICENSE.BSD + * + * Inclusion into Apache products is allowed according to http://www.apache.org/legal/3party.html + */ + +var system = require('system'); + + +/** + * Wait until the test condition is true or a timeout occurs. Useful for waiting + * on a server response or for a ui change (fadeIn, etc.) to occur. + * + * @param testFx javascript condition that evaluates to a boolean, + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or + * as a callback function. + * @param onReady what to do when testFx condition is fulfilled, + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or + * as a callback function. + * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. + */ +function waitFor(testFx, onReady, timeOutMillis) { + var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s + start = new Date().getTime(), + condition = false, + interval = setInterval(function() { + if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { + // If not time-out yet and condition not yet fulfilled + condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code + } else { + if(!condition) { + // If condition still not fulfilled (timeout but condition is 'false') + console.log("'waitFor()' timeout"); + phantom.exit(1); + } else { + // Condition fulfilled (timeout and/or condition is 'true') + console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); + if (typeof(onReady) === "string") { + eval(onReady); + } else { + onReady(); //< Do what it's supposed to do once the condition is fulfilled + } + clearInterval(interval); //< Stop this interval + } + } + }, 100); //< repeat check every 250ms +} + + +if (system.args.length === 1 || system.args.length > 3) { + console.log('Usage: phantomjs phantomjs-qunit.js URL'); + phantom.exit(1); +} + +var page = new WebPage(); + +// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") +page.onConsoleMessage = function(msg) { + console.log(msg); +}; + +page.open(system.args[1], function(status){ + if (status !== "success") { + console.log("Unable to access network"); + phantom.exit(1); + } else { + waitFor(function(){ + return page.evaluate(function(){ + var el = document.getElementById('qunit-testresult'); + if (el && el.innerText.match('completed')) { + return true; + } + return false; + }); + }, function(){ + var failedNum = page.evaluate(function(){ + var el = document.getElementById('qunit-testresult'); + console.log(el.innerText); + try { + return el.getElementsByClassName('failed')[0].innerHTML; + } catch (e) { } + return 10000; + }); + phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0); + }); + } +}); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/server_http.js b/vendor/src/github.com/apache/thrift/lib/js/test/server_http.js new file mode 100644 index 00000000..e195e802 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/server_http.js @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//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). + +var thrift = require('../../nodejs/lib/thrift'); +var ThriftTestSvc = require('./gen-nodejs/ThriftTest.js'); +var ThriftTestHandler = require('./test_handler').ThriftTestHandler; + +var ThriftTestSvcOpt = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: ThriftTestSvc, + handler: ThriftTestHandler +}; + +var ThriftWebServerOptions = { + files: ".", + services: { + "/service": ThriftTestSvcOpt + } +}; + +var server = thrift.createWebServer(ThriftWebServerOptions); +var port = 8088; +server.listen(port); +console.log("Serving files from: " + __dirname); +console.log("Http/Thrift Server running on port: " + port); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/server_https.js b/vendor/src/github.com/apache/thrift/lib/js/test/server_https.js new file mode 100644 index 00000000..af1745ba --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/server_https.js @@ -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. + */ + +//This HTTP server is designed to server the test.html browser +// based JavaScript test page (which must be in the current directory). +// This server also supplies the Thrift based test service, which depends +// on the standard ThriftTest.thrift IDL service (which must be compiled +// for Node and browser based JavaScript in ./gen-nodejs and ./gen-js +// respectively). The current directory must also include the browser +// support libraries for test.html (jquery.js, qunit.js and qunit.css +// in ./build/js/lib). + +var fs = require("fs"); +var thrift = require('../../nodejs/lib/thrift'); +var ThriftTestSvc = require('./gen-nodejs/ThriftTest.js'); +var ThriftTestHandler = require('./test_handler').ThriftTestHandler; + +//Setup the I/O stack options for the ThriftTest service +var ThriftTestSvcOpt = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: ThriftTestSvc, + handler: ThriftTestHandler +}; + +var ThriftWebServerOptions = { + files: ".", + tls: { + key: fs.readFileSync("../../../test/keys/server.key"), + cert: fs.readFileSync("../../../test/keys/server.crt") + }, + services: { + "/service": ThriftTestSvcOpt + } +}; + +var server = thrift.createWebServer(ThriftWebServerOptions); +var port = 8089; +server.listen(port); +console.log("Serving files from: " + __dirname); +console.log("Http/Thrift Server running on port: " + port); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/src/test/Httpd.java b/vendor/src/github.com/apache/thrift/lib/js/test/src/test/Httpd.java new file mode 100644 index 00000000..e4fc0ccd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/src/test/Httpd.java @@ -0,0 +1,323 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package test; + +import java.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URLDecoder; +import java.util.Locale; + +import org.apache.http.ConnectionClosedException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpServerConnection; +import org.apache.http.HttpStatus; +import org.apache.http.MethodNotSupportedException; +import org.apache.http.entity.ContentProducer; +import org.apache.http.entity.EntityTemplate; +import org.apache.http.entity.FileEntity; +import org.apache.http.impl.DefaultHttpResponseFactory; +import org.apache.http.impl.DefaultHttpServerConnection; +import org.apache.http.impl.NoConnectionReuseStrategy; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.BasicHttpProcessor; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpProcessor; +import org.apache.http.protocol.HttpRequestHandler; +import org.apache.http.protocol.HttpRequestHandlerRegistry; +import org.apache.http.protocol.HttpService; +import org.apache.http.util.EntityUtils; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TMemoryBuffer; + +import thrift.test.ThriftTest; +import org.apache.thrift.server.ServerTestBase.TestHandler; + +import eu.medsea.mimeutil.detector.ExtensionMimeDetector; +import eu.medsea.mimeutil.MimeUtil2; +import eu.medsea.mimeutil.MimeType; +import java.util.Collection; +import java.util.Iterator; + +/** + * Basic, yet fully functional and spec compliant, HTTP/1.1 file server. + *

+ * Please note the purpose of this application is demonstrate the usage of + * HttpCore APIs. It is NOT intended to demonstrate the most efficient way of + * building an HTTP file server. + * + * + */ +public class Httpd { + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.err.println("Please specify document root directory"); + System.exit(1); + } + Thread t = new RequestListenerThread(8088, args[0]); + t.setDaemon(false); + t.start(); + } + + static class HttpFileHandler implements HttpRequestHandler { + + private final String docRoot; + + public HttpFileHandler(final String docRoot) { + super(); + this.docRoot = docRoot; + } + + public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { + + String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH); + if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { + throw new MethodNotSupportedException(method + " method not supported"); + } + String target = request.getRequestLine().getUri(); + + if (request instanceof HttpEntityEnclosingRequest && target.equals("/service")) { + HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + byte[] entityContent = EntityUtils.toByteArray(entity); + System.out.println("Incoming content: " + new String(entityContent)); + + final String output = this.thriftRequest(entityContent); + + System.out.println("Outgoing content: "+output); + + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write(output); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + } else { + if(target.indexOf("?") != -1) { + target = target.substring(1, target.indexOf("?")); + } + + final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8")); + + if (!file.exists()) { + + response.setStatusCode(HttpStatus.SC_NOT_FOUND); + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write("

"); + writer.write("File "); + writer.write(file.getPath()); + writer.write(" not found"); + writer.write("

"); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + System.out.println("File " + file.getPath() + " not found"); + + } else if (!file.canRead() || file.isDirectory()) { + + response.setStatusCode(HttpStatus.SC_FORBIDDEN); + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write("

"); + writer.write("Access denied"); + writer.write("

"); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + System.out.println("Cannot read file " + file.getPath()); + + } else { + + String mimeType = "application/octet-stream"; + MimeUtil2 mimeUtil = new MimeUtil2(); + synchronized (this) { + mimeUtil.registerMimeDetector(ExtensionMimeDetector.class.getName()); + } + Collection collection = mimeUtil.getMimeTypes(file); + Iterator iterator = collection.iterator(); + while(iterator.hasNext()) { + MimeType mt = iterator.next(); + mimeType = mt.getMediaType() + "/" + mt.getSubType(); + break; + } + + response.setStatusCode(HttpStatus.SC_OK); + FileEntity body = new FileEntity(file, mimeType); + response.addHeader("Content-Type", mimeType); + response.setEntity(body); + System.out.println("Serving file " + file.getPath()); + + } + } + } + + private String thriftRequest(byte[] input){ + try{ + + //Input + TMemoryBuffer inbuffer = new TMemoryBuffer(input.length); + inbuffer.write(input); + TProtocol inprotocol = new TJSONProtocol(inbuffer); + + //Output + TMemoryBuffer outbuffer = new TMemoryBuffer(100); + TProtocol outprotocol = new TJSONProtocol(outbuffer); + + TProcessor processor = new ThriftTest.Processor(new TestHandler()); + processor.process(inprotocol, outprotocol); + + byte[] output = new byte[outbuffer.length()]; + outbuffer.readAll(output, 0, output.length); + + return new String(output,"UTF-8"); + }catch(Throwable t){ + return "Error:"+t.getMessage(); + } + + + } + + } + + static class RequestListenerThread extends Thread { + + private final ServerSocket serversocket; + private final HttpParams params; + private final HttpService httpService; + + public RequestListenerThread(int port, final String docroot) throws IOException { + this.serversocket = new ServerSocket(port); + this.params = new BasicHttpParams(); + this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) + .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) + .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); + + // Set up the HTTP protocol processor + HttpProcessor httpproc = new BasicHttpProcessor(); + + // Set up request handlers + HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); + reqistry.register("*", new HttpFileHandler(docroot)); + + // Set up the HTTP service + this.httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory()); + this.httpService.setParams(this.params); + this.httpService.setHandlerResolver(reqistry); + } + + public void run() { + System.out.println("Listening on port " + this.serversocket.getLocalPort()); + System.out.println("Point your browser to http://localhost:8088/test/test.html"); + + while (!Thread.interrupted()) { + try { + // Set up HTTP connection + Socket socket = this.serversocket.accept(); + DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + System.out.println("Incoming connection from " + socket.getInetAddress()); + conn.bind(socket, this.params); + + // Start worker thread + Thread t = new WorkerThread(this.httpService, conn); + t.setDaemon(true); + t.start(); + } catch (InterruptedIOException ex) { + break; + } catch (IOException e) { + System.err.println("I/O error initialising connection thread: " + e.getMessage()); + break; + } + } + } + } + + static class WorkerThread extends Thread { + + private final HttpService httpservice; + private final HttpServerConnection conn; + + public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) { + super(); + this.httpservice = httpservice; + this.conn = conn; + } + + public void run() { + System.out.println("New connection thread"); + HttpContext context = new BasicHttpContext(null); + try { + while (!Thread.interrupted() && this.conn.isOpen()) { + this.httpservice.handleRequest(this.conn, context); + } + } catch (ConnectionClosedException ex) { + System.err.println("Client closed connection"); + } catch (IOException ex) { + System.err.println("I/O error: " + ex.getMessage()); + } catch (HttpException ex) { + System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage()); + } finally { + try { + this.conn.shutdown(); + } catch (IOException ignore) { + } + } + } + + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test-async.js b/vendor/src/github.com/apache/thrift/lib/js/test/test-async.js new file mode 100644 index 00000000..336e2bc2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test-async.js @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +/* + * Fully Async JavaScript test suite for ThriftTest.thrift. + * These tests are designed to exercise the WebSocket transport + * (which is exclusively async). + * + * To compile client code for this test use: + * $ thrift -gen js ThriftTest.thrift + */ + + + +// all Languages in UTF-8 +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]); + } + } + } +} + +module("Base Types"); + + asyncTest("Void", function() { + expect( 1 ); + client.testVoid(function(result) { + equal(result, undefined); + QUnit.start(); + }); + }); + + + asyncTest("String", function() { + expect( 3 ); + QUnit.stop(2); + client.testString('', function(result){ + equal(result, ''); + QUnit.start(); + }); + client.testString(stringTest, function(result){ + equal(result, stringTest); + QUnit.start(); + }); + + var specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; + client.testString(specialCharacters, function(result){ + equal(result, specialCharacters); + QUnit.start(); + }); + }); + asyncTest("Double", function() { + expect( 4 ); + QUnit.stop(3); + client.testDouble(0, function(result){ + equal(result, 0); + QUnit.start(); + }); + client.testDouble(-1, function(result){ + equal(result, -1); + QUnit.start(); + }); + client.testDouble(3.14, function(result){ + equal(result, 3.14); + QUnit.start(); + }); + client.testDouble(Math.pow(2,60), function(result){ + equal(result, Math.pow(2,60)); + QUnit.start(); + }); + }); + // TODO: add testBinary() + asyncTest("Byte", function() { + expect( 2 ); + QUnit.stop(); + client.testByte(0, function(result) { + equal(result, 0); + QUnit.start(); + }); + client.testByte(0x01, function(result) { + equal(result, 0x01); + QUnit.start(); + }); + }); + asyncTest("I32", function() { + expect( 3 ); + QUnit.stop(2); + client.testI32(0, function(result){ + equal(result, 0); + QUnit.start(); + }); + client.testI32(Math.pow(2,30), function(result){ + equal(result, Math.pow(2,30)); + QUnit.start(); + }); + client.testI32(-Math.pow(2,30), function(result){ + equal(result, -Math.pow(2,30)); + QUnit.start(); + }); + }); + asyncTest("I64", function() { + expect( 3 ); + QUnit.stop(2); + client.testI64(0, function(result){ + equal(result, 0); + QUnit.start(); + }); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + client.testI64(Math.pow(2,52), function(result){ + equal(result, Math.pow(2,52)); + QUnit.start(); + }); + client.testI64(-Math.pow(2,52), function(result){ + equal(result, -Math.pow(2,52)); + QUnit.start(); + }); + }); + + + + +module("Structured Types"); + + asyncTest("Struct", function() { + expect( 5 ); + var structTestInput = new ThriftTest.Xtruct(); + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + structTestInput.i64_thing = Math.pow(2,52); + + client.testStruct(structTestInput, function(result){ + equal(result.string_thing, structTestInput.string_thing); + equal(result.byte_thing, structTestInput.byte_thing); + equal(result.i32_thing, structTestInput.i32_thing); + equal(result.i64_thing, structTestInput.i64_thing); + equal(JSON.stringify(result), JSON.stringify(structTestInput)); + QUnit.start(); + }); + }); + + asyncTest("Nest", function() { + expect( 7 ); + var xtrTestInput = new ThriftTest.Xtruct(); + xtrTestInput.string_thing = 'worked'; + xtrTestInput.byte_thing = 0x01; + xtrTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + xtrTestInput.i64_thing = Math.pow(2,52); + + var nestTestInput = new ThriftTest.Xtruct2(); + nestTestInput.byte_thing = 0x02; + nestTestInput.struct_thing = xtrTestInput; + nestTestInput.i32_thing = Math.pow(2,15); + + client.testNest(nestTestInput, function(result){ + equal(result.byte_thing, nestTestInput.byte_thing); + equal(result.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); + equal(result.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); + equal(result.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); + equal(result.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing); + equal(result.i32_thing, nestTestInput.i32_thing); + equal(JSON.stringify(result), JSON.stringify(nestTestInput)); + QUnit.start(); + }); + }); + + asyncTest("Map", function() { + expect( 3 ); + var mapTestInput = {7:77, 8:88, 9:99}; + + client.testMap(mapTestInput, function(result){ + for (var key in result) { + equal(result[key], mapTestInput[key]); + } + QUnit.start(); + }); + }); + + asyncTest("StringMap", function() { + expect( 6 ); + var mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" + }; + + client.testStringMap(mapTestInput, function(result){ + for (var key in result) { + equal(result[key], mapTestInput[key]); + } + QUnit.start(); + }); + }); + + asyncTest("Set", function() { + expect( 1 ); + var setTestInput = [1,2,3]; + client.testSet(setTestInput, function(result){ + ok(result, setTestInput); + QUnit.start(); + }); + }); + + asyncTest("List", function() { + expect( 1 ); + var listTestInput = [1,2,3]; + client.testList(listTestInput, function(result){ + ok(result, listTestInput); + QUnit.start(); + }); + }); + + asyncTest("Enum", function() { + expect( 1 ); + client.testEnum(ThriftTest.Numberz.ONE, function(result){ + equal(result, ThriftTest.Numberz.ONE); + QUnit.start(); + }); + }); + + asyncTest("TypeDef", function() { + expect( 1 ); + client.testTypedef(69, function(result){ + equal(result, 69); + QUnit.start(); + }); + }); + + +module("deeper!"); + + asyncTest("MapMap", function() { + expect( 16 ); + var mapMapTestExpectedResult = { + "4":{"1":1,"2":2,"3":3,"4":4}, + "-4":{"-4":-4, "-3":-3, "-2":-2, "-1":-1} + }; + + client.testMapMap(1, function(result){ + for (var key in result) { + for (var key2 in result[key]) { + equal(result[key][key2], mapMapTestExpectedResult[key][key2]); + } + } + checkRecursively(result, mapMapTestExpectedResult); + QUnit.start(); + }); + }); + + +module("Exception"); + + asyncTest("Xception", function() { + expect(2); + client.testException("Xception", function(e){ + equal(e.errorCode, 1001); + equal(e.message, "Xception"); + QUnit.start(); + }); + }); + + asyncTest("no Exception", 0, function() { + expect( 1 ); + client.testException("no Exception", function(e){ + ok(!e); + QUnit.start(); + }); + }); + +module("Insanity"); + + asyncTest("testInsanity", function() { + expect( 24 ); + var insanity = { + "1":{ + "2":{ + "userMap":{ "5":5, "8":8 }, + "xtructs":[{ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }, + { + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + } + ] + }, + "3":{ + "userMap":{ "5":5, "8":8 }, + "xtructs":[{ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }, + { + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + } + ] + } + }, + "2":{ "6":{ "userMap":null, "xtructs":null } } + }; + client.testInsanity(new ThriftTest.Insanity(), function(res){ + ok(res, JSON.stringify(res)); + ok(insanity, JSON.stringify(insanity)); + checkRecursively(res, insanity); + QUnit.start(); + }); + }); + + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test-deep-constructor.html b/vendor/src/github.com/apache/thrift/lib/js/test/test-deep-constructor.html new file mode 100644 index 00000000..5835dc84 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test-deep-constructor.html @@ -0,0 +1,49 @@ + + + + + + Thrift Javascript Bindings: Unit Test + + + + + + + + + + + + + + +

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

+

+
+

+
+

+ Valid XHTML 1.0! +

+ + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test-jq.js b/vendor/src/github.com/apache/thrift/lib/js/test/test-jq.js new file mode 100644 index 00000000..23ed60f7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test-jq.js @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +/* + * JavaScript test suite for ThriftTest.thrift. These tests + * will run only with jQuery (-gen js:jquery) Apache Thrift + * interfaces. To create client code: + * $ thrift -gen js:jquery ThriftTest.thrift + * + * See also: + * ++ test.js for generic tests + * ++ test-nojq.js for "-gen js" only tests + */ + + +////////////////////////////////// +//jQuery asynchronous tests +jQuery.ajaxSetup({ timeout: 0 }); +$(document).ajaxError( function() { QUnit.start(); } ); + +module("jQ Async Manual"); + + test("testI32", function() { + expect( 2 ); + QUnit.stop(); + + var transport = new Thrift.Transport(); + var protocol = new Thrift.Protocol(transport); + var client = new ThriftTest.ThriftTestClient(protocol); + + var jqxhr = jQuery.ajax({ + url: "/service", + data: client.send_testI32(Math.pow(-2,31)), + type: "POST", + cache: false, + dataType: "text", + success: function(res){ + transport.setRecvBuffer( res ); + equal(client.recv_testI32(), Math.pow(-2,31)); + }, + error: function() { ok(false); }, + complete: function() { + ok(true); + QUnit.start(); + } + }); + }); + + test("testI64", function() { + expect( 2 ); + QUnit.stop(); + + var transport = new Thrift.Transport(); + var protocol = new Thrift.Protocol(transport); + var client = new ThriftTest.ThriftTestClient(protocol); + + jQuery.ajax({ + url: "/service", + //This is usually 2^61 but JS cannot represent anything over 2^52 accurately + data: client.send_testI64(Math.pow(-2,52)), + type: "POST", + cache: false, + dataType: "text", + success: function(res){ + transport.setRecvBuffer( res ); + //This is usually 2^61 but JS cannot represent anything over 2^52 accurately + equal(client.recv_testI64(), Math.pow(-2,52)); + }, + error: function() { ok(false); }, + complete: function() { + ok(true); + QUnit.start(); + } + }); + }); + + +module("jQ Async"); + test("I32", function() { + expect( 3 ); + + QUnit.stop(); + client.testI32(Math.pow(2,30), function(result) { + equal(result, Math.pow(2,30)); + QUnit.start(); + }); + + QUnit.stop(); + var jqxhr = client.testI32(Math.pow(-2,31), function(result) { + equal(result, Math.pow(-2,31)); + }); + + jqxhr.success(function(result) { + equal(result, Math.pow(-2,31)); + QUnit.start(); + }); + }); + + test("I64", function() { + expect( 4 ); + + QUnit.stop(); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + client.testI64(Math.pow(2,52), function(result) { + equal(result, Math.pow(2,52)); + QUnit.start(); + }); + + QUnit.stop(); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + client.testI64(Math.pow(-2,52), function(result) { + equal(result, Math.pow(-2,52)); + }) + .error( function(xhr, status, e) { ok(false, e.message); } ) + .success(function(result) { + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + equal(result, Math.pow(-2,52)); + }) + .complete(function() { + ok(true); + QUnit.start(); + }); + }); + + test("Xception", function() { + expect( 2 ); + + QUnit.stop(); + + var dfd = client.testException("Xception", function(result) { + ok(false); + QUnit.start(); + }) + .error(function(xhr, status, e){ + equal(e.errorCode, 1001); + equal(e.message, "Xception"); + //QUnit.start(); + //Note start is not required here because: + //$(document).ajaxError( function() { QUnit.start(); } ); + }); + }); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.html b/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.html new file mode 100644 index 00000000..541bffe0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.html @@ -0,0 +1,52 @@ + + + + + + Thrift Javascript Bindings: Unit Test + + + + + + + + + + + + + + +

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

+
+

+
+ + + + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.js b/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.js new file mode 100644 index 00000000..19f9e61d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test-nojq.js @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +/* + * JavaScript test suite for ThriftTest.thrift. These tests + * will run only with normal "-gen js" Apache Thrift interfaces. + * To create client code: + * $ thrift -gen js ThriftTest.thrift + * + * See also: + * ++ test.js for generic tests + * ++ test-jq.js for "-gen js:jquery" only tests + */ + + +////////////////////////////////// +//Async exception tests + +module("NojQ Async"); + + test("Xception", function() { + expect( 2 ); + + QUnit.stop(); + + client.testException("Xception", function(result) { + equal(result.errorCode, 1001); + equal(result.message, "Xception"); + QUnit.start(); + }); + }); + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test.html b/vendor/src/github.com/apache/thrift/lib/js/test/test.html new file mode 100644 index 00000000..edec3a33 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test.html @@ -0,0 +1,59 @@ + + + + + + Thrift Javascript Bindings: Unit Test + + + + + + + + + + + + + + + + + + +

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

+
+

+
+ + + diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test.js b/vendor/src/github.com/apache/thrift/lib/js/test/test.js new file mode 100644 index 00000000..2d8ec9b1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test.js @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* jshint -W100 */ + +/* + * JavaScript test suite for ThriftTest.thrift. These tests + * will run against Normal (-gen js) and jQuery (-gen js:jquery) + * Apache Thrift interfaces. + * + * Synchronous blocking calls should be identical in both + * Normal and jQuery interfaces. All synchronous tests belong + * here. + * + * Asynchronous success callbacks passed as the last parameter + * of an RPC call should be identical in both Normal and jQuery + * interfaces. Async success tests belong here. + * + * Asynchronous exception processing is different in Normal + * and jQuery interfaces. Such tests belong in the test-nojq.js + * or test-jq.js files respectively. jQuery specific XHR object + * tests also belong in test-jq.js. Do not create any jQuery + * dependencies in this file or in test-nojq.js + * + * To compile client code for this test use: + * $ thrift -gen js ThriftTest.thrift + * -- or -- + * $ thrift -gen js:jquery ThriftTest.thrift + * + * See also: + * ++ test-nojq.js for "-gen js" only tests + * ++ test-jq.js for "-gen js:jquery" only tests + */ + +var transport = new Thrift.Transport("/service"); +var protocol = new Thrift.Protocol(transport); +var client = new ThriftTest.ThriftTestClient(protocol); + +// Work around for old API used by QUnitAdapter of jsTestDriver +if (typeof QUnit.log == 'function') { + // When using real QUnit (fron PhantomJS) log failures to console + QUnit.log(function(details) { + if (!details.result) { + console.log('======== FAIL ========'); + console.log('TestName: ' + details.name); + if (details.message) console.log(details.message); + console.log('Expected: ' + details.expected); + console.log('Actual : ' + details.actual); + console.log('======================'); + } + }); +} + +// all Languages in UTF-8 +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]); + } + } + } +} + +module("Base Types"); + + test("Void", function() { + equal(client.testVoid(), undefined); + }); + test("Binary (String)", function() { + var binary = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + equal(client.testBinary(binary), binary); + }); + test("Binary (Uint8Array)", function() { + var binary = ''; + for (var v = 255; v >= 0; --v) { + binary += String.fromCharCode(v); + } + var arr = new Uint8Array(binary.length); + for (var i = 0; i < binary.length; ++i) { + arr[i] = binary[i].charCodeAt(); + } + equal(client.testBinary(arr), binary); + }); + test("String", function() { + equal(client.testString(''), ''); + equal(client.testString(stringTest), stringTest); + + var specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; + equal(client.testString(specialCharacters),specialCharacters); + }); + test("Double", function() { + equal(client.testDouble(0), 0); + equal(client.testDouble(-1), -1); + equal(client.testDouble(3.14), 3.14); + equal(client.testDouble(Math.pow(2,60)), Math.pow(2,60)); + }); + test("Byte", function() { + equal(client.testByte(0), 0); + equal(client.testByte(0x01), 0x01); + }); + test("I32", function() { + equal(client.testI32(0), 0); + equal(client.testI32(Math.pow(2,30)), Math.pow(2,30)); + equal(client.testI32(-Math.pow(2,30)), -Math.pow(2,30)); + }); + test("I64", function() { + equal(client.testI64(0), 0); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + equal(client.testI64(Math.pow(2,52)), Math.pow(2,52)); + equal(client.testI64(-Math.pow(2,52)), -Math.pow(2,52)); + }); + + +module("Structured Types"); + + test("Struct", function() { + var structTestInput = new ThriftTest.Xtruct(); + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + structTestInput.i64_thing = Math.pow(2,52); + + var structTestOutput = client.testStruct(structTestInput); + + equal(structTestOutput.string_thing, structTestInput.string_thing); + equal(structTestOutput.byte_thing, structTestInput.byte_thing); + equal(structTestOutput.i32_thing, structTestInput.i32_thing); + equal(structTestOutput.i64_thing, structTestInput.i64_thing); + + equal(JSON.stringify(structTestOutput), JSON.stringify(structTestInput)); + }); + + test("Nest", function() { + var xtrTestInput = new ThriftTest.Xtruct(); + xtrTestInput.string_thing = 'worked'; + xtrTestInput.byte_thing = 0x01; + xtrTestInput.i32_thing = Math.pow(2,30); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + xtrTestInput.i64_thing = Math.pow(2,52); + + var nestTestInput = new ThriftTest.Xtruct2(); + nestTestInput.byte_thing = 0x02; + nestTestInput.struct_thing = xtrTestInput; + nestTestInput.i32_thing = Math.pow(2,15); + + var nestTestOutput = client.testNest(nestTestInput); + + equal(nestTestOutput.byte_thing, nestTestInput.byte_thing); + equal(nestTestOutput.struct_thing.string_thing, nestTestInput.struct_thing.string_thing); + equal(nestTestOutput.struct_thing.byte_thing, nestTestInput.struct_thing.byte_thing); + equal(nestTestOutput.struct_thing.i32_thing, nestTestInput.struct_thing.i32_thing); + equal(nestTestOutput.struct_thing.i64_thing, nestTestInput.struct_thing.i64_thing); + equal(nestTestOutput.i32_thing, nestTestInput.i32_thing); + + equal(JSON.stringify(nestTestOutput), JSON.stringify(nestTestInput)); + }); + + test("Map", function() { + var mapTestInput = {7:77, 8:88, 9:99}; + + var mapTestOutput = client.testMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test("StringMap", function() { + var mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" + }; + + var mapTestOutput = client.testStringMap(mapTestInput); + + for (var key in mapTestOutput) { + equal(mapTestOutput[key], mapTestInput[key]); + } + }); + + test("Set", function() { + var setTestInput = [1,2,3]; + ok(client.testSet(setTestInput), setTestInput); + }); + + test("List", function() { + var listTestInput = [1,2,3]; + ok(client.testList(listTestInput), listTestInput); + }); + + test("Enum", function() { + equal(client.testEnum(ThriftTest.Numberz.ONE), ThriftTest.Numberz.ONE); + }); + + test("TypeDef", function() { + equal(client.testTypedef(69), 69); + }); + + test("Skip", function() { + var structTestInput = new ThriftTest.Xtruct(); + var modifiedClient = new ThriftTest.ThriftTestClient(protocol); + + modifiedClient.recv_testStruct = function() { + var input = modifiedClient.input; + var xtruct3 = new ThriftTest.Xtruct3(); + + input.readMessageBegin(); + input.readStructBegin(); + + // read Xtruct data with Xtruct3 + input.readFieldBegin(); + xtruct3.read(input); + input.readFieldEnd(); + // read Thrift.Type.STOP message + input.readFieldBegin(); + input.readFieldEnd(); + + input.readStructEnd(); + input.readMessageEnd(); + + return xtruct3; + }; + + structTestInput.string_thing = 'worked'; + structTestInput.byte_thing = 0x01; + structTestInput.i32_thing = Math.pow(2,30); + structTestInput.i64_thing = Math.pow(2,52); + + var structTestOutput = modifiedClient.testStruct(structTestInput); + + equal(structTestOutput instanceof ThriftTest.Xtruct3, true); + equal(structTestOutput.string_thing, structTestInput.string_thing); + equal(structTestOutput.changed, null); + equal(structTestOutput.i32_thing, structTestInput.i32_thing); + equal(structTestOutput.i64_thing, structTestInput.i64_thing); + }); + + +module("deeper!"); + + test("MapMap", function() { + var mapMapTestExpectedResult = { + "4":{"1":1,"2":2,"3":3,"4":4}, + "-4":{"-4":-4, "-3":-3, "-2":-2, "-1":-1} + }; + + var mapMapTestOutput = client.testMapMap(1); + + + for (var key in mapMapTestOutput) { + for (var key2 in mapMapTestOutput[key]) { + equal(mapMapTestOutput[key][key2], mapMapTestExpectedResult[key][key2]); + } + } + + checkRecursively(mapMapTestOutput, mapMapTestExpectedResult); + }); + + +module("Exception"); + + test("Xception", function() { + expect(2); + try{ + client.testException("Xception"); + }catch(e){ + equal(e.errorCode, 1001); + equal(e.message, "Xception"); + } + }); + + test("no Exception", 0, function() { + try{ + client.testException("no Exception"); + }catch(e){ + ok(false); + } + }); + + test("TException", function() { + //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" + expect(1); + try{ + client.testException("TException"); + } catch(e) { + //ok(false); + } + ok(true); + }); + + +module("Insanity"); + + var crazy = { + "userMap":{ "5":5, "8":8 }, + "xtructs":[{ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }, + { + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + }] + }; + test("testInsanity", function() { + var insanity = { + "1":{ + "2":crazy, + "3":crazy + }, + "2":{ "6":{ "userMap":null, "xtructs":null } } + }; + var res = client.testInsanity(new ThriftTest.Insanity(crazy)); + ok(res, JSON.stringify(res)); + ok(insanity, JSON.stringify(insanity)); + + checkRecursively(res, insanity); + }); + + +////////////////////////////////// +//Run same tests asynchronously + +module("Async"); + + test("Double", function() { + expect( 1 ); + + QUnit.stop(); + client.testDouble(3.14159265, function(result) { + equal(result, 3.14159265); + QUnit.start(); + }); + }); + + test("Byte", function() { + expect( 1 ); + + QUnit.stop(); + client.testByte(0x01, function(result) { + equal(result, 0x01); + QUnit.start(); + }); + }); + + test("I32", function() { + expect( 2 ); + + QUnit.stop(); + client.testI32(Math.pow(2,30), function(result) { + equal(result, Math.pow(2,30)); + QUnit.start(); + }); + + QUnit.stop(); + client.testI32(Math.pow(-2,31), function(result) { + equal(result, Math.pow(-2,31)); + QUnit.start(); + }); + }); + + test("I64", function() { + expect( 2 ); + + QUnit.stop(); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + client.testI64(Math.pow(2,52), function(result) { + equal(result, Math.pow(2,52)); + QUnit.start(); + }); + + QUnit.stop(); + //This is usually 2^60 but JS cannot represent anything over 2^52 accurately + client.testI64(Math.pow(-2,52), function(result) { + equal(result, Math.pow(-2,52)); + QUnit.start(); + }); + }); diff --git a/vendor/src/github.com/apache/thrift/lib/js/test/test_handler.js b/vendor/src/github.com/apache/thrift/lib/js/test/test_handler.js new file mode 100644 index 00000000..e1fa74fa --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/test_handler.js @@ -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. + */ + +//This is the server side Node test handler for the standard +// Apache Thrift test service. + +var ttypes = require('./gen-nodejs/ThriftTest_types'); +var TException = require('../../nodejs/lib/thrift').TException; + +var ThriftTestHandler = 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 + ')'); + + var mapmap = []; + var pos = []; + var neg = []; + for (var 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(')'); + + var hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = 2; + hello.i32_thing = 2; + hello.i64_thing = 2; + + var goodbye = new ttypes.Xtruct(); + goodbye.string_thing = 'Goodbye4'; + goodbye.byte_thing = 4; + goodbye.i32_thing = 4; + goodbye.i64_thing = 4; + + var crazy = new ttypes.Insanity(); + crazy.userMap = []; + crazy.userMap[ttypes.Numberz.EIGHT] = 8; + crazy.userMap[ttypes.Numberz.FIVE] = 5; + crazy.xtructs = [goodbye, hello]; + + var first_map = []; + var second_map = []; + + first_map[ttypes.Numberz.TWO] = crazy; + first_map[ttypes.Numberz.THREE] = crazy; + + var looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + var 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()'); + + var 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') { + var 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')) { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + result(x); + } else if (arg0 === ('Xception2')) { + var x2 = new ttypes.Xception2(); + x2.errorCode = 2002; + x2.struct_thing = new ttypes.Xtruct(); + x2.struct_thing.string_thing = 'This is an Xception2'; + result(x2); + } + + var 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/vendor/src/github.com/apache/thrift/lib/js/test/testws.html b/vendor/src/github.com/apache/thrift/lib/js/test/testws.html new file mode 100644 index 00000000..f99a1460 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/js/test/testws.html @@ -0,0 +1,62 @@ + + + + + + Thrift Javascript Bindings: Unit Test + + + + + + + + + + + + + + + + + +

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

+

+
+

+
+ + + diff --git a/vendor/src/github.com/apache/thrift/lib/json/Makefile.am b/vendor/src/github.com/apache/thrift/lib/json/Makefile.am new file mode 100644 index 00000000..1051b9ba --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/json/Makefile.am @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = + +if WITH_JAVA +# Schema validation test depends on java +SUBDIRS += test +endif + +EXTRA_DIST = \ + schema.json \ + test diff --git a/vendor/src/github.com/apache/thrift/lib/json/schema.json b/vendor/src/github.com/apache/thrift/lib/json/schema.json new file mode 100644 index 00000000..011b4d37 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/json/schema.json @@ -0,0 +1,330 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "id": "http://thrift.apache.org/schema.json#", + "description": "Schema for Apache Thrift protocol descriptors", + + "definitions": { + "type-id": { + "title": "Any type id (name)", + "enum": [ + "void", + "string", + "bool", + "byte", + "i8", + "i16", + "i32", + "i64", + "double", + "list", + "set", + "map", + "union", + "struct", + "binary" + ] + }, + "base-type": { + "title": "Base type schema", + "type": "object", + "properties": { + "typeId": { + "enum": ["void", "string", "bool", "byte", "i8", "i16", "i32", "i64", "double", "binary" ] + } + }, + "required": [ "typeId" ] + }, + "list-type": { + "title": "List and set schema", + "type": "object", + "properties": { + "typeId": { + "enum": [ "list", "set" ] + }, + "elemTypeId": { "$ref": "#/definitions/type-id" }, + "elemType": { "$ref": "#/definitions/type-desc" } + }, + "required": [ "typeId", "elemTypeId" ] + }, + "map-type": { + "title": "Map schema", + "type": "object", + "properties": { + "typeId": { + "enum": [ "map" ] + }, + "keyTypeId": { "$ref": "#/definitions/type-id" }, + "keyType": { "$ref": "#/definitions/type-desc" }, + "valueTypeId": { "$ref": "#/definitions/type-id" }, + "valueType": { "$ref": "#/definitions/type-desc" } + }, + "required": [ "typeId", "keyTypeId", "valueTypeId" ] + }, + "struct-type": { + "title": "Struct, union and exception schema", + "type": "object", + "properties": { + "typeId": { + "enum": [ "union", "struct", "exception" ] + } + }, + "required": [ "typeId", "class" ] + }, + "type-desc": { + "title": "Type descriptor schema", + "allOf": [ + { + "type": "object", + "properties": { + "typeId": { "type": "string" }, + "class": { "type": "string" } + } + }, + { + "oneOf": + [ + { "$ref": "#/definitions/base-type" }, + { "$ref": "#/definitions/list-type" }, + { "$ref": "#/definitions/map-type" }, + { "$ref": "#/definitions/struct-type" } + ] + } + ] + }, + "name-and-doc": { + "title": "Name and documentation sub-schema", + "type": "object", + "properties": { + "name": { "type": "string" }, + "doc": { "type": "string" } + }, + "required": [ "name" ] + }, + "enum": { + "title": "Thrift 'enum' definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "required": [ "members" ], + "properties": { + "members": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "type": "integer" } + }, + "required": [ "name", "value" ] + } + } + } + } + ] + }, + "typedef": { + "title": "Thrift typedef definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "properties": { + "typeId": { "$ref": "#/definitions/type-id" }, + "type": { "$ref": "#/definitions/type-desc" } + }, + "required": [ "typeId" ] + } + ] + }, + "constant": { + "title": "Thrift constant definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { "$ref": "#/definitions/type-desc" }, + { + "properties": { + "value": { + "oneOf": [ + { "type": "string" }, + { "type": "number" }, + { "type": "array" }, + { "type": "object" } + ] + } + }, + "required": [ "value" ] + } + ] + }, + "field": { + "title": "Thrift struct field definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "properties": { + "key": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "required": { + "enum": [ "required", "optional", "req_out" ] + }, + "typeId": { "$ref": "#/definitions/type-id" }, + "type": { "$ref": "#/definitions/type-desc" }, + "default": { + "oneOf": [ + { "type": "string" }, + { "type": "number" }, + { "type": "array" }, + { "type": "object" } + ] + } + }, + "required": [ "key", "required" ] + } + ] + }, + "struct": { + "title": "Thrift struct definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "properties": { + "isException": { "type": "boolean" }, + "isUnion": { "type": "boolean" }, + "fields": { + "type": "array", + "items": { + "$ref": "#/definitions/field" + } + } + }, + "required": [ "isException", "isUnion", "fields" ] + } + ] + }, + "union": { + "title": "Thrift union definition schema", + "$ref": "#/definitions/struct" + }, + "exception": { + "title": "Thrift exception definition schema", + "type": "object", + "properties": { + "key": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "name": { "type": "string" }, + "typeId": { "enum": [ "exception" ] }, + "type": { "$ref": "#/definitions/struct-type" } + }, + "required": [ "key", "name", "typeId" ] + }, + "function": { + "title": "Thrift service function definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "properties": { + "oneway": { + "type": "boolean" + }, + "returnType": { + "$ref": "#/definitions/type-desc" + }, + "arguments": { + "type": "array", + "items": { + "$ref": "#/definitions/field" + } + }, + "exceptions": { + "type": "array", + "items": { "$ref": "#/definitions/exception" } + } + }, + "required": [ "oneway", "arguments", "exceptions" ] + } + ] + }, + "service": { + "title": "Thrift service definition schema", + "type": "object", + "allOf": [ + { "$ref": "#/definitions/name-and-doc" }, + { + "properties": { + "functions": { + "type": "array", + "items": { + "$ref": "#/definitions/function" + } + } + }, + "required": [ "functions" ] + } + ] + } + }, + + "type": "object", + "required": [ + "name", + "enums", + "typedefs", + "structs", + "constants", + "services" + ], + "properties": { + "name": { + "type": "string" + }, + "includes": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "enums": { + "type": "array", + "items": { + "$ref": "#/definitions/enum" + } + }, + "typedefs": { + "type": "array", + "items": { + "$ref": "#/definitions/typedef" + } + }, + "structs": { + "type": "array", + "items": { + "$ref": "#/definitions/struct" + } + }, + "constants": { + "type": "array", + "items": { + "$ref": "#/definitions/constant" + } + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/service" + } + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/json/test/Makefile.am b/vendor/src/github.com/apache/thrift/lib/json/test/Makefile.am new file mode 100644 index 00000000..bb87a520 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/json/test/Makefile.am @@ -0,0 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +check: + $(ANT) $(ANT_FLAGS) test + +# Make sure this doesn't fail if ant is not configured. +clean-local: + ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ + $$ANT $(ANT_FLAGS) clean diff --git a/vendor/src/github.com/apache/thrift/lib/json/test/build.properties b/vendor/src/github.com/apache/thrift/lib/json/test/build.properties new file mode 100644 index 00000000..075f6400 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/json/test/build.properties @@ -0,0 +1,10 @@ +# Jar versions +mvn.ant.task.version=2.1.3 + +# Dependency versions +json-schema-validator.version=2.2.6 + +# Maven dependency download locations +mvn.repo=http://repo1.maven.org/maven2 +mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version} +mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar diff --git a/vendor/src/github.com/apache/thrift/lib/json/test/build.xml b/vendor/src/github.com/apache/thrift/lib/json/test/build.xml new file mode 100644 index 00000000..956a2382 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/json/test/build.xml @@ -0,0 +1,144 @@ + + + + JSON Schema Validation Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Thrift compiler is missing ! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/lib/lua/Makefile.am b/vendor/src/github.com/apache/thrift/lib/lua/Makefile.am new file mode 100644 index 00000000..3dfc27c8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/Makefile.am @@ -0,0 +1,73 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +AUTOMAKE_OPTIONS = subdir-objects + +SUBDIRS = . + +lib_LTLIBRARIES = \ + libluasocket.la \ + libluabpack.la \ + libluabitwise.la \ + liblualongnumber.la + +libluasocket_la_SOURCES = \ + src/luasocket.c \ + src/usocket.c + +nobase_include_HEADERS = src/socket.h + +libluasocket_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE +libluasocket_la_LDFLAGS = $(AM_LDFLAGS) +libluasocket_la_LIBADD = $(LUA_LIB) -lm + +libluabpack_la_SOURCES = src/luabpack.c + +libluabpack_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE +libluabpack_la_LDFLAGS = $(AM_LDFLAGS) +libluabpack_la_LIBADD = liblualongnumber.la $(LUA_LIB) -lm + +libluabitwise_la_SOURCES = src/luabitwise.c + +libluabitwise_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE +libluabitwise_la_LDFLAGS = $(AM_LDFLAGS) +libluabitwise_la_LIBADD = $(LUA_LIB) -lm + +liblualongnumber_la_SOURCES = \ + src/lualongnumber.c \ + src/longnumberutils.c + +liblualongnumber_la_CPPFLAGS = $(AM_CPPFLAGS) $(LUA_INCLUDE) -DLUA_COMPAT_MODULE +liblualongnumber_la_LDFLAGS = $(AM_LDFLAGS) +liblualongnumber_la_LIBADD = $(LUA_LIB) -lm + +EXTRA_DIST = \ + coding_standards.md \ + TBinaryProtocol.lua \ + TBufferedTransport.lua \ + TCompactProtocol.lua \ + TFramedTransport.lua \ + Thrift.lua \ + THttpTransport.lua \ + TJsonProtocol.lua \ + TMemoryBuffer.lua \ + TProtocol.lua \ + TServer.lua \ + TSocket.lua \ + TTransport.lua diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TBinaryProtocol.lua b/vendor/src/github.com/apache/thrift/lib/lua/TBinaryProtocol.lua new file mode 100644 index 00000000..4b8e98a9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TBinaryProtocol.lua @@ -0,0 +1,264 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TProtocol' +require 'libluabpack' +require 'libluabitwise' + +TBinaryProtocol = __TObject.new(TProtocolBase, { + __type = 'TBinaryProtocol', + VERSION_MASK = -65536, -- 0xffff0000 + VERSION_1 = -2147418112, -- 0x80010000 + TYPE_MASK = 0x000000ff, + strictRead = false, + strictWrite = true +}) + +function TBinaryProtocol:writeMessageBegin(name, ttype, seqid) + if self.strictWrite then + self:writeI32(libluabitwise.bor(TBinaryProtocol.VERSION_1, ttype)) + self:writeString(name) + self:writeI32(seqid) + else + self:writeString(name) + self:writeByte(ttype) + self:writeI32(seqid) + end +end + +function TBinaryProtocol:writeMessageEnd() +end + +function TBinaryProtocol:writeStructBegin(name) +end + +function TBinaryProtocol:writeStructEnd() +end + +function TBinaryProtocol:writeFieldBegin(name, ttype, id) + self:writeByte(ttype) + self:writeI16(id) +end + +function TBinaryProtocol:writeFieldEnd() +end + +function TBinaryProtocol:writeFieldStop() + self:writeByte(TType.STOP); +end + +function TBinaryProtocol:writeMapBegin(ktype, vtype, size) + self:writeByte(ktype) + self:writeByte(vtype) + self:writeI32(size) +end + +function TBinaryProtocol:writeMapEnd() +end + +function TBinaryProtocol:writeListBegin(etype, size) + self:writeByte(etype) + self:writeI32(size) +end + +function TBinaryProtocol:writeListEnd() +end + +function TBinaryProtocol:writeSetBegin(etype, size) + self:writeByte(etype) + self:writeI32(size) +end + +function TBinaryProtocol:writeSetEnd() +end + +function TBinaryProtocol:writeBool(bool) + if bool then + self:writeByte(1) + else + self:writeByte(0) + end +end + +function TBinaryProtocol:writeByte(byte) + local buff = libluabpack.bpack('c', byte) + self.trans:write(buff) +end + +function TBinaryProtocol:writeI16(i16) + local buff = libluabpack.bpack('s', i16) + self.trans:write(buff) +end + +function TBinaryProtocol:writeI32(i32) + local buff = libluabpack.bpack('i', i32) + self.trans:write(buff) +end + +function TBinaryProtocol:writeI64(i64) + local buff = libluabpack.bpack('l', i64) + self.trans:write(buff) +end + +function TBinaryProtocol:writeDouble(dub) + local buff = libluabpack.bpack('d', dub) + self.trans:write(buff) +end + +function TBinaryProtocol:writeString(str) + -- Should be utf-8 + self:writeI32(string.len(str)) + self.trans:write(str) +end + +function TBinaryProtocol:readMessageBegin() + local sz, ttype, name, seqid = self:readI32() + if sz < 0 then + local version = libluabitwise.band(sz, TBinaryProtocol.VERSION_MASK) + if version ~= TBinaryProtocol.VERSION_1 then + terror(TProtocolException:new{ + message = 'Bad version in readMessageBegin: ' .. sz + }) + end + ttype = libluabitwise.band(sz, TBinaryProtocol.TYPE_MASK) + name = self:readString() + seqid = self:readI32() + else + if self.strictRead then + terror(TProtocolException:new{message = 'No protocol version header'}) + end + name = self.trans:readAll(sz) + ttype = self:readByte() + seqid = self:readI32() + end + return name, ttype, seqid +end + +function TBinaryProtocol:readMessageEnd() +end + +function TBinaryProtocol:readStructBegin() + return nil +end + +function TBinaryProtocol:readStructEnd() +end + +function TBinaryProtocol:readFieldBegin() + local ttype = self:readByte() + if ttype == TType.STOP then + return nil, ttype, 0 + end + local id = self:readI16() + return nil, ttype, id +end + +function TBinaryProtocol:readFieldEnd() +end + +function TBinaryProtocol:readMapBegin() + local ktype = self:readByte() + local vtype = self:readByte() + local size = self:readI32() + return ktype, vtype, size +end + +function TBinaryProtocol:readMapEnd() +end + +function TBinaryProtocol:readListBegin() + local etype = self:readByte() + local size = self:readI32() + return etype, size +end + +function TBinaryProtocol:readListEnd() +end + +function TBinaryProtocol:readSetBegin() + local etype = self:readByte() + local size = self:readI32() + return etype, size +end + +function TBinaryProtocol:readSetEnd() +end + +function TBinaryProtocol:readBool() + local byte = self:readByte() + if byte == 0 then + return false + end + return true +end + +function TBinaryProtocol:readByte() + local buff = self.trans:readAll(1) + local val = libluabpack.bunpack('c', buff) + return val +end + +function TBinaryProtocol:readI16() + local buff = self.trans:readAll(2) + local val = libluabpack.bunpack('s', buff) + return val +end + +function TBinaryProtocol:readI32() + local buff = self.trans:readAll(4) + local val = libluabpack.bunpack('i', buff) + return val +end + +function TBinaryProtocol:readI64() + local buff = self.trans:readAll(8) + local val = libluabpack.bunpack('l', buff) + return val +end + +function TBinaryProtocol:readDouble() + local buff = self.trans:readAll(8) + local val = libluabpack.bunpack('d', buff) + return val +end + +function TBinaryProtocol:readString() + local len = self:readI32() + local str = self.trans:readAll(len) + return str +end + +TBinaryProtocolFactory = TProtocolFactory:new{ + __type = 'TBinaryProtocolFactory', + strictRead = false +} + +function TBinaryProtocolFactory:getProtocol(trans) + -- TODO Enforce that this must be a transport class (ie not a bool) + if not trans then + terror(TProtocolException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return TBinaryProtocol:new{ + trans = trans, + strictRead = self.strictRead, + strictWrite = true + } +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TBufferedTransport.lua b/vendor/src/github.com/apache/thrift/lib/lua/TBufferedTransport.lua new file mode 100644 index 00000000..45ef4b1c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TBufferedTransport.lua @@ -0,0 +1,91 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TTransport' + +TBufferedTransport = TTransportBase:new{ + __type = 'TBufferedTransport', + rBufSize = 2048, + wBufSize = 2048, + wBuf = '', + rBuf = '' +} + +function TBufferedTransport:new(obj) + if ttype(obj) ~= 'table' then + error(ttype(self) .. 'must be initialized with a table') + end + + -- Ensure a transport is provided + if not obj.trans then + error('You must provide ' .. ttype(self) .. ' with a trans') + end + + return TTransportBase.new(self, obj) +end + +function TBufferedTransport:isOpen() + return self.trans:isOpen() +end + +function TBufferedTransport:open() + return self.trans:open() +end + +function TBufferedTransport:close() + return self.trans:close() +end + +function TBufferedTransport:read(len) + return self.trans:read(len) +end + +function TBufferedTransport:readAll(len) + return self.trans:readAll(len) +end + +function TBufferedTransport:write(buf) + self.wBuf = self.wBuf .. buf + if string.len(self.wBuf) >= self.wBufSize then + self.trans:write(self.wBuf) + self.wBuf = '' + end +end + +function TBufferedTransport:flush() + if string.len(self.wBuf) > 0 then + self.trans:write(self.wBuf) + self.wBuf = '' + end +end + +TBufferedTransportFactory = TTransportFactoryBase:new{ + __type = 'TBufferedTransportFactory' +} + +function TBufferedTransportFactory:getTransport(trans) + if not trans then + terror(TTransportException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return TBufferedTransport:new{ + trans = trans + } +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TCompactProtocol.lua b/vendor/src/github.com/apache/thrift/lib/lua/TCompactProtocol.lua new file mode 100644 index 00000000..877595a5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TCompactProtocol.lua @@ -0,0 +1,457 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TProtocol' +require 'libluabpack' +require 'libluabitwise' +require 'liblualongnumber' + +TCompactProtocol = __TObject.new(TProtocolBase, { + __type = 'TCompactProtocol', + COMPACT_PROTOCOL_ID = 0x82, + COMPACT_VERSION = 1, + COMPACT_VERSION_MASK = 0x1f, + COMPACT_TYPE_MASK = 0xE0, + COMPACT_TYPE_BITS = 0x07, + COMPACT_TYPE_SHIFT_AMOUNT = 5, + + -- Used to keep track of the last field for the current and previous structs, + -- so we can do the delta stuff. + lastField = {}, + lastFieldId = 0, + lastFieldIndex = 1, + + -- If we encounter a boolean field begin, save the TField here so it can + -- have the value incorporated. + booleanFieldName = "", + booleanFieldId = 0, + booleanFieldPending = false, + + -- If we read a field header, and it's a boolean field, save the boolean + -- value here so that readBool can use it. + boolValue = false, + boolValueIsNotNull = false, +}) + +TCompactType = { + COMPACT_BOOLEAN_TRUE = 0x01, + COMPACT_BOOLEAN_FALSE = 0x02, + COMPACT_BYTE = 0x03, + COMPACT_I16 = 0x04, + COMPACT_I32 = 0x05, + COMPACT_I64 = 0x06, + COMPACT_DOUBLE = 0x07, + COMPACT_BINARY = 0x08, + COMPACT_LIST = 0x09, + COMPACT_SET = 0x0A, + COMPACT_MAP = 0x0B, + COMPACT_STRUCT = 0x0C +} + +TTypeToCompactType = {} +TTypeToCompactType[TType.STOP] = TType.STOP +TTypeToCompactType[TType.BOOL] = TCompactType.COMPACT_BOOLEAN_TRUE +TTypeToCompactType[TType.BYTE] = TCompactType.COMPACT_BYTE +TTypeToCompactType[TType.I16] = TCompactType.COMPACT_I16 +TTypeToCompactType[TType.I32] = TCompactType.COMPACT_I32 +TTypeToCompactType[TType.I64] = TCompactType.COMPACT_I64 +TTypeToCompactType[TType.DOUBLE] = TCompactType.COMPACT_DOUBLE +TTypeToCompactType[TType.STRING] = TCompactType.COMPACT_BINARY +TTypeToCompactType[TType.LIST] = TCompactType.COMPACT_LIST +TTypeToCompactType[TType.SET] = TCompactType.COMPACT_SET +TTypeToCompactType[TType.MAP] = TCompactType.COMPACT_MAP +TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT + +CompactTypeToTType = {} +CompactTypeToTType[TType.STOP] = TType.STOP +CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE] = TType.BOOL +CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_FALSE] = TType.BOOL +CompactTypeToTType[TCompactType.COMPACT_BYTE] = TType.BYTE +CompactTypeToTType[TCompactType.COMPACT_I16] = TType.I16 +CompactTypeToTType[TCompactType.COMPACT_I32] = TType.I32 +CompactTypeToTType[TCompactType.COMPACT_I64] = TType.I64 +CompactTypeToTType[TCompactType.COMPACT_DOUBLE] = TType.DOUBLE +CompactTypeToTType[TCompactType.COMPACT_BINARY] = TType.STRING +CompactTypeToTType[TCompactType.COMPACT_LIST] = TType.LIST +CompactTypeToTType[TCompactType.COMPACT_SET] = TType.SET +CompactTypeToTType[TCompactType.COMPACT_MAP] = TType.MAP +CompactTypeToTType[TCompactType.COMPACT_STRUCT] = TType.STRUCT + +function TCompactProtocol:resetLastField() + self.lastField = {} + self.lastFieldId = 0 + self.lastFieldIndex = 1 +end + +function TCompactProtocol:packCompactType(ktype, vtype) + return libluabitwise.bor(libluabitwise.shiftl(ktype, 4), vtype) +end + +function TCompactProtocol:writeMessageBegin(name, ttype, seqid) + self:writeByte(TCompactProtocol.COMPACT_PROTOCOL_ID) + self:writeByte(libluabpack.packMesgType(TCompactProtocol.COMPACT_VERSION, + TCompactProtocol.COMPACT_VERSION_MASK,ttype, + TCompactProtocol.COMPACT_TYPE_SHIFT_AMOUNT, + TCompactProtocol.COMPACT_TYPE_MASK)) + self:writeVarint32(seqid) + self:writeString(name) + self:resetLastField() +end + +function TCompactProtocol:writeMessageEnd() +end + +function TCompactProtocol:writeStructBegin(name) + self.lastFieldIndex = self.lastFieldIndex + 1 + self.lastField[self.lastFieldIndex] = self.lastFieldId + self.lastFieldId = 0 +end + +function TCompactProtocol:writeStructEnd() + self.lastFieldIndex = self.lastFieldIndex - 1 + self.lastFieldId = self.lastField[self.lastFieldIndex] +end + +function TCompactProtocol:writeFieldBegin(name, ttype, id) + if ttype == TType.BOOL then + self.booleanFieldName = name + self.booleanFieldId = id + self.booleanFieldPending = true + else + self:writeFieldBeginInternal(name, ttype, id, -1) + end +end + +function TCompactProtocol:writeFieldEnd() +end + +function TCompactProtocol:writeFieldStop() + self:writeByte(TType.STOP); +end + +function TCompactProtocol:writeMapBegin(ktype, vtype, size) + if size == 0 then + self:writeByte(0) + else + self:writeVarint32(size) + self:writeByte(self:packCompactType(TTypeToCompactType[ktype], TTypeToCompactType[vtype])) + end +end + +function TCompactProtocol:writeMapEnd() +end + +function TCompactProtocol:writeListBegin(etype, size) + self:writeCollectionBegin(etype, size) +end + +function TCompactProtocol:writeListEnd() +end + +function TCompactProtocol:writeSetBegin(etype, size) + self:writeCollectionBegin(etype, size) +end + +function TCompactProtocol:writeSetEnd() +end + +function TCompactProtocol:writeBool(bool) + local value = TCompactType.COMPACT_BOOLEAN_FALSE + if bool then + value = TCompactType.COMPACT_BOOLEAN_TRUE + end + print(value,self.booleanFieldPending,self.booleanFieldId) + if self.booleanFieldPending then + self:writeFieldBeginInternal(self.booleanFieldName, TType.BOOL, self.booleanFieldId, value) + self.booleanFieldPending = false + else + self:writeByte(value) + end +end + +function TCompactProtocol:writeByte(byte) + local buff = libluabpack.bpack('c', byte) + self.trans:write(buff) +end + +function TCompactProtocol:writeI16(i16) + self:writeVarint32(libluabpack.i32ToZigzag(i16)) +end + +function TCompactProtocol:writeI32(i32) + self:writeVarint32(libluabpack.i32ToZigzag(i32)) +end + +function TCompactProtocol:writeI64(i64) + self:writeVarint64(libluabpack.i64ToZigzag(i64)) +end + +function TCompactProtocol:writeDouble(dub) + local buff = libluabpack.bpack('d', dub) + self.trans:write(buff) +end + +function TCompactProtocol:writeString(str) + -- Should be utf-8 + self:writeBinary(str) +end + +function TCompactProtocol:writeBinary(str) + -- Should be utf-8 + self:writeVarint32(string.len(str)) + self.trans:write(str) +end + +function TCompactProtocol:writeFieldBeginInternal(name, ttype, id, typeOverride) + if typeOverride == -1 then + typeOverride = TTypeToCompactType[ttype] + end + local offset = id - self.lastFieldId + if id > self.lastFieldId and offset <= 15 then + self:writeByte(libluabitwise.bor(libluabitwise.shiftl(offset, 4), typeOverride)) + else + self:writeByte(typeOverride) + self:writeI16(id) + end + self.lastFieldId = id +end + +function TCompactProtocol:writeCollectionBegin(etype, size) + if size <= 14 then + self:writeByte(libluabitwise.bor(libluabitwise.shiftl(size, 4), TTypeToCompactType[etype])) + else + self:writeByte(libluabitwise.bor(0xf0, TTypeToCompactType[etype])) + self:writeVarint32(size) + end +end + +function TCompactProtocol:writeVarint32(i32) + -- Should be utf-8 + local str = libluabpack.toVarint32(i32) + self.trans:write(str) +end + +function TCompactProtocol:writeVarint64(i64) + -- Should be utf-8 + local str = libluabpack.toVarint64(i64) + self.trans:write(str) +end + +function TCompactProtocol:readMessageBegin() + local protocolId = self:readSignByte() + if protocolId ~= self.COMPACT_PROTOCOL_ID then + terror(TProtocolException:new{ + message = "Expected protocol id " .. self.COMPACT_PROTOCOL_ID .. " but got " .. protocolId}) + end + local versionAndType = self:readSignByte() + local version = libluabitwise.band(versionAndType, self.COMPACT_VERSION_MASK) + local ttype = libluabitwise.band(libluabitwise.shiftr(versionAndType, + self.COMPACT_TYPE_SHIFT_AMOUNT), self.COMPACT_TYPE_BITS) + if version ~= self.COMPACT_VERSION then + terror(TProtocolException:new{ + message = "Expected version " .. self.COMPACT_VERSION .. " but got " .. version}) + end + local seqid = self:readVarint32() + local name = self:readString() + return name, ttype, seqid +end + +function TCompactProtocol:readMessageEnd() +end + +function TCompactProtocol:readStructBegin() + self.lastField[self.lastFieldIndex] = self.lastFieldId + self.lastFieldIndex = self.lastFieldIndex + 1 + self.lastFieldId = 0 + return nil +end + +function TCompactProtocol:readStructEnd() + self.lastFieldIndex = self.lastFieldIndex - 1 + self.lastFieldId = self.lastField[self.lastFieldIndex] +end + +function TCompactProtocol:readFieldBegin() + local field_and_ttype = self:readSignByte() + local ttype = self:getTType(field_and_ttype) + if ttype == TType.STOP then + return nil, ttype, 0 + end + -- mask off the 4 MSB of the type header. it could contain a field id delta. + local modifier = libluabitwise.shiftr(libluabitwise.band(field_and_ttype, 0xf0), 4) + local id = 0 + if modifier == 0 then + id = self:readI16() + else + id = self.lastFieldId + modifier + end + if ttype == TType.BOOL then + boolValue = libluabitwise.band(field_and_ttype, 0x0f) == TCompactType.COMPACT_BOOLEAN_TRUE + boolValueIsNotNull = true + end + self.lastFieldId = id + return nil, ttype, id +end + +function TCompactProtocol:readFieldEnd() +end + +function TCompactProtocol:readMapBegin() + local size = self:readVarint32() + if size < 0 then + return nil,nil,nil + end + local kvtype = self:readSignByte() + local ktype = self:getTType(libluabitwise.shiftr(kvtype, 4)) + local vtype = self:getTType(kvtype) + return ktype, vtype, size +end + +function TCompactProtocol:readMapEnd() +end + +function TCompactProtocol:readListBegin() + local size_and_type = self:readSignByte() + local size = libluabitwise.band(libluabitwise.shiftr(size_and_type, 4), 0x0f) + if size == 15 then + size = self:readVarint32() + end + if size < 0 then + return nil,nil + end + local etype = self:getTType(libluabitwise.band(size_and_type, 0x0f)) + return etype, size +end + +function TCompactProtocol:readListEnd() +end + +function TCompactProtocol:readSetBegin() + return self:readListBegin() +end + +function TCompactProtocol:readSetEnd() +end + +function TCompactProtocol:readBool() + if boolValueIsNotNull then + boolValueIsNotNull = true + return boolValue + end + local val = self:readSignByte() + if val == TCompactType.COMPACT_BOOLEAN_TRUE then + return true + end + return false +end + +function TCompactProtocol:readByte() + local buff = self.trans:readAll(1) + local val = libluabpack.bunpack('c', buff) + return val +end + +function TCompactProtocol:readSignByte() + local buff = self.trans:readAll(1) + local val = libluabpack.bunpack('C', buff) + return val +end + +function TCompactProtocol:readI16() + return self:readI32() +end + +function TCompactProtocol:readI32() + local v = self:readVarint32() + local value = libluabpack.zigzagToI32(v) + return value +end + +function TCompactProtocol:readI64() + local value = self:readVarint64() + return value +end + +function TCompactProtocol:readDouble() + local buff = self.trans:readAll(8) + local val = libluabpack.bunpack('d', buff) + return val +end + +function TCompactProtocol:readString() + return self:readBinary() +end + +function TCompactProtocol:readBinary() + local size = self:readVarint32() + if size <= 0 then + return "" + end + return self.trans:readAll(size) +end + +function TCompactProtocol:readVarint32() + local shiftl = 0 + local result = 0 + while true do + b = self:readByte() + result = libluabitwise.bor(result, + libluabitwise.shiftl(libluabitwise.band(b, 0x7f), shiftl)) + if libluabitwise.band(b, 0x80) ~= 0x80 then + break + end + shiftl = shiftl + 7 + end + return result +end + +function TCompactProtocol:readVarint64() + local result = liblualongnumber.new + local data = result(0) + local shiftl = 0 + while true do + b = self:readByte() + endFlag, data = libluabpack.fromVarint64(b, shiftl, data) + shiftl = shiftl + 7 + if endFlag == 0 then + break + end + end + return data +end + +function TCompactProtocol:getTType(ctype) + return CompactTypeToTType[libluabitwise.band(ctype, 0x0f)] +end + +TCompactProtocolFactory = TProtocolFactory:new{ + __type = 'TCompactProtocolFactory', +} + +function TCompactProtocolFactory:getProtocol(trans) + -- TODO Enforce that this must be a transport class (ie not a bool) + if not trans then + terror(TProtocolException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return TCompactProtocol:new{ + trans = trans + } +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TFramedTransport.lua b/vendor/src/github.com/apache/thrift/lib/lua/TFramedTransport.lua new file mode 100644 index 00000000..437b7013 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TFramedTransport.lua @@ -0,0 +1,118 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TTransport' +require 'libluabpack' + +TFramedTransport = TTransportBase:new{ + __type = 'TFramedTransport', + doRead = true, + doWrite = true, + wBuf = '', + rBuf = '' +} + +function TFramedTransport:new(obj) + if ttype(obj) ~= 'table' then + error(ttype(self) .. 'must be initialized with a table') + end + + -- Ensure a transport is provided + if not obj.trans then + error('You must provide ' .. ttype(self) .. ' with a trans') + end + + return TTransportBase.new(self, obj) +end + +function TFramedTransport:isOpen() + return self.trans:isOpen() +end + +function TFramedTransport:open() + return self.trans:open() +end + +function TFramedTransport:close() + return self.trans:close() +end + +function TFramedTransport:read(len) + if string.len(self.rBuf) == 0 then + self:__readFrame() + end + + if self.doRead == false then + return self.trans:read(len) + end + + if len > string.len(self.rBuf) then + local val = self.rBuf + self.rBuf = '' + return val + end + + local val = string.sub(self.rBuf, 0, len) + self.rBuf = string.sub(self.rBuf, len+1) + return val +end + +function TFramedTransport:__readFrame() + local buf = self.trans:readAll(4) + local frame_len = libluabpack.bunpack('i', buf) + self.rBuf = self.trans:readAll(frame_len) +end + + +function TFramedTransport:write(buf, len) + if self.doWrite == false then + return self.trans:write(buf, len) + end + + if len and len < string.len(buf) then + buf = string.sub(buf, 0, len) + end + self.wBuf = self.wBuf .. buf +end + +function TFramedTransport:flush() + if self.doWrite == false then + return self.trans:flush() + end + + -- If the write fails we still want wBuf to be clear + local tmp = self.wBuf + self.wBuf = '' + local frame_len_buf = libluabpack.bpack("i", string.len(tmp)) + self.trans:write(frame_len_buf) + self.trans:write(tmp) + self.trans:flush() +end + +TFramedTransportFactory = TTransportFactoryBase:new{ + __type = 'TFramedTransportFactory' +} +function TFramedTransportFactory:getTransport(trans) + if not trans then + terror(TProtocolException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return TFramedTransport:new{trans = trans} +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/THttpTransport.lua b/vendor/src/github.com/apache/thrift/lib/lua/THttpTransport.lua new file mode 100644 index 00000000..19b7705b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/THttpTransport.lua @@ -0,0 +1,182 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TTransport' + +THttpTransport = TTransportBase:new{ + __type = 'THttpTransport', + path = '/', + wBuf = '', + rBuf = '', + CRLF = '\r\n', + VERSION = '0.10.0', + isServer = true +} + +function THttpTransport:new(obj) + if ttype(obj) ~= 'table' then + error(ttype(self) .. 'must be initialized with a table') + end + + -- Ensure a transport is provided + if not obj.trans then + error('You must provide ' .. ttype(self) .. ' with a trans') + end + + return TTransportBase.new(self, obj) +end + +function THttpTransport:isOpen() + return self.trans:isOpen() +end + +function THttpTransport:open() + return self.trans:open() +end + +function THttpTransport:close() + return self.trans:close() +end + +function THttpTransport:readAll(len) + return self:read(len) +end + +function THttpTransport:read(len) + if string.len(self.rBuf) == 0 then + self:_readMsg() + end + if len > string.len(self.rBuf) then + local val = self.rBuf + self.rBuf = '' + return val + end + + local val = string.sub(self.rBuf, 0, len) + self.rBuf = string.sub(self.rBuf, len+1) + return val +end + +function THttpTransport:_readMsg() + while true do + self.rBuf = self.rBuf .. self.trans:read(4) + if string.find(self.rBuf, self.CRLF .. self.CRLF) then + break + end + end + if not self.rBuf then + self.rBuf = "" + return + end + self:getLine() + local headers = self:_parseHeaders() + if not headers then + self.rBuf = "" + return + end + + local length = tonumber(headers["Content-Length"]) + if length then + length = length - string.len(self.rBuf) + self.rBuf = self.rBuf .. self.trans:readAll(length) + end + if self.rBuf == nil then + self.rBuf = "" + end +end + +function THttpTransport:getLine() + local a,b = string.find(self.rBuf, self.CRLF) + local line = "" + if a and b then + line = string.sub(self.rBuf, 0, a-1) + self.rBuf = string.sub(self.rBuf, b+1) + end + return line +end + +function THttpTransport:_parseHeaders() + local headers = {} + + repeat + local line = self:getLine() + for key, val in string.gmatch(line, "([%w%-]+)%s*:%s*(.+)") do + if headers[key] then + local delimiter = ", " + if key == "Set-Cookie" then + delimiter = "; " + end + headers[key] = headers[key] .. delimiter .. tostring(val) + else + headers[key] = tostring(val) + end + end + until string.find(line, "^%s*$") + + return headers +end + +function THttpTransport:write(buf, len) + if len and len < string.len(buf) then + buf = string.sub(buf, 0, len) + end + self.wBuf = self.wBuf .. buf +end + +function THttpTransport:writeHttpHeader(content_len) + if self.isServer then + local header = "HTTP/1.1 200 OK" .. self.CRLF + .. "Server: Thrift/" .. self.VERSION .. self.CRLF + .. "Access-Control-Allow-Origin: *" .. self.CRLF + .. "Content-Type: application/x-thrift" .. self.CRLF + .. "Content-Length: " .. content_len .. self.CRLF + .. "Connection: Keep-Alive" .. self.CRLF .. self.CRLF + self.trans:write(header) + else + local header = "POST " .. self.path .. " HTTP/1.1" .. self.CRLF + .. "Host: " .. self.trans.host .. self.CRLF + .. "Content-Type: application/x-thrift" .. self.CRLF + .. "Content-Length: " .. content_len .. self.CRLF + .. "Accept: application/x-thrift " .. self.CRLF + .. "User-Agent: Thrift/" .. self.VERSION .. " (Lua/THttpClient)" + .. self.CRLF .. self.CRLF + self.trans:write(header) + end +end + +function THttpTransport:flush() + -- If the write fails we still want wBuf to be clear + local tmp = self.wBuf + self.wBuf = '' + self:writeHttpHeader(string.len(tmp)) + self.trans:write(tmp) + self.trans:flush() +end + +THttpTransportFactory = TTransportFactoryBase:new{ + __type = 'THttpTransportFactory' +} +function THttpTransportFactory:getTransport(trans) + if not trans then + terror(TProtocolException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return THttpTransport:new{trans = trans} +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TJsonProtocol.lua b/vendor/src/github.com/apache/thrift/lib/lua/TJsonProtocol.lua new file mode 100644 index 00000000..db08eecf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TJsonProtocol.lua @@ -0,0 +1,727 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"), you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TProtocol' +require 'libluabpack' +require 'libluabitwise' + +TJSONProtocol = __TObject.new(TProtocolBase, { + __type = 'TJSONProtocol', + THRIFT_JSON_PROTOCOL_VERSION = 1, + jsonContext = {}, + jsonContextVal = {first = true, colon = true, ttype = 2, null = true}, + jsonContextIndex = 1, + hasReadByte = "" +}) + +TTypeToString = {} +TTypeToString[TType.BOOL] = "tf" +TTypeToString[TType.BYTE] = "i8" +TTypeToString[TType.I16] = "i16" +TTypeToString[TType.I32] = "i32" +TTypeToString[TType.I64] = "i64" +TTypeToString[TType.DOUBLE] = "dbl" +TTypeToString[TType.STRING] = "str" +TTypeToString[TType.STRUCT] = "rec" +TTypeToString[TType.LIST] = "lst" +TTypeToString[TType.SET] = "set" +TTypeToString[TType.MAP] = "map" + +StringToTType = { + tf = TType.BOOL, + i8 = TType.BYTE, + i16 = TType.I16, + i32 = TType.I32, + i64 = TType.I64, + dbl = TType.DOUBLE, + str = TType.STRING, + rec = TType.STRUCT, + map = TType.MAP, + set = TType.SET, + lst = TType.LIST +} + +JSONNode = { + ObjectBegin = '{', + ObjectEnd = '}', + ArrayBegin = '[', + ArrayEnd = ']', + PairSeparator = ':', + ElemSeparator = ',', + Backslash = '\\', + StringDelimiter = '"', + ZeroChar = '0', + EscapeChar = 'u', + Nan = 'NaN', + Infinity = 'Infinity', + NegativeInfinity = '-Infinity', + EscapeChars = "\"\\bfnrt", + EscapePrefix = "\\u00" +} + +EscapeCharVals = { + '"', '\\', '\b', '\f', '\n', '\r', '\t' +} + +JSONCharTable = { + --0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 98,116,110, 0,102,114, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1,34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +} + +-- character table string +local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function base64_encode(data) + return ((data:gsub('.', function(x) + local r,b='',x:byte() + for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end + return r; + end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) + if (#x < 6) then return '' end + local c=0 + for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end + return b:sub(c+1,c+1) + end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function base64_decode(data) + data = string.gsub(data, '[^'..b..'=]', '') + return (data:gsub('.', function(x) + if (x == '=') then return '' end + local r,f='',(b:find(x)-1) + for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end + return r; + end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) + if (#x ~= 8) then return '' end + local c=0 + for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end + return string.char(c) + end)) +end + +function TJSONProtocol:resetContext() + self.jsonContext = {} + self.jsonContextVal = {first = true, colon = true, ttype = 2, null = true} + self.jsonContextIndex = 1 +end + +function TJSONProtocol:contextPush(context) + self.jsonContextIndex = self.jsonContextIndex + 1 + self.jsonContext[self.jsonContextIndex] = self.jsonContextVal + self.jsonContextVal = context +end + +function TJSONProtocol:contextPop() + self.jsonContextVal = self.jsonContext[self.jsonContextIndex] + self.jsonContextIndex = self.jsonContextIndex - 1 +end + +function TJSONProtocol:escapeNum() + if self.jsonContextVal.ttype == 1 then + return self.jsonContextVal.colon + else + return false + end +end + +function TJSONProtocol:writeElemSeparator() + if self.jsonContextVal.null then + return + end + if self.jsonContextVal.first then + self.jsonContextVal.first = false + else + if self.jsonContextVal.ttype == 1 then + if self.jsonContextVal.colon then + self.trans:write(JSONNode.PairSeparator) + self.jsonContextVal.colon = false + else + self.trans:write(JSONNode.ElemSeparator) + self.jsonContextVal.colon = true + end + else + self.trans:write(JSONNode.ElemSeparator) + end + end +end + +function TJSONProtocol:hexChar(val) + val = libluabitwise.band(val, 0x0f) + if val < 10 then + return val + 48 + else + return val + 87 + end +end + +function TJSONProtocol:writeJSONEscapeChar(ch) + self.trans:write(JSONNode.EscapePrefix) + local outCh = hexChar(libluabitwise.shiftr(ch, 4)) + local buff = libluabpack.bpack('c', outCh) + self.trans:write(buff) + outCh = hexChar(ch) + buff = libluabpack.bpack('c', outCh) + self.trans:write(buff) +end + +function TJSONProtocol:writeJSONChar(byte) + ch = string.byte(byte) + if ch >= 0x30 then + if ch == JSONNode.Backslash then + self.trans:write(JSONNode.Backslash) + self.trans:write(JSONNode.Backslash) + else + self.trans:write(byte) + end + else + local outCh = JSONCharTable[ch+1] + if outCh == 1 then + self.trans:write(byte) + elseif outCh > 1 then + self.trans:write(JSONNode.Backslash) + local buff = libluabpack.bpack('c', outCh) + self.trans:write(buff) + else + self:writeJSONEscapeChar(ch) + end + end +end + +function TJSONProtocol:writeJSONString(str) + self:writeElemSeparator() + self.trans:write(JSONNode.StringDelimiter) + -- TODO escape special characters + local length = string.len(str) + local ii = 1 + while ii <= length do + self:writeJSONChar(string.sub(str, ii, ii)) + ii = ii + 1 + end + self.trans:write(JSONNode.StringDelimiter) +end + +function TJSONProtocol:writeJSONBase64(str) + self:writeElemSeparator() + self.trans:write(JSONNode.StringDelimiter) + local length = string.len(str) + local offset = 1 + while length >= 3 do + -- Encode 3 bytes at a time + local bytes = base64_encode(string.sub(str, offset, offset+3)) + self.trans:write(bytes) + length = length - 3 + offset = offset + 3 + end + if length > 0 then + local bytes = base64_encode(string.sub(str, offset, offset+length)) + self.trans:write(bytes) + end + self.trans:write(JSONNode.StringDelimiter) +end + +function TJSONProtocol:writeJSONInteger(num) + self:writeElemSeparator() + if self:escapeNum() then + self.trans:write(JSONNode.StringDelimiter) + end + local numstr = "" .. num + numstr = string.sub(numstr, string.find(numstr, "^[+-]?%d+")) + self.trans:write(numstr) + if self:escapeNum() then + self.trans:write(JSONNode.StringDelimiter) + end +end + +function TJSONProtocol:writeJSONDouble(dub) + self:writeElemSeparator() + local val = "" .. dub + local prefix = string.sub(val, 1, 1) + local special = false + if prefix == 'N' or prefix == 'n' then + val = JSONNode.Nan + special = true + elseif prefix == 'I' or prefix == 'i' then + val = JSONNode.Infinity + special = true + elseif prefix == '-' then + local secondByte = string.sub(val, 2, 2) + if secondByte == 'I' or secondByte == 'i' then + val = JSONNode.NegativeInfinity + special = true + end + end + + if special or self:escapeNum() then + self.trans:write(JSONNode.StringDelimiter) + end + self.trans:write(val) + if special or self:escapeNum() then + self.trans:write(JSONNode.StringDelimiter) + end +end + +function TJSONProtocol:writeJSONObjectBegin() + self:writeElemSeparator() + self.trans:write(JSONNode.ObjectBegin) + self:contextPush({first = true, colon = true, ttype = 1, null = false}) +end + +function TJSONProtocol:writeJSONObjectEnd() + self:contextPop() + self.trans:write(JSONNode.ObjectEnd) +end + +function TJSONProtocol:writeJSONArrayBegin() + self:writeElemSeparator() + self.trans:write(JSONNode.ArrayBegin) + self:contextPush({first = true, colon = true, ttype = 2, null = false}) +end + +function TJSONProtocol:writeJSONArrayEnd() + self:contextPop() + self.trans:write(JSONNode.ArrayEnd) +end + +function TJSONProtocol:writeMessageBegin(name, ttype, seqid) + self:resetContext() + self:writeJSONArrayBegin() + self:writeJSONInteger(TJSONProtocol.THRIFT_JSON_PROTOCOL_VERSION) + self:writeJSONString(name) + self:writeJSONInteger(ttype) + self:writeJSONInteger(seqid) +end + +function TJSONProtocol:writeMessageEnd() + self:writeJSONArrayEnd() +end + +function TJSONProtocol:writeStructBegin(name) + self:writeJSONObjectBegin() +end + +function TJSONProtocol:writeStructEnd() + self:writeJSONObjectEnd() +end + +function TJSONProtocol:writeFieldBegin(name, ttype, id) + self:writeJSONInteger(id) + self:writeJSONObjectBegin() + self:writeJSONString(TTypeToString[ttype]) +end + +function TJSONProtocol:writeFieldEnd() + self:writeJSONObjectEnd() +end + +function TJSONProtocol:writeFieldStop() +end + +function TJSONProtocol:writeMapBegin(ktype, vtype, size) + self:writeJSONArrayBegin() + self:writeJSONString(TTypeToString[ktype]) + self:writeJSONString(TTypeToString[vtype]) + self:writeJSONInteger(size) + return self:writeJSONObjectBegin() +end + +function TJSONProtocol:writeMapEnd() + self:writeJSONObjectEnd() + self:writeJSONArrayEnd() +end + +function TJSONProtocol:writeListBegin(etype, size) + self:writeJSONArrayBegin() + self:writeJSONString(TTypeToString[etype]) + self:writeJSONInteger(size) +end + +function TJSONProtocol:writeListEnd() + self:writeJSONArrayEnd() +end + +function TJSONProtocol:writeSetBegin(etype, size) + self:writeJSONArrayBegin() + self:writeJSONString(TTypeToString[etype]) + self:writeJSONInteger(size) +end + +function TJSONProtocol:writeSetEnd() + self:writeJSONArrayEnd() +end + +function TJSONProtocol:writeBool(bool) + if bool then + self:writeJSONInteger(1) + else + self:writeJSONInteger(0) + end +end + +function TJSONProtocol:writeByte(byte) + local buff = libluabpack.bpack('c', byte) + local val = libluabpack.bunpack('c', buff) + self:writeJSONInteger(val) +end + +function TJSONProtocol:writeI16(i16) + local buff = libluabpack.bpack('s', i16) + local val = libluabpack.bunpack('s', buff) + self:writeJSONInteger(val) +end + +function TJSONProtocol:writeI32(i32) + local buff = libluabpack.bpack('i', i32) + local val = libluabpack.bunpack('i', buff) + self:writeJSONInteger(val) +end + +function TJSONProtocol:writeI64(i64) + local buff = libluabpack.bpack('l', i64) + local val = libluabpack.bunpack('l', buff) + self:writeJSONInteger(tostring(val)) +end + +function TJSONProtocol:writeDouble(dub) + self:writeJSONDouble(string.format("%.16f", dub)) +end + +function TJSONProtocol:writeString(str) + self:writeJSONString(str) +end + +function TJSONProtocol:writeBinary(str) + -- Should be utf-8 + self:writeJSONBase64(str) +end + +function TJSONProtocol:readJSONSyntaxChar(ch) + local ch2 = "" + if self.hasReadByte ~= "" then + ch2 = self.hasReadByte + self.hasReadByte = "" + else + ch2 = self.trans:readAll(1) + end + if ch2 ~= ch then + terror(TProtocolException:new{message = "Expected ".. ch .. ", got " .. ch2}) + end +end + +function TJSONProtocol:readElemSeparator() + if self.jsonContextVal.null then + return + end + if self.jsonContextVal.first then + self.jsonContextVal.first = false + else + if self.jsonContextVal.ttype == 1 then + if self.jsonContextVal.colon then + self:readJSONSyntaxChar(JSONNode.PairSeparator) + self.jsonContextVal.colon = false + else + self:readJSONSyntaxChar(JSONNode.ElemSeparator) + self.jsonContextVal.colon = true + end + else + self:readJSONSyntaxChar(JSONNode.ElemSeparator) + end + end +end + +function TJSONProtocol:hexVal(ch) + local val = string.byte(ch) + if val >= 48 and val <= 57 then + return val - 48 + elseif val >= 97 and val <= 102 then + return val - 87 + else + terror(TProtocolException:new{message = "Expected hex val ([0-9a-f]); got " .. ch}) + end +end + +function TJSONProtocol:readJSONEscapeChar(ch) + self:readJSONSyntaxChar(JSONNode.ZeroChar) + self:readJSONSyntaxChar(JSONNode.ZeroChar) + local b1 = self.trans:readAll(1) + local b2 = self.trans:readAll(1) + return libluabitwise.shiftl(self:hexVal(b1), 4) + self:hexVal(b2) +end + + +function TJSONProtocol:readJSONString() + self:readElemSeparator() + self:readJSONSyntaxChar(JSONNode.StringDelimiter) + local result = "" + while true do + local ch = self.trans:readAll(1) + if ch == JSONNode.StringDelimiter then + break + end + if ch == JSONNode.Backslash then + ch = self.trans:readAll(1) + if ch == JSONNode.EscapeChar then + self:readJSONEscapeChar(ch) + else + local pos, _ = string.find(JSONNode.EscapeChars, ch) + if pos == nil then + terror(TProtocolException:new{message = "Expected control char, got " .. ch}) + end + ch = EscapeCharVals[pos] + end + end + result = result .. ch + end + return result +end + +function TJSONProtocol:readJSONBase64() + local result = self:readJSONString() + local length = string.len(result) + local str = "" + local offset = 1 + while length >= 4 do + local bytes = string.sub(result, offset, offset+4) + str = str .. base64_decode(bytes) + offset = offset + 4 + length = length - 4 + end + if length >= 0 then + str = str .. base64_decode(string.sub(result, offset, offset + length)) + end + return str +end + +function TJSONProtocol:readJSONNumericChars() + local result = "" + while true do + local ch = self.trans:readAll(1) + if string.find(ch, '[-+0-9.Ee]') then + result = result .. ch + else + self.hasReadByte = ch + break + end + end + return result +end + +function TJSONProtocol:readJSONLongInteger() + self:readElemSeparator() + if self:escapeNum() then + self:readJSONSyntaxChar(JSONNode.StringDelimiter) + end + local result = self:readJSONNumericChars() + if self:escapeNum() then + self:readJSONSyntaxChar(JSONNode.StringDelimiter) + end + return result +end + +function TJSONProtocol:readJSONInteger() + return tonumber(self:readJSONLongInteger()) +end + +function TJSONProtocol:readJSONDouble() + self:readElemSeparator() + local delimiter = self.trans:readAll(1) + local num = 0.0 + if delimiter == JSONNode.StringDelimiter then + local str = self:readJSONString() + if str == JSONNode.Nan then + num = 1.0 + elseif str == JSONNode.Infinity then + num = math.maxinteger + elseif str == JSONNode.NegativeInfinity then + num = math.mininteger + else + num = tonumber(str) + end + else + if self:escapeNum() then + self:readJSONSyntaxChar(JSONNode.StringDelimiter) + end + local result = self:readJSONNumericChars() + num = tonumber(delimiter.. result) + end + return num +end + +function TJSONProtocol:readJSONObjectBegin() + self:readElemSeparator() + self:readJSONSyntaxChar(JSONNode.ObjectBegin) + self:contextPush({first = true, colon = true, ttype = 1, null = false}) +end + +function TJSONProtocol:readJSONObjectEnd() + self:readJSONSyntaxChar(JSONNode.ObjectEnd) + self:contextPop() +end + +function TJSONProtocol:readJSONArrayBegin() + self:readElemSeparator() + self:readJSONSyntaxChar(JSONNode.ArrayBegin) + self:contextPush({first = true, colon = true, ttype = 2, null = false}) +end + +function TJSONProtocol:readJSONArrayEnd() + self:readJSONSyntaxChar(JSONNode.ArrayEnd) + self:contextPop() +end + +function TJSONProtocol:readMessageBegin() + self:resetContext() + self:readJSONArrayBegin() + local version = self:readJSONInteger() + if version ~= self.THRIFT_JSON_PROTOCOL_VERSION then + terror(TProtocolException:new{message = "Message contained bad version."}) + end + local name = self:readJSONString() + local ttype = self:readJSONInteger() + local seqid = self:readJSONInteger() + return name, ttype, seqid +end + +function TJSONProtocol:readMessageEnd() + self:readJSONArrayEnd() +end + +function TJSONProtocol:readStructBegin() + self:readJSONObjectBegin() + return nil +end + +function TJSONProtocol:readStructEnd() + self:readJSONObjectEnd() +end + +function TJSONProtocol:readFieldBegin() + local ttype = TType.STOP + local id = 0 + local ch = self.trans:readAll(1) + self.hasReadByte = ch + if ch ~= JSONNode.ObjectEnd then + id = self:readJSONInteger() + self:readJSONObjectBegin() + local typeName = self:readJSONString() + ttype = StringToTType[typeName] + end + return nil, ttype, id +end + +function TJSONProtocol:readFieldEnd() + self:readJSONObjectEnd() +end + +function TJSONProtocol:readMapBegin() + self:readJSONArrayBegin() + local typeName = self:readJSONString() + local ktype = StringToTType[typeName] + typeName = self:readJSONString() + local vtype = StringToTType[typeName] + local size = self:readJSONInteger() + self:readJSONObjectBegin() + return ktype, vtype, size +end + +function TJSONProtocol:readMapEnd() + self:readJSONObjectEnd() + self:readJSONArrayEnd() +end + +function TJSONProtocol:readListBegin() + self:readJSONArrayBegin() + local typeName = self:readJSONString() + local etype = StringToTType[typeName] + local size = self:readJSONInteger() + return etype, size +end + +function TJSONProtocol:readListEnd() + return self:readJSONArrayEnd() +end + +function TJSONProtocol:readSetBegin() + return self:readListBegin() +end + +function TJSONProtocol:readSetEnd() + return self:readJSONArrayEnd() +end + +function TJSONProtocol:readBool() + local result = self:readJSONInteger() + if result == 1 then + return true + else + return false + end +end + +function TJSONProtocol:readByte() + local result = self:readJSONInteger() + if result >= 256 then + terror(TProtocolException:new{message = "UnExpected Byte " .. result}) + end + return result +end + +function TJSONProtocol:readI16() + return self:readJSONInteger() +end + +function TJSONProtocol:readI32() + return self:readJSONInteger() +end + +function TJSONProtocol:readI64() + local long = liblualongnumber.new + return long(self:readJSONLongInteger()) +end + +function TJSONProtocol:readDouble() + return self:readJSONDouble() +end + +function TJSONProtocol:readString() + return self:readJSONString() +end + +function TJSONProtocol:readBinary() + return self:readJSONBase64() +end + +TJSONProtocolFactory = TProtocolFactory:new{ + __type = 'TJSONProtocolFactory', +} + +function TJSONProtocolFactory:getProtocol(trans) + -- TODO Enforce that this must be a transport class (ie not a bool) + if not trans then + terror(TProtocolException:new{ + message = 'Must supply a transport to ' .. ttype(self) + }) + end + return TJSONProtocol:new{ + trans = trans + } +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TMemoryBuffer.lua b/vendor/src/github.com/apache/thrift/lib/lua/TMemoryBuffer.lua new file mode 100644 index 00000000..78b2f5cf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TMemoryBuffer.lua @@ -0,0 +1,91 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TTransport' + +TMemoryBuffer = TTransportBase:new{ + __type = 'TMemoryBuffer', + buffer = '', + bufferSize = 1024, + wPos = 0, + rPos = 0 +} +function TMemoryBuffer:isOpen() + return 1 +end +function TMemoryBuffer:open() end +function TMemoryBuffer:close() end + +function TMemoryBuffer:peak() + return self.rPos < self.wPos +end + +function TMemoryBuffer:getBuffer() + return self.buffer +end + +function TMemoryBuffer:resetBuffer(buf) + if buf then + self.buffer = buf + self.bufferSize = string.len(buf) + else + self.buffer = '' + self.bufferSize = 1024 + end + self.wPos = string.len(buf) + self.rPos = 0 +end + +function TMemoryBuffer:available() + return self.wPos - self.rPos +end + +function TMemoryBuffer:read(len) + local avail = self:available() + if avail == 0 then + return '' + end + + if avail < len then + len = avail + end + + local val = string.sub(self.buffer, self.rPos + 1, self.rPos + len) + self.rPos = self.rPos + len + return val +end + +function TMemoryBuffer:readAll(len) + local avail = self:available() + + if avail < len then + local msg = string.format('Attempt to readAll(%d) found only %d available', + len, avail) + terror(TTransportException:new{message = msg}) + end + -- read should block so we don't need a loop here + return self:read(len) +end + +function TMemoryBuffer:write(buf) + self.buffer = self.buffer .. buf + self.wPos = self.wPos + string.len(buf) +end + +function TMemoryBuffer:flush() end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TProtocol.lua b/vendor/src/github.com/apache/thrift/lib/lua/TProtocol.lua new file mode 100644 index 00000000..616e167a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TProtocol.lua @@ -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. +-- + +require 'Thrift' + +TProtocolException = TException:new { + UNKNOWN = 0, + INVALID_DATA = 1, + NEGATIVE_SIZE = 2, + SIZE_LIMIT = 3, + BAD_VERSION = 4, + INVALID_PROTOCOL = 5, + DEPTH_LIMIT = 6, + errorCode = 0, + __type = 'TProtocolException' +} +function TProtocolException:__errorCodeToString() + if self.errorCode == self.INVALID_DATA then + return 'Invalid data' + elseif self.errorCode == self.NEGATIVE_SIZE then + return 'Negative size' + elseif self.errorCode == self.SIZE_LIMIT then + return 'Size limit' + elseif self.errorCode == self.BAD_VERSION then + return 'Bad version' + elseif self.errorCode == self.INVALID_PROTOCOL then + return 'Invalid protocol' + elseif self.errorCode == self.DEPTH_LIMIT then + return 'Exceeded size limit' + else + return 'Default (unknown)' + end +end + +TProtocolBase = __TObject:new{ + __type = 'TProtocolBase', + trans +} + +function TProtocolBase:new(obj) + if ttype(obj) ~= 'table' then + error(ttype(self) .. 'must be initialized with a table') + end + + -- Ensure a transport is provided + if not obj.trans then + error('You must provide ' .. ttype(self) .. ' with a trans') + end + + return __TObject.new(self, obj) +end + +function TProtocolBase:writeMessageBegin(name, ttype, seqid) end +function TProtocolBase:writeMessageEnd() end +function TProtocolBase:writeStructBegin(name) end +function TProtocolBase:writeStructEnd() end +function TProtocolBase:writeFieldBegin(name, ttype, id) end +function TProtocolBase:writeFieldEnd() end +function TProtocolBase:writeFieldStop() end +function TProtocolBase:writeMapBegin(ktype, vtype, size) end +function TProtocolBase:writeMapEnd() end +function TProtocolBase:writeListBegin(ttype, size) end +function TProtocolBase:writeListEnd() end +function TProtocolBase:writeSetBegin(ttype, size) end +function TProtocolBase:writeSetEnd() end +function TProtocolBase:writeBool(bool) end +function TProtocolBase:writeByte(byte) end +function TProtocolBase:writeI16(i16) end +function TProtocolBase:writeI32(i32) end +function TProtocolBase:writeI64(i64) end +function TProtocolBase:writeDouble(dub) end +function TProtocolBase:writeString(str) end +function TProtocolBase:readMessageBegin() end +function TProtocolBase:readMessageEnd() end +function TProtocolBase:readStructBegin() end +function TProtocolBase:readStructEnd() end +function TProtocolBase:readFieldBegin() end +function TProtocolBase:readFieldEnd() end +function TProtocolBase:readMapBegin() end +function TProtocolBase:readMapEnd() end +function TProtocolBase:readListBegin() end +function TProtocolBase:readListEnd() end +function TProtocolBase:readSetBegin() end +function TProtocolBase:readSetEnd() end +function TProtocolBase:readBool() end +function TProtocolBase:readByte() end +function TProtocolBase:readI16() end +function TProtocolBase:readI32() end +function TProtocolBase:readI64() end +function TProtocolBase:readDouble() end +function TProtocolBase:readString() end + +function TProtocolBase:skip(ttype) + if type == TType.STOP then + return + elseif ttype == TType.BOOL then + self:readBool() + elseif ttype == TType.BYTE then + self:readByte() + elseif ttype == TType.I16 then + self:readI16() + elseif ttype == TType.I32 then + self:readI32() + elseif ttype == TType.I64 then + self:readI64() + elseif ttype == TType.DOUBLE then + self:readDouble() + elseif ttype == TType.STRING then + self:readString() + elseif ttype == TType.STRUCT then + local name = self:readStructBegin() + while true do + local name, ttype, id = self:readFieldBegin() + if ttype == TType.STOP then + break + end + self:skip(ttype) + self:readFieldEnd() + end + self:readStructEnd() + elseif ttype == TType.MAP then + local kttype, vttype, size = self:readMapBegin() + for i = 1, size, 1 do + self:skip(kttype) + self:skip(vttype) + end + self:readMapEnd() + elseif ttype == TType.SET then + local ettype, size = self:readSetBegin() + for i = 1, size, 1 do + self:skip(ettype) + end + self:readSetEnd() + elseif ttype == TType.LIST then + local ettype, size = self:readListBegin() + for i = 1, size, 1 do + self:skip(ettype) + end + self:readListEnd() + end +end + +TProtocolFactory = __TObject:new{ + __type = 'TProtocolFactory', +} +function TProtocolFactory:getProtocol(trans) end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TServer.lua b/vendor/src/github.com/apache/thrift/lib/lua/TServer.lua new file mode 100644 index 00000000..4e37d587 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TServer.lua @@ -0,0 +1,140 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'Thrift' +require 'TFramedTransport' +require 'TBinaryProtocol' + +-- TServer +TServer = __TObject:new{ + __type = 'TServer' +} + +-- 2 possible constructors +-- 1. {processor, serverTransport} +-- 2. {processor, serverTransport, transportFactory, protocolFactory} +function TServer:new(args) + if ttype(args) ~= 'table' then + error('TServer must be initialized with a table') + end + if args.processor == nil then + terror('You must provide ' .. ttype(self) .. ' with a processor') + end + if args.serverTransport == nil then + terror('You must provide ' .. ttype(self) .. ' with a serverTransport') + end + + -- Create the object + local obj = __TObject.new(self, args) + + if obj.transportFactory then + obj.inputTransportFactory = obj.transportFactory + obj.outputTransportFactory = obj.transportFactory + obj.transportFactory = nil + else + obj.inputTransportFactory = TFramedTransportFactory:new{} + obj.outputTransportFactory = obj.inputTransportFactory + end + + if obj.protocolFactory then + obj.inputProtocolFactory = obj.protocolFactory + obj.outputProtocolFactory = obj.protocolFactory + obj.protocolFactory = nil + else + obj.inputProtocolFactory = TBinaryProtocolFactory:new{} + obj.outputProtocolFactory = obj.inputProtocolFactory + end + + -- Set the __server variable in the handler so we can stop the server + obj.processor.handler.__server = self + + return obj +end + +function TServer:setServerEventHandler(handler) + self.serverEventHandler = handler +end + +function TServer:_clientBegin(content, iprot, oprot) + if self.serverEventHandler and + type(self.serverEventHandler.clientBegin) == 'function' then + self.serverEventHandler:clientBegin(iprot, oprot) + end +end + +function TServer:_preServe() + if self.serverEventHandler and + type(self.serverEventHandler.preServe) == 'function' then + self.serverEventHandler:preServe(self.serverTransport:getSocketInfo()) + end +end + +function TServer:_handleException(err) + if string.find(err, 'TTransportException') == nil then + print(err) + end +end + +function TServer:serve() end +function TServer:handle(client) + local itrans, otrans = + self.inputTransportFactory:getTransport(client), + self.outputTransportFactory:getTransport(client) + local iprot, oprot = + self.inputProtocolFactory:getProtocol(itrans), + self.outputProtocolFactory:getProtocol(otrans) + + self:_clientBegin(iprot, oprot) + while true do + local ret, err = pcall(self.processor.process, self.processor, iprot, oprot) + if ret == false and err then + if not string.find(err, "TTransportException") then + self:_handleException(err) + end + break + end + end + itrans:close() + otrans:close() +end + +function TServer:close() + self.serverTransport:close() +end + +-- TSimpleServer +-- Single threaded server that handles one transport (connection) +TSimpleServer = __TObject:new(TServer, { + __type = 'TSimpleServer', + __stop = false +}) + +function TSimpleServer:serve() + self.serverTransport:listen() + self:_preServe() + while not self.__stop do + client = self.serverTransport:accept() + self:handle(client) + end + self:close() +end + +function TSimpleServer:stop() + self.__stop = true +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TSocket.lua b/vendor/src/github.com/apache/thrift/lib/lua/TSocket.lua new file mode 100644 index 00000000..d71fc1f9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TSocket.lua @@ -0,0 +1,132 @@ +---- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'TTransport' +require 'libluasocket' + +-- TSocketBase +TSocketBase = TTransportBase:new{ + __type = 'TSocketBase', + timeout = 1000, + host = 'localhost', + port = 9090, + handle +} + +function TSocketBase:close() + if self.handle then + self.handle:destroy() + self.handle = nil + end +end + +-- Returns a table with the fields host and port +function TSocketBase:getSocketInfo() + if self.handle then + return self.handle:getsockinfo() + end + terror(TTransportException:new{errorCode = TTransportException.NOT_OPEN}) +end + +function TSocketBase:setTimeout(timeout) + if timeout and ttype(timeout) == 'number' then + if self.handle then + self.handle:settimeout(timeout) + end + self.timeout = timeout + end +end + +-- TSocket +TSocket = TSocketBase:new{ + __type = 'TSocket', + host = 'localhost', + port = 9090 +} + +function TSocket:isOpen() + if self.handle then + return true + end + return false +end + +function TSocket:open() + if self.handle then + self:close() + end + + -- Create local handle + local sock, err = luasocket.create_and_connect( + self.host, self.port, self.timeout) + if err == nil then + self.handle = sock + end + + if err then + terror(TTransportException:new{ + message = 'Could not connect to ' .. self.host .. ':' .. self.port + .. ' (' .. err .. ')' + }) + end +end + +function TSocket:read(len) + local buf = self.handle:receive(self.handle, len) + if not buf or string.len(buf) ~= len then + terror(TTransportException:new{errorCode = TTransportException.UNKNOWN}) + end + return buf +end + +function TSocket:write(buf) + self.handle:send(self.handle, buf) +end + +function TSocket:flush() +end + +-- TServerSocket +TServerSocket = TSocketBase:new{ + __type = 'TServerSocket', + host = 'localhost', + port = 9090 +} + +function TServerSocket:listen() + if self.handle then + self:close() + end + + local sock, err = luasocket.create(self.host, self.port) + if not err then + self.handle = sock + else + terror(err) + end + self.handle:settimeout(self.timeout) + self.handle:listen() +end + +function TServerSocket:accept() + local client, err = self.handle:accept() + if err then + terror(err) + end + return TSocket:new({handle = client}) +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/TTransport.lua b/vendor/src/github.com/apache/thrift/lib/lua/TTransport.lua new file mode 100644 index 00000000..01c7e597 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/TTransport.lua @@ -0,0 +1,93 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +require 'Thrift' + +TTransportException = TException:new { + UNKNOWN = 0, + NOT_OPEN = 1, + ALREADY_OPEN = 2, + TIMED_OUT = 3, + END_OF_FILE = 4, + INVALID_FRAME_SIZE = 5, + INVALID_TRANSFORM = 6, + INVALID_CLIENT_TYPE = 7, + errorCode = 0, + __type = 'TTransportException' +} + +function TTransportException:__errorCodeToString() + if self.errorCode == self.NOT_OPEN then + return 'Transport not open' + elseif self.errorCode == self.ALREADY_OPEN then + return 'Transport already open' + elseif self.errorCode == self.TIMED_OUT then + return 'Transport timed out' + elseif self.errorCode == self.END_OF_FILE then + return 'End of file' + elseif self.errorCode == self.INVALID_FRAME_SIZE then + return 'Invalid frame size' + elseif self.errorCode == self.INVALID_TRANSFORM then + return 'Invalid transform' + elseif self.errorCode == self.INVALID_CLIENT_TYPE then + return 'Invalid client type' + else + return 'Default (unknown)' + end +end + +TTransportBase = __TObject:new{ + __type = 'TTransportBase' +} + +function TTransportBase:isOpen() end +function TTransportBase:open() end +function TTransportBase:close() end +function TTransportBase:read(len) end +function TTransportBase:readAll(len) + local buf, have, chunk = '', 0 + while have < len do + chunk = self:read(len - have) + have = have + string.len(chunk) + buf = buf .. chunk + + if string.len(chunk) == 0 then + terror(TTransportException:new{ + errorCode = TTransportException.END_OF_FILE + }) + end + end + return buf +end +function TTransportBase:write(buf) end +function TTransportBase:flush() end + +TServerTransportBase = __TObject:new{ + __type = 'TServerTransportBase' +} +function TServerTransportBase:listen() end +function TServerTransportBase:accept() end +function TServerTransportBase:close() end + +TTransportFactoryBase = __TObject:new{ + __type = 'TTransportFactoryBase' +} +function TTransportFactoryBase:getTransport(trans) + return trans +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/Thrift.lua b/vendor/src/github.com/apache/thrift/lib/lua/Thrift.lua new file mode 100644 index 00000000..3eddb640 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/Thrift.lua @@ -0,0 +1,281 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +---- namespace thrift +--thrift = {} +--setmetatable(thrift, {__index = _G}) --> perf hit for accessing global methods +--setfenv(1, thrift) + +package.cpath = package.cpath .. ';bin/?.so' -- TODO FIX +function ttype(obj) + if type(obj) == 'table' and + obj.__type and + type(obj.__type) == 'string' then + return obj.__type + end + return type(obj) +end + +function terror(e) + if e and e.__tostring then + error(e:__tostring()) + return + end + error(e) +end + +function ttable_size(t) + local count = 0 + for k, v in pairs(t) do + count = count + 1 + end + return count +end + +version = 0.10 + +TType = { + STOP = 0, + VOID = 1, + BOOL = 2, + BYTE = 3, + I08 = 3, + DOUBLE = 4, + I16 = 6, + I32 = 8, + I64 = 10, + STRING = 11, + UTF7 = 11, + STRUCT = 12, + MAP = 13, + SET = 14, + LIST = 15, + UTF8 = 16, + UTF16 = 17 +} + +TMessageType = { + CALL = 1, + REPLY = 2, + EXCEPTION = 3, + ONEWAY = 4 +} + +-- Recursive __index function to achieve inheritance +function __tobj_index(self, key) + local v = rawget(self, key) + if v ~= nil then + return v + end + + local p = rawget(self, '__parent') + if p then + return __tobj_index(p, key) + end + + return nil +end + +-- Basic Thrift-Lua Object +__TObject = { + __type = '__TObject', + __mt = { + __index = __tobj_index + } +} +function __TObject:new(init_obj) + local obj = {} + if ttype(obj) == 'table' then + obj = init_obj + end + + -- Use the __parent key and the __index function to achieve inheritance + obj.__parent = self + setmetatable(obj, __TObject.__mt) + return obj +end + +-- Return a string representation of any lua variable +function thrift_print_r(t) + local ret = '' + local ltype = type(t) + if (ltype == 'table') then + ret = ret .. '{ ' + for key,value in pairs(t) do + ret = ret .. tostring(key) .. '=' .. thrift_print_r(value) .. ' ' + end + ret = ret .. '}' + elseif ltype == 'string' then + ret = ret .. "'" .. tostring(t) .. "'" + else + ret = ret .. tostring(t) + end + return ret +end + +-- Basic Exception +TException = __TObject:new{ + message, + errorCode, + __type = 'TException' +} +function TException:__tostring() + if self.message then + return string.format('%s: %s', self.__type, self.message) + else + local message + if self.errorCode and self.__errorCodeToString then + message = string.format('%d: %s', self.errorCode, self:__errorCodeToString()) + else + message = thrift_print_r(self) + end + return string.format('%s:%s', self.__type, message) + end +end + +TApplicationException = TException:new{ + UNKNOWN = 0, + UNKNOWN_METHOD = 1, + INVALID_MESSAGE_TYPE = 2, + WRONG_METHOD_NAME = 3, + BAD_SEQUENCE_ID = 4, + MISSING_RESULT = 5, + INTERNAL_ERROR = 6, + PROTOCOL_ERROR = 7, + INVALID_TRANSFORM = 8, + INVALID_PROTOCOL = 9, + UNSUPPORTED_CLIENT_TYPE = 10, + errorCode = 0, + __type = 'TApplicationException' +} + +function TApplicationException:__errorCodeToString() + if self.errorCode == self.UNKNOWN_METHOD then + return 'Unknown method' + elseif self.errorCode == self.INVALID_MESSAGE_TYPE then + return 'Invalid message type' + elseif self.errorCode == self.WRONG_METHOD_NAME then + return 'Wrong method name' + elseif self.errorCode == self.BAD_SEQUENCE_ID then + return 'Bad sequence ID' + elseif self.errorCode == self.MISSING_RESULT then + return 'Missing result' + elseif self.errorCode == self.INTERNAL_ERROR then + return 'Internal error' + elseif self.errorCode == self.PROTOCOL_ERROR then + return 'Protocol error' + elseif self.errorCode == self.INVALID_TRANSFORM then + return 'Invalid transform' + elseif self.errorCode == self.INVALID_PROTOCOL then + return 'Invalid protocol' + elseif self.errorCode == self.UNSUPPORTED_CLIENT_TYPE then + return 'Unsupported client type' + else + return 'Default (unknown)' + end +end + +function TException:read(iprot) + iprot:readStructBegin() + while true do + local fname, ftype, fid = iprot:readFieldBegin() + if ftype == TType.STOP then + break + elseif fid == 1 then + if ftype == TType.STRING then + self.message = iprot:readString() + else + iprot:skip(ftype) + end + elseif fid == 2 then + if ftype == TType.I32 then + self.errorCode = iprot:readI32() + else + iprot:skip(ftype) + end + else + iprot:skip(ftype) + end + iprot:readFieldEnd() + end + iprot:readStructEnd() +end + +function TException:write(oprot) + oprot:writeStructBegin('TApplicationException') + if self.message then + oprot:writeFieldBegin('message', TType.STRING, 1) + oprot:writeString(self.message) + oprot:writeFieldEnd() + end + if self.errorCode then + oprot:writeFieldBegin('type', TType.I32, 2) + oprot:writeI32(self.errorCode) + oprot:writeFieldEnd() + end + oprot:writeFieldStop() + oprot:writeStructEnd() +end + +-- Basic Client (used in generated lua code) +__TClient = __TObject:new{ + __type = '__TClient', + _seqid = 0 +} +function __TClient:new(obj) + if ttype(obj) ~= 'table' then + error('TClient must be initialized with a table') + end + + -- Set iprot & oprot + if obj.protocol then + obj.iprot = obj.protocol + obj.oprot = obj.protocol + obj.protocol = nil + elseif not obj.iprot then + error('You must provide ' .. ttype(self) .. ' with an iprot') + end + if not obj.oprot then + obj.oprot = obj.iprot + end + + return __TObject.new(self, obj) +end + +function __TClient:close() + self.iprot.trans:close() + self.oprot.trans:close() +end + +-- Basic Processor (used in generated lua code) +__TProcessor = __TObject:new{ + __type = '__TProcessor' +} +function __TProcessor:new(obj) + if ttype(obj) ~= 'table' then + error('TProcessor must be initialized with a table') + end + + -- Ensure a handler is provided + if not obj.handler then + error('You must provide ' .. ttype(self) .. ' with a handler') + end + + return __TObject.new(self, obj) +end diff --git a/vendor/src/github.com/apache/thrift/lib/lua/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/lua/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/longnumberutils.c b/vendor/src/github.com/apache/thrift/lib/lua/src/longnumberutils.c new file mode 100644 index 00000000..fbc67890 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/longnumberutils.c @@ -0,0 +1,47 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include +#include +#include + +const char * LONG_NUM_TYPE = "__thrift_longnumber"; +int64_t lualongnumber_checklong(lua_State *L, int index) { + switch (lua_type(L, index)) { + case LUA_TNUMBER: + return (int64_t)lua_tonumber(L, index); + case LUA_TSTRING: + return atoll(lua_tostring(L, index)); + default: + return *((int64_t *)luaL_checkudata(L, index, LONG_NUM_TYPE)); + } +} + +// Creates a new longnumber and pushes it onto the statck +int64_t * lualongnumber_pushlong(lua_State *L, int64_t *val) { + int64_t *data = (int64_t *)lua_newuserdata(L, sizeof(int64_t)); // longnum + luaL_getmetatable(L, LONG_NUM_TYPE); // longnum, mt + lua_setmetatable(L, -2); // longnum + if (val) { + *data = *val; + } + return data; +} + diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/luabitwise.c b/vendor/src/github.com/apache/thrift/lib/lua/src/luabitwise.c new file mode 100644 index 00000000..2e07e172 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/luabitwise.c @@ -0,0 +1,83 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include + +static int l_not(lua_State *L) { + int a = luaL_checkinteger(L, 1); + a = ~a; + lua_pushnumber(L, a); + return 1; +} + +static int l_xor(lua_State *L) { + int a = luaL_checkinteger(L, 1); + int b = luaL_checkinteger(L, 2); + a ^= b; + lua_pushnumber(L, a); + return 1; +} + +static int l_and(lua_State *L) { + int a = luaL_checkinteger(L, 1); + int b = luaL_checkinteger(L, 2); + a &= b; + lua_pushnumber(L, a); + return 1; +} + +static int l_or(lua_State *L) { + int a = luaL_checkinteger(L, 1); + int b = luaL_checkinteger(L, 2); + a |= b; + lua_pushnumber(L, a); + return 1; +} + +static int l_shiftr(lua_State *L) { + int a = luaL_checkinteger(L, 1); + int b = luaL_checkinteger(L, 2); + a = a >> b; + lua_pushnumber(L, a); + return 1; +} + +static int l_shiftl(lua_State *L) { + int a = luaL_checkinteger(L, 1); + int b = luaL_checkinteger(L, 2); + a = a << b; + lua_pushnumber(L, a); + return 1; +} + +static const struct luaL_Reg funcs[] = { + {"band", l_and}, + {"bor", l_or}, + {"bxor", l_xor}, + {"bnot", l_not}, + {"shiftl", l_shiftl}, + {"shiftr", l_shiftr}, + {NULL, NULL} +}; + +int luaopen_libluabitwise(lua_State *L) { + luaL_register(L, "libluabitwise", funcs); + return 1; +} diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/luabpack.c b/vendor/src/github.com/apache/thrift/lib/lua/src/luabpack.c new file mode 100644 index 00000000..a86fc3ed --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/luabpack.c @@ -0,0 +1,308 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include +#include +#include +#include + +extern int64_t lualongnumber_checklong(lua_State *L, int index); +extern int64_t lualongnumber_pushlong(lua_State *L, int64_t *val); + +// host order to network order (64-bit) +static int64_t T_htonll(uint64_t data) { + uint32_t d1 = htonl((uint32_t)data); + uint32_t d2 = htonl((uint32_t)(data >> 32)); + return ((uint64_t)d1 << 32) + (uint64_t)d2; +} + +// network order to host order (64-bit) +static int64_t T_ntohll(uint64_t data) { + uint32_t d1 = ntohl((uint32_t)data); + uint32_t d2 = ntohl((uint32_t)(data >> 32)); + return ((uint64_t)d1 << 32) + (uint64_t)d2; +} + +/** + * bpack(type, data) + * c - Signed Byte + * s - Signed Short + * i - Signed Int + * l - Signed Long + * d - Double + */ +static int l_bpack(lua_State *L) { + const char *code = luaL_checkstring(L, 1); + luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character."); + luaL_Buffer buf; + luaL_buffinit(L, &buf); + + switch (code[0]) { + case 'c': { + int8_t data = luaL_checknumber(L, 2); + luaL_addlstring(&buf, (void*)&data, sizeof(data)); + break; + } + case 's': { + int16_t data = luaL_checknumber(L, 2); + data = (int16_t)htons(data); + luaL_addlstring(&buf, (void*)&data, sizeof(data)); + break; + } + case 'i': { + int32_t data = luaL_checkinteger(L, 2); + data = (int32_t)htonl(data); + luaL_addlstring(&buf, (void*)&data, sizeof(data)); + break; + } + case 'l': { + int64_t data = lualongnumber_checklong(L, 2); + data = (int64_t)T_htonll(data); + luaL_addlstring(&buf, (void*)&data, sizeof(data)); + break; + } + case 'd': { + double data = luaL_checknumber(L, 2); + luaL_addlstring(&buf, (void*)&data, sizeof(data)); + break; + } + default: + luaL_argcheck(L, 0, 0, "Invalid format code."); + } + + luaL_pushresult(&buf); + return 1; +} + +/** + * bunpack(type, data) + * c - Signed Byte + * C - Unsigned Byte + * s - Signed Short + * i - Signed Int + * l - Signed Long + * d - Double + */ +static int l_bunpack(lua_State *L) { + const char *code = luaL_checkstring(L, 1); + luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character."); + const char *data = luaL_checkstring(L, 2); +#ifdef _LUA51_ + size_t len = lua_objlen(L, 2); +#else + size_t len = lua_rawlen(L, 2); +#endif + + switch (code[0]) { + case 'c': { + int8_t val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + lua_pushnumber(L, val); + break; + } + /** + * unpack unsigned Byte. + */ + case 'C': { + uint8_t val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + lua_pushnumber(L, val); + break; + } + case 's': { + int16_t val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + val = (int16_t)ntohs(val); + lua_pushnumber(L, val); + break; + } + case 'i': { + int32_t val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + val = (int32_t)ntohl(val); + lua_pushnumber(L, val); + break; + } + case 'l': { + int64_t val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + val = (int64_t)T_ntohll(val); + lualongnumber_pushlong(L, &val); + break; + } + case 'd': { + double val; + luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size."); + memcpy(&val, data, sizeof(val)); + lua_pushnumber(L, val); + break; + } + default: + luaL_argcheck(L, 0, 0, "Invalid format code."); + } + return 1; +} + +/** + * Convert l into a zigzag long. This allows negative numbers to be + * represented compactly as a varint. + */ +static int l_i64ToZigzag(lua_State *L) { + int64_t n = lualongnumber_checklong(L, 1); + int64_t result = (n << 1) ^ (n >> 63); + lualongnumber_pushlong(L, &result); + return 1; +} +/** + * Convert n into a zigzag int. This allows negative numbers to be + * represented compactly as a varint. + */ +static int l_i32ToZigzag(lua_State *L) { + int32_t n = luaL_checkinteger(L, 1); + uint32_t result = (uint32_t)(n << 1) ^ (n >> 31); + lua_pushnumber(L, result); + return 1; +} + +/** + * Convert from zigzag int to int. + */ +static int l_zigzagToI32(lua_State *L) { + uint32_t n = luaL_checkinteger(L, 1); + int32_t result = (int32_t)(n >> 1) ^ (uint32_t)(-(int32_t)(n & 1)); + lua_pushnumber(L, result); + return 1; +} + +/** + * Convert from zigzag long to long. + */ +static int l_zigzagToI64(lua_State *L) { + int64_t n = lualongnumber_checklong(L, 1); + int64_t result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1)); + lualongnumber_pushlong(L, &result); + return 1; +} + +/** + * Convert an i32 to a varint. Results in 1-5 bytes on the buffer. + */ +static int l_toVarint32(lua_State *L) { + uint8_t buf[5]; + uint32_t n = luaL_checkinteger(L, 1); + uint32_t wsize = 0; + + while (1) { + if ((n & ~0x7F) == 0) { + buf[wsize++] = (int8_t)n; + break; + } else { + buf[wsize++] = (int8_t)((n & 0x7F) | 0x80); + n >>= 7; + } + } + lua_pushlstring(L, buf, wsize); + return 1; +} + +/** + * Convert an i64 to a varint. Results in 1-10 bytes on the buffer. + */ +static int l_toVarint64(lua_State *L) { + uint8_t data[10]; + uint64_t n = lualongnumber_checklong(L, 1); + uint32_t wsize = 0; + luaL_Buffer buf; + luaL_buffinit(L, &buf); + + while (1) { + if ((n & ~0x7FL) == 0) { + data[wsize++] = (int8_t)n; + break; + } else { + data[wsize++] = (int8_t)((n & 0x7F) | 0x80); + n >>= 7; + } + } + + luaL_addlstring(&buf, (void*)&data, wsize); + luaL_pushresult(&buf); + return 1; +} + +/** + * Convert a varint to i64. + */ +static int l_fromVarint64(lua_State *L) { + int64_t result; + uint8_t byte = luaL_checknumber(L, 1); + int32_t shift = luaL_checknumber(L, 2); + uint64_t n = (uint64_t)lualongnumber_checklong(L, 3); + n |= (uint64_t)(byte & 0x7f) << shift; + + if (!(byte & 0x80)) { + result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1)); + lua_pushnumber(L, 0); + } else { + result = n; + lua_pushnumber(L, 1); + } + lualongnumber_pushlong(L, &result); + return 2; +} + +/** + * To pack message type of compact protocol. + */ +static int l_packMesgType(lua_State *L) { + int32_t version_n = luaL_checkinteger(L, 1); + int32_t version_mask = luaL_checkinteger(L, 2); + int32_t messagetype = luaL_checkinteger(L, 3); + int32_t type_shift_amount = luaL_checkinteger(L, 4); + int32_t type_mask = luaL_checkinteger(L, 5); + int32_t to_mesg_type = (version_n & version_mask) | + (((int32_t)messagetype << type_shift_amount) & type_mask); + lua_pushnumber(L, to_mesg_type); + return 1; +} + +static const struct luaL_Reg lua_bpack[] = { + {"bpack", l_bpack}, + {"bunpack", l_bunpack}, + {"i32ToZigzag", l_i32ToZigzag}, + {"i64ToZigzag", l_i64ToZigzag}, + {"zigzagToI32", l_zigzagToI32}, + {"zigzagToI64", l_zigzagToI64}, + {"toVarint32", l_toVarint32}, + {"toVarint64", l_toVarint64}, + {"fromVarint64", l_fromVarint64}, + {"packMesgType", l_packMesgType}, + {NULL, NULL} +}; + +int luaopen_libluabpack(lua_State *L) { + luaL_register(L, "libluabpack", lua_bpack); + return 1; +} diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/lualongnumber.c b/vendor/src/github.com/apache/thrift/lib/lua/src/lualongnumber.c new file mode 100644 index 00000000..9001e4a9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/lualongnumber.c @@ -0,0 +1,228 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include +#include +#include +#include +#include + +extern const char * LONG_NUM_TYPE; +extern int64_t lualongnumber_checklong(lua_State *L, int index); +extern int64_t lualongnumber_pushlong(lua_State *L, int64_t *val); + +//////////////////////////////////////////////////////////////////////////////// + +static void l_serialize(char *buf, int len, int64_t val) { + snprintf(buf, len, "%"PRId64, val); +} + +static int64_t l_deserialize(const char *buf) { + int64_t data; + int rv; + // Support hex prefixed with '0x' + if (strstr(buf, "0x") == buf) { + rv = sscanf(buf, "%"PRIx64, &data); + } else { + rv = sscanf(buf, "%"PRId64, &data); + } + if (rv == 1) { + return data; + } + return 0; // Failed +} + +//////////////////////////////////////////////////////////////////////////////// + +static int l_new(lua_State *L) { + int64_t val; + const char *str = NULL; + if (lua_type(L, 1) == LUA_TSTRING) { + str = lua_tostring(L, 1); + val = l_deserialize(str); + } else if (lua_type(L, 1) == LUA_TNUMBER) { + val = (int64_t)lua_tonumber(L, 1); + str = (const char *)1; + } + lualongnumber_pushlong(L, (str ? &val : NULL)); + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// + +// a + b +static int l_add(lua_State *L) { + int64_t a, b, c; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + c = a + b; + lualongnumber_pushlong(L, &c); + return 1; +} + +// a / b +static int l_div(lua_State *L) { + int64_t a, b, c; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + c = a / b; + lualongnumber_pushlong(L, &c); + return 1; +} + +// a == b (both a and b are lualongnumber's) +static int l_eq(lua_State *L) { + int64_t a, b; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + lua_pushboolean(L, (a == b ? 1 : 0)); + return 1; +} + +// garbage collection +static int l_gc(lua_State *L) { + lua_pushnil(L); + lua_setmetatable(L, 1); + return 0; +} + +// a < b +static int l_lt(lua_State *L) { + int64_t a, b; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + lua_pushboolean(L, (a < b ? 1 : 0)); + return 1; +} + +// a <= b +static int l_le(lua_State *L) { + int64_t a, b; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + lua_pushboolean(L, (a <= b ? 1 : 0)); + return 1; +} + +// a % b +static int l_mod(lua_State *L) { + int64_t a, b, c; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + c = a % b; + lualongnumber_pushlong(L, &c); + return 1; +} + +// a * b +static int l_mul(lua_State *L) { + int64_t a, b, c; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + c = a * b; + lualongnumber_pushlong(L, &c); + return 1; +} + +// a ^ b +static int l_pow(lua_State *L) { + long double a, b; + int64_t c; + a = (long double)lualongnumber_checklong(L, 1); + b = (long double)lualongnumber_checklong(L, 2); + c = (int64_t)pow(a, b); + lualongnumber_pushlong(L, &c); + return 1; +} + +// a - b +static int l_sub(lua_State *L) { + int64_t a, b, c; + a = lualongnumber_checklong(L, 1); + b = lualongnumber_checklong(L, 2); + c = a - b; + lualongnumber_pushlong(L, &c); + return 1; +} + +// tostring() +static int l_tostring(lua_State *L) { + int64_t a; + char str[256]; + l_serialize(str, 256, lualongnumber_checklong(L, 1)); + lua_pushstring(L, str); + return 1; +} + +// -a +static int l_unm(lua_State *L) { + int64_t a, c; + a = lualongnumber_checklong(L, 1); + c = -a; + lualongnumber_pushlong(L, &c); + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// + +static const luaL_Reg methods[] = { + {"__add", l_add}, + {"__div", l_div}, + {"__eq", l_eq}, + {"__gc", l_gc}, + {"__lt", l_lt}, + {"__le", l_le}, + {"__mod", l_mod}, + {"__mul", l_mul}, + {"__pow", l_pow}, + {"__sub", l_sub}, + {"__tostring", l_tostring}, + {"__unm", l_unm}, + {NULL, NULL}, +}; + +static const luaL_Reg funcs[] = { + {"new", l_new}, + {NULL, NULL} +}; + +//////////////////////////////////////////////////////////////////////////////// + +static void set_methods(lua_State *L, + const char *metatablename, + const struct luaL_Reg *methods) { + luaL_getmetatable(L, metatablename); // mt + // No need for a __index table since everything is __* + for (; methods->name; methods++) { + lua_pushstring(L, methods->name); // mt, "name" + lua_pushcfunction(L, methods->func); // mt, "name", func + lua_rawset(L, -3); // mt + } + lua_pop(L, 1); +} + +LUALIB_API int luaopen_liblualongnumber(lua_State *L) { + luaL_newmetatable(L, LONG_NUM_TYPE); + lua_pop(L, 1); + set_methods(L, LONG_NUM_TYPE, methods); + + luaL_register(L, "liblualongnumber", funcs); + return 1; +} diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/luasocket.c b/vendor/src/github.com/apache/thrift/lib/lua/src/luasocket.c new file mode 100644 index 00000000..d4835107 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/luasocket.c @@ -0,0 +1,380 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include + +#include +#include "string.h" +#include "socket.h" + +//////////////////////////////////////////////////////////////////////////////// + +static const char *SOCKET_ANY = "__thrift_socket_any"; +static const char *SOCKET_CONN = "__thrift_socket_connected"; + +static const char *SOCKET_GENERIC = "__thrift_socket_generic"; +static const char *SOCKET_CLIENT = "__thrift_socket_client"; +static const char *SOCKET_SERVER = "__thrift_socket_server"; + +static const char *DEFAULT_HOST = "localhost"; + +typedef struct __t_tcp { + t_socket sock; + int timeout; // Milliseconds +} t_tcp; +typedef t_tcp *p_tcp; + +//////////////////////////////////////////////////////////////////////////////// +// Util + +static void throw_argerror(lua_State *L, int index, const char *expected) { + char msg[256]; + sprintf(msg, "%s expected, got %s", expected, luaL_typename(L, index)); + luaL_argerror(L, index, msg); +} + +static void *checkgroup(lua_State *L, int index, const char *groupname) { + if (!lua_getmetatable(L, index)) { + throw_argerror(L, index, groupname); + } + + lua_pushstring(L, groupname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + throw_argerror(L, index, groupname); + } else { + lua_pop(L, 2); + return lua_touserdata(L, index); + } + return NULL; // Not reachable +} + +static void *checktype(lua_State *L, int index, const char *typename) { + if (strcmp(typename, SOCKET_ANY) == 0 || + strcmp(typename, SOCKET_CONN) == 0) { + return checkgroup(L, index, typename); + } else { + return luaL_checkudata(L, index, typename); + } +} + +static void settype(lua_State *L, int index, const char *typename) { + luaL_getmetatable(L, typename); + lua_setmetatable(L, index); +} + +#define LUA_SUCCESS_RETURN(L) \ + lua_pushnumber(L, 1); \ + return 1 + +#define LUA_CHECK_RETURN(L, err) \ + if (err) { \ + lua_pushnil(L); \ + lua_pushstring(L, err); \ + return 2; \ + } \ + LUA_SUCCESS_RETURN(L) + +//////////////////////////////////////////////////////////////////////////////// + +static int l_socket_create(lua_State *L); +static int l_socket_destroy(lua_State *L); +static int l_socket_settimeout(lua_State *L); +static int l_socket_getsockinfo(lua_State *L); + +static int l_socket_accept(lua_State *L); +static int l_socket_listen(lua_State *L); + +static int l_socket_create_and_connect(lua_State *L); +static int l_socket_connect(lua_State *L); +static int l_socket_send(lua_State *L); +static int l_socket_receive(lua_State *L); + +//////////////////////////////////////////////////////////////////////////////// + +static const struct luaL_Reg methods_generic[] = { + {"destroy", l_socket_destroy}, + {"settimeout", l_socket_settimeout}, + {"getsockinfo", l_socket_getsockinfo}, + {"listen", l_socket_listen}, + {"connect", l_socket_connect}, + {NULL, NULL} +}; + +static const struct luaL_Reg methods_server[] = { + {"destroy", l_socket_destroy}, + {"getsockinfo", l_socket_getsockinfo}, + {"accept", l_socket_accept}, + {"send", l_socket_send}, + {"receive", l_socket_receive}, + {NULL, NULL} +}; + +static const struct luaL_Reg methods_client[] = { + {"destroy", l_socket_destroy}, + {"settimeout", l_socket_settimeout}, + {"getsockinfo", l_socket_getsockinfo}, + {"send", l_socket_send}, + {"receive", l_socket_receive}, + {NULL, NULL} +}; + +static const struct luaL_Reg funcs_luasocket[] = { + {"create", l_socket_create}, + {"create_and_connect", l_socket_create_and_connect}, + {NULL, NULL} +}; + +//////////////////////////////////////////////////////////////////////////////// + +// Check/enforce inheritance +static void add_to_group(lua_State *L, + const char *metatablename, + const char *groupname) { + luaL_getmetatable(L, metatablename); // mt + lua_pushstring(L, groupname); // mt, "name" + lua_pushboolean(L, 1); // mt, "name", true + lua_rawset(L, -3); // mt + lua_pop(L, 1); +} + +static void set_methods(lua_State *L, + const char *metatablename, + const struct luaL_Reg *methods) { + luaL_getmetatable(L, metatablename); // mt + // Create the __index table + lua_pushstring(L, "__index"); // mt, "__index" + lua_newtable(L); // mt, "__index", t + for (; methods->name; methods++) { + lua_pushstring(L, methods->name); // mt, "__index", t, "name" + lua_pushcfunction(L, methods->func); // mt, "__index", t, "name", func + lua_rawset(L, -3); // mt, "__index", t + } + lua_rawset(L, -3); // mt + lua_pop(L, 1); +} + +int luaopen_libluasocket(lua_State *L) { + luaL_newmetatable(L, SOCKET_GENERIC); + luaL_newmetatable(L, SOCKET_CLIENT); + luaL_newmetatable(L, SOCKET_SERVER); + lua_pop(L, 3); + add_to_group(L, SOCKET_GENERIC, SOCKET_ANY); + add_to_group(L, SOCKET_CLIENT, SOCKET_ANY); + add_to_group(L, SOCKET_SERVER, SOCKET_ANY); + add_to_group(L, SOCKET_CLIENT, SOCKET_CONN); + add_to_group(L, SOCKET_SERVER, SOCKET_CONN); + set_methods(L, SOCKET_GENERIC, methods_generic); + set_methods(L, SOCKET_CLIENT, methods_client); + set_methods(L, SOCKET_SERVER, methods_server); + + luaL_register(L, "luasocket", funcs_luasocket); + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// General + +// sock,err create(bind_host, bind_port) +// sock,err create(bind_host) -> any port +// sock,err create() -> any port on localhost +static int l_socket_create(lua_State *L) { + const char *err; + t_socket sock; + const char *addr = lua_tostring(L, 1); + if (!addr) { + addr = DEFAULT_HOST; + } + unsigned short port = lua_tonumber(L, 2); + err = tcp_create(&sock); + if (!err) { + err = tcp_bind(&sock, addr, port); // bind on create + if (err) { + tcp_destroy(&sock); + } else { + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + settype(L, -2, SOCKET_GENERIC); + socket_setnonblocking(&sock); + tcp->sock = sock; + tcp->timeout = 0; + return 1; // Return userdata + } + } + LUA_CHECK_RETURN(L, err); +} + +// destroy() +static int l_socket_destroy(lua_State *L) { + p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY); + const char *err = tcp_destroy(&tcp->sock); + LUA_CHECK_RETURN(L, err); +} + +// send(socket, data) +static int l_socket_send(lua_State *L) { + p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN); + p_tcp tcp = (p_tcp) checktype(L, 2, SOCKET_CONN); + size_t len; + const char *data = luaL_checklstring(L, 3, &len); + const char *err = + tcp_send(&tcp->sock, data, len, tcp->timeout); + LUA_CHECK_RETURN(L, err); +} + +#define LUA_READ_STEP 8192 +static int l_socket_receive(lua_State *L) { + p_tcp self = (p_tcp) checktype(L, 1, SOCKET_CONN); + p_tcp handle = (p_tcp) checktype(L, 2, SOCKET_CONN); + size_t len = luaL_checknumber(L, 3); + char buf[LUA_READ_STEP]; + const char *err = NULL; + int received; + size_t got = 0, step = 0; + luaL_Buffer b; + + luaL_buffinit(L, &b); + do { + step = (LUA_READ_STEP < len - got ? LUA_READ_STEP : len - got); + err = tcp_raw_receive(&handle->sock, buf, step, self->timeout, &received); + if (err == NULL) { + luaL_addlstring(&b, buf, received); + got += received; + } + } while (err == NULL && got < len); + + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } + luaL_pushresult(&b); + return 1; +} + +// settimeout(timeout) +static int l_socket_settimeout(lua_State *L) { + p_tcp self = (p_tcp) checktype(L, 1, SOCKET_ANY); + int timeout = luaL_checknumber(L, 2); + self->timeout = timeout; + LUA_SUCCESS_RETURN(L); +} + +// table getsockinfo() +static int l_socket_getsockinfo(lua_State *L) { + char buf[256]; + short port = 0; + p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_ANY); + if (socket_get_info(&tcp->sock, &port, buf, 256) == SUCCESS) { + lua_newtable(L); // t + lua_pushstring(L, "host"); // t, "host" + lua_pushstring(L, buf); // t, "host", buf + lua_rawset(L, -3); // t + lua_pushstring(L, "port"); // t, "port" + lua_pushnumber(L, port); // t, "port", port + lua_rawset(L, -3); // t + return 1; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Server + +// accept() +static int l_socket_accept(lua_State *L) { + const char *err; + p_tcp self = (p_tcp) checktype(L, 1, SOCKET_SERVER); + t_socket sock; + err = tcp_accept(&self->sock, &sock, self->timeout); + if (!err) { // Success + // Create a reference to the client + p_tcp client = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + settype(L, 2, SOCKET_CLIENT); + socket_setnonblocking(&sock); + client->sock = sock; + client->timeout = self->timeout; + return 1; + } + LUA_CHECK_RETURN(L, err); +} + +static int l_socket_listen(lua_State *L) { + const char* err; + p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC); + int backlog = 10; + err = tcp_listen(&tcp->sock, backlog); + if (!err) { + // Set the current as a server + settype(L, 1, SOCKET_SERVER); // Now a server + } + LUA_CHECK_RETURN(L, err); +} + +//////////////////////////////////////////////////////////////////////////////// +// Client + +// create_and_connect(host, port, timeout) +extern double __gettime(); +static int l_socket_create_and_connect(lua_State *L) { + const char* err = NULL; + double end; + t_socket sock; + const char *host = luaL_checkstring(L, 1); + unsigned short port = luaL_checknumber(L, 2); + int timeout = luaL_checknumber(L, 3); + + // Create and connect loop for timeout milliseconds + end = __gettime() + timeout/1000; + do { + // Create the socket + err = tcp_create(&sock); + if (!err) { + // Connect + err = tcp_connect(&sock, host, port, timeout); + if (err) { + tcp_destroy(&sock); + usleep(100000); // sleep for 100ms + } else { + p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); + settype(L, -2, SOCKET_CLIENT); + socket_setnonblocking(&sock); + tcp->sock = sock; + tcp->timeout = timeout; + return 1; // Return userdata + } + } + } while (err && __gettime() < end); + + LUA_CHECK_RETURN(L, err); +} + +// connect(host, port) +static int l_socket_connect(lua_State *L) { + const char *err; + p_tcp tcp = (p_tcp) checktype(L, 1, SOCKET_GENERIC); + const char *host = luaL_checkstring(L, 2); + unsigned short port = luaL_checknumber(L, 3); + err = tcp_connect(&tcp->sock, host, port, tcp->timeout); + if (!err) { + settype(L, 1, SOCKET_CLIENT); // Now a client + } + LUA_CHECK_RETURN(L, err); +} diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/socket.h b/vendor/src/github.com/apache/thrift/lib/lua/src/socket.h new file mode 100644 index 00000000..8019ffed --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/socket.h @@ -0,0 +1,78 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#ifndef LUA_THRIFT_SOCKET_H +#define LUA_THRIFT_SOCKET_H + +#include + +#ifdef _WIN32 +// SOL +#else +typedef int t_socket; +typedef t_socket* p_socket; +#endif + +// Error Codes +enum { + SUCCESS = 0, + TIMEOUT = -1, + CLOSED = -2, +}; +typedef int T_ERRCODE; + +static const char * TIMEOUT_MSG = "Timeout"; +static const char * CLOSED_MSG = "Connection Closed"; + +typedef struct sockaddr t_sa; +typedef t_sa * p_sa; + +T_ERRCODE socket_create(p_socket sock, int domain, int type, int protocol); +T_ERRCODE socket_destroy(p_socket sock); +T_ERRCODE socket_bind(p_socket sock, p_sa addr, int addr_len); +T_ERRCODE socket_get_info(p_socket sock, short *port, char *buf, size_t len); +T_ERRCODE socket_send(p_socket sock, const char *data, size_t len, int timeout); +T_ERRCODE socket_recv(p_socket sock, char *data, size_t len, int timeout, + int *received); + +void socket_setblocking(p_socket sock); +void socket_setnonblocking(p_socket sock); + +T_ERRCODE socket_accept(p_socket sock, p_socket sibling, + p_sa addr, socklen_t *addr_len, int timeout); +T_ERRCODE socket_listen(p_socket sock, int backlog); + +T_ERRCODE socket_connect(p_socket sock, p_sa addr, int addr_len, int timeout); + +const char * tcp_create(p_socket sock); +const char * tcp_destroy(p_socket sock); +const char * tcp_bind(p_socket sock, const char *host, unsigned short port); +const char * tcp_send(p_socket sock, const char *data, size_t w_len, + int timeout); +const char * tcp_receive(p_socket sock, char *data, size_t r_len, int timeout); +const char * tcp_raw_receive(p_socket sock, char * data, size_t r_len, + int timeout, int *received); + +const char * tcp_listen(p_socket sock, int backlog); +const char * tcp_accept(p_socket sock, p_socket client, int timeout); + +const char * tcp_connect(p_socket sock, const char *host, unsigned short port, + int timeout); + +#endif diff --git a/vendor/src/github.com/apache/thrift/lib/lua/src/usocket.c b/vendor/src/github.com/apache/thrift/lib/lua/src/usocket.c new file mode 100644 index 00000000..864fa365 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/lua/src/usocket.c @@ -0,0 +1,370 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // TODO REMOVE + +#include "socket.h" + +//////////////////////////////////////////////////////////////////////////////// +// Private + +// Num seconds since Jan 1 1970 (UTC) +#ifdef _WIN32 +// SOL +#else + double __gettime() { + struct timeval v; + gettimeofday(&v, (struct timezone*) NULL); + return v.tv_sec + v.tv_usec/1.0e6; + } +#endif + +#define WAIT_MODE_R 1 +#define WAIT_MODE_W 2 +#define WAIT_MODE_C (WAIT_MODE_R|WAIT_MODE_W) +T_ERRCODE socket_wait(p_socket sock, int mode, int timeout) { + int ret = 0; + fd_set rfds, wfds; + struct timeval tv; + double end, t; + if (!timeout) { + return TIMEOUT; + } + + end = __gettime() + timeout/1000; + do { + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + // Specify what I/O operations we care about + if (mode & WAIT_MODE_R) { + FD_SET(*sock, &rfds); + } + if (mode & WAIT_MODE_W) { + FD_SET(*sock, &wfds); + } + + // Check for timeout + t = end - __gettime(); + if (t < 0.0) { + break; + } + + // Wait + tv.tv_sec = (int)t; + tv.tv_usec = (int)((t - tv.tv_sec) * 1.0e6); + ret = select(*sock+1, &rfds, &wfds, NULL, &tv); + } while (ret == -1 && errno == EINTR); + if (ret == -1) { + return errno; + } + + // Check for timeout + if (ret == 0) { + return TIMEOUT; + } + + // Verify that we can actually read from the remote host + if (mode & WAIT_MODE_C && FD_ISSET(*sock, &rfds) && + recv(*sock, (char*) &rfds, 0, 0) != 0) { + return errno; + } + + return SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// General + +T_ERRCODE socket_create(p_socket sock, int domain, int type, int protocol) { + *sock = socket(domain, type, protocol); + if (*sock > 0) { + return SUCCESS; + } else { + return errno; + } +} + +T_ERRCODE socket_destroy(p_socket sock) { + // TODO Figure out if I should be free-ing this + if (*sock > 0) { + socket_setblocking(sock); + close(*sock); + *sock = -1; + } + return SUCCESS; +} + +T_ERRCODE socket_bind(p_socket sock, p_sa addr, int addr_len) { + int ret = SUCCESS; + socket_setblocking(sock); + if (bind(*sock, addr, addr_len)) { + ret = errno; + } + socket_setnonblocking(sock); + return ret; +} + +T_ERRCODE socket_get_info(p_socket sock, short *port, char *buf, size_t len) { + struct sockaddr_storage sa; + memset(&sa, 0, sizeof(sa)); + socklen_t addrlen = sizeof(sa); + int rc = getsockname(*sock, (struct sockaddr*)&sa, &addrlen); + if (!rc) { + if (sa.ss_family == AF_INET6) { + struct sockaddr_in6* sin = (struct sockaddr_in6*)(&sa); + if (!inet_ntop(AF_INET6, &sin->sin6_addr, buf, len)) { + return errno; + } + *port = ntohs(sin->sin6_port); + } else { + struct sockaddr_in* sin = (struct sockaddr_in*)(&sa); + if (!inet_ntop(AF_INET, &sin->sin_addr, buf, len)) { + return errno; + } + *port = ntohs(sin->sin_port); + } + return SUCCESS; + } + return errno; +} + +//////////////////////////////////////////////////////////////////////////////// +// Server + +T_ERRCODE socket_accept(p_socket sock, p_socket client, + p_sa addr, socklen_t *addrlen, int timeout) { + int err; + if (*sock < 0) { + return CLOSED; + } + do { + *client = accept(*sock, addr, addrlen); + if (*client > 0) { + return SUCCESS; + } + err = errno; + } while (err != EINTR); + if (err == EAGAIN || err == ECONNABORTED) { + return socket_wait(sock, WAIT_MODE_R, timeout); + } + return err; +} + +T_ERRCODE socket_listen(p_socket sock, int backlog) { + int ret = SUCCESS; + socket_setblocking(sock); + if (listen(*sock, backlog)) { + ret = errno; + } + socket_setnonblocking(sock); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////// +// Client + +T_ERRCODE socket_connect(p_socket sock, p_sa addr, int addr_len, int timeout) { + int err; + if (*sock < 0) { + return CLOSED; + } + + do { + if (connect(*sock, addr, addr_len) == 0) { + return SUCCESS; + } + } while ((err = errno) == EINTR); + if (err != EINPROGRESS && err != EAGAIN) { + return err; + } + return socket_wait(sock, WAIT_MODE_C, timeout); +} + +T_ERRCODE socket_send( + p_socket sock, const char *data, size_t len, int timeout) { + int err, put = 0; + if (*sock < 0) { + return CLOSED; + } + do { + put = send(*sock, data, len, 0); + if (put > 0) { + return SUCCESS; + } + err = errno; + } while (err != EINTR); + + if (err == EAGAIN) { + return socket_wait(sock, WAIT_MODE_W, timeout); + } + return err; +} + +T_ERRCODE socket_recv( + p_socket sock, char *data, size_t len, int timeout, int *received) { + int err, got = 0; + if (*sock < 0) { + return CLOSED; + } + + int flags = fcntl(*sock, F_GETFL, 0); + do { + got = recv(*sock, data, len, 0); + if (got > 0) { + *received = got; + return SUCCESS; + } + err = errno; + + // Connection has been closed by peer + if (got == 0) { + return CLOSED; + } + } while (err != EINTR); + + if (err == EAGAIN) { + return socket_wait(sock, WAIT_MODE_R, timeout); + } + return err; +} + +//////////////////////////////////////////////////////////////////////////////// +// Util + +void socket_setnonblocking(p_socket sock) { + int flags = fcntl(*sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(*sock, F_SETFL, flags); +} + +void socket_setblocking(p_socket sock) { + int flags = fcntl(*sock, F_GETFL, 0); + flags &= (~(O_NONBLOCK)); + fcntl(*sock, F_SETFL, flags); +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP + +#define ERRORSTR_RETURN(err) \ + if (err == SUCCESS) { \ + return NULL; \ + } else if (err == TIMEOUT) { \ + return TIMEOUT_MSG; \ + } else if (err == CLOSED) { \ + return CLOSED_MSG; \ + } \ + return strerror(err) + +const char * tcp_create(p_socket sock) { + int err = socket_create(sock, AF_INET, SOCK_STREAM, 0); + ERRORSTR_RETURN(err); +} + +const char * tcp_destroy(p_socket sock) { + int err = socket_destroy(sock); + ERRORSTR_RETURN(err); +} + +const char * tcp_bind(p_socket sock, const char *host, unsigned short port) { + int err; + struct hostent *h; + struct sockaddr_in local; + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_port = htons(port); + if (strcmp(host, "*") && !inet_aton(host, &local.sin_addr)) { + h = gethostbyname(host); + if (!h) { + return hstrerror(h_errno); + } + memcpy(&local.sin_addr, + (struct in_addr *)h->h_addr_list[0], + sizeof(struct in_addr)); + } + err = socket_bind(sock, (p_sa) &local, sizeof(local)); + ERRORSTR_RETURN(err); +} + +const char * tcp_listen(p_socket sock, int backlog) { + int err = socket_listen(sock, backlog); + ERRORSTR_RETURN(err); +} + +const char * tcp_accept(p_socket sock, p_socket client, int timeout) { + int err = socket_accept(sock, client, NULL, NULL, timeout); + ERRORSTR_RETURN(err); +} + +const char * tcp_connect(p_socket sock, + const char *host, + unsigned short port, + int timeout) { + int err; + struct hostent *h; + struct sockaddr_in remote; + memset(&remote, 0, sizeof(remote)); + remote.sin_family = AF_INET; + remote.sin_port = htons(port); + if (strcmp(host, "*") && !inet_aton(host, &remote.sin_addr)) { + h = gethostbyname(host); + if (!h) { + return hstrerror(h_errno); + } + memcpy(&remote.sin_addr, + (struct in_addr *)h->h_addr_list[0], + sizeof(struct in_addr)); + } + err = socket_connect(sock, (p_sa) &remote, sizeof(remote), timeout); + ERRORSTR_RETURN(err); +} + +#define WRITE_STEP 8192 +const char * tcp_send( + p_socket sock, const char * data, size_t w_len, int timeout) { + int err; + size_t put = 0, step; + if (!w_len) { + return NULL; + } + + do { + step = (WRITE_STEP < w_len - put ? WRITE_STEP : w_len - put); + err = socket_send(sock, data + put, step, timeout); + put += step; + } while (err == SUCCESS && put < w_len); + ERRORSTR_RETURN(err); +} + +const char * tcp_raw_receive( + p_socket sock, char * data, size_t r_len, int timeout, int *received) { + int err = socket_recv(sock, data, r_len, timeout, received); + ERRORSTR_RETURN(err); +} diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/Makefile.am b/vendor/src/github.com/apache/thrift/lib/nodejs/Makefile.am new file mode 100644 index 00000000..6d785bec --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/Makefile.am @@ -0,0 +1,43 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +stubs: $(top_srcdir)/test/ThriftTest.thrift + $(THRIFT) --gen js:node -o test/ $(top_srcdir)/test/ThriftTest.thrift + +deps: $(top_srcdir)/package.json + $(NPM) install --no-bin-links $(top_srcdir)/ + +all-local: deps + +precross: deps stubs + +check: deps + cd $(top_srcdir) && $(NPM) test && cd lib/nodejs + +clean-local: + $(RM) -r test/gen-nodejs + $(RM) -r $(top_srcdir)/node_modules + +EXTRA_DIST = \ + examples \ + lib \ + test \ + coding_standards.md \ + README.md diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/README.md b/vendor/src/github.com/apache/thrift/lib/nodejs/README.md new file mode 100644 index 00000000..88499f75 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/README.md @@ -0,0 +1,65 @@ +Thrift Node.js Library +========================= + +License +------- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + + +## Install + + npm install thrift + +## Thrift Compiler + +You can compile IDL sources for Node.js with the following command: + + thrift --gen js:node thrift_file + +## Cassandra Client Example: + +Here is a Cassandra example: + + var thrift = require('thrift'), + Cassandra = require('./gen-nodejs/Cassandra') + ttypes = require('./gen-nodejs/cassandra_types'); + + var connection = thrift.createConnection("localhost", 9160), + client = thrift.createClient(Cassandra, connection); + + connection.on('error', function(err) { + console.error(err); + }); + + client.get_slice("Keyspace", "key", new ttypes.ColumnParent({column_family: "ExampleCF"}), new ttypes.SlicePredicate({slice_range: new ttypes.SliceRange({start: '', finish: ''})}), ttypes.ConsistencyLevel.ONE, function(err, data) { + if (err) { + // handle err + } else { + // data == [ttypes.ColumnOrSuperColumn, ...] + } + connection.end(); + }); + + +## Int64 + +Since JavaScript represents all numbers as doubles, int64 values cannot be accurately represented naturally. To solve this, int64 values in responses will be wrapped with Thirft.Int64 objects. The Int64 implementation used is [broofa/node-int64](https://github.com/broofa/node-int64). + +## Client and server examples + +Several example clients and servers are included in the thrift/lib/nodejs/examples folder and the cross language tutorial thrift/tutorial/nodejs folder. diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/nodejs/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/Makefile b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/Makefile new file mode 100644 index 00000000..87157db6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/Makefile @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +all: + ../../../compiler/cpp/thrift --gen js:node user.thrift + +server: all + NODE_PATH=../lib:../lib/thrift:$(NODE_PATH) node server.js + +client: all + NODE_PATH=../lib:../lib/thrift:$(NODE_PATH) node client.js diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/README.md b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/README.md new file mode 100644 index 00000000..7350c10c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/README.md @@ -0,0 +1,40 @@ +# Thrift Node.js Examples + +## License +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +## Running the user example + +Generate the bindings: + + ../../../compiler/cpp/thrift --gen js:node user.thrift + ../../../compiler/cpp/thrift --gen js:node --gen py hello.thrift + +To run the user example, first start up the server in one terminal: + + NODE_PATH=../lib:../lib/thrift node server.js + +Now run the client: + + NODE_PATH=../lib:../lib/thrift node client.js + +For an example using JavaScript in the browser to connect to +a node.js server look at hello.html, hello.js and hello.thrift + +HTTP examples are provided also: httpClient.js and httpServer.js +You can test HTTP cross platform with the httpServer.py Python server diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client.js new file mode 100644 index 00000000..c83b3423 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client.js @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'); + +var UserStorage = require('./gen-nodejs/UserStorage.js'), + ttypes = require('./gen-nodejs/user_types'); + +var connection = thrift.createConnection('localhost', 9090), + client = thrift.createClient(UserStorage, connection); + +var user = new ttypes.UserProfile({uid: 1, + name: "Mark Slee", + blurb: "I'll find something to put here."}); + +connection.on('error', function(err) { + console.error(err); +}); + +client.store(user, function(err, response) { + if (err) { + console.error(err); + } else { + console.log("client stored:", user.uid); + client.retrieve(user.uid, function(err, responseUser) { + if (err) { + console.error(err); + } else { + console.log("client retrieved:", responseUser.uid); + connection.end(); + } + }); + } +}); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client_multitransport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client_multitransport.js new file mode 100644 index 00000000..1e37de32 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/client_multitransport.js @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'), + ttransport = require('thrift/transport'); + +var UserStorage = require('./gen-nodejs/UserStorage'), + ttypes = require('./gen-nodejs/user_types'); + +var f_conn = thrift.createConnection('localhost', 9090), // default: framed + f_client = thrift.createClient(UserStorage, f_conn); +var b_conn = thrift.createConnection('localhost', 9091, {transport: ttransport.TBufferedTransport}), + b_client = thrift.createClient(UserStorage, b_conn); +var user1 = new ttypes.UserProfile({uid: 1, + name: "Mark Slee", + blurb: "I'll find something to put here."}); +var user2 = new ttypes.UserProfile({uid: 2, + name: "Satoshi Tagomori", + blurb: "ok, let's test with buffered transport."}); + +f_conn.on('error', function(err) { + console.error("framed:", err); +}); + +f_client.store(user1, function(err, response) { + if (err) { console.error(err); return; } + + console.log("stored:", user1.uid, " as ", user1.name); + b_client.retrieve(user1.uid, function(err, responseUser) { + if (err) { console.error(err); return; } + console.log("retrieved:", responseUser.uid, " as ", responseUser.name); + }); +}); + +b_client.store(user2, function(err, response) { + if (err) { console.error(err); return; } + + console.log("stored:", user2.uid, " as ", user2.name); + f_client.retrieve(user2.uid, function(err, responseUser) { + if (err) { console.error(err); return; } + console.log("retrieved:", responseUser.uid, " as ", responseUser.name); + }); +}); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.html b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.html new file mode 100644 index 00000000..fe85a7e9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.html @@ -0,0 +1,65 @@ + + + + + Apache Thrift JavaScript Browser Client Demo + + + + + +

Apache Thrift JavaScript Browser Client Demo

+

This html file demonstrates Apache Thrift JavaScrpt RPC between a browser client to a node.js server. Clicking the buttons below will call the RPC functions hosted by the Apache Thrift server at localhost:8585. The file hello.js contains the JavaScript node.js server required. Here are the steps to get the example running:

+
    +
  1. Install Node.js
    nodejs.org
  2. +
  3. Install Apache Thrift for node (note that the node package manager will create the node_modules folder in the current directory so make sure to run npm from the same directory as hello.js so that the server can find the Thrift libraries. This example requires Apache Thrift 0.9.2+)
    $ npm install thrift
  4. +
  5. Compile the hello.idl for JavaScript and Node.js (you'll need to have the Apache Thrift compiler installed for this step. This also needs to be executed in the same directory as hello.js because hello.js and hello.html look for the gen-nodejs and gen-js directories here.)
    $ thrift -gen js -gen js:node hello.thrift
  6. +
  7. Run the node server in the directory with the hello.html file
    $ node hello.js
  8. +
  9. Copy the Apache Thrift JavaScript library, thrift.js, into the directory with this html file.
    $ cp ...../thrift.js . (you should be able to use Bower to install the browser based Apache Thrift library in the near future.)
    +
  10. Reload this page in a browser through the node server using using the URL:
    http://localhost:8585/hello.html
    then click a button below to make an RPC call
  11. +
+ + + +

Server Response:

+

Server Dbl:

+ + + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.js new file mode 100644 index 00000000..8b7c4e4e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.js @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'); +var HelloSvc = require('./gen-nodejs/HelloSvc.js'); +var TimesTwoSvc = require('./gen-nodejs/TimesTwo.js'); + +var helloHandler = { + hello_func: function(result) { + this.call_counter = this.call_counter || 0; + console.log("Client call: " + (++this.call_counter)); + result(null, "Hello Apache Thrift for JavaScript " + this.call_counter); + } +} + +var timesTwoHandler = { + dbl: function(val, result) { + console.log("Client call: " + val); + result(null, val * 2); + } +} + +var helloService = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: HelloSvc, + handler: helloHandler +}; + +var dblService = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + processor: TimesTwoSvc, + handler: timesTwoHandler +}; + +var ServerOptions = { + files: ".", + services: { + "/hello": helloService, + "/dbl": dblService, + } +} + +var server = thrift.createWebServer(ServerOptions); +var port = 8585; +server.listen(port); +console.log("Http/Thrift Server running on port: " + port); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.thrift b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.thrift new file mode 100644 index 00000000..deaf5a5f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/hello.thrift @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +service HelloSvc { + string hello_func(), +} + +service TimesTwo { + i64 dbl(1: i64 val), +} + + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpClient.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpClient.js new file mode 100644 index 00000000..19cc0c36 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpClient.js @@ -0,0 +1,23 @@ +var thrift = require('thrift'); +var helloSvc = require('./gen-nodejs/HelloSvc.js'); + +var options = { + transport: thrift.TBufferedTransport, + protocol: thrift.TJSONProtocol, + path: "/hello", + headers: {"Connection": "close"}, + https: false +}; + +var connection = thrift.createHttpConnection("localhost", 9090, options); +var client = thrift.createHttpClient(helloSvc, connection); + +connection.on("error", function(err) { + console.log("Error: " + err); +}); + +client.hello_func(function(error, result) { + console.log("Msg from server: " + result); +}); + + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.js new file mode 100644 index 00000000..acae1369 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.js @@ -0,0 +1,31 @@ +var thrift = require('thrift'); +var helloSvc = require('./gen-nodejs/HelloSvc'); + +//ServiceHandler: Implement the hello service +var helloHandler = { + hello_func: function (result) { + console.log("Received Hello call"); + result(null, "Hello from Node.js"); + } +}; + +//ServiceOptions: The I/O stack for the service +var helloSvcOpt = { + handler: helloHandler, + processor: helloSvc, + protocol: thrift.TJSONProtocol, + transport: thrift.TBufferedTransport +}; + +//ServerOptions: Define server features +var serverOpt = { + services: { + "/hello": helloSvcOpt + } +} + +//Create and start the web server +var port = 9090; +thrift.createWebServer(serverOpt).listen(port); +console.log("Http/Thrift Server running on port: " + port); + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.py b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.py new file mode 100644 index 00000000..b8ba5861 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/httpServer.py @@ -0,0 +1,19 @@ +import sys +sys.path.append('gen-py') + +from hello import HelloSvc +from thrift.protocol import TJSONProtocol +from thrift.server import THttpServer + +class HelloSvcHandler: + def hello_func(self): + print "Hello Called" + return "hello from Python" + +processor = HelloSvc.Processor(HelloSvcHandler()) +protoFactory = TJSONProtocol.TJSONProtocolFactory() +port = 9090 +server = THttpServer.THttpServer(processor, ("localhost", port), protoFactory) +print "Python server running on port " + str(port) +server.serve() + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/parse.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/parse.js new file mode 100644 index 00000000..168a1aee --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/parse.js @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/** + + This is a standalone deserialize/parse example if you just want to deserialize + thrift decoupled from cassandra server + + 1. acquire thrift template specification files from who ever built it (eg: EXAMPLE.thrift) + + 2. Install thrift on local machine + + 3. generate thrift clients for nodejs using template specification files (#1) + thrift --gen js:node schema/EXAMPLE.thrift + + This creates creates gen-node.js directory containing a new file, GENERATED.js + + 4. Inside GENERATED.js is a class you will want to instanciate. Find this class name and plug + it into the example code below (ie, "YOUR_CLASS_NAME") + */ + +function parseThrift(thriftEncodedData, callback) { + var thrift = require('thrift'); + var transport = new thrift.TFramedTransport(thriftEncodedData); + var protocol = new thrift.TBinaryProtocol(transport); + + var clientClass = require('../gen-nodejs/GENERATED').YOUR_CLASS_NAME; + var client = new clientClass(); + client.read(protocol); + callback(null, client); +} diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server.js new file mode 100644 index 00000000..1c482fe7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server.js @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'); + +var UserStorage = require('./gen-nodejs/UserStorage.js'), + ttypes = require('./gen-nodejs/user_types'); + +var users = {}; + +var server = thrift.createServer(UserStorage, { + store: function(user, result) { + console.log("server stored:", user.uid); + users[user.uid] = user; + result(null); + }, + + retrieve: function(uid, result) { + console.log("server retrieved:", uid); + result(null, users[uid]); + }, +}); + +server.listen(9090); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_http.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_http.js new file mode 100644 index 00000000..ef2dc83a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_http.js @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var connect = require('connect'); +var thrift = require('thrift'); + +var UserStorage = require('./gen-nodejs/UserStorage'), + ttypes = require('./gen-nodejs/user_types'); + +var users = {}; + +var store = function(user, result) { + console.log("stored:", user.uid); + users[user.uid] = user; + result(null); +}; +var retrieve = function(uid, result) { + console.log("retrieved:", uid); + result(null, users[uid]); +}; + +var server_http = thrift.createHttpServer(UserStorage, { + store: store, + retrieve: retrieve +}); +server_http.listen(9090); + +var server_connect = connect(thrift.httpMiddleware(UserStorage, { + store: store, + retrieve: retrieve +})); +server_http.listen(9091); + +var server_connect_json = connect(thrift.httpMiddleware(UserStorage, { + store: store, + retrieve: retrieve +}, {protocol: thrift.TJSONProtocol})); +server_connect_json.listen(9092); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_multitransport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_multitransport.js new file mode 100644 index 00000000..a348e684 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/server_multitransport.js @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'), + ttransport = require('thrift/transport'); + +var UserStorage = require('./gen-nodejs/UserStorage'), + ttypes = require('./gen-nodejs/user_types'); + +var users = {}; + +var store = function(user, result) { + console.log("stored:", user.uid); + users[user.uid] = user; + result(null); +}; +var retrieve = function(uid, result) { + console.log("retrieved:", uid); + result(null, users[uid]); +}; + +var server_framed = thrift.createServer(UserStorage, { + store: store, + retrieve: retrieve +}); +server_framed.listen(9090); +var server_buffered = thrift.createServer(UserStorage, { + store: store, + retrieve: retrieve +}, {transport: ttransport.TBufferedTransport}); +server_buffered.listen(9091); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/examples/user.thrift b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/user.thrift new file mode 100644 index 00000000..d087fd44 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/examples/user.thrift @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +struct UserProfile { + 1: i32 uid, + 2: string name, + 3: string blurb +} + +service UserStorage { + void store(1: UserProfile user), + UserProfile retrieve(1: i32 uid) +} diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary.js new file mode 100644 index 00000000..9813ffdb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary.js @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var POW_8 = Math.pow(2, 8); +var POW_16 = Math.pow(2, 16); +var POW_24 = Math.pow(2, 24); +var POW_32 = Math.pow(2, 32); +var POW_40 = Math.pow(2, 40); +var POW_48 = Math.pow(2, 48); +var POW_52 = Math.pow(2, 52); +var POW_1022 = Math.pow(2, 1022); + +exports.readByte = function(b){ + return b > 127 ? b-256 : b; +}; + +exports.readI16 = function(buff, off) { + off = off || 0; + var v = buff[off + 1]; + v += buff[off] << 8; + if (buff[off] & 128) { + v -= POW_16; + } + return v; +}; + +exports.readI32 = function(buff, off) { + off = off || 0; + var v = buff[off + 3]; + v += buff[off + 2] << 8; + v += buff[off + 1] << 16; + v += buff[off] * POW_24; + if (buff[off] & 0x80) { + v -= POW_32; + } + return v; +}; + +exports.writeI16 = function(buff, v) { + buff[1] = v & 0xff; + v >>= 8; + buff[0] = v & 0xff; + return buff; +}; + +exports.writeI32 = function(buff, v) { + buff[3] = v & 0xff; + v >>= 8; + buff[2] = v & 0xff; + v >>= 8; + buff[1] = v & 0xff; + v >>= 8; + buff[0] = v & 0xff; + return buff; +}; + +exports.readDouble = function(buff, off) { + off = off || 0; + var signed = buff[off] & 0x80; + var e = (buff[off+1] & 0xF0) >> 4; + e += (buff[off] & 0x7F) << 4; + + var m = buff[off+7]; + m += buff[off+6] << 8; + m += buff[off+5] << 16; + m += buff[off+4] * POW_24; + m += buff[off+3] * POW_32; + m += buff[off+2] * POW_40; + m += (buff[off+1] & 0x0F) * POW_48; + + switch (e) { + case 0: + e = -1022; + break; + case 2047: + return m ? NaN : (signed ? -Infinity : Infinity); + default: + m += POW_52; + e -= 1023; + } + + if (signed) { + m *= -1; + } + + return m * Math.pow(2, e - 52); +}; + +/* + * Based on code from the jspack module: + * http://code.google.com/p/jspack/ + */ +exports.writeDouble = function(buff, v) { + var m, e, c; + + buff[0] = (v < 0 ? 0x80 : 0x00); + + v = Math.abs(v); + if (v !== v) { + // NaN, use QNaN IEEE format + m = 2251799813685248; + e = 2047; + } else if (v === Infinity) { + m = 0; + e = 2047; + } else { + e = Math.floor(Math.log(v) / Math.LN2); + c = Math.pow(2, -e); + if (v * c < 1) { + e--; + c *= 2; + } + + if (e + 1023 >= 2047) + { + // Overflow + m = 0; + e = 2047; + } + else if (e + 1023 >= 1) + { + // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow + m = (v*c-1) * POW_52; + e += 1023; + } + else + { + // Denormalized - also catches the '0' case, somewhat by chance + m = (v * POW_1022) * POW_52; + e = 0; + } + } + + buff[1] = (e << 4) & 0xf0; + buff[0] |= (e >> 4) & 0x7f; + + buff[7] = m & 0xff; + m = Math.floor(m / POW_8); + buff[6] = m & 0xff; + m = Math.floor(m / POW_8); + buff[5] = m & 0xff; + m = Math.floor(m / POW_8); + buff[4] = m & 0xff; + m >>= 8; + buff[3] = m & 0xff; + m >>= 8; + buff[2] = m & 0xff; + m >>= 8; + buff[1] |= m & 0x0f; + + return buff; +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary_protocol.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary_protocol.js new file mode 100644 index 00000000..6d3918eb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/binary_protocol.js @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var log = require('./log'); +var binary = require('./binary'); +var Int64 = require('node-int64'); +var Thrift = require('./thrift'); +var Type = Thrift.Type; + +module.exports = TBinaryProtocol; + +// JavaScript supports only numeric doubles, therefore even hex values are always signed. +// The largest integer value which can be represented in JavaScript is +/-2^53. +// Bitwise operations convert numbers to 32 bit integers but perform sign extension +// upon assigning values back to variables. +var VERSION_MASK = -65536, // 0xffff0000 + VERSION_1 = -2147418112, // 0x80010000 + TYPE_MASK = 0x000000ff; + +function TBinaryProtocol(trans, strictRead, strictWrite) { + this.trans = trans; + this.strictRead = (strictRead !== undefined ? strictRead : false); + this.strictWrite = (strictWrite !== undefined ? strictWrite : true); +}; + +TBinaryProtocol.prototype.flush = function() { + return this.trans.flush(); +}; + +TBinaryProtocol.prototype.writeMessageBegin = function(name, type, seqid) { + if (this.strictWrite) { + this.writeI32(VERSION_1 | type); + this.writeString(name); + this.writeI32(seqid); + } else { + this.writeString(name); + this.writeByte(type); + this.writeI32(seqid); + } + // Record client seqid to find callback again + if (this._seqid) { + // TODO better logging log warning + log.warning('SeqId already set', { 'name': name }); + } else { + this._seqid = seqid; + this.trans.setCurrSeqId(seqid); + } +}; + +TBinaryProtocol.prototype.writeMessageEnd = function() { + if (this._seqid) { + this._seqid = null; + } else { + log.warning('No seqid to unset'); + } +}; + +TBinaryProtocol.prototype.writeStructBegin = function(name) { +}; + +TBinaryProtocol.prototype.writeStructEnd = function() { +}; + +TBinaryProtocol.prototype.writeFieldBegin = function(name, type, id) { + this.writeByte(type); + this.writeI16(id); +}; + +TBinaryProtocol.prototype.writeFieldEnd = function() { +}; + +TBinaryProtocol.prototype.writeFieldStop = function() { + this.writeByte(Type.STOP); +}; + +TBinaryProtocol.prototype.writeMapBegin = function(ktype, vtype, size) { + this.writeByte(ktype); + this.writeByte(vtype); + this.writeI32(size); +}; + +TBinaryProtocol.prototype.writeMapEnd = function() { +}; + +TBinaryProtocol.prototype.writeListBegin = function(etype, size) { + this.writeByte(etype); + this.writeI32(size); +}; + +TBinaryProtocol.prototype.writeListEnd = function() { +}; + +TBinaryProtocol.prototype.writeSetBegin = function(etype, size) { + this.writeByte(etype); + this.writeI32(size); +}; + +TBinaryProtocol.prototype.writeSetEnd = function() { +}; + +TBinaryProtocol.prototype.writeBool = function(bool) { + if (bool) { + this.writeByte(1); + } else { + this.writeByte(0); + } +}; + +TBinaryProtocol.prototype.writeByte = function(b) { + this.trans.write(new Buffer([b])); +}; + +TBinaryProtocol.prototype.writeI16 = function(i16) { + this.trans.write(binary.writeI16(new Buffer(2), i16)); +}; + +TBinaryProtocol.prototype.writeI32 = function(i32) { + this.trans.write(binary.writeI32(new Buffer(4), i32)); +}; + +TBinaryProtocol.prototype.writeI64 = function(i64) { + if (i64.buffer) { + this.trans.write(i64.buffer); + } else { + this.trans.write(new Int64(i64).buffer); + } +}; + +TBinaryProtocol.prototype.writeDouble = function(dub) { + this.trans.write(binary.writeDouble(new Buffer(8), dub)); +}; + +TBinaryProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) { + if (typeof(arg) === 'string') { + this.writeI32(Buffer.byteLength(arg, encoding)); + this.trans.write(new Buffer(arg, encoding)); + } else if ((arg instanceof Buffer) || + (Object.prototype.toString.call(arg) == '[object Uint8Array]')) { + // Buffers in Node.js under Browserify may extend UInt8Array instead of + // defining a new object. We detect them here so we can write them + // correctly + this.writeI32(arg.length); + this.trans.write(arg); + } else { + throw new Error(name + ' called without a string/Buffer argument: ' + arg); + } +}; + +TBinaryProtocol.prototype.writeString = function(arg) { + this.writeStringOrBinary('writeString', 'utf8', arg); +}; + +TBinaryProtocol.prototype.writeBinary = function(arg) { + this.writeStringOrBinary('writeBinary', 'binary', arg); +}; + +TBinaryProtocol.prototype.readMessageBegin = function() { + var sz = this.readI32(); + var type, name, seqid; + + if (sz < 0) { + var version = sz & VERSION_MASK; + if (version != VERSION_1) { + console.log("BAD: " + version); + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad version in readMessageBegin: " + sz); + } + type = sz & TYPE_MASK; + name = this.readString(); + seqid = this.readI32(); + } else { + if (this.strictRead) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "No protocol version header"); + } + name = this.trans.read(sz); + type = this.readByte(); + seqid = this.readI32(); + } + return {fname: name, mtype: type, rseqid: seqid}; +}; + +TBinaryProtocol.prototype.readMessageEnd = function() { +}; + +TBinaryProtocol.prototype.readStructBegin = function() { + return {fname: ''}; +}; + +TBinaryProtocol.prototype.readStructEnd = function() { +}; + +TBinaryProtocol.prototype.readFieldBegin = function() { + var type = this.readByte(); + if (type == Type.STOP) { + return {fname: null, ftype: type, fid: 0}; + } + var id = this.readI16(); + return {fname: null, ftype: type, fid: id}; +}; + +TBinaryProtocol.prototype.readFieldEnd = function() { +}; + +TBinaryProtocol.prototype.readMapBegin = function() { + var ktype = this.readByte(); + var vtype = this.readByte(); + var size = this.readI32(); + return {ktype: ktype, vtype: vtype, size: size}; +}; + +TBinaryProtocol.prototype.readMapEnd = function() { +}; + +TBinaryProtocol.prototype.readListBegin = function() { + var etype = this.readByte(); + var size = this.readI32(); + return {etype: etype, size: size}; +}; + +TBinaryProtocol.prototype.readListEnd = function() { +}; + +TBinaryProtocol.prototype.readSetBegin = function() { + var etype = this.readByte(); + var size = this.readI32(); + return {etype: etype, size: size}; +}; + +TBinaryProtocol.prototype.readSetEnd = function() { +}; + +TBinaryProtocol.prototype.readBool = function() { + var b = this.readByte(); + if (b === 0) { + return false; + } + return true; +}; + +TBinaryProtocol.prototype.readByte = function() { + return this.trans.readByte(); +}; + +TBinaryProtocol.prototype.readI16 = function() { + return this.trans.readI16(); +}; + +TBinaryProtocol.prototype.readI32 = function() { + return this.trans.readI32(); +}; + +TBinaryProtocol.prototype.readI64 = function() { + var buff = this.trans.read(8); + return new Int64(buff); +}; + +TBinaryProtocol.prototype.readDouble = function() { + return this.trans.readDouble(); +}; + +TBinaryProtocol.prototype.readBinary = function() { + var len = this.readI32(); + if (len === 0) { + return new Buffer(0); + } + + if (len < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size"); + } + return this.trans.read(len); +}; + +TBinaryProtocol.prototype.readString = function() { + var len = this.readI32(); + if (len === 0) { + return ""; + } + + if (len < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size"); + } + return this.trans.readString(len); +}; + +TBinaryProtocol.prototype.getTransport = function() { + return this.trans; +}; + +TBinaryProtocol.prototype.skip = function(type) { + switch (type) { + case Type.STOP: + return; + case Type.BOOL: + this.readBool(); + break; + case Type.BYTE: + this.readByte(); + break; + case Type.I16: + this.readI16(); + break; + case Type.I32: + this.readI32(); + break; + case Type.I64: + this.readI64(); + break; + case Type.DOUBLE: + this.readDouble(); + break; + case Type.STRING: + this.readString(); + break; + case Type.STRUCT: + this.readStructBegin(); + while (true) { + var r = this.readFieldBegin(); + if (r.ftype === Type.STOP) { + break; + } + this.skip(r.ftype); + this.readFieldEnd(); + } + this.readStructEnd(); + break; + case Type.MAP: + var mapBegin = this.readMapBegin(); + for (var i = 0; i < mapBegin.size; ++i) { + this.skip(mapBegin.ktype); + this.skip(mapBegin.vtype); + } + this.readMapEnd(); + break; + case Type.SET: + var setBegin = this.readSetBegin(); + for (var i2 = 0; i2 < setBegin.size; ++i2) { + this.skip(setBegin.etype); + } + this.readSetEnd(); + break; + case Type.LIST: + var listBegin = this.readListBegin(); + for (var i3 = 0; i3 < listBegin.size; ++i3) { + this.skip(listBegin.etype); + } + this.readListEnd(); + break; + default: + throw new Error("Invalid type: " + type); + } +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/browser.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/browser.js new file mode 100644 index 00000000..4593a8fd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/browser.js @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +exports.Thrift = require('./thrift'); + +var xhrConnection = require('./xhr_connection'); +exports.XHRConnection = xhrConnection.XHRConnection; +exports.createXHRConnection = xhrConnection.createXHRConnection; +exports.createXHRClient = xhrConnection.createXHRClient; + +exports.Multiplexer = require('./multiplexed_protocol').Multiplexer; + +exports.TWebSocketTransport = require('./ws_transport'); +exports.TBufferedTransport = require('./buffered_transport'); +exports.TFramedTransport = require('./framed_transport'); + +exports.Protocol = exports.TJSONProtocol = require('./json_protocol'); +exports.TBinaryProtocol = require('./binary_protocol'); +exports.TCompactProtocol = require('./compact_protocol'); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/buffered_transport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/buffered_transport.js new file mode 100644 index 00000000..13636b57 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/buffered_transport.js @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var binary = require('./binary'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +module.exports = TBufferedTransport; + +function TBufferedTransport(buffer, callback) { + this.defaultReadBufferSize = 1024; + this.writeBufferSize = 512; // Soft Limit + this.inBuf = new Buffer(this.defaultReadBufferSize); + this.readCursor = 0; + this.writeCursor = 0; // for input buffer + this.outBuffers = []; + this.outCount = 0; + this.onFlush = callback; +}; + +TBufferedTransport.receiver = function(callback, seqid) { + var reader = new TBufferedTransport(); + + return function(data) { + if (reader.writeCursor + data.length > reader.inBuf.length) { + var buf = new Buffer(reader.writeCursor + data.length); + reader.inBuf.copy(buf, 0, 0, reader.writeCursor); + reader.inBuf = buf; + } + data.copy(reader.inBuf, reader.writeCursor, 0); + reader.writeCursor += data.length; + + callback(reader, seqid); + }; +}; + + +TBufferedTransport.prototype.commitPosition = function(){ + var unreadSize = this.writeCursor - this.readCursor; + var bufSize = (unreadSize * 2 > this.defaultReadBufferSize) ? + unreadSize * 2 : this.defaultReadBufferSize; + var buf = new Buffer(bufSize); + if (unreadSize > 0) { + this.inBuf.copy(buf, 0, this.readCursor, this.writeCursor); + } + this.readCursor = 0; + this.writeCursor = unreadSize; + this.inBuf = buf; +}; + +TBufferedTransport.prototype.rollbackPosition = function(){ + this.readCursor = 0; +} + + // TODO: Implement open/close support +TBufferedTransport.prototype.isOpen = function() { + return true; +}; + +TBufferedTransport.prototype.open = function() { +}; + +TBufferedTransport.prototype.close = function() { +}; + + // Set the seqid of the message in the client + // So that callbacks can be found +TBufferedTransport.prototype.setCurrSeqId = function(seqid) { + this._seqid = seqid; +}; + +TBufferedTransport.prototype.ensureAvailable = function(len) { + if (this.readCursor + len > this.writeCursor) { + throw new InputBufferUnderrunError(); + } +}; + +TBufferedTransport.prototype.read = function(len) { + this.ensureAvailable(len); + var buf = new Buffer(len); + this.inBuf.copy(buf, 0, this.readCursor, this.readCursor + len); + this.readCursor += len; + return buf; +}; + +TBufferedTransport.prototype.readByte = function() { + this.ensureAvailable(1); + return binary.readByte(this.inBuf[this.readCursor++]); +}; + +TBufferedTransport.prototype.readI16 = function() { + this.ensureAvailable(2); + var i16 = binary.readI16(this.inBuf, this.readCursor); + this.readCursor += 2; + return i16; +}; + +TBufferedTransport.prototype.readI32 = function() { + this.ensureAvailable(4); + var i32 = binary.readI32(this.inBuf, this.readCursor); + this.readCursor += 4; + return i32; +}; + +TBufferedTransport.prototype.readDouble = function() { + this.ensureAvailable(8); + var d = binary.readDouble(this.inBuf, this.readCursor); + this.readCursor += 8; + return d; +}; + +TBufferedTransport.prototype.readString = function(len) { + this.ensureAvailable(len); + var str = this.inBuf.toString('utf8', this.readCursor, this.readCursor + len); + this.readCursor += len; + return str; +}; + +TBufferedTransport.prototype.borrow = function() { + var obj = {buf: this.inBuf, readIndex: this.readCursor, writeIndex: this.writeCursor}; + return obj; +}; + +TBufferedTransport.prototype.consume = function(bytesConsumed) { + this.readCursor += bytesConsumed; +}; + +TBufferedTransport.prototype.write = function(buf) { + if (typeof(buf) === "string") { + buf = new Buffer(buf, 'utf8'); + } + this.outBuffers.push(buf); + this.outCount += buf.length; +}; + +TBufferedTransport.prototype.flush = function() { + // If the seqid of the callback is available pass it to the onFlush + // Then remove the current seqid + var seqid = this._seqid; + this._seqid = null; + + if (this.outCount < 1) { + return; + } + + var msg = new Buffer(this.outCount), + pos = 0; + this.outBuffers.forEach(function(buf) { + buf.copy(msg, pos, 0); + pos += buf.length; + }); + + if (this.onFlush) { + // Passing seqid through this call to get it to the connection + this.onFlush(msg, seqid); + } + + this.outBuffers = []; + this.outCount = 0; +} diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/compact_protocol.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/compact_protocol.js new file mode 100644 index 00000000..b0e91485 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/compact_protocol.js @@ -0,0 +1,918 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var log = require('./log'); +var Int64 = require('node-int64'); +var Thrift = require('./thrift'); +var Type = Thrift.Type; + +module.exports = TCompactProtocol; + +var POW_8 = Math.pow(2, 8); +var POW_24 = Math.pow(2, 24); +var POW_32 = Math.pow(2, 32); +var POW_40 = Math.pow(2, 40); +var POW_48 = Math.pow(2, 48); +var POW_52 = Math.pow(2, 52); +var POW_1022 = Math.pow(2, 1022); + +/** + * Constructor Function for the Compact Protocol. + * @constructor + * @param {object} [trans] - The underlying transport to read/write. + * @classdesc The Apache Thrift Protocol layer performs serialization + * of base types, the compact protocol serializes data in binary + * form with minimal space used for scalar values. + */ +function TCompactProtocol(trans) { + this.trans = trans; + this.lastField_ = []; + this.lastFieldId_ = 0; + this.string_limit_ = 0; + this.string_buf_ = null; + this.string_buf_size_ = 0; + this.container_limit_ = 0; + this.booleanField_ = { + name: null, + hasBoolValue: false + }; + this.boolValue_ = { + hasBoolValue: false, + boolValue: false + }; +}; + + +// +// Compact Protocol Constants +// + +/** + * Compact Protocol ID number. + * @readonly + * @const {number} PROTOCOL_ID + */ +TCompactProtocol.PROTOCOL_ID = -126; //1000 0010 + +/** + * Compact Protocol version number. + * @readonly + * @const {number} VERSION_N + */ +TCompactProtocol.VERSION_N = 1; + +/** + * Compact Protocol version mask for combining protocol version and message type in one byte. + * @readonly + * @const {number} VERSION_MASK + */ +TCompactProtocol.VERSION_MASK = 0x1f; //0001 1111 + +/** + * Compact Protocol message type mask for combining protocol version and message type in one byte. + * @readonly + * @const {number} TYPE_MASK + */ +TCompactProtocol.TYPE_MASK = -32; //1110 0000 + +/** + * Compact Protocol message type bits for ensuring message type bit size. + * @readonly + * @const {number} TYPE_BITS + */ +TCompactProtocol.TYPE_BITS = 7; //0000 0111 + +/** + * Compact Protocol message type shift amount for combining protocol version and message type in one byte. + * @readonly + * @const {number} TYPE_SHIFT_AMOUNT + */ +TCompactProtocol.TYPE_SHIFT_AMOUNT = 5; + +/** + * Compact Protocol type IDs used to keep type data within one nibble. + * @readonly + * @property {number} CT_STOP - End of a set of fields. + * @property {number} CT_BOOLEAN_TRUE - Flag for Boolean field with true value (packed field and value). + * @property {number} CT_BOOLEAN_FALSE - Flag for Boolean field with false value (packed field and value). + * @property {number} CT_BYTE - Signed 8 bit integer. + * @property {number} CT_I16 - Signed 16 bit integer. + * @property {number} CT_I32 - Signed 32 bit integer. + * @property {number} CT_I64 - Signed 64 bit integer (2^53 max in JavaScript). + * @property {number} CT_DOUBLE - 64 bit IEEE 854 floating point. + * @property {number} CT_BINARY - Array of bytes (used for strings also). + * @property {number} CT_LIST - A collection type (unordered). + * @property {number} CT_SET - A collection type (unordered and without repeated values). + * @property {number} CT_MAP - A collection type (map/associative-array/dictionary). + * @property {number} CT_STRUCT - A multifield type. + */ +TCompactProtocol.Types = { + CT_STOP: 0x00, + CT_BOOLEAN_TRUE: 0x01, + CT_BOOLEAN_FALSE: 0x02, + CT_BYTE: 0x03, + CT_I16: 0x04, + CT_I32: 0x05, + CT_I64: 0x06, + CT_DOUBLE: 0x07, + CT_BINARY: 0x08, + CT_LIST: 0x09, + CT_SET: 0x0A, + CT_MAP: 0x0B, + CT_STRUCT: 0x0C +}; + +/** + * Array mapping Compact type IDs to standard Thrift type IDs. + * @readonly + */ +TCompactProtocol.TTypeToCType = [ + TCompactProtocol.Types.CT_STOP, // T_STOP + 0, // unused + TCompactProtocol.Types.CT_BOOLEAN_TRUE, // T_BOOL + TCompactProtocol.Types.CT_BYTE, // T_BYTE + TCompactProtocol.Types.CT_DOUBLE, // T_DOUBLE + 0, // unused + TCompactProtocol.Types.CT_I16, // T_I16 + 0, // unused + TCompactProtocol.Types.CT_I32, // T_I32 + 0, // unused + TCompactProtocol.Types.CT_I64, // T_I64 + TCompactProtocol.Types.CT_BINARY, // T_STRING + TCompactProtocol.Types.CT_STRUCT, // T_STRUCT + TCompactProtocol.Types.CT_MAP, // T_MAP + TCompactProtocol.Types.CT_SET, // T_SET + TCompactProtocol.Types.CT_LIST, // T_LIST +]; + + +// +// Compact Protocol Utilities +// + +/** + * Returns the underlying transport layer. + * @return {object} The underlying transport layer. + */TCompactProtocol.prototype.getTransport = function() { + return this.trans; +}; + +/** + * Lookup a Compact Protocol Type value for a given Thrift Type value. + * N.B. Used only internally. + * @param {number} ttype - Thrift type value + * @returns {number} Compact protocol type value + */ +TCompactProtocol.prototype.getCompactType = function(ttype) { + return TCompactProtocol.TTypeToCType[ttype]; +}; + +/** + * Lookup a Thrift Type value for a given Compact Protocol Type value. + * N.B. Used only internally. + * @param {number} type - Compact Protocol type value + * @returns {number} Thrift Type value + */ +TCompactProtocol.prototype.getTType = function(type) { + switch (type) { + case Type.STOP: + return Type.STOP; + case TCompactProtocol.Types.CT_BOOLEAN_FALSE: + case TCompactProtocol.Types.CT_BOOLEAN_TRUE: + return Type.BOOL; + case TCompactProtocol.Types.CT_BYTE: + return Type.BYTE; + case TCompactProtocol.Types.CT_I16: + return Type.I16; + case TCompactProtocol.Types.CT_I32: + return Type.I32; + case TCompactProtocol.Types.CT_I64: + return Type.I64; + case TCompactProtocol.Types.CT_DOUBLE: + return Type.DOUBLE; + case TCompactProtocol.Types.CT_BINARY: + return Type.STRING; + case TCompactProtocol.Types.CT_LIST: + return Type.LIST; + case TCompactProtocol.Types.CT_SET: + return Type.SET; + case TCompactProtocol.Types.CT_MAP: + return Type.MAP; + case TCompactProtocol.Types.CT_STRUCT: + return Type.STRUCT; + default: + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Unknown type: " + type); + } + return Type.STOP; +}; + + +// +// Compact Protocol write operations +// + +/** + * Send any buffered bytes to the end point. + */ +TCompactProtocol.prototype.flush = function() { + return this.trans.flush(); +}; + +/** + * Writes an RPC message header + * @param {string} name - The method name for the message. + * @param {number} type - The type of message (CALL, REPLY, EXCEPTION, ONEWAY). + * @param {number} seqid - The call sequence number (if any). + */ +TCompactProtocol.prototype.writeMessageBegin = function(name, type, seqid) { + this.writeByte(TCompactProtocol.PROTOCOL_ID); + this.writeByte((TCompactProtocol.VERSION_N & TCompactProtocol.VERSION_MASK) | + ((type << TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_MASK)); + this.writeVarint32(seqid); + this.writeString(name); + + // Record client seqid to find callback again + if (this._seqid) { + // TODO better logging log warning + log.warning('SeqId already set', { 'name': name }); + } else { + this._seqid = seqid; + this.trans.setCurrSeqId(seqid); + } +}; + +TCompactProtocol.prototype.writeMessageEnd = function() { +}; + +TCompactProtocol.prototype.writeStructBegin = function(name) { + this.lastField_.push(this.lastFieldId_); + this.lastFieldId_ = 0; +}; + +TCompactProtocol.prototype.writeStructEnd = function() { + this.lastFieldId_ = this.lastField_.pop(); +}; + +/** + * Writes a struct field header + * @param {string} name - The field name (not written with the compact protocol). + * @param {number} type - The field data type (a normal Thrift field Type). + * @param {number} id - The IDL field Id. + */ +TCompactProtocol.prototype.writeFieldBegin = function(name, type, id) { + if (type != Type.BOOL) { + return this.writeFieldBeginInternal(name, type, id, -1); + } + + this.booleanField_.name = name; + this.booleanField_.fieldType = type; + this.booleanField_.fieldId = id; +}; + +TCompactProtocol.prototype.writeFieldEnd = function() { +}; + +TCompactProtocol.prototype.writeFieldStop = function() { + this.writeByte(TCompactProtocol.Types.CT_STOP); +}; + +/** + * Writes a map collection header + * @param {number} keyType - The Thrift type of the map keys. + * @param {number} valType - The Thrift type of the map values. + * @param {number} size - The number of k/v pairs in the map. + */ +TCompactProtocol.prototype.writeMapBegin = function(keyType, valType, size) { + if (size === 0) { + this.writeByte(0); + } else { + this.writeVarint32(size); + this.writeByte(this.getCompactType(keyType) << 4 | this.getCompactType(valType)); + } +}; + +TCompactProtocol.prototype.writeMapEnd = function() { +}; + +/** + * Writes a list collection header + * @param {number} elemType - The Thrift type of the list elements. + * @param {number} size - The number of elements in the list. + */ +TCompactProtocol.prototype.writeListBegin = function(elemType, size) { + this.writeCollectionBegin(elemType, size); +}; + +TCompactProtocol.prototype.writeListEnd = function() { +}; + +/** + * Writes a set collection header + * @param {number} elemType - The Thrift type of the set elements. + * @param {number} size - The number of elements in the set. + */ +TCompactProtocol.prototype.writeSetBegin = function(elemType, size) { + this.writeCollectionBegin(elemType, size); +}; + +TCompactProtocol.prototype.writeSetEnd = function() { +}; + +TCompactProtocol.prototype.writeBool = function(value) { + if (this.booleanField_.name !== null) { + // we haven't written the field header yet + this.writeFieldBeginInternal(this.booleanField_.name, + this.booleanField_.fieldType, + this.booleanField_.fieldId, + (value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE + : TCompactProtocol.Types.CT_BOOLEAN_FALSE)); + this.booleanField_.name = null; + } else { + // we're not part of a field, so just write the value + this.writeByte((value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE + : TCompactProtocol.Types.CT_BOOLEAN_FALSE)); + } +}; + +TCompactProtocol.prototype.writeByte = function(b) { + this.trans.write(new Buffer([b])); +}; + +TCompactProtocol.prototype.writeI16 = function(i16) { + this.writeVarint32(this.i32ToZigzag(i16)); +}; + +TCompactProtocol.prototype.writeI32 = function(i32) { + this.writeVarint32(this.i32ToZigzag(i32)); +}; + +TCompactProtocol.prototype.writeI64 = function(i64) { + this.writeVarint64(this.i64ToZigzag(i64)); +}; + +// Little-endian, unlike TBinaryProtocol +TCompactProtocol.prototype.writeDouble = function(v) { + var buff = new Buffer(8); + var m, e, c; + + buff[7] = (v < 0 ? 0x80 : 0x00); + + v = Math.abs(v); + if (v !== v) { + // NaN, use QNaN IEEE format + m = 2251799813685248; + e = 2047; + } else if (v === Infinity) { + m = 0; + e = 2047; + } else { + e = Math.floor(Math.log(v) / Math.LN2); + c = Math.pow(2, -e); + if (v * c < 1) { + e--; + c *= 2; + } + + if (e + 1023 >= 2047) + { + // Overflow + m = 0; + e = 2047; + } + else if (e + 1023 >= 1) + { + // Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow + m = (v*c-1) * POW_52; + e += 1023; + } + else + { + // Denormalized - also catches the '0' case, somewhat by chance + m = (v * POW_1022) * POW_52; + e = 0; + } + } + + buff[6] = (e << 4) & 0xf0; + buff[7] |= (e >> 4) & 0x7f; + + buff[0] = m & 0xff; + m = Math.floor(m / POW_8); + buff[1] = m & 0xff; + m = Math.floor(m / POW_8); + buff[2] = m & 0xff; + m = Math.floor(m / POW_8); + buff[3] = m & 0xff; + m >>= 8; + buff[4] = m & 0xff; + m >>= 8; + buff[5] = m & 0xff; + m >>= 8; + buff[6] |= m & 0x0f; + + this.trans.write(buff); +}; + +TCompactProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) { + if (typeof arg === 'string') { + this.writeVarint32(Buffer.byteLength(arg, encoding)) ; + this.trans.write(new Buffer(arg, encoding)); + } else if (arg instanceof Buffer || + Object.prototype.toString.call(arg) == '[object Uint8Array]') { + // Buffers in Node.js under Browserify may extend UInt8Array instead of + // defining a new object. We detect them here so we can write them + // correctly + this.writeVarint32(arg.length); + this.trans.write(arg); + } else { + throw new Error(name + ' called without a string/Buffer argument: ' + arg); + } +}; + +TCompactProtocol.prototype.writeString = function(arg) { + this.writeStringOrBinary('writeString', 'utf8', arg); +}; + +TCompactProtocol.prototype.writeBinary = function(arg) { + this.writeStringOrBinary('writeBinary', 'binary', arg); +}; + + +// +// Compact Protocol internal write methods +// + +TCompactProtocol.prototype.writeFieldBeginInternal = function(name, + fieldType, + fieldId, + typeOverride) { + //If there's a type override, use that. + var typeToWrite = (typeOverride == -1 ? this.getCompactType(fieldType) : typeOverride); + //Check if we can delta encode the field id + if (fieldId > this.lastFieldId_ && fieldId - this.lastFieldId_ <= 15) { + //Include the type delta with the field ID + this.writeByte((fieldId - this.lastFieldId_) << 4 | typeToWrite); + } else { + //Write separate type and ID values + this.writeByte(typeToWrite); + this.writeI16(fieldId); + } + this.lastFieldId_ = fieldId; +}; + +TCompactProtocol.prototype.writeCollectionBegin = function(elemType, size) { + if (size <= 14) { + //Combine size and type in one byte if possible + this.writeByte(size << 4 | this.getCompactType(elemType)); + } else { + this.writeByte(0xf0 | this.getCompactType(elemType)); + this.writeVarint32(size); + } +}; + +/** + * Write an i32 as a varint. Results in 1-5 bytes on the wire. + */ +TCompactProtocol.prototype.writeVarint32 = function(n) { + var buf = new Buffer(5); + var wsize = 0; + while (true) { + if ((n & ~0x7F) === 0) { + buf[wsize++] = n; + break; + } else { + buf[wsize++] = ((n & 0x7F) | 0x80); + n = n >>> 7; + } + } + var wbuf = new Buffer(wsize); + buf.copy(wbuf,0,0,wsize); + this.trans.write(wbuf); +}; + +/** + * Write an i64 as a varint. Results in 1-10 bytes on the wire. + * N.B. node-int64 is always big endian + */ +TCompactProtocol.prototype.writeVarint64 = function(n) { + if (typeof n === "number"){ + n = new Int64(n); + } + if (! (n instanceof Int64)) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + n); + } + + var buf = new Buffer(10); + var wsize = 0; + var hi = n.buffer.readUInt32BE(0, true); + var lo = n.buffer.readUInt32BE(4, true); + var mask = 0; + while (true) { + if (((lo & ~0x7F) === 0) && (hi === 0)) { + buf[wsize++] = lo; + break; + } else { + buf[wsize++] = ((lo & 0x7F) | 0x80); + mask = hi << 25; + lo = lo >>> 7; + hi = hi >>> 7; + lo = lo | mask; + } + } + var wbuf = new Buffer(wsize); + buf.copy(wbuf,0,0,wsize); + this.trans.write(wbuf); +}; + +/** + * Convert l into a zigzag long. This allows negative numbers to be + * represented compactly as a varint. + */ +TCompactProtocol.prototype.i64ToZigzag = function(l) { + if (typeof l === 'string') { + l = new Int64(parseInt(l, 10)); + } else if (typeof l === 'number') { + l = new Int64(l); + } + if (! (l instanceof Int64)) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + l); + } + var hi = l.buffer.readUInt32BE(0, true); + var lo = l.buffer.readUInt32BE(4, true); + var sign = hi >>> 31; + hi = ((hi << 1) | (lo >>> 31)) ^ ((!!sign) ? 0xFFFFFFFF : 0); + lo = (lo << 1) ^ ((!!sign) ? 0xFFFFFFFF : 0); + return new Int64(hi, lo); +}; + +/** + * Convert n into a zigzag int. This allows negative numbers to be + * represented compactly as a varint. + */ +TCompactProtocol.prototype.i32ToZigzag = function(n) { + return (n << 1) ^ ((n & 0x80000000) ? 0xFFFFFFFF : 0); +}; + + +// +// Compact Protocol read operations +// + +TCompactProtocol.prototype.readMessageBegin = function() { + //Read protocol ID + var protocolId = this.trans.readByte(); + if (protocolId != TCompactProtocol.PROTOCOL_ID) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol identifier " + protocolId); + } + + //Read Version and Type + var versionAndType = this.trans.readByte(); + var version = (versionAndType & TCompactProtocol.VERSION_MASK); + if (version != TCompactProtocol.VERSION_N) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol version " + version); + } + var type = ((versionAndType >> TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_BITS); + + //Read SeqId + var seqid = this.readVarint32(); + + //Read name + var name = this.readString(); + + return {fname: name, mtype: type, rseqid: seqid}; +}; + +TCompactProtocol.prototype.readMessageEnd = function() { +}; + +TCompactProtocol.prototype.readStructBegin = function() { + this.lastField_.push(this.lastFieldId_); + this.lastFieldId_ = 0; + return {fname: ''}; +}; + +TCompactProtocol.prototype.readStructEnd = function() { + this.lastFieldId_ = this.lastField_.pop(); +}; + +TCompactProtocol.prototype.readFieldBegin = function() { + var fieldId = 0; + var b = this.trans.readByte(b); + var type = (b & 0x0f); + + if (type == TCompactProtocol.Types.CT_STOP) { + return {fname: null, ftype: Thrift.Type.STOP, fid: 0}; + } + + //Mask off the 4 MSB of the type header to check for field id delta. + var modifier = ((b & 0x000000f0) >>> 4); + if (modifier === 0) { + //If not a delta read the field id. + fieldId = this.readI16(); + } else { + //Recover the field id from the delta + fieldId = (this.lastFieldId_ + modifier); + } + var fieldType = this.getTType(type); + + //Boolean are encoded with the type + if (type == TCompactProtocol.Types.CT_BOOLEAN_TRUE || + type == TCompactProtocol.Types.CT_BOOLEAN_FALSE) { + this.boolValue_.hasBoolValue = true; + this.boolValue_.boolValue = + (type == TCompactProtocol.Types.CT_BOOLEAN_TRUE ? true : false); + } + + //Save the new field for the next delta computation. + this.lastFieldId_ = fieldId; + return {fname: null, ftype: fieldType, fid: fieldId}; +}; + +TCompactProtocol.prototype.readFieldEnd = function() { +}; + +TCompactProtocol.prototype.readMapBegin = function() { + var msize = this.readVarint32(); + if (msize < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative map size"); + } + + var kvType = 0; + if (msize !== 0) { + kvType = this.trans.readByte(); + } + + var keyType = this.getTType((kvType & 0xf0) >>> 4); + var valType = this.getTType(kvType & 0xf); + return {ktype: keyType, vtype: valType, size: msize}; +}; + +TCompactProtocol.prototype.readMapEnd = function() { +}; + +TCompactProtocol.prototype.readListBegin = function() { + var size_and_type = this.trans.readByte(); + + var lsize = (size_and_type >>> 4) & 0x0000000f; + if (lsize == 15) { + lsize = this.readVarint32(); + } + + if (lsize < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative list size"); + } + + var elemType = this.getTType(size_and_type & 0x0000000f); + + return {etype: elemType, size: lsize}; +}; + +TCompactProtocol.prototype.readListEnd = function() { +}; + +TCompactProtocol.prototype.readSetBegin = function() { + return this.readListBegin(); +}; + +TCompactProtocol.prototype.readSetEnd = function() { +}; + +TCompactProtocol.prototype.readBool = function() { + var value = false; + var rsize = 0; + if (this.boolValue_.hasBoolValue === true) { + value = this.boolValue_.boolValue; + this.boolValue_.hasBoolValue = false; + } else { + var res = this.trans.readByte(); + rsize = res.rsize; + value = (res.value == TCompactProtocol.Types.CT_BOOLEAN_TRUE); + } + return value; +}; + +TCompactProtocol.prototype.readByte = function() { + return this.trans.readByte(); +}; + +TCompactProtocol.prototype.readI16 = function() { + return this.readI32(); +}; + +TCompactProtocol.prototype.readI32 = function() { + return this.zigzagToI32(this.readVarint32()); +}; + +TCompactProtocol.prototype.readI64 = function() { + return this.zigzagToI64(this.readVarint64()); +}; + +// Little-endian, unlike TBinaryProtocol +TCompactProtocol.prototype.readDouble = function() { + var buff = this.trans.read(8); + var off = 0; + + var signed = buff[off + 7] & 0x80; + var e = (buff[off+6] & 0xF0) >> 4; + e += (buff[off+7] & 0x7F) << 4; + + var m = buff[off]; + m += buff[off+1] << 8; + m += buff[off+2] << 16; + m += buff[off+3] * POW_24; + m += buff[off+4] * POW_32; + m += buff[off+5] * POW_40; + m += (buff[off+6] & 0x0F) * POW_48; + + switch (e) { + case 0: + e = -1022; + break; + case 2047: + return m ? NaN : (signed ? -Infinity : Infinity); + default: + m += POW_52; + e -= 1023; + } + + if (signed) { + m *= -1; + } + + return m * Math.pow(2, e - 52); +}; + +TCompactProtocol.prototype.readBinary = function() { + var size = this.readVarint32(); + if (size === 0) { + return new Buffer(0); + } + + if (size < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size"); + } + return this.trans.read(size); +}; + +TCompactProtocol.prototype.readString = function() { + var size = this.readVarint32(); + // Catch empty string case + if (size === 0) { + return ""; + } + + // Catch error cases + if (size < 0) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size"); + } + return this.trans.readString(size); +}; + + +// +// Compact Protocol internal read operations +// + +/** + * Read an i32 from the wire as a varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 5 bytes. + */ +TCompactProtocol.prototype.readVarint32 = function() { + return this.readVarint64().toNumber(); +}; + +/** + * Read an i64 from the wire as a proper varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 10 bytes. + */ +TCompactProtocol.prototype.readVarint64 = function() { + var rsize = 0; + var lo = 0; + var hi = 0; + var shift = 0; + while (true) { + var b = this.trans.readByte(); + rsize ++; + if (shift <= 25) { + lo = lo | ((b & 0x7f) << shift); + } else if (25 < shift && shift < 32) { + lo = lo | ((b & 0x7f) << shift); + hi = hi | ((b & 0x7f) >>> (32-shift)); + } else { + hi = hi | ((b & 0x7f) << (shift-32)); + } + shift += 7; + if (!(b & 0x80)) { + break; + } + if (rsize >= 10) { + throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Variable-length int over 10 bytes."); + } + } + return new Int64(hi, lo); +}; + +/** + * Convert from zigzag int to int. + */ +TCompactProtocol.prototype.zigzagToI32 = function(n) { + return (n >>> 1) ^ (-1 * (n & 1)); +}; + +/** + * Convert from zigzag long to long. + */ +TCompactProtocol.prototype.zigzagToI64 = function(n) { + var hi = n.buffer.readUInt32BE(0, true); + var lo = n.buffer.readUInt32BE(4, true); + + var neg = new Int64(hi & 0, lo & 1); + neg._2scomp(); + var hi_neg = neg.buffer.readUInt32BE(0, true); + var lo_neg = neg.buffer.readUInt32BE(4, true); + + var hi_lo = (hi << 31); + hi = (hi >>> 1) ^ (hi_neg); + lo = ((lo >>> 1) | hi_lo) ^ (lo_neg); + return new Int64(hi, lo); +}; + +TCompactProtocol.prototype.skip = function(type) { + switch (type) { + case Type.STOP: + return; + case Type.BOOL: + this.readBool(); + break; + case Type.BYTE: + this.readByte(); + break; + case Type.I16: + this.readI16(); + break; + case Type.I32: + this.readI32(); + break; + case Type.I64: + this.readI64(); + break; + case Type.DOUBLE: + this.readDouble(); + break; + case Type.STRING: + this.readString(); + break; + case Type.STRUCT: + this.readStructBegin(); + while (true) { + var r = this.readFieldBegin(); + if (r.ftype === Type.STOP) { + break; + } + this.skip(r.ftype); + this.readFieldEnd(); + } + this.readStructEnd(); + break; + case Type.MAP: + var mapBegin = this.readMapBegin(); + for (var i = 0; i < mapBegin.size; ++i) { + this.skip(mapBegin.ktype); + this.skip(mapBegin.vtype); + } + this.readMapEnd(); + break; + case Type.SET: + var setBegin = this.readSetBegin(); + for (var i2 = 0; i2 < setBegin.size; ++i2) { + this.skip(setBegin.etype); + } + this.readSetEnd(); + break; + case Type.LIST: + var listBegin = this.readListBegin(); + for (var i3 = 0; i3 < listBegin.size; ++i3) { + this.skip(listBegin.etype); + } + this.readListEnd(); + break; + default: + throw new Error("Invalid type: " + type); + } +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/connection.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/connection.js new file mode 100644 index 00000000..575d5169 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/connection.js @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var EventEmitter = require("events").EventEmitter; +var net = require('net'); +var tls = require('tls'); +var thrift = require('./thrift'); + +var TBufferedTransport = require('./buffered_transport'); +var TBinaryProtocol = require('./binary_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +var createClient = require('./create_client'); + +var binary = require('./binary'); + +var Connection = exports.Connection = function(stream, options) { + var self = this; + EventEmitter.call(this); + + this.seqId2Service = {}; + this.connection = stream; + this.ssl = (stream.encrypted); + this.options = options || {}; + this.transport = this.options.transport || TBufferedTransport; + this.protocol = this.options.protocol || TBinaryProtocol; + this.offline_queue = []; + this.connected = false; + this.initialize_retry_vars(); + + this._debug = this.options.debug || false; + if (this.options.max_attempts && + !isNaN(this.options.max_attempts) && + this.options.max_attempts > 0) { + this.max_attempts = +this.options.max_attempts; + } + this.retry_max_delay = null; + if (this.options.retry_max_delay !== undefined && + !isNaN(this.options.retry_max_delay) && + this.options.retry_max_delay > 0) { + this.retry_max_delay = this.options.retry_max_delay; + } + this.connect_timeout = false; + if (this.options.connect_timeout && + !isNaN(this.options.connect_timeout) && + this.options.connect_timeout > 0) { + this.connect_timeout = +this.options.connect_timeout; + } + + this.connection.addListener(this.ssl ? "secureConnect" : "connect", function() { + self.connected = true; + + this.setTimeout(self.options.timeout || 0); + this.setNoDelay(); + this.frameLeft = 0; + this.framePos = 0; + this.frame = null; + self.initialize_retry_vars(); + + self.offline_queue.forEach(function(data) { + self.connection.write(data); + }); + + self.emit("connect"); + }); + + this.connection.addListener("error", function(err) { + // Only emit the error if no-one else is listening on the connection + // or if someone is listening on us, because Node turns unhandled + // 'error' events into exceptions. + if (self.connection.listeners('error').length === 1 || + self.listeners('error').length > 0) { + self.emit("error", err); + } + }); + + // Add a close listener + this.connection.addListener("close", function() { + self.connection_gone(); // handle close event. try to reconnect + }); + + this.connection.addListener("timeout", function() { + self.emit("timeout"); + }); + + this.connection.addListener("data", self.transport.receiver(function(transport_with_data) { + var message = new self.protocol(transport_with_data); + try { + while (true) { + var header = message.readMessageBegin(); + var dummy_seqid = header.rseqid * -1; + var client = self.client; + //The Multiplexed Protocol stores a hash of seqid to service names + // in seqId2Service. If the SeqId is found in the hash we need to + // lookup the appropriate client for this call. + // The connection.client object is a single client object when not + // multiplexing, when using multiplexing it is a service name keyed + // hash of client objects. + //NOTE: The 2 way interdependencies between protocols, transports, + // connections and clients in the Node.js implementation are irregular + // and make the implementation difficult to extend and maintain. We + // should bring this stuff inline with typical thrift I/O stack + // operation soon. + // --ra + var service_name = self.seqId2Service[header.rseqid]; + if (service_name) { + client = self.client[service_name]; + delete self.seqId2Service[header.rseqid]; + } + /*jshint -W083 */ + client._reqs[dummy_seqid] = function(err, success){ + transport_with_data.commitPosition(); + + var callback = client._reqs[header.rseqid]; + delete client._reqs[header.rseqid]; + if (callback) { + callback(err, success); + } + }; + /*jshint +W083 */ + + if(client['recv_' + header.fname]) { + client['recv_' + header.fname](message, header.mtype, dummy_seqid); + } else { + delete client._reqs[dummy_seqid]; + self.emit("error", + new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME, + "Received a response to an unknown RPC function")); + } + } + } + catch (e) { + if (e instanceof InputBufferUnderrunError) { + transport_with_data.rollbackPosition(); + } + else { + self.emit('error', e); + } + } + })); +}; +util.inherits(Connection, EventEmitter); + +Connection.prototype.end = function() { + this.connection.end(); +}; + +Connection.prototype.destroy = function() { + this.connection.destroy(); +}; + +Connection.prototype.initialize_retry_vars = function () { + this.retry_timer = null; + this.retry_totaltime = 0; + this.retry_delay = 150; + this.retry_backoff = 1.7; + this.attempts = 0; +}; + +Connection.prototype.write = function(data) { + if (!this.connected) { + this.offline_queue.push(data); + return; + } + this.connection.write(data); +}; + +Connection.prototype.connection_gone = function () { + var self = this; + this.connected = false; + + // If a retry is already in progress, just let that happen + if (this.retry_timer) { + return; + } + // We cannot reconnect a secure socket. + if (!this.max_attempts || this.ssl) { + self.emit("close"); + return; + } + + if (this.retry_max_delay !== null && this.retry_delay >= this.retry_max_delay) { + this.retry_delay = this.retry_max_delay; + } else { + this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff); + } + + if (self._debug) { + console.log("Retry connection in " + this.retry_delay + " ms"); + } + + if (this.max_attempts && this.attempts >= this.max_attempts) { + this.retry_timer = null; + console.error("thrift: Couldn't get thrift connection after " + this.max_attempts + " attempts."); + self.emit("close"); + return; + } + + this.attempts += 1; + this.emit("reconnecting", { + delay: self.retry_delay, + attempt: self.attempts + }); + + this.retry_timer = setTimeout(function () { + if (self._debug) { + console.log("Retrying connection..."); + } + + self.retry_totaltime += self.retry_delay; + + if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) { + self.retry_timer = null; + console.error("thrift: Couldn't get thrift connection after " + self.retry_totaltime + "ms."); + self.emit("close"); + return; + } + + self.connection.connect(self.port, self.host); + self.retry_timer = null; + }, this.retry_delay); +}; + +exports.createConnection = function(host, port, options) { + var stream = net.createConnection(port, host); + var connection = new Connection(stream, options); + connection.host = host; + connection.port = port; + + return connection; +}; + +exports.createSSLConnection = function(host, port, options) { + var stream = tls.connect(port, host, options); + var connection = new Connection(stream, options); + connection.host = host; + connection.port = port; + + return connection; +}; + + +exports.createClient = createClient; + +var child_process = require('child_process'); +var StdIOConnection = exports.StdIOConnection = function(command, options) { + var command_parts = command.split(' '); + command = command_parts[0]; + var args = command_parts.splice(1,command_parts.length -1); + var child = this.child = child_process.spawn(command,args); + + var self = this; + EventEmitter.call(this); + + this._debug = options.debug || false; + this.connection = child.stdin; + this.options = options || {}; + this.transport = this.options.transport || TBufferedTransport; + this.protocol = this.options.protocol || TBinaryProtocol; + this.offline_queue = []; + + if(this._debug === true){ + this.child.stderr.on('data',function(err){ + console.log(err.toString(),'CHILD ERROR'); + }); + + this.child.on('exit',function(code,signal){ + console.log(code+':'+signal,'CHILD EXITED'); + }); + } + + this.frameLeft = 0; + this.framePos = 0; + this.frame = null; + this.connected = true; + + self.offline_queue.forEach(function(data) { + self.connection.write(data); + }); + + + this.connection.addListener("error", function(err) { + self.emit("error", err); + }); + + // Add a close listener + this.connection.addListener("close", function() { + self.emit("close"); + }); + + child.stdout.addListener("data", self.transport.receiver(function(transport_with_data) { + var message = new self.protocol(transport_with_data); + try { + var header = message.readMessageBegin(); + var dummy_seqid = header.rseqid * -1; + var client = self.client; + client._reqs[dummy_seqid] = function(err, success){ + transport_with_data.commitPosition(); + + var callback = client._reqs[header.rseqid]; + delete client._reqs[header.rseqid]; + if (callback) { + callback(err, success); + } + }; + client['recv_' + header.fname](message, header.mtype, dummy_seqid); + } + catch (e) { + if (e instanceof InputBufferUnderrunError) { + transport_with_data.rollbackPosition(); + } + else { + throw e; + } + } + })); +}; + +util.inherits(StdIOConnection, EventEmitter); + +StdIOConnection.prototype.end = function() { + this.connection.end(); +}; + +StdIOConnection.prototype.write = function(data) { + if (!this.connected) { + this.offline_queue.push(data); + return; + } + this.connection.write(data); +}; + +exports.createStdIOConnection = function(command,options){ + return new StdIOConnection(command,options); +}; + +exports.createStdIOClient = createClient; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/create_client.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/create_client.js new file mode 100644 index 00000000..d6b77a83 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/create_client.js @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = createClient; + +/** + * Creates a new client object for the specified Thrift service. + * @param {object} ServiceClient - The module containing the generated service client + * @param {Connection} Connection - The connection to use. + * @returns {object} The client object. + */ +function createClient(ServiceClient, connection) { + // TODO validate required options and throw otherwise + if (ServiceClient.Client) { + ServiceClient = ServiceClient.Client; + } + // TODO detangle these initialization calls + // creating "client" requires + // - new service client instance + // + // New service client instance requires + // - new transport instance + // - protocol class reference + // + // New transport instance requires + // - Buffer to use (or none) + // - Callback to call on flush + + // Wrap the write method + var writeCb = function(buf, seqid) { + connection.write(buf, seqid); + }; + var transport = new connection.transport(undefined, writeCb); + var client = new ServiceClient(transport, connection.protocol); + transport.client = client; + connection.client = client; + return client; +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/framed_transport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/framed_transport.js new file mode 100644 index 00000000..6947925a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/framed_transport.js @@ -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. + */ + +var binary = require('./binary'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +module.exports = TFramedTransport; + +function TFramedTransport(buffer, callback) { + this.inBuf = buffer || new Buffer(0); + this.outBuffers = []; + this.outCount = 0; + this.readPos = 0; + this.onFlush = callback; +}; + +TFramedTransport.receiver = function(callback, seqid) { + var residual = null; + + return function(data) { + // Prepend any residual data from our previous read + if (residual) { + data = Buffer.concat([residual, data]); + residual = null; + } + + // framed transport + while (data.length) { + if (data.length < 4) { + // Not enough bytes to continue, save and resume on next packet + residual = data; + return; + } + var frameSize = binary.readI32(data, 0); + if (data.length < 4 + frameSize) { + // Not enough bytes to continue, save and resume on next packet + residual = data; + return; + } + + var frame = data.slice(4, 4 + frameSize); + residual = data.slice(4 + frameSize); + + callback(new TFramedTransport(frame), seqid); + + data = residual; + residual = null; + } + }; +}; + +TFramedTransport.prototype.commitPosition = function(){}, +TFramedTransport.prototype.rollbackPosition = function(){}, + + // TODO: Implement open/close support +TFramedTransport.prototype.isOpen = function() { + return true; +}; +TFramedTransport.prototype.open = function() {}; +TFramedTransport.prototype.close = function() {}; + + // Set the seqid of the message in the client + // So that callbacks can be found +TFramedTransport.prototype.setCurrSeqId = function(seqid) { + this._seqid = seqid; +}; + +TFramedTransport.prototype.ensureAvailable = function(len) { + if (this.readPos + len > this.inBuf.length) { + throw new InputBufferUnderrunError(); + } +}; + +TFramedTransport.prototype.read = function(len) { // this function will be used for each frames. + this.ensureAvailable(len); + var end = this.readPos + len; + + if (this.inBuf.length < end) { + throw new Error('read(' + len + ') failed - not enough data'); + } + + var buf = this.inBuf.slice(this.readPos, end); + this.readPos = end; + return buf; +}; + +TFramedTransport.prototype.readByte = function() { + this.ensureAvailable(1); + return binary.readByte(this.inBuf[this.readPos++]); +}; + +TFramedTransport.prototype.readI16 = function() { + this.ensureAvailable(2); + var i16 = binary.readI16(this.inBuf, this.readPos); + this.readPos += 2; + return i16; +}; + +TFramedTransport.prototype.readI32 = function() { + this.ensureAvailable(4); + var i32 = binary.readI32(this.inBuf, this.readPos); + this.readPos += 4; + return i32; +}; + +TFramedTransport.prototype.readDouble = function() { + this.ensureAvailable(8); + var d = binary.readDouble(this.inBuf, this.readPos); + this.readPos += 8; + return d; +}; + +TFramedTransport.prototype.readString = function(len) { + this.ensureAvailable(len); + var str = this.inBuf.toString('utf8', this.readPos, this.readPos + len); + this.readPos += len; + return str; +}; + +TFramedTransport.prototype.borrow = function() { + return { + buf: this.inBuf, + readIndex: this.readPos, + writeIndex: this.inBuf.length + }; +}; + +TFramedTransport.prototype.consume = function(bytesConsumed) { + this.readPos += bytesConsumed; +}; + +TFramedTransport.prototype.write = function(buf, encoding) { + if (typeof(buf) === "string") { + buf = new Buffer(buf, encoding || 'utf8'); + } + this.outBuffers.push(buf); + this.outCount += buf.length; +}; + +TFramedTransport.prototype.flush = function() { + // If the seqid of the callback is available pass it to the onFlush + // Then remove the current seqid + var seqid = this._seqid; + this._seqid = null; + + var out = new Buffer(this.outCount), + pos = 0; + this.outBuffers.forEach(function(buf) { + buf.copy(out, pos, 0); + pos += buf.length; + }); + + if (this.onFlush) { + // TODO: optimize this better, allocate one buffer instead of both: + var msg = new Buffer(out.length + 4); + binary.writeI32(msg, out.length); + out.copy(msg, 4, 0, out.length); + if (this.onFlush) { + // Passing seqid through this call to get it to the connection + this.onFlush(msg, seqid); + } + } + + this.outBuffers = []; + this.outCount = 0; +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/http_connection.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/http_connection.js new file mode 100644 index 00000000..18bfadbc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/http_connection.js @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var http = require('http'); +var https = require('https'); +var EventEmitter = require('events').EventEmitter; +var thrift = require('./thrift'); + +var TBufferedTransport = require('./buffered_transport'); +var TBinaryProtocol = require('./binary_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +var createClient = require('./create_client'); + +/** + * @class + * @name ConnectOptions + * @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc). + * @property {string} protocol - The Thrift serialization protocol to use (TBinaryProtocol, etc.). + * @property {string} path - The URL path to POST to (e.g. "/", "/mySvc", "/thrift/quoteSvc", etc.). + * @property {object} headers - A standard Node.js header hash, an object hash containing key/value + * pairs where the key is the header name string and the value is the header value string. + * @property {boolean} https - True causes the connection to use https, otherwise http is used. + * @property {object} nodeOptions - Options passed on to node. + * @example + * //Use a connection that requires ssl/tls, closes the connection after each request, + * // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic + * // to https://thrift.example.com:9090/hello + * var thrift = require('thrift'); + * var options = { + * transport: thrift.TBufferedTransport, + * protocol: thrift.TJSONProtocol, + * path: "/hello", + * headers: {"Connection": "close"}, + * https: true + * }; + * var con = thrift.createHttpConnection("thrift.example.com", 9090, options); + * var client = thrift.createHttpClient(myService, connection); + * client.myServiceFunction(); + */ + +/** + * Initializes a Thrift HttpConnection instance (use createHttpConnection() rather than + * instantiating directly). + * @constructor + * @param {string} host - The host name or IP to connect to. + * @param {number} port - The TCP port to connect to. + * @param {ConnectOptions} options - The configuration options to use. + * @throws {error} Exceptions other than InputBufferUnderrunError are rethrown + * @event {error} The "error" event is fired when a Node.js error event occurs during + * request or response processing, in which case the node error is passed on. An "error" + * event may also be fired when the connection can not map a response back to the + * appropriate client (an internal error), generating a TApplicationException. + * @classdesc HttpConnection objects provide Thrift end point transport + * semantics implemented over the Node.js http.request() method. + * @see {@link createHttpConnection} + */ +var HttpConnection = exports.HttpConnection = function(host, port, options) { + //Initialize the emitter base object + EventEmitter.call(this); + + //Set configuration + var self = this; + this.options = options || {}; + this.host = host; + this.port = port; + this.https = this.options.https || false; + this.transport = this.options.transport || TBufferedTransport; + this.protocol = this.options.protocol || TBinaryProtocol; + + //Prepare Node.js options + this.nodeOptions = { + host: this.host, + port: this.port || 80, + path: this.options.path || '/', + method: 'POST', + headers: this.options.headers || {}, + responseType: this.options.responseType || null + }; + for (var attrname in this.options.nodeOptions) { + this.nodeOptions[attrname] = this.options.nodeOptions[attrname]; + } + /*jshint -W069 */ + if (! this.nodeOptions.headers['Connection']) { + this.nodeOptions.headers['Connection'] = 'keep-alive'; + } + /*jshint +W069 */ + + //The sequence map is used to map seqIDs back to the + // calling client in multiplexed scenarios + this.seqId2Service = {}; + + function decodeCallback(transport_with_data) { + var proto = new self.protocol(transport_with_data); + try { + while (true) { + var header = proto.readMessageBegin(); + var dummy_seqid = header.rseqid * -1; + var client = self.client; + //The Multiplexed Protocol stores a hash of seqid to service names + // in seqId2Service. If the SeqId is found in the hash we need to + // lookup the appropriate client for this call. + // The client var is a single client object when not multiplexing, + // when using multiplexing it is a service name keyed hash of client + // objects. + //NOTE: The 2 way interdependencies between protocols, transports, + // connections and clients in the Node.js implementation are irregular + // and make the implementation difficult to extend and maintain. We + // should bring this stuff inline with typical thrift I/O stack + // operation soon. + // --ra + var service_name = self.seqId2Service[header.rseqid]; + if (service_name) { + client = self.client[service_name]; + delete self.seqId2Service[header.rseqid]; + } + /*jshint -W083 */ + client._reqs[dummy_seqid] = function(err, success){ + transport_with_data.commitPosition(); + var clientCallback = client._reqs[header.rseqid]; + delete client._reqs[header.rseqid]; + if (clientCallback) { + process.nextTick(function() { + clientCallback(err, success); + }); + } + }; + /*jshint +W083 */ + if(client['recv_' + header.fname]) { + client['recv_' + header.fname](proto, header.mtype, dummy_seqid); + } else { + delete client._reqs[dummy_seqid]; + self.emit("error", + new thrift.TApplicationException( + thrift.TApplicationExceptionType.WRONG_METHOD_NAME, + "Received a response to an unknown RPC function")); + } + } + } + catch (e) { + if (e instanceof InputBufferUnderrunError) { + transport_with_data.rollbackPosition(); + } else { + self.emit('error', e); + } + } + } + + + //Response handler + ////////////////////////////////////////////////// + this.responseCallback = function(response) { + var data = []; + var dataLen = 0; + + response.on('error', function (e) { + self.emit("error", e); + }); + + // When running directly under node, chunk will be a buffer, + // however, when running in a Browser (e.g. Browserify), chunk + // will be a string or an ArrayBuffer. + response.on('data', function (chunk) { + if ((typeof chunk == 'string') || + (Object.prototype.toString.call(chunk) == '[object Uint8Array]')) { + // Wrap ArrayBuffer/string in a Buffer so data[i].copy will work + data.push(new Buffer(chunk)); + } else { + data.push(chunk); + } + dataLen += chunk.length; + }); + + response.on('end', function(){ + var buf = new Buffer(dataLen); + for (var i=0, len=data.length, pos=0; i= 0; --i) { + buffer[i] = (~b[o + i] + (incremented ? 0 : 1)) & 0xff; + incremented |= b[o + i]; + } + b = buffer; + } + var high2 = b[o + 1] + (b[o] << 8); + // Lesser 11 digits with exceeding values but is under 53 bits capacity. + var low = b[o + 7] + (b[o + 6] << 8) + (b[o + 5] << 16) + + b[o + 4] * POW2_24 // Bit shift renders 32th bit as sign, so use multiplication + + (b[o + 3] + (b[o + 2] << 8)) * POW2_32 + high2 * 74976710656; // The literal is 2^48 % 10^11 + // 12th digit and greater. + var high = Math.floor(low / POW10_11) + high2 * 2814; // The literal is 2^48 / 10^11 + // Make it exactly 11 with leading zeros. + low = ('00000000000' + String(low % POW10_11)).slice(-11); + return (negative ? '-' : '') + String(high) + low; + } +}; + +Int64Util.fromDecimalString = function(text) { + var negative = text.charAt(0) === '-'; + if (text.length < (negative ? 17 : 16)) { + // The magnitude is smaller than 2^53. + return new Int64(+text); + } else if (text.length > (negative ? 20 : 19)) { + throw new RangeError('Too many digits for Int64: ' + text); + } else { + // Most significant (up to 5) digits + var high5 = +text.slice(negative ? 1 : 0, -15); + var low = +text.slice(-15) + high5 * 2764472320; // The literal is 10^15 % 2^32 + var high = Math.floor(low / POW2_32) + high5 * 232830; // The literal is 10^15 / 2^&32 + low = low % POW2_32; + if (high >= POW2_31 && + !(negative && high == POW2_31 && low == 0) // Allow minimum Int64 + ) { + throw new RangeError('The magnitude is too large for Int64.'); + } + if (negative) { + // 2's complement + high = ~high; + if (low === 0) { + high = (high + 1) & 0xffffffff; + } else { + low = ~low + 1; + } + high = 0x80000000 | high; + } + return new Int64(high, low); + } +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_parse.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_parse.js new file mode 100644 index 00000000..93b0bf2a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_parse.js @@ -0,0 +1,299 @@ +/* + * Imported from Douglas Crockford's reference implementation with minimum modification + * to handle Int64. + * + * https://github.com/douglascrockford/JSON-js/blob/c98948ae1944a28e2e8ebc3717894e580aeaaa05/json_parse.js + * + * Original license header: + * + * json_parse.js + * 2015-05-02 + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ + + +/*jslint for */ + +/*property + at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name, + prototype, push, r, t, text +*/ + +var Int64 = require('node-int64'); +var Int64Util = require('./int64_util'); + +var json_parse = module.exports = (function () { + "use strict"; + +// This is a function that can parse a JSON text, producing a JavaScript +// data structure. It is a simple, recursive descent parser. It does not use +// eval or regular expressions, so it can be used as a model for implementing +// a JSON parser in other languages. + +// We are defining the function inside of another function to avoid creating +// global variables. + + var at, // The index of the current character + ch, // The current character + escapee = { + '"': '"', + '\\': '\\', + '/': '/', + b: '\b', + f: '\f', + n: '\n', + r: '\r', + t: '\t' + }, + text, + + error = function (m) { + +// Call error when something is wrong. + + throw new SyntaxError(m); + }, + + next = function (c) { + +// If a c parameter is provided, verify that it matches the current character. + + if (c && c !== ch) { + error("Expected '" + c + "' instead of '" + ch + "'"); + } + +// Get the next character. When there are no more characters, +// return the empty string. + + ch = text.charAt(at); + at += 1; + return ch; + }, + + number = function () { + +// Parse a number value. + + var number, + string = ''; + + if (ch === '-') { + string = '-'; + next('-'); + } + while (ch >= '0' && ch <= '9') { + string += ch; + next(); + } + if (ch === '.') { + string += '.'; + while (next() && ch >= '0' && ch <= '9') { + string += ch; + } + } + if (ch === 'e' || ch === 'E') { + string += ch; + next(); + if (ch === '-' || ch === '+') { + string += ch; + next(); + } + while (ch >= '0' && ch <= '9') { + string += ch; + next(); + } + } + number = +string; + if (!isFinite(number)) { + error("Bad number"); + } else if (number >= Int64.MAX_INT || number <= Int64.MIN_INT) { + // Return raw string for further process in TJSONProtocol + return string; + } else { + return number; + } + }, + + string = function () { + +// Parse a string value. + + var hex, + i, + string = '', + uffff; + +// When parsing for string values, we must look for " and \ characters. + + if (ch === '"') { + while (next()) { + if (ch === '"') { + next(); + return string; + } + if (ch === '\\') { + next(); + if (ch === 'u') { + uffff = 0; + for (i = 0; i < 4; i += 1) { + hex = parseInt(next(), 16); + if (!isFinite(hex)) { + break; + } + uffff = uffff * 16 + hex; + } + string += String.fromCharCode(uffff); + } else if (typeof escapee[ch] === 'string') { + string += escapee[ch]; + } else { + break; + } + } else { + string += ch; + } + } + } + error("Bad string"); + }, + + white = function () { + +// Skip whitespace. + + while (ch && ch <= ' ') { + next(); + } + }, + + word = function () { + +// true, false, or null. + + switch (ch) { + case 't': + next('t'); + next('r'); + next('u'); + next('e'); + return true; + case 'f': + next('f'); + next('a'); + next('l'); + next('s'); + next('e'); + return false; + case 'n': + next('n'); + next('u'); + next('l'); + next('l'); + return null; + } + error("Unexpected '" + ch + "'"); + }, + + value, // Place holder for the value function. + + array = function () { + +// Parse an array value. + + var array = []; + + if (ch === '[') { + next('['); + white(); + if (ch === ']') { + next(']'); + return array; // empty array + } + while (ch) { + array.push(value()); + white(); + if (ch === ']') { + next(']'); + return array; + } + next(','); + white(); + } + } + error("Bad array"); + }, + + object = function () { + +// Parse an object value. + + var key, + object = {}; + + if (ch === '{') { + next('{'); + white(); + if (ch === '}') { + next('}'); + return object; // empty object + } + while (ch) { + key = string(); + white(); + next(':'); + if (Object.hasOwnProperty.call(object, key)) { + error('Duplicate key "' + key + '"'); + } + object[key] = value(); + white(); + if (ch === '}') { + next('}'); + return object; + } + next(','); + white(); + } + } + error("Bad object"); + }; + + value = function () { + +// Parse a JSON value. It could be an object, an array, a string, a number, +// or a word. + + white(); + switch (ch) { + case '{': + return object(); + case '[': + return array(); + case '"': + return string(); + case '-': + return number(); + default: + return ch >= '0' && ch <= '9' + ? number() + : word(); + } + }; + +// Return the json_parse function. It will have access to all of the above +// functions and variables. + + return function (source) { + var result; + + text = source; + at = 0; + ch = ' '; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + + return result; + }; +}()); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_protocol.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_protocol.js new file mode 100644 index 00000000..f31e3b2d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/json_protocol.js @@ -0,0 +1,743 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var log = require('./log'); +var Int64 = require('node-int64'); +var InputBufferUnderrunError = require('./transport').InputBufferUnderrunError; +var Thrift = require('./thrift'); +var Type = Thrift.Type; +var util = require("util"); + +var Int64Util = require('./int64_util'); +var json_parse = require('./json_parse'); + +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +module.exports = TJSONProtocol; + +/** + * Initializes a Thrift JSON protocol instance. + * @constructor + * @param {Thrift.Transport} trans - The transport to serialize to/from. + * @classdesc Apache Thrift Protocols perform serialization which enables cross + * language RPC. The Protocol type is the JavaScript browser implementation + * of the Apache Thrift TJSONProtocol. + * @example + * var protocol = new Thrift.Protocol(transport); + */ +function TJSONProtocol(trans) { + this.tstack = []; + this.tpos = []; + this.trans = trans; +}; + +/** + * Thrift IDL type Id to string mapping. + * @readonly + * @see {@link Thrift.Type} + */ +TJSONProtocol.Type = {}; +TJSONProtocol.Type[Type.BOOL] = '"tf"'; +TJSONProtocol.Type[Type.BYTE] = '"i8"'; +TJSONProtocol.Type[Type.I16] = '"i16"'; +TJSONProtocol.Type[Type.I32] = '"i32"'; +TJSONProtocol.Type[Type.I64] = '"i64"'; +TJSONProtocol.Type[Type.DOUBLE] = '"dbl"'; +TJSONProtocol.Type[Type.STRUCT] = '"rec"'; +TJSONProtocol.Type[Type.STRING] = '"str"'; +TJSONProtocol.Type[Type.MAP] = '"map"'; +TJSONProtocol.Type[Type.LIST] = '"lst"'; +TJSONProtocol.Type[Type.SET] = '"set"'; + +/** + * Thrift IDL type string to Id mapping. + * @readonly + * @see {@link Thrift.Type} + */ +TJSONProtocol.RType = {}; +TJSONProtocol.RType.tf = Type.BOOL; +TJSONProtocol.RType.i8 = Type.BYTE; +TJSONProtocol.RType.i16 = Type.I16; +TJSONProtocol.RType.i32 = Type.I32; +TJSONProtocol.RType.i64 = Type.I64; +TJSONProtocol.RType.dbl = Type.DOUBLE; +TJSONProtocol.RType.rec = Type.STRUCT; +TJSONProtocol.RType.str = Type.STRING; +TJSONProtocol.RType.map = Type.MAP; +TJSONProtocol.RType.lst = Type.LIST; +TJSONProtocol.RType.set = Type.SET; + +/** + * The TJSONProtocol version number. + * @readonly + * @const {number} Version + * @memberof Thrift.Protocol + */ +TJSONProtocol.Version = 1; + +TJSONProtocol.prototype.flush = function() { + this.writeToTransportIfStackIsFlushable(); + return this.trans.flush(); +}; + +TJSONProtocol.prototype.writeToTransportIfStackIsFlushable = function() { + if (this.tstack.length === 1) { + this.trans.write(this.tstack.pop()); + } +}; + +/** + * Serializes the beginning of a Thrift RPC message. + * @param {string} name - The service method to call. + * @param {Thrift.MessageType} messageType - The type of method call. + * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift). + */ +TJSONProtocol.prototype.writeMessageBegin = function(name, messageType, seqid) { + this.tstack.push([TJSONProtocol.Version, '"' + name + '"', messageType, seqid]); +}; + +/** + * Serializes the end of a Thrift RPC message. + */ +TJSONProtocol.prototype.writeMessageEnd = function() { + var obj = this.tstack.pop(); + + this.wobj = this.tstack.pop(); + this.wobj.push(obj); + + this.wbuf = '[' + this.wobj.join(',') + ']'; + + // we assume there is nothing more to come so we write + this.trans.write(this.wbuf); +}; + +/** + * Serializes the beginning of a struct. + * @param {string} name - The name of the struct. + */ +TJSONProtocol.prototype.writeStructBegin = function(name) { + this.tpos.push(this.tstack.length); + this.tstack.push({}); +}; + +/** + * Serializes the end of a struct. + */ +TJSONProtocol.prototype.writeStructEnd = function() { + var p = this.tpos.pop(); + var struct = this.tstack[p]; + var str = '{'; + var first = true; + for (var key in struct) { + if (first) { + first = false; + } else { + str += ','; + } + + str += key + ':' + struct[key]; + } + + str += '}'; + this.tstack[p] = str; + + this.writeToTransportIfStackIsFlushable(); +}; + +/** + * Serializes the beginning of a struct field. + * @param {string} name - The name of the field. + * @param {Thrift.Protocol.Type} fieldType - The data type of the field. + * @param {number} fieldId - The field's unique identifier. + */ +TJSONProtocol.prototype.writeFieldBegin = function(name, fieldType, fieldId) { + this.tpos.push(this.tstack.length); + this.tstack.push({ 'fieldId': '"' + + fieldId + '"', 'fieldType': TJSONProtocol.Type[fieldType] + }); +}; + +/** + * Serializes the end of a field. + */ +TJSONProtocol.prototype.writeFieldEnd = function() { + var value = this.tstack.pop(); + var fieldInfo = this.tstack.pop(); + + if (':' + value === ":[object Object]") { + this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' + + fieldInfo.fieldType + ':' + JSON.stringify(value) + '}'; + } else { + this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' + + fieldInfo.fieldType + ':' + value + '}'; + } + this.tpos.pop(); + + this.writeToTransportIfStackIsFlushable(); +}; + +/** + * Serializes the end of the set of fields for a struct. + */ +TJSONProtocol.prototype.writeFieldStop = function() { +}; + +/** + * Serializes the beginning of a map collection. + * @param {Thrift.Type} keyType - The data type of the key. + * @param {Thrift.Type} valType - The data type of the value. + * @param {number} [size] - The number of elements in the map (ignored). + */ +TJSONProtocol.prototype.writeMapBegin = function(keyType, valType, size) { + //size is invalid, we'll set it on end. + this.tpos.push(this.tstack.length); + this.tstack.push([TJSONProtocol.Type[keyType], TJSONProtocol.Type[valType], 0]); +}; + +/** + * Serializes the end of a map. + */ +TJSONProtocol.prototype.writeMapEnd = function() { + var p = this.tpos.pop(); + + if (p == this.tstack.length) { + return; + } + + if ((this.tstack.length - p - 1) % 2 !== 0) { + this.tstack.push(''); + } + + var size = (this.tstack.length - p - 1) / 2; + + this.tstack[p][this.tstack[p].length - 1] = size; + + var map = '}'; + var first = true; + while (this.tstack.length > p + 1) { + var v = this.tstack.pop(); + var k = this.tstack.pop(); + if (first) { + first = false; + } else { + map = ',' + map; + } + + if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings + map = k + ':' + v + map; + } + map = '{' + map; + + this.tstack[p].push(map); + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + + this.writeToTransportIfStackIsFlushable(); +}; + +/** + * Serializes the beginning of a list collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ +TJSONProtocol.prototype.writeListBegin = function(elemType, size) { + this.tpos.push(this.tstack.length); + this.tstack.push([TJSONProtocol.Type[elemType], size]); +}; + +/** + * Serializes the end of a list. + */ +TJSONProtocol.prototype.writeListEnd = function() { + var p = this.tpos.pop(); + + while (this.tstack.length > p + 1) { + var tmpVal = this.tstack[p + 1]; + this.tstack.splice(p + 1, 1); + this.tstack[p].push(tmpVal); + } + + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + + this.writeToTransportIfStackIsFlushable(); +}; + +/** + * Serializes the beginning of a set collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ +TJSONProtocol.prototype.writeSetBegin = function(elemType, size) { + this.tpos.push(this.tstack.length); + this.tstack.push([TJSONProtocol.Type[elemType], size]); +}; + +/** + * Serializes the end of a set. + */ +TJSONProtocol.prototype.writeSetEnd = function() { + var p = this.tpos.pop(); + + while (this.tstack.length > p + 1) { + var tmpVal = this.tstack[p + 1]; + this.tstack.splice(p + 1, 1); + this.tstack[p].push(tmpVal); + } + + this.tstack[p] = '[' + this.tstack[p].join(',') + ']'; + + this.writeToTransportIfStackIsFlushable(); +}; + +/** Serializes a boolean */ +TJSONProtocol.prototype.writeBool = function(bool) { + this.tstack.push(bool ? 1 : 0); +}; + +/** Serializes a number */ +TJSONProtocol.prototype.writeByte = function(byte) { + this.tstack.push(byte); +}; + +/** Serializes a number */ +TJSONProtocol.prototype.writeI16 = function(i16) { + this.tstack.push(i16); +}; + +/** Serializes a number */ +TJSONProtocol.prototype.writeI32 = function(i32) { + this.tstack.push(i32); +}; + +/** Serializes a number */ +TJSONProtocol.prototype.writeI64 = function(i64) { + if (i64 instanceof Int64) { + this.tstack.push(Int64Util.toDecimalString(i64)); + } else { + this.tstack.push(i64); + } +}; + +/** Serializes a number */ +TJSONProtocol.prototype.writeDouble = function(dub) { + this.tstack.push(dub); +}; + +/** Serializes a string */ +TJSONProtocol.prototype.writeString = function(arg) { + // We do not encode uri components for wire transfer: + if (arg === null) { + this.tstack.push(null); + } else { + if (typeof arg === 'string') { + var str = arg; + } else if (arg instanceof Buffer) { + var str = arg.toString('utf8'); + } else { + throw new Error('writeString called without a string/Buffer argument: ' + arg); + } + + // concat may be slower than building a byte buffer + var escapedString = ''; + for (var i = 0; i < str.length; i++) { + var ch = str.charAt(i); // a single double quote: " + if (ch === '\"') { + escapedString += '\\\"'; // write out as: \" + } else if (ch === '\\') { // a single backslash: \ + escapedString += '\\\\'; // write out as: \\ + /* Currently escaped forward slashes break TJSONProtocol. + * As it stands, we can simply pass forward slashes into + * our strings across the wire without being escaped. + * I think this is the protocol's bug, not thrift.js + * } else if(ch === '/') { // a single forward slash: / + * escapedString += '\\/'; // write out as \/ + * } + */ + } else if (ch === '\b') { // a single backspace: invisible + escapedString += '\\b'; // write out as: \b" + } else if (ch === '\f') { // a single formfeed: invisible + escapedString += '\\f'; // write out as: \f" + } else if (ch === '\n') { // a single newline: invisible + escapedString += '\\n'; // write out as: \n" + } else if (ch === '\r') { // a single return: invisible + escapedString += '\\r'; // write out as: \r" + } else if (ch === '\t') { // a single tab: invisible + escapedString += '\\t'; // write out as: \t" + } else { + escapedString += ch; // Else it need not be escaped + } + } + this.tstack.push('"' + escapedString + '"'); + } +}; + +/** Serializes a string */ +TJSONProtocol.prototype.writeBinary = function(arg) { + if (typeof arg === 'string') { + var buf = new Buffer(arg, 'binary'); + } else if (arg instanceof Buffer || + Object.prototype.toString.call(arg) == '[object Uint8Array]') { + var buf = arg; + } else { + throw new Error('writeBinary called without a string/Buffer argument: ' + arg); + } + this.tstack.push('"' + buf.toString('base64') + '"'); +}; + +/** + * @class + * @name AnonReadMessageBeginReturn + * @property {string} fname - The name of the service method. + * @property {Thrift.MessageType} mtype - The type of message call. + * @property {number} rseqid - The sequence number of the message (0 in Thrift RPC). + */ +/** + * Deserializes the beginning of a message. + * @returns {AnonReadMessageBeginReturn} + */ +TJSONProtocol.prototype.readMessageBegin = function() { + this.rstack = []; + this.rpos = []; + + //Borrow the inbound transport buffer and ensure data is present/consistent + var transBuf = this.trans.borrow(); + if (transBuf.readIndex >= transBuf.writeIndex) { + throw new InputBufferUnderrunError(); + } + var cursor = transBuf.readIndex; + + if (transBuf.buf[cursor] !== 0x5B) { //[ + throw new Error("Malformed JSON input, no opening bracket"); + } + + //Parse a single message (there may be several in the buffer) + // TODO: Handle characters using multiple code units + cursor++; + var openBracketCount = 1; + var inString = false; + for (; cursor < transBuf.writeIndex; cursor++) { + var chr = transBuf.buf[cursor]; + //we use hexa charcode here because data[i] returns an int and not a char + if (inString) { + if (chr === 0x22) { //" + inString = false; + } else if (chr === 0x5C) { //\ + //escaped character, skip + cursor += 1; + } + } else { + if (chr === 0x5B) { //[ + openBracketCount += 1; + } else if (chr === 0x5D) { //] + openBracketCount -= 1; + if (openBracketCount === 0) { + //end of json message detected + break; + } + } else if (chr === 0x22) { //" + inString = true; + } + } + } + + if (openBracketCount !== 0) { + // Missing closing bracket. Can be buffer underrun. + throw new InputBufferUnderrunError(); + } + + //Reconstitute the JSON object and conume the necessary bytes + this.robj = json_parse(transBuf.buf.slice(transBuf.readIndex, cursor+1).toString()); + this.trans.consume(cursor + 1 - transBuf.readIndex); + + //Verify the protocol version + var version = this.robj.shift(); + if (version != TJSONProtocol.Version) { + throw new Error('Wrong thrift protocol version: ' + version); + } + + //Objectify the thrift message {name/type/sequence-number} for return + // and then save the JSON object in rstack + var r = {}; + r.fname = this.robj.shift(); + r.mtype = this.robj.shift(); + r.rseqid = this.robj.shift(); + this.rstack.push(this.robj.shift()); + return r; +}; + +/** Deserializes the end of a message. */ +TJSONProtocol.prototype.readMessageEnd = function() { +}; + +/** + * Deserializes the beginning of a struct. + * @param {string} [name] - The name of the struct (ignored) + * @returns {object} - An object with an empty string fname property + */ +TJSONProtocol.prototype.readStructBegin = function() { + var r = {}; + r.fname = ''; + + //incase this is an array of structs + if (this.rstack[this.rstack.length - 1] instanceof Array) { + this.rstack.push(this.rstack[this.rstack.length - 1].shift()); + } + + return r; +}; + +/** Deserializes the end of a struct. */ +TJSONProtocol.prototype.readStructEnd = function() { + this.rstack.pop(); +}; + +/** + * @class + * @name AnonReadFieldBeginReturn + * @property {string} fname - The name of the field (always ''). + * @property {Thrift.Type} ftype - The data type of the field. + * @property {number} fid - The unique identifier of the field. + */ +/** + * Deserializes the beginning of a field. + * @returns {AnonReadFieldBeginReturn} + */ +TJSONProtocol.prototype.readFieldBegin = function() { + var r = {}; + + var fid = -1; + var ftype = Type.STOP; + + //get a fieldId + for (var f in (this.rstack[this.rstack.length - 1])) { + if (f === null) { + continue; + } + + fid = parseInt(f, 10); + this.rpos.push(this.rstack.length); + + var field = this.rstack[this.rstack.length - 1][fid]; + + //remove so we don't see it again + delete this.rstack[this.rstack.length - 1][fid]; + + this.rstack.push(field); + + break; + } + + if (fid != -1) { + //should only be 1 of these but this is the only + //way to match a key + for (var i in (this.rstack[this.rstack.length - 1])) { + if (TJSONProtocol.RType[i] === null) { + continue; + } + + ftype = TJSONProtocol.RType[i]; + this.rstack[this.rstack.length - 1] = this.rstack[this.rstack.length - 1][i]; + } + } + + r.fname = ''; + r.ftype = ftype; + r.fid = fid; + + return r; +}; + +/** Deserializes the end of a field. */ +TJSONProtocol.prototype.readFieldEnd = function() { + var pos = this.rpos.pop(); + + //get back to the right place in the stack + while (this.rstack.length > pos) { + this.rstack.pop(); + } +}; + +/** + * @class + * @name AnonReadMapBeginReturn + * @property {Thrift.Type} ktype - The data type of the key. + * @property {Thrift.Type} vtype - The data type of the value. + * @property {number} size - The number of elements in the map. + */ +/** + * Deserializes the beginning of a map. + * @returns {AnonReadMapBeginReturn} + */ +TJSONProtocol.prototype.readMapBegin = function() { + var map = this.rstack.pop(); + var first = map.shift(); + if (first instanceof Array) { + this.rstack.push(map); + map = first; + first = map.shift(); + } + + var r = {}; + r.ktype = TJSONProtocol.RType[first]; + r.vtype = TJSONProtocol.RType[map.shift()]; + r.size = map.shift(); + + + this.rpos.push(this.rstack.length); + this.rstack.push(map.shift()); + + return r; +}; + +/** Deserializes the end of a map. */ +TJSONProtocol.prototype.readMapEnd = function() { + this.readFieldEnd(); +}; + +/** + * @class + * @name AnonReadColBeginReturn + * @property {Thrift.Type} etype - The data type of the element. + * @property {number} size - The number of elements in the collection. + */ +/** + * Deserializes the beginning of a list. + * @returns {AnonReadColBeginReturn} + */ +TJSONProtocol.prototype.readListBegin = function() { + var list = this.rstack[this.rstack.length - 1]; + + var r = {}; + r.etype = TJSONProtocol.RType[list.shift()]; + r.size = list.shift(); + + this.rpos.push(this.rstack.length); + this.rstack.push(list.shift()); + + return r; +}; + +/** Deserializes the end of a list. */ +TJSONProtocol.prototype.readListEnd = function() { + var pos = this.rpos.pop() - 2; + var st = this.rstack; + st.pop(); + if (st instanceof Array && st.length > pos && st[pos].length > 0) { + st.push(st[pos].shift()); + } +}; + +/** + * Deserializes the beginning of a set. + * @returns {AnonReadColBeginReturn} + */ +TJSONProtocol.prototype.readSetBegin = function() { + return this.readListBegin(); +}; + +/** Deserializes the end of a set. */ +TJSONProtocol.prototype.readSetEnd = function() { + return this.readListEnd(); +}; + +TJSONProtocol.prototype.readBool = function() { + return this.readValue() == '1'; +}; + +TJSONProtocol.prototype.readByte = function() { + return this.readI32(); +}; + +TJSONProtocol.prototype.readI16 = function() { + return this.readI32(); +}; + +TJSONProtocol.prototype.readI32 = function(f) { + return +this.readValue(); +} + +/** Returns the next value found in the protocol buffer */ +TJSONProtocol.prototype.readValue = function(f) { + if (f === undefined) { + f = this.rstack[this.rstack.length - 1]; + } + + var r = {}; + + if (f instanceof Array) { + if (f.length === 0) { + r.value = undefined; + } else { + r.value = f.shift(); + } + } else if (!(f instanceof Int64) && f instanceof Object) { + for (var i in f) { + if (i === null) { + continue; + } + this.rstack.push(f[i]); + delete f[i]; + + r.value = i; + break; + } + } else { + r.value = f; + this.rstack.pop(); + } + + return r.value; +}; + +TJSONProtocol.prototype.readI64 = function() { + var n = this.readValue() + if (typeof n === 'string') { + // Assuming no one is sending in 1.11111e+33 format + return Int64Util.fromDecimalString(n); + } else { + return new Int64(n); + } +}; + +TJSONProtocol.prototype.readDouble = function() { + return this.readI32(); +}; + +TJSONProtocol.prototype.readBinary = function() { + return new Buffer(this.readValue(), 'base64'); +}; + +TJSONProtocol.prototype.readString = function() { + return this.readValue(); +}; + +/** + * Returns the underlying transport. + * @readonly + * @returns {Thrift.Transport} The underlying transport. + */ +TJSONProtocol.prototype.getTransport = function() { + return this.trans; +}; + +/** + * Method to arbitrarily skip over data + */ +TJSONProtocol.prototype.skip = function(type) { + throw new Error('skip not supported yet'); +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/log.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/log.js new file mode 100644 index 00000000..0e13ea80 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/log.js @@ -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. + */ + +module.exports = { + 'info' : function logInfo() {}, + 'warning' : function logWarning() {}, + 'error' : function logError() {}, + 'debug' : function logDebug() {}, + 'trace' : function logTrace() {} +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js new file mode 100644 index 00000000..67b62f7a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_processor.js @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var Thrift = require('./thrift'); + +exports.MultiplexedProcessor = MultiplexedProcessor; + +function MultiplexedProcessor(stream, options) { + this.services = {}; +}; + +MultiplexedProcessor.prototype.registerProcessor = function(name, handler) { + this.services[name] = handler; +}; + +MultiplexedProcessor.prototype.process = function(inp, out) { + var begin = inp.readMessageBegin(); + + if (begin.mtype != Thrift.MessageType.CALL && begin.mtype != Thrift.MessageType.ONEWAY) { + throw new Thrift.TException('TMultiplexedProcessor: Unexpected message type'); + } + + var p = begin.fname.split(':'); + var sname = p[0]; + var fname = p[1]; + + if (! (sname in this.services)) { + throw new Thrift.TException('TMultiplexedProcessor: Unknown service: ' + sname); + } + + //construct a proxy object which stubs the readMessageBegin + //for the service + var inpProxy = {}; + + for (var attr in inp) { + inpProxy[attr] = inp[attr]; + } + + inpProxy.readMessageBegin = function() { + return { + fname: fname, + mtype: begin.mtype, + rseqid: begin.rseqid + }; + }; + + this.services[sname].process(inpProxy, out); +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js new file mode 100644 index 00000000..9caf6ab5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/multiplexed_protocol.js @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var Thrift = require('./thrift'); + +exports.Multiplexer = Multiplexer; + +function Wrapper(serviceName, protocol, connection) { + + function MultiplexProtocol(trans, strictRead, strictWrite) { + protocol.call(this, trans, strictRead, strictWrite); + }; + + util.inherits(MultiplexProtocol, protocol); + + MultiplexProtocol.prototype.writeMessageBegin = function(name, type, seqid) { + if (type == Thrift.MessageType.CALL || type == Thrift.MessageType.ONEWAY) { + connection.seqId2Service[seqid] = serviceName; + MultiplexProtocol.super_.prototype.writeMessageBegin.call(this, + serviceName + ":" + name, + type, + seqid); + } else { + MultiplexProtocol.super_.prototype.writeMessageBegin.call(this, name, type, seqid); + } + }; + + return MultiplexProtocol; +}; + +function Multiplexer() { + this.seqid = 0; +}; + +Multiplexer.prototype.createClient = function(serviceName, ServiceClient, connection) { + if (ServiceClient.Client) { + ServiceClient = ServiceClient.Client; + } + var self = this; + ServiceClient.prototype.new_seqid = function() { + self.seqid += 1; + return self.seqid; + }; + var writeCb = function(buf, seqid) { + connection.write(buf,seqid); + }; + var transport = new connection.transport(undefined, writeCb); + var protocolWrapper = new Wrapper(serviceName, connection.protocol, connection); + var client = new ServiceClient(transport, protocolWrapper); + + if (typeof connection.client !== 'object') { + connection.client = {}; + } + connection.client[serviceName] = client; + + return client; +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/protocol.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/protocol.js new file mode 100644 index 00000000..a70ebe28 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/protocol.js @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports.TBinaryProtocol = require('./binary_protocol'); +module.exports.TCompactProtocol = require('./compact_protocol'); +module.exports.TJSONProtocol = require('./json_protocol'); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/server.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/server.js new file mode 100644 index 00000000..921bb868 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/server.js @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var net = require('net'); +var tls = require('tls'); + +var TBufferedTransport = require('./buffered_transport'); +var TBinaryProtocol = require('./binary_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +/** + * Create a Thrift server which can serve one or multiple services. + * @param {object} processor - A normal or multiplexedProcessor (must + * be preconstructed with the desired handler). + * @param {ServerOptions} options - Optional additional server configuration. + * @returns {object} - The Apache Thrift Multiplex Server. + */ +exports.createMultiplexServer = function(processor, options) { + var transport = (options && options.transport) ? options.transport : TBufferedTransport; + var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol; + + function serverImpl(stream) { + var self = this; + stream.on('error', function(err) { + self.emit('error', err); + }); + stream.on('data', transport.receiver(function(transportWithData) { + var input = new protocol(transportWithData); + var output = new protocol(new transport(undefined, function(buf) { + try { + stream.write(buf); + } catch (err) { + self.emit('error', err); + stream.end(); + } + })); + + try { + do { + processor.process(input, output); + transportWithData.commitPosition(); + } while (true); + } catch (err) { + if (err instanceof InputBufferUnderrunError) { + //The last data in the buffer was not a complete message, wait for the rest + transportWithData.rollbackPosition(); + } + else if (err.message === "Invalid type: undefined") { + //No more data in the buffer + //This trap is a bit hackish + //The next step to improve the node behavior here is to have + // the compiler generated process method throw a more explicit + // error when the network buffer is empty (regardles of the + // protocol/transport stack in use) and replace this heuristic. + // Also transports should probably not force upper layers to + // manage their buffer positions (i.e. rollbackPosition() and + // commitPosition() should be eliminated in lieu of a transport + // encapsulated buffer management strategy.) + transportWithData.rollbackPosition(); + } + else { + //Unexpected error + self.emit('error', err); + stream.end(); + } + } + })); + + stream.on('end', function() { + stream.end(); + }); + } + + if (options && options.tls) { + return tls.createServer(options.tls, serverImpl); + } else { + return net.createServer(serverImpl); + } +}; + +/** + * Create a single service Apache Thrift server. + * @param {object} processor - A service class or processor function. + * @param {ServerOptions} options - Optional additional server configuration. + * @returns {object} - The Apache Thrift Multiplex Server. + */ +exports.createServer = function(processor, handler, options) { + if (processor.Processor) { + processor = processor.Processor; + } + return exports.createMultiplexServer(new processor(handler), options); +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/thrift.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/thrift.js new file mode 100644 index 00000000..f2b28967 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/thrift.js @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); + +var Type = exports.Type = { + STOP: 0, + VOID: 1, + BOOL: 2, + BYTE: 3, + I08: 3, + DOUBLE: 4, + I16: 6, + I32: 8, + I64: 10, + STRING: 11, + UTF7: 11, + STRUCT: 12, + MAP: 13, + SET: 14, + LIST: 15, + UTF8: 16, + UTF16: 17 +}; + +exports.MessageType = { + CALL: 1, + REPLY: 2, + EXCEPTION: 3, + ONEWAY: 4 +}; + +exports.TException = TException; + +function TException(message) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; +}; +util.inherits(TException, Error); + +var TApplicationExceptionType = exports.TApplicationExceptionType = { + UNKNOWN: 0, + UNKNOWN_METHOD: 1, + INVALID_MESSAGE_TYPE: 2, + WRONG_METHOD_NAME: 3, + BAD_SEQUENCE_ID: 4, + MISSING_RESULT: 5, + INTERNAL_ERROR: 6, + PROTOCOL_ERROR: 7, + INVALID_TRANSFORM: 8, + INVALID_PROTOCOL: 9, + UNSUPPORTED_CLIENT_TYPE: 10 +}; + +exports.TApplicationException = TApplicationException; + +function TApplicationException(type, message) { + TException.call(this); + Error.captureStackTrace(this, this.constructor); + this.type = type || TApplicationExceptionType.UNKNOWN; + this.name = this.constructor.name; + this.message = message; +}; +util.inherits(TApplicationException, TException); + +TApplicationException.prototype.read = function(input) { + var ftype; + var ret = input.readStructBegin('TApplicationException'); + + while(1){ + ret = input.readFieldBegin(); + if(ret.ftype == Type.STOP) + break; + + switch(ret.fid){ + case 1: + if( ret.ftype == Type.STRING ){ + ret = input.readString(); + this.message = ret; + } else { + ret = input.skip(ret.ftype); + } + break; + case 2: + if( ret.ftype == Type.I32 ){ + ret = input.readI32(); + this.type = ret; + } else { + ret = input.skip(ret.ftype); + } + break; + default: + ret = input.skip(ret.ftype); + break; + } + input.readFieldEnd(); + } + input.readStructEnd(); +}; + +TApplicationException.prototype.write = function(output){ + output.writeStructBegin('TApplicationException'); + + if (this.message) { + output.writeFieldBegin('message', Type.STRING, 1); + output.writeString(this.message); + output.writeFieldEnd(); + } + + if (this.code) { + output.writeFieldBegin('type', Type.I32, 2); + output.writeI32(this.code); + output.writeFieldEnd(); + } + + output.writeFieldStop(); + output.writeStructEnd(); +}; + +var TProtocolExceptionType = exports.TProtocolExceptionType = { + UNKNOWN: 0, + INVALID_DATA: 1, + NEGATIVE_SIZE: 2, + SIZE_LIMIT: 3, + BAD_VERSION: 4, + NOT_IMPLEMENTED: 5, + DEPTH_LIMIT: 6 +}; + + +exports.TProtocolException = TProtocolException; + +function TProtocolException(type, message) { + Error.call(this); + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.type = type; + this.message = message; +}; +util.inherits(TProtocolException, Error); + +exports.objectLength = function(obj) { + return Object.keys(obj).length; +}; + +exports.inherits = function(constructor, superConstructor) { + util.inherits(constructor, superConstructor); +}; + +var copyList, copyMap; + +copyList = function(lst, types) { + + if (!lst) {return lst; } + + var type; + + if (types.shift === undefined) { + type = types; + } + else { + type = types[0]; + } + var Type = type; + + var len = lst.length, result = [], i, val; + for (i = 0; i < len; i++) { + val = lst[i]; + if (type === null) { + result.push(val); + } + else if (type === copyMap || type === copyList) { + result.push(type(val, types.slice(1))); + } + else { + result.push(new Type(val)); + } + } + return result; +}; + +copyMap = function(obj, types){ + + if (!obj) {return obj; } + + var type; + + if (types.shift === undefined) { + type = types; + } + else { + type = types[0]; + } + var Type = type; + + var result = {}, val; + for(var prop in obj) { + if(obj.hasOwnProperty(prop)) { + val = obj[prop]; + if (type === null) { + result[prop] = val; + } + else if (type === copyMap || type === copyList) { + result[prop] = type(val, types.slice(1)); + } + else { + result[prop] = new Type(val); + } + } + } + return result; +}; + +module.exports.copyMap = copyMap; +module.exports.copyList = copyList; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/transport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/transport.js new file mode 100644 index 00000000..59daa987 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/transport.js @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports.TBufferedTransport = require('./buffered_transport'); +module.exports.TFramedTransport = require('./framed_transport'); +module.exports.InputBufferUnderrunError = require('./input_buffer_underrun_error'); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/web_server.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/web_server.js new file mode 100644 index 00000000..37159eaf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/web_server.js @@ -0,0 +1,564 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var http = require('http'); +var https = require('https'); +var url = require("url"); +var path = require("path"); +var fs = require("fs"); +var crypto = require("crypto"); + +var MultiplexedProcessor = require('./multiplexed_processor').MultiplexedProcessor; + +var TBufferedTransport = require('./buffered_transport'); +var TBinaryProtocol = require('./binary_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +// WSFrame constructor and prototype +///////////////////////////////////////////////////////////////////// + +/** Apache Thrift RPC Web Socket Transport + * Frame layout conforming to RFC 6455 circa 12/2011 + * + * Theoretical frame size limit is 4GB*4GB, however the Node Buffer + * limit is 1GB as of v0.10. The frame length encoding is also + * configured for a max of 4GB presently and needs to be adjusted + * if Node/Browsers become capabile of > 4GB frames. + * + * - FIN is 1 if the message is complete + * - RSV1/2/3 are always 0 + * - Opcode is 1(TEXT) for TJSONProtocol and 2(BIN) for TBinaryProtocol + * - Mask Present bit is 1 sending to-server and 0 sending to-client + * - Payload Len: + * + If < 126: then represented directly + * + If >=126: but within range of an unsigned 16 bit integer + * then Payload Len is 126 and the two following bytes store + * the length + * + Else: Payload Len is 127 and the following 8 bytes store the + * length as an unsigned 64 bit integer + * - Masking key is a 32 bit key only present when sending to the server + * - Payload follows the masking key or length + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|M| Payload len | Extended payload length | + * |I|S|S|S| (4) |A| (7) | (16/64) | + * |N|V|V|V| |S| | (if payload len==126/127) | + * | |1|2|3| |K| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | |Masking-key, if MASK set to 1 | + * +-------------------------------+-------------------------------+ + * | Masking-key (continued) | Payload Data | + * +-------------------------------- - - - - - - - - - - - - - - - + + * : Payload Data continued ... : + * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + * | Payload Data continued ... | + * +---------------------------------------------------------------+ + */ +var wsFrame = { + /** Encodes a WebSocket frame + * + * @param {Buffer} data - The raw data to encode + * @param {Buffer} mask - The mask to apply when sending to server, null for no mask + * @param {Boolean} binEncoding - True for binary encoding, false for text encoding + * @returns {Buffer} - The WebSocket frame, ready to send + */ + encode: function(data, mask, binEncoding) { + var frame = new Buffer(wsFrame.frameSizeFromData(data, mask)); + //Byte 0 - FIN & OPCODE + frame[0] = wsFrame.fin.FIN + + (binEncoding ? wsFrame.frameOpCodes.BIN : wsFrame.frameOpCodes.TEXT); + //Byte 1 or 1-3 or 1-9 - MASK FLAG & SIZE + var payloadOffset = 2; + if (data.length < 0x7E) { + frame[1] = data.length + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT); + } else if (data.length < 0xFFFF) { + frame[1] = 0x7E + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT); + frame.writeUInt16BE(data.length, 2, true); + payloadOffset = 4; + } else { + frame[1] = 0x7F + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT); + frame.writeUInt32BE(0, 2, true); + frame.writeUInt32BE(data.length, 6, true); + payloadOffset = 10; + } + //MASK + if (mask) { + mask.copy(frame, payloadOffset, 0, 4); + payloadOffset += 4; + } + //Payload + data.copy(frame, payloadOffset); + if (mask) { + wsFrame.applyMask(frame.slice(payloadOffset), frame.slice(payloadOffset-4,payloadOffset)); + } + return frame; + }, + + /** + * @class + * @name WSDecodeResult + * @property {Buffer} data - The decoded data for the first ATRPC message + * @property {Buffer} mask - The frame mask + * @property {Boolean} binEncoding - True if binary (TBinaryProtocol), + * False if text (TJSONProtocol) + * @property {Buffer} nextFrame - Multiple ATRPC messages may be sent in a + * single WebSocket frame, this Buffer contains + * any bytes remaining to be decoded + * @property {Boolean} FIN - True is the message is complete + */ + + /** Decodes a WebSocket frame + * + * @param {Buffer} frame - The raw inbound frame, if this is a continuation + * frame it must have a mask property with the mask. + * @returns {WSDecodeResult} - The decoded payload + * + * @see {@link WSDecodeResult} + */ + decode: function(frame) { + var result = { + data: null, + mask: null, + binEncoding: false, + nextFrame: null, + FIN: true + }; + + //Byte 0 - FIN & OPCODE + if (wsFrame.fin.FIN != (frame[0] & wsFrame.fin.FIN)) { + result.FIN = false; + } + result.binEncoding = (wsFrame.frameOpCodes.BIN == (frame[0] & wsFrame.frameOpCodes.BIN)); + //Byte 1 or 1-3 or 1-9 - SIZE + var lenByte = (frame[1] & 0x0000007F); + var len = lenByte; + var dataOffset = 2; + if (lenByte == 0x7E) { + len = frame.readUInt16BE(2); + dataOffset = 4; + } else if (lenByte == 0x7F) { + len = frame.readUInt32BE(6); + dataOffset = 10; + } + //MASK + if (wsFrame.mask.TO_SERVER == (frame[1] & wsFrame.mask.TO_SERVER)) { + result.mask = new Buffer(4); + frame.copy(result.mask, 0, dataOffset, dataOffset + 4); + dataOffset += 4; + } + //Payload + result.data = new Buffer(len); + frame.copy(result.data, 0, dataOffset, dataOffset+len); + if (result.mask) { + wsFrame.applyMask(result.data, result.mask); + } + //Next Frame + if (frame.length > dataOffset+len) { + result.nextFrame = new Buffer(frame.length - (dataOffset+len)); + frame.copy(result.nextFrame, 0, dataOffset+len, frame.length); + } + //Don't forward control frames + if (frame[0] & wsFrame.frameOpCodes.FINCTRL) { + result.data = null; + } + + return result; + }, + + /** Masks/Unmasks data + * + * @param {Buffer} data - data to mask/unmask in place + * @param {Buffer} mask - the mask + */ + applyMask: function(data, mask){ + //TODO: look into xoring words at a time + var dataLen = data.length; + var maskLen = mask.length; + for (var i = 0; i < dataLen; i++) { + data[i] = data[i] ^ mask[i%maskLen]; + } + }, + + /** Computes frame size on the wire from data to be sent + * + * @param {Buffer} data - data.length is the assumed payload size + * @param {Boolean} mask - true if a mask will be sent (TO_SERVER) + */ + frameSizeFromData: function(data, mask) { + var headerSize = 10; + if (data.length < 0x7E) { + headerSize = 2; + } else if (data.length < 0xFFFF) { + headerSize = 4; + } + return headerSize + data.length + (mask ? 4 : 0); + }, + + frameOpCodes: { + CONT: 0x00, + TEXT: 0x01, + BIN: 0x02, + CTRL: 0x80 + }, + + mask: { + TO_SERVER: 0x80, + TO_CLIENT: 0x00 + }, + + fin: { + CONT: 0x00, + FIN: 0x80 + } +}; + + +// createWebServer constructor and options +///////////////////////////////////////////////////////////////////// + +/** + * @class + * @name ServerOptions + * @property {array} cors - Array of CORS origin strings to permit requests from. + * @property {string} files - Path to serve static files from, if absent or "" + * static file service is disabled. + * @property {object} headers - An object hash mapping header strings to header value + * strings, these headers are transmitted in response to + * static file GET operations. + * @property {object} services - An object hash mapping service URI strings + * to ServiceOptions objects + * @property {object} tls - Node.js TLS options (see: nodejs.org/api/tls.html), + * if not present or null regular http is used, + * at least a key and a cert must be defined to use SSL/TLS + * @see {@link ServiceOptions} + */ + +/** + * @class + * @name ServiceOptions + * @property {object} transport - The layered transport to use (defaults + * to TBufferedTransport). + * @property {object} protocol - The serialization Protocol to use (defaults to + * TBinaryProtocol). + * @property {object} processor - The Thrift Service class/processor generated + * by the IDL Compiler for the service (the "cls" + * key can also be used for this attribute). + * @property {object} handler - The handler methods for the Thrift Service. + */ + +/** + * Create a Thrift server which can serve static files and/or one or + * more Thrift Services. + * @param {ServerOptions} options - The server configuration. + * @returns {object} - The Apache Thrift Web Server. + */ +exports.createWebServer = function(options) { + var baseDir = options.files; + var contentTypesByExtension = { + '.txt': 'text/plain', + '.html': 'text/html', + '.css': 'text/css', + '.xml': 'application/xml', + '.json': 'application/json', + '.js': 'application/javascript', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.png': 'image/png', +   '.svg': 'image/svg+xml' + }; + + //Setup all of the services + var services = options.services; + for (var uri in services) { + var svcObj = services[uri]; + + //Setup the processor + if (svcObj.processor instanceof MultiplexedProcessor) { + //Multiplex processors have pre embedded processor/handler pairs, save as is + svcObj.processor = svcObj.processor; + } else { + //For historical reasons Node.js supports processors passed in directly or via the + // IDL Compiler generated class housing the processor. Also, the options property + // for a Processor has been called both cls and processor at different times. We + // support any of the four possibilities here. + var processor = (svcObj.processor) ? (svcObj.processor.Processor || svcObj.processor) : + (svcObj.cls.Processor || svcObj.cls); + //Processors can be supplied as constructed objects with handlers already embedded, + // if a handler is provided we construct a new processor, if not we use the processor + // object directly + if (svcObj.handler) { + svcObj.processor = new processor(svcObj.handler); + } else { + svcObj.processor = processor; + } + } + svcObj.transport = svcObj.transport ? svcObj.transport : TBufferedTransport; + svcObj.protocol = svcObj.protocol ? svcObj.protocol : TBinaryProtocol; + } + + //Verify CORS requirements + function VerifyCORSAndSetHeaders(request, response) { + if (request.headers.origin && options.cors) { + if (options.cors["*"] || options.cors[request.headers.origin]) { + //Allow, origin allowed + response.setHeader("access-control-allow-origin", request.headers.origin); + response.setHeader("access-control-allow-methods", "GET, POST, OPTIONS"); + response.setHeader("access-control-allow-headers", "content-type, accept"); + response.setHeader("access-control-max-age", "60"); + return true; + } else { + //Disallow, origin denied + return false; + } + } + //Allow, CORS is not in use + return true; + } + + + //Handle OPTIONS method (CORS) + /////////////////////////////////////////////////// + function processOptions(request, response) { + if (VerifyCORSAndSetHeaders(request, response)) { + response.writeHead("204", "No Content", {"content-length": 0}); + } else { + response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); + } + response.end(); + } + + + //Handle POST methods (TXHRTransport) + /////////////////////////////////////////////////// + function processPost(request, response) { + //Lookup service + var uri = url.parse(request.url).pathname; + var svc = services[uri]; + if (!svc) { + response.writeHead("403", "No Apache Thrift Service at " + uri, {}); + response.end(); + return; + } + + //Verify CORS requirements + if (!VerifyCORSAndSetHeaders(request, response)) { + response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); + response.end(); + return; + } + + //Process XHR payload + request.on('data', svc.transport.receiver(function(transportWithData) { + var input = new svc.protocol(transportWithData); + var output = new svc.protocol(new svc.transport(undefined, function(buf) { + try { + response.writeHead(200); + response.end(buf); + } catch (err) { + response.writeHead(500); + response.end(); + } + })); + + try { + svc.processor.process(input, output); + transportWithData.commitPosition(); + } catch (err) { + if (err instanceof InputBufferUnderrunError) { + transportWithData.rollbackPosition(); + } else { + response.writeHead(500); + response.end(); + } + } + })); + } + + + //Handle GET methods (Static Page Server) + /////////////////////////////////////////////////// + function processGet(request, response) { + //Undefined or empty base directory means do not serve static files + if (!baseDir || "" === baseDir) { + response.writeHead(404); + response.end(); + return; + } + + //Verify CORS requirements + if (!VerifyCORSAndSetHeaders(request, response)) { + response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); + response.end(); + return; + } + + //Locate the file requested and send it + var uri = url.parse(request.url).pathname; + var filename = path.join(baseDir, uri); + fs.exists(filename, function(exists) { + if(!exists) { + response.writeHead(404); + response.end(); + return; + } + + if (fs.statSync(filename).isDirectory()) { + filename += '/index.html'; + } + + fs.readFile(filename, "binary", function(err, file) { + if (err) { + response.writeHead(500); + response.end(err + "\n"); + return; + } + var headers = {}; + var contentType = contentTypesByExtension[path.extname(filename)]; + if (contentType) { + headers["Content-Type"] = contentType; + } + for (var k in options.headers) { + headers[k] = options.headers[k]; + } + response.writeHead(200, headers); + response.write(file, "binary"); + response.end(); + }); + }); + } + + + //Handle WebSocket calls (TWebSocketTransport) + /////////////////////////////////////////////////// + function processWS(data, socket, svc, binEncoding) { + svc.transport.receiver(function(transportWithData) { + var input = new svc.protocol(transportWithData); + var output = new svc.protocol(new svc.transport(undefined, function(buf) { + try { + var frame = wsFrame.encode(buf, null, binEncoding); + socket.write(frame); + } catch (err) { + //TODO: Add better error processing + } + })); + + try { + svc.processor.process(input, output); + transportWithData.commitPosition(); + } + catch (err) { + if (err instanceof InputBufferUnderrunError) { + transportWithData.rollbackPosition(); + } + else { + //TODO: Add better error processing + } + } + })(data); + } + + //Create the server (HTTP or HTTPS) + var server = null; + if (options.tls) { + server = https.createServer(options.tls); + } else { + server = http.createServer(); + } + + //Wire up listeners for upgrade(to WebSocket) & request methods for: + // - GET static files, + // - POST XHR Thrift services + // - OPTIONS CORS requests + server.on('request', function(request, response) { + if (request.method === 'POST') { + processPost(request, response); + } else if (request.method === 'GET') { + processGet(request, response); + } else if (request.method === 'OPTIONS') { + processOptions(request, response); + } else { + response.writeHead(500); + response.end(); + } + }).on('upgrade', function(request, socket, head) { + //Lookup service + var svc; + try { + svc = services[Object.keys(services)[0]]; + } catch(e) { + socket.write("HTTP/1.1 403 No Apache Thrift Service available\r\n\r\n"); + return; + } + //Perform upgrade + var hash = crypto.createHash("sha1"); + hash.update(request.headers['sec-websocket-key'] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + socket.write("HTTP/1.1 101 Switching Protocols\r\n" + + "Upgrade: websocket\r\n" + + "Connection: Upgrade\r\n" + + "Sec-WebSocket-Accept: " + hash.digest("base64") + "\r\n" + + "Sec-WebSocket-Origin: " + request.headers.origin + "\r\n" + + "Sec-WebSocket-Location: ws://" + request.headers.host + request.url + "\r\n" + + "\r\n"); + //Handle WebSocket traffic + var data = null; + socket.on('data', function(frame) { + try { + while (frame) { + var result = wsFrame.decode(frame); + //Prepend any existing decoded data + if (data) { + if (result.data) { + var newData = new Buffer(data.length + result.data.length); + data.copy(newData); + result.data.copy(newData, data.length); + result.data = newData; + } else { + result.data = data; + } + data = null; + } + //If this completes a message process it + if (result.FIN) { + processWS(result.data, socket, svc, result.binEncoding); + } else { + data = result.data; + } + //Prepare next frame for decoding (if any) + frame = result.nextFrame; + } + } catch(e) { + console.log("TWebSocketTransport Exception: " + e); + socket.destroy(); + } + }); + }); + + //Return the server + return server; +}; + + + + + + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_connection.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_connection.js new file mode 100644 index 00000000..052cbd4e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_connection.js @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var WebSocket = require('ws'); +var EventEmitter = require("events").EventEmitter; +var thrift = require('./thrift'); +var ttransport = require('./transport'); +var tprotocol = require('./protocol'); + +var TBufferedTransport = require('./buffered_transport'); +var TJSONProtocol = require('./json_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +var createClient = require('./create_client'); + +exports.WSConnection = WSConnection; + +/** + * @class + * @name WSConnectOptions + * @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc). + * @property {string} protocol - The Thrift serialization protocol to use (TJSONProtocol, etc.). + * @property {string} path - The URL path to connect to (e.g. "/", "/mySvc", "/thrift/quoteSvc", etc.). + * @property {object} headers - A standard Node.js header hash, an object hash containing key/value + * pairs where the key is the header name string and the value is the header value string. + * @property {boolean} secure - True causes the connection to use wss, otherwise ws is used. + * @property {object} wsOptions - Options passed on to WebSocket. + * @example + * //Use a secured websocket connection + * // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic + * // to wss://thrift.example.com:9090/hello + * var thrift = require('thrift'); + * var options = { + * transport: thrift.TBufferedTransport, + * protocol: thrift.TJSONProtocol, + * path: "/hello", + * secure: true + * }; + * var con = thrift.createWSConnection("thrift.example.com", 9090, options); + * con.open() + * var client = thrift.createWSClient(myService, connection); + * client.myServiceFunction(); + * con.close() + */ + +/** + * Initializes a Thrift WSConnection instance (use createWSConnection() rather than + * instantiating directly). + * @constructor + * @param {string} host - The host name or IP to connect to. + * @param {number} port - The TCP port to connect to. + * @param {WSConnectOptions} options - The configuration options to use. + * @throws {error} Exceptions other than ttransport.InputBufferUnderrunError are rethrown + * @event {error} The "error" event is fired when a Node.js error event occurs during + * request or response processing, in which case the node error is passed on. An "error" + * event may also be fired when the connectison can not map a response back to the + * appropriate client (an internal error), generating a TApplicationException. + * @classdesc WSConnection objects provide Thrift end point transport + * semantics implemented using Websockets. + * @see {@link createWSConnection} + */ +function WSConnection(host, port, options) { + //Initialize the emitter base object + EventEmitter.call(this); + + //Set configuration + var self = this; + this.options = options || {}; + this.host = host; + this.port = port; + this.secure = this.options.secure || false; + this.transport = this.options.transport || TBufferedTransport; + this.protocol = this.options.protocol || TJSONProtocol; + this.path = this.options.path; + this.send_pending = []; + + //The sequence map is used to map seqIDs back to the + // calling client in multiplexed scenarios + this.seqId2Service = {}; + + //Prepare WebSocket options + this.wsOptions = { + host: this.host, + port: this.port || 80, + path: this.options.path || '/', + headers: this.options.headers || {} + }; + for (var attrname in this.options.wsOptions) { + this.wsOptions[attrname] = this.options.wsOptions[attrname]; + } +}; +util.inherits(WSConnection, EventEmitter); + +WSConnection.prototype.__reset = function() { + this.socket = null; //The web socket + this.send_pending = []; //Buffers/Callback pairs waiting to be sent +}; + +WSConnection.prototype.__onOpen = function() { + var self = this; + this.emit("open"); + if (this.send_pending.length > 0) { + //If the user made calls before the connection was fully + //open, send them now + this.send_pending.forEach(function(data) { + self.socket.send(data); + }); + this.send_pending = []; + } +}; + +WSConnection.prototype.__onClose = function(evt) { + this.emit("close"); + this.__reset(); +}; + +WSConnection.prototype.__decodeCallback = function(transport_with_data) { + var proto = new this.protocol(transport_with_data); + try { + while (true) { + var header = proto.readMessageBegin(); + var dummy_seqid = header.rseqid * -1; + var client = this.client; + //The Multiplexed Protocol stores a hash of seqid to service names + // in seqId2Service. If the SeqId is found in the hash we need to + // lookup the appropriate client for this call. + // The client var is a single client object when not multiplexing, + // when using multiplexing it is a service name keyed hash of client + // objects. + //NOTE: The 2 way interdependencies between protocols, transports, + // connections and clients in the Node.js implementation are irregular + // and make the implementation difficult to extend and maintain. We + // should bring this stuff inline with typical thrift I/O stack + // operation soon. + // --ra + var service_name = this.seqId2Service[header.rseqid]; + if (service_name) { + client = this.client[service_name]; + delete this.seqId2Service[header.rseqid]; + } + /*jshint -W083 */ + client._reqs[dummy_seqid] = function(err, success) { + transport_with_data.commitPosition(); + var clientCallback = client._reqs[header.rseqid]; + delete client._reqs[header.rseqid]; + if (clientCallback) { + clientCallback(err, success); + } + }; + /*jshint +W083 */ + if (client['recv_' + header.fname]) { + client['recv_' + header.fname](proto, header.mtype, dummy_seqid); + } else { + delete client._reqs[dummy_seqid]; + this.emit("error", + new thrift.TApplicationException( + thrift.TApplicationExceptionType.WRONG_METHOD_NAME, + "Received a response to an unknown RPC function")); + } + } + } catch (e) { + if (e instanceof InputBufferUnderrunError) { + transport_with_data.rollbackPosition(); + } else { + throw e; + } + } +}; + +WSConnection.prototype.__onData = function(data) { + if (Object.prototype.toString.call(data) == "[object ArrayBuffer]") { + data = new Uint8Array(data); + } + var buf = new Buffer(data); + this.transport.receiver(this.__decodeCallback.bind(this))(buf); + +}; + +WSConnection.prototype.__onMessage = function(evt) { + this.__onData(evt.data); +}; + +WSConnection.prototype.__onError = function(evt) { + this.emit("error", evt); + this.socket.close(); +}; + +/** + * Returns true if the transport is open + * @readonly + * @returns {boolean} + */ +WSConnection.prototype.isOpen = function() { + return this.socket && this.socket.readyState == this.socket.OPEN; +}; + +/** + * Opens the transport connection + */ +WSConnection.prototype.open = function() { + //If OPEN/CONNECTING/CLOSING ignore additional opens + if (this.socket && this.socket.readyState != this.socket.CLOSED) { + return; + } + //If there is no socket or the socket is closed: + this.socket = new WebSocket(this.uri(), "", this.wsOptions); + this.socket.binaryType = 'arraybuffer'; + this.socket.onopen = this.__onOpen.bind(this); + this.socket.onmessage = this.__onMessage.bind(this); + this.socket.onerror = this.__onError.bind(this); + this.socket.onclose = this.__onClose.bind(this); +}; + +/** + * Closes the transport connection + */ +WSConnection.prototype.close = function() { + this.socket.close(); +}; + +/** + * Return URI for the connection + * @returns {string} URI + */ +WSConnection.prototype.uri = function() { + var schema = this.secure ? 'wss' : 'ws'; + var port = ''; + var path = this.path || '/'; + var host = this.host; + + // avoid port if default for schema + if (this.port && (('wss' == schema && this.port != 443) || + ('ws' == schema && this.port != 80))) { + port = ':' + this.port; + } + + return schema + '://' + host + port + path; +}; + +/** + * Writes Thrift message data to the connection + * @param {Buffer} data - A Node.js Buffer containing the data to write + * @returns {void} No return value. + * @event {error} the "error" event is raised upon request failure passing the + * Node.js error object to the listener. + */ +WSConnection.prototype.write = function(data) { + if (this.isOpen()) { + //Send data and register a callback to invoke the client callback + this.socket.send(data); + } else { + //Queue the send to go out __onOpen + this.send_pending.push(data); + } +}; + +/** + * Creates a new WSConnection object, used by Thrift clients to connect + * to Thrift HTTP based servers. + * @param {string} host - The host name or IP to connect to. + * @param {number} port - The TCP port to connect to. + * @param {WSConnectOptions} options - The configuration options to use. + * @returns {WSConnection} The connection object. + * @see {@link WSConnectOptions} + */ +exports.createWSConnection = function(host, port, options) { + return new WSConnection(host, port, options); +}; + +exports.createWSClient = createClient; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_transport.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_transport.js new file mode 100644 index 00000000..8e750e23 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/ws_transport.js @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = TWebSocketTransport; + +/** + * Constructor Function for the WebSocket transport. + * @constructor + * @param {string} [url] - The URL to connect to. + * @classdesc The Apache Thrift Transport layer performs byte level I/O + * between RPC clients and servers. The JavaScript TWebSocketTransport object + * uses the WebSocket protocol. Target servers must implement WebSocket. + * (see: node.js example server_http.js). + * @example + * var transport = new Thrift.TWebSocketTransport("http://localhost:8585"); + */ +function TWebSocketTransport(url) { + this.__reset(url); +}; + + +TWebSocketTransport.prototype.__reset = function(url) { + this.url = url; //Where to connect + this.socket = null; //The web socket + this.callbacks = []; //Pending callbacks + this.send_pending = []; //Buffers/Callback pairs waiting to be sent + this.send_buf = ''; //Outbound data, immutable until sent + this.recv_buf = ''; //Inbound data + this.rb_wpos = 0; //Network write position in receive buffer + this.rb_rpos = 0; //Client read position in receive buffer +}; + +/** + * Sends the current WS request and registers callback. The async + * parameter is ignored (WS flush is always async) and the callback + * function parameter is required. + * @param {object} async - Ignored. + * @param {object} callback - The client completion callback. + * @returns {undefined|string} Nothing (undefined) + */ +TWebSocketTransport.prototype.flush = function(async, callback) { + var self = this; + if (this.isOpen()) { + //Send data and register a callback to invoke the client callback + this.socket.send(this.send_buf); + this.callbacks.push((function() { + var clientCallback = callback; + return function(msg) { + self.setRecvBuffer(msg); + clientCallback(); + }; + }())); + } else { + //Queue the send to go out __onOpen + this.send_pending.push({ + buf: this.send_buf, + cb: callback + }); + } +}; + +TWebSocketTransport.prototype.__onOpen = function() { + var self = this; + if (this.send_pending.length > 0) { + //If the user made calls before the connection was fully + //open, send them now + this.send_pending.forEach(function(elem) { + this.socket.send(elem.buf); + this.callbacks.push((function() { + var clientCallback = elem.cb; + return function(msg) { + self.setRecvBuffer(msg); + clientCallback(); + }; + }())); + }); + this.send_pending = []; + } +}; + +TWebSocketTransport.prototype.__onClose = function(evt) { + this.__reset(this.url); +}; + +TWebSocketTransport.prototype.__onMessage = function(evt) { + if (this.callbacks.length) { + this.callbacks.shift()(evt.data); + } +}; + +TWebSocketTransport.prototype.__onError = function(evt) { + console.log("Thrift WebSocket Error: " + evt.toString()); + this.socket.close(); +}; + +/** + * Sets the buffer to use when receiving server responses. + * @param {string} buf - The buffer to receive server responses. + */ +TWebSocketTransport.prototype.setRecvBuffer = function(buf) { + this.recv_buf = buf; + this.recv_buf_sz = this.recv_buf.length; + this.wpos = this.recv_buf.length; + this.rpos = 0; +}; + +/** + * Returns true if the transport is open + * @readonly + * @returns {boolean} + */ +TWebSocketTransport.prototype.isOpen = function() { + return this.socket && this.socket.readyState == this.socket.OPEN; +}; + +/** + * Opens the transport connection + */ +TWebSocketTransport.prototype.open = function() { + //If OPEN/CONNECTING/CLOSING ignore additional opens + if (this.socket && this.socket.readyState != this.socket.CLOSED) { + return; + } + //If there is no socket or the socket is closed: + this.socket = new WebSocket(this.url); + this.socket.onopen = this.__onOpen.bind(this); + this.socket.onmessage = this.__onMessage.bind(this); + this.socket.onerror = this.__onError.bind(this); + this.socket.onclose = this.__onClose.bind(this); +}; + +/** + * Closes the transport connection + */ +TWebSocketTransport.prototype.close = function() { + this.socket.close(); +}; + +/** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ +TWebSocketTransport.prototype.read = function(len) { + var avail = this.wpos - this.rpos; + + if (avail === 0) { + return ''; + } + + var give = len; + + if (avail < len) { + give = avail; + } + + var ret = this.read_buf.substr(this.rpos, give); + this.rpos += give; + + //clear buf when complete? + return ret; +}; + +/** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ +TWebSocketTransport.prototype.readAll = function() { + return this.recv_buf; +}; + +/** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ +TWebSocketTransport.prototype.write = function(buf) { + this.send_buf = buf; +}; + +/** + * Returns the send buffer. + * @readonly + * @returns {string} The send buffer. + */ +TWebSocketTransport.prototype.getSendBuffer = function() { + return this.send_buf; +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/xhr_connection.js b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/xhr_connection.js new file mode 100644 index 00000000..6459c900 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/lib/thrift/xhr_connection.js @@ -0,0 +1,280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var util = require('util'); +var EventEmitter = require("events").EventEmitter; +var thrift = require('./thrift'); + +var TBufferedTransport = require('./buffered_transport'); +var TJSONProtocol = require('./json_protocol'); +var InputBufferUnderrunError = require('./input_buffer_underrun_error'); + +var createClient = require('./create_client'); + +exports.XHRConnection = XHRConnection; + +/** + * Constructor Function for the XHR Connection. + * If you do not specify a host and port then XHRConnection will default to the + * host and port of the page from which this javascript is served. + * @constructor + * @param {string} [url] - The URL to connect to. + * @classdesc TXHRConnection objects provide Thrift end point transport + * semantics implemented using XHR. + * @example + * var transport = new Thrift.TXHRConnection('localhost', 9099, {}); + */ +function XHRConnection(host, port, options) { + this.options = options || {}; + this.wpos = 0; + this.rpos = 0; + this.useCORS = (options && options.useCORS); + this.send_buf = ''; + this.recv_buf = ''; + this.transport = options.transport || TBufferedTransport; + this.protocol = options.protocol || TJSONProtocol; + this.headers = options.headers || {}; + + host = host || window.location.host; + port = port || window.location.port; + var prefix = options.https ? 'https://' : 'http://'; + var path = options.path || '/'; + + if (port === '') { + port = undefined; + } + + if (!port || port === 80 || port === '80') { + this.url = prefix + host + path; + } else { + this.url = prefix + host + ':' + port + path; + } + + //The sequence map is used to map seqIDs back to the + // calling client in multiplexed scenarios + this.seqId2Service = {}; +}; + +util.inherits(XHRConnection, EventEmitter); + +/** +* Gets the browser specific XmlHttpRequest Object. +* @returns {object} the browser XHR interface object +*/ +XHRConnection.prototype.getXmlHttpRequestObject = function() { + try { return new XMLHttpRequest(); } catch (e1) { } + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { } + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { } + + throw "Your browser doesn't support XHR."; +}; + +/** + * Sends the current XRH request if the transport was created with a URL + * and the async parameter is false. If the transport was not created with + * a URL, or the async parameter is True and no callback is provided, or + * the URL is an empty string, the current send buffer is returned. + * @param {object} async - If true the current send buffer is returned. + * @param {object} callback - Optional async completion callback + * @returns {undefined|string} Nothing or the current send buffer. + * @throws {string} If XHR fails. + */ +XHRConnection.prototype.flush = function() { + var self = this; + if (this.url === undefined || this.url === '') { + return this.send_buf; + } + + var xreq = this.getXmlHttpRequestObject(); + + if (xreq.overrideMimeType) { + xreq.overrideMimeType('application/json'); + } + + xreq.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + self.setRecvBuffer(this.responseText); + } + }; + + xreq.open('POST', this.url, true); + + Object.keys(this.headers).forEach(function(headerKey) { + xreq.setRequestHeader(headerKey, self.headers[headerKey]); + }); + + xreq.send(this.send_buf); +}; + +/** + * Sets the buffer to provide the protocol when deserializing. + * @param {string} buf - The buffer to supply the protocol. + */ +XHRConnection.prototype.setRecvBuffer = function(buf) { + this.recv_buf = buf; + this.recv_buf_sz = this.recv_buf.length; + this.wpos = this.recv_buf.length; + this.rpos = 0; + + if (Object.prototype.toString.call(buf) == "[object ArrayBuffer]") { + var data = new Uint8Array(buf); + } + var thing = new Buffer(data || buf); + + this.transport.receiver(this.__decodeCallback.bind(this))(thing); + +}; + +XHRConnection.prototype.__decodeCallback = function(transport_with_data) { + var proto = new this.protocol(transport_with_data); + try { + while (true) { + var header = proto.readMessageBegin(); + var dummy_seqid = header.rseqid * -1; + var client = this.client; + //The Multiplexed Protocol stores a hash of seqid to service names + // in seqId2Service. If the SeqId is found in the hash we need to + // lookup the appropriate client for this call. + // The client var is a single client object when not multiplexing, + // when using multiplexing it is a service name keyed hash of client + // objects. + //NOTE: The 2 way interdependencies between protocols, transports, + // connections and clients in the Node.js implementation are irregular + // and make the implementation difficult to extend and maintain. We + // should bring this stuff inline with typical thrift I/O stack + // operation soon. + // --ra + var service_name = this.seqId2Service[header.rseqid]; + if (service_name) { + client = this.client[service_name]; + delete this.seqId2Service[header.rseqid]; + } + /*jshint -W083 */ + client._reqs[dummy_seqid] = function(err, success) { + transport_with_data.commitPosition(); + var clientCallback = client._reqs[header.rseqid]; + delete client._reqs[header.rseqid]; + if (clientCallback) { + clientCallback(err, success); + } + }; + /*jshint +W083 */ + if (client['recv_' + header.fname]) { + client['recv_' + header.fname](proto, header.mtype, dummy_seqid); + } else { + delete client._reqs[dummy_seqid]; + this.emit("error", + new thrift.TApplicationException( + thrift.TApplicationExceptionType.WRONG_METHOD_NAME, + "Received a response to an unknown RPC function")); + } + } + } catch (e) { + if (e instanceof InputBufferUnderrunError) { + transport_with_data.rollbackPosition(); + } else { + throw e; + } + } +}; + +/** + * Returns true if the transport is open, XHR always returns true. + * @readonly + * @returns {boolean} Always True. + */ +XHRConnection.prototype.isOpen = function() { + return true; +}; + +/** + * Opens the transport connection, with XHR this is a nop. + */ +XHRConnection.prototype.open = function() {}; + +/** + * Closes the transport connection, with XHR this is a nop. + */ +XHRConnection.prototype.close = function() {}; + +/** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ +XHRConnection.prototype.read = function(len) { + var avail = this.wpos - this.rpos; + + if (avail === 0) { + return ''; + } + + var give = len; + + if (avail < len) { + give = avail; + } + + var ret = this.read_buf.substr(this.rpos, give); + this.rpos += give; + + //clear buf when complete? + return ret; +}; + +/** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ +XHRConnection.prototype.readAll = function() { + return this.recv_buf; +}; + +/** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ +XHRConnection.prototype.write = function(buf) { + this.send_buf = buf; + this.flush(); +}; + +/** + * Returns the send buffer. + * @readonly + * @returns {string} The send buffer. + */ +XHRConnection.prototype.getSendBuffer = function() { + return this.send_buf; +}; + +/** + * Creates a new TXHRTransport object, used by Thrift clients to connect + * to Thrift HTTP based servers. + * @param {string} host - The host name or IP to connect to. + * @param {number} port - The TCP port to connect to. + * @param {XHRConnectOptions} options - The configuration options to use. + * @returns {XHRConnection} The connection object. + * @see {@link XHRConnectOptions} + */ +exports.createXHRConnection = function(host, port, options) { + return new XHRConnection(host, port, options); +}; + +exports.createXHRClient = createClient; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/binary.test.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/binary.test.js new file mode 100644 index 00000000..38ba6341 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/binary.test.js @@ -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. + */ + +var test = require('tape'); +var binary = require('thrift/binary'); + +var cases = { + "Should read signed byte": function(assert){ + assert.equal(1, binary.readByte(0x01)); + assert.equal(-1, binary.readByte(0xFF)); + + assert.equal(127, binary.readByte(0x7F)); + assert.equal(-128, binary.readByte(0x80)); + assert.end(); + }, + "Should write byte": function(assert){ + //Protocol simply writes to the buffer. Nothing to test.. yet. + assert.ok(true); + assert.end(); + }, + "Should read I16": function(assert) { + assert.equal(0, binary.readI16([0x00, 0x00])); + assert.equal(1, binary.readI16([0x00, 0x01])); + assert.equal(-1, binary.readI16([0xff, 0xff])); + + // Min I16 + assert.equal(-32768, binary.readI16([0x80, 0x00])); + // Max I16 + assert.equal(32767, binary.readI16([0x7f, 0xff])); + assert.end(); + }, + + "Should write I16": function(assert) { + assert.deepEqual([0x00, 0x00], binary.writeI16([], 0)); + assert.deepEqual([0x00, 0x01], binary.writeI16([], 1)); + assert.deepEqual([0xff, 0xff], binary.writeI16([], -1)); + + // Min I16 + assert.deepEqual([0x80, 0x00], binary.writeI16([], -32768)); + // Max I16 + assert.deepEqual([0x7f, 0xff], binary.writeI16([], 32767)); + assert.end(); + }, + + "Should read I32": function(assert) { + assert.equal(0, binary.readI32([0x00, 0x00, 0x00, 0x00])); + assert.equal(1, binary.readI32([0x00, 0x00, 0x00, 0x01])); + assert.equal(-1, binary.readI32([0xff, 0xff, 0xff, 0xff])); + + // Min I32 + assert.equal(-2147483648, binary.readI32([0x80, 0x00, 0x00, 0x00])); + // Max I32 + assert.equal(2147483647, binary.readI32([0x7f, 0xff, 0xff, 0xff])); + assert.end(); + }, + + "Should write I32": function(assert) { + assert.deepEqual([0x00, 0x00, 0x00, 0x00], binary.writeI32([], 0)); + assert.deepEqual([0x00, 0x00, 0x00, 0x01], binary.writeI32([], 1)); + assert.deepEqual([0xff, 0xff, 0xff, 0xff], binary.writeI32([], -1)); + + // Min I32 + assert.deepEqual([0x80, 0x00, 0x00, 0x00], binary.writeI32([], -2147483648)); + // Max I32 + assert.deepEqual([0x7f, 0xff, 0xff, 0xff], binary.writeI32([], 2147483647)); + assert.end(); + }, + + "Should read doubles": function(assert) { + assert.equal(0, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + assert.equal(0, binary.readDouble([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + assert.equal(1, binary.readDouble([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + assert.equal(2, binary.readDouble([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + assert.equal(-2, binary.readDouble([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + + assert.equal(Math.PI, binary.readDouble([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18])) + + assert.equal(Infinity, binary.readDouble([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + assert.equal(-Infinity, binary.readDouble([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + + assert.ok(isNaN(binary.readDouble([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))) + + assert.equal(1/3, binary.readDouble([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55])) + + // Min subnormal positive double + assert.equal(4.9406564584124654e-324, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])) + // Min normal positive double + assert.equal(2.2250738585072014e-308, binary.readDouble([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])) + // Max positive double + assert.equal(1.7976931348623157e308, binary.readDouble([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])) + assert.end(); + }, + + "Should write doubles": function(assert) { + assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 0)); + assert.deepEqual([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 1)); + assert.deepEqual([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2)); + assert.deepEqual([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -2)); + + assert.deepEqual([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], binary.writeDouble([], Math.PI)); + + assert.deepEqual([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], Infinity)); + assert.deepEqual([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -Infinity)); + + assert.deepEqual([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], NaN)); + + assert.deepEqual([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55], binary.writeDouble([], 1/3)); + + // Min subnormal positive double + assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], binary.writeDouble([], 4.9406564584124654e-324)); + // Min normal positive double + assert.deepEqual([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2.2250738585072014e-308)); + // Max positive double + assert.deepEqual([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], binary.writeDouble([], 1.7976931348623157e308)); + assert.end(); + } +}; + +Object.keys(cases).forEach(function(caseName) { + test(caseName, cases[caseName]); +}); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/browser_client.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/browser_client.js new file mode 100644 index 00000000..27db543b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/browser_client.js @@ -0,0 +1,27 @@ + +var assert = require('assert'); +var thrift = require('thrift'); +var helpers = require('./helpers'); +var ThriftTest = require('./gen-nodejs/ThriftTest'); +var ThriftTestDriver = require('./test_driver').ThriftTestDriver; + +// createXHRConnection createWSConnection +var connection = thrift.createXHRConnection("localhost", 9090, { + transport: helpers.transports['buffered'], + protocol: helpers.protocols['json'], + path: '/test' +}); + +connection.on('error', function(err) { + assert(false, err); +}); + +// Uncomment the following line to start a websockets connection +// connection.open(); + +// createWSClient createXHRClient +var client = thrift.createXHRClient(ThriftTest, connection); + +ThriftTestDriver(client, function (status) { + console.log('Browser:', status); +}); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/certificates.README b/vendor/src/github.com/apache/thrift/lib/nodejs/test/certificates.README new file mode 100644 index 00000000..06c507e7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/certificates.README @@ -0,0 +1,7 @@ +server.crt AND server.key ARE PROVIDED FOR TEST PURPOSE AND SHOULD *NEVER* BE USED IN PRODUCTION + + +Origin of the test key and cert is the folder test/keys of Apache Thrift source code distribution + +We need copies for npm deployment + diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/client.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/client.js new file mode 100644 index 00000000..a38a66b7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/client.js @@ -0,0 +1,130 @@ +#!/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. + */ + +var fs = require('fs'); +var assert = require('assert'); +var thrift = require('thrift'); +var helpers = require('./helpers'); +var ThriftTest = require('./gen-nodejs/ThriftTest'); +var ThriftTestDriver = require('./test_driver').ThriftTestDriver; +var ThriftTestDriverPromise = require('./test_driver').ThriftTestDriverPromise; +var SecondService = require('./gen-nodejs/SecondService'); +var ttypes = require('./gen-nodejs/ThriftTest_types'); + +var program = require('commander'); + +program + .option('-p, --protocol ', 'Set thrift protocol (binary|json) [protocol]') + .option('-t, --transport ', 'Set thrift transport (buffered|framed) [transport]') + .option('--port ', 'Set thrift server port number to connect', 9090) + .option('--host ', 'Set thrift server host to connect', 'localhost') + .option('--ssl', 'use SSL transport') + .option('--promise', 'test with promise style functions') + .option('-t, --type ', 'Select server type (tcp|multiplex|http)', 'tcp') + .parse(process.argv); + +var host = program.host; +var port = program.port; +var type = program.type; +var ssl = program.ssl; +var promise = program.promise; + +var options = { + transport: helpers.transports[program.transport], + protocol: helpers.protocols[program.protocol] +}; + +if (type === 'http' || type === 'websocket') { + options.path = '/test'; +} + +if (type === 'http') { + options.headers = {"Connection": "close"}; +} + +if (ssl) { + if (type === 'tcp' || type === 'multiplex') { + options.rejectUnauthorized = false; + } else if (type === 'http') { + options.nodeOptions = { rejectUnauthorized: false }; + options.https = true; + } else if (type === 'websocket') { + options.wsOptions = { rejectUnauthorized: false }; + options.secure = true; + } +} + +var connection; +var client; +var testDriver = promise ? ThriftTestDriverPromise : ThriftTestDriver; + +if (type === 'tcp' || type === 'multiplex') { + connection = ssl ? + thrift.createSSLConnection(host, port, options) : + thrift.createConnection(host, port, options); +} else if (type === 'http') { + connection = thrift.createHttpConnection(host, port, options); +} else if (type === 'websocket') { + connection = thrift.createWSConnection(host, port, options); + connection.open(); +} + +connection.on('error', function(err) { + assert(false, err); +}); + +if (type === 'tcp') { + client = thrift.createClient(ThriftTest, connection); + runTests(); +} else if (type === 'multiplex') { + var mp = new thrift.Multiplexer(); + client = mp.createClient("ThriftTest", ThriftTest, connection); + secondclient = mp.createClient("SecondService", SecondService, connection); + + connection.on('connect', function() { + secondclient.secondtestString("Test", function(err, response) { + assert(!err); + assert.equal("Test", response); + }); + + runTests(); + }); +} else if (type === 'http') { + client = thrift.createHttpClient(ThriftTest, connection); + runTests(); +} else if (type === 'websocket') { + client = thrift.createWSClient(ThriftTest, connection); + runTests(); +} + +function runTests() { + testDriver(client, function (status) { + console.log(status); + if (type !== 'http' && type !== 'websocket') { + connection.end(); + } + if (type !== 'multiplex') { + process.exit(0); + } + }); +} + +exports.expressoTest = function() {}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/deep-constructor.test.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/deep-constructor.test.js new file mode 100644 index 00000000..2caeb824 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/deep-constructor.test.js @@ -0,0 +1,314 @@ +var ttypes = require('./gen-nodejs/JsDeepConstructorTest_types'); +var thrift = require('thrift'); +var test = require('tape'); +var bufferEquals = require('buffer-equals'); + +function serializeBinary(data) { + var buff; + var transport = new thrift.TBufferedTransport(null, function(msg){ + buff = msg; + }); + var prot = new thrift.TBinaryProtocol(transport); + data.write(prot); + prot.flush(); + return buff; + +} + + +function deserializeBinary(serialized, type) { + var t = new thrift.TFramedTransport(serialized); + var p = new thrift.TBinaryProtocol(t); + var data = new type(); + data.read(p); + return data; +} + + +function serializeJSON(data) { + var buff; + var transport = new thrift.TBufferedTransport(null, function(msg){ + buff = msg; + }); + var protocol = new thrift.TJSONProtocol(transport); + protocol.writeMessageBegin("", 0, 0); + data.write(protocol); + protocol.writeMessageEnd(); + protocol.flush(); + return buff; +} + + +function deserializeJSON(serialized, type) { + var transport = new thrift.TFramedTransport(serialized); + var protocol = new thrift.TJSONProtocol(transport); + protocol.readMessageBegin(); + var data = new type(); + data.read(protocol); + protocol.readMessageEnd(); + return data; +} + + +function createThriftObj() { + + return new ttypes.Complex({ + + struct_field: new ttypes.Simple({value: 'a'}), + + struct_list_field: [ + new ttypes.Simple({value: 'b'}), + new ttypes.Simple({value: 'c'}), + ], + + struct_set_field: [ + new ttypes.Simple({value: 'd'}), + new ttypes.Simple({value: 'e'}), + ], + + struct_map_field: { + A: new ttypes.Simple({value: 'f'}), + B: new ttypes.Simple({value: 'g'}) + }, + + struct_nested_containers_field: [ + [ + { + C: [ + new ttypes.Simple({value: 'h'}), + new ttypes.Simple({value: 'i'}) + ] + } + ] + ], + + struct_nested_containers_field2: { + D: [ + { + DA: new ttypes.Simple({value: 'j'}) + }, + { + DB: new ttypes.Simple({value: 'k'}) + } + ] + }, + + list_of_list_field: [ + ['l00', 'l01', 'l02'], + ['l10', 'l11', 'l12'], + ['l20', 'l21', 'l22'], + ], + + list_of_list_of_list_field: [ + [['m000', 'm001', 'm002'], ['m010', 'm011', 'm012'], ['m020', 'm021', 'm022']], + [['m100', 'm101', 'm102'], ['m110', 'm111', 'm112'], ['m120', 'm121', 'm122']], + [['m200', 'm201', 'm202'], ['m210', 'm211', 'm212'], ['m220', 'm221', 'm222']], + ], + + + }); +} + + +function createJsObj() { + + return { + + struct_field: {value: 'a'}, + + struct_list_field: [ + {value: 'b'}, + {value: 'c'}, + ], + + struct_set_field: [ + {value: 'd'}, + {value: 'e'}, + ], + + struct_map_field: { + A: {value: 'f'}, + B: {value: 'g'} + }, + + struct_nested_containers_field: [ + [ + { + C: [ + {value: 'h'}, + {value: 'i'} + ] + } + ] + ], + + struct_nested_containers_field2: { + D: [ + { + DA: {value: 'j'} + }, + { + DB: {value: 'k'} + } + ] + }, + + list_of_list_field: [ + ['l00', 'l01', 'l02'], + ['l10', 'l11', 'l12'], + ['l20', 'l21', 'l22'], + ], + + list_of_list_of_list_field: [ + [['m000', 'm001', 'm002'], ['m010', 'm011', 'm012'], ['m020', 'm021', 'm022']], + [['m100', 'm101', 'm102'], ['m110', 'm111', 'm112'], ['m120', 'm121', 'm122']], + [['m200', 'm201', 'm202'], ['m210', 'm211', 'm212'], ['m220', 'm221', 'm222']], + ], + + }; +} + + +function assertValues(obj, assert) { + assert.equals(obj.struct_field.value, 'a'); + assert.equals(obj.struct_list_field[0].value, 'b'); + assert.equals(obj.struct_list_field[1].value, 'c'); + assert.equals(obj.struct_set_field[0].value, 'd'); + assert.equals(obj.struct_set_field[1].value, 'e'); + assert.equals(obj.struct_map_field.A.value, 'f'); + assert.equals(obj.struct_map_field.B.value, 'g'); + assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, 'h'); + assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, 'i'); + assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, 'j'); + assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, 'k'); + assert.equals(obj.list_of_list_field[0][0], 'l00'); + assert.equals(obj.list_of_list_field[0][1], 'l01'); + assert.equals(obj.list_of_list_field[0][2], 'l02'); + assert.equals(obj.list_of_list_field[1][0], 'l10'); + assert.equals(obj.list_of_list_field[1][1], 'l11'); + assert.equals(obj.list_of_list_field[1][2], 'l12'); + assert.equals(obj.list_of_list_field[2][0], 'l20'); + assert.equals(obj.list_of_list_field[2][1], 'l21'); + assert.equals(obj.list_of_list_field[2][2], 'l22'); + + assert.equals(obj.list_of_list_of_list_field[0][0][0], 'm000'); + assert.equals(obj.list_of_list_of_list_field[0][0][1], 'm001'); + assert.equals(obj.list_of_list_of_list_field[0][0][2], 'm002'); + assert.equals(obj.list_of_list_of_list_field[0][1][0], 'm010'); + assert.equals(obj.list_of_list_of_list_field[0][1][1], 'm011'); + assert.equals(obj.list_of_list_of_list_field[0][1][2], 'm012'); + assert.equals(obj.list_of_list_of_list_field[0][2][0], 'm020'); + assert.equals(obj.list_of_list_of_list_field[0][2][1], 'm021'); + assert.equals(obj.list_of_list_of_list_field[0][2][2], 'm022'); + + assert.equals(obj.list_of_list_of_list_field[1][0][0], 'm100'); + assert.equals(obj.list_of_list_of_list_field[1][0][1], 'm101'); + assert.equals(obj.list_of_list_of_list_field[1][0][2], 'm102'); + assert.equals(obj.list_of_list_of_list_field[1][1][0], 'm110'); + assert.equals(obj.list_of_list_of_list_field[1][1][1], 'm111'); + assert.equals(obj.list_of_list_of_list_field[1][1][2], 'm112'); + assert.equals(obj.list_of_list_of_list_field[1][2][0], 'm120'); + assert.equals(obj.list_of_list_of_list_field[1][2][1], 'm121'); + assert.equals(obj.list_of_list_of_list_field[1][2][2], 'm122'); + + assert.equals(obj.list_of_list_of_list_field[2][0][0], 'm200'); + assert.equals(obj.list_of_list_of_list_field[2][0][1], 'm201'); + assert.equals(obj.list_of_list_of_list_field[2][0][2], 'm202'); + assert.equals(obj.list_of_list_of_list_field[2][1][0], 'm210'); + assert.equals(obj.list_of_list_of_list_field[2][1][1], 'm211'); + assert.equals(obj.list_of_list_of_list_field[2][1][2], 'm212'); + assert.equals(obj.list_of_list_of_list_field[2][2][0], 'm220'); + assert.equals(obj.list_of_list_of_list_field[2][2][1], 'm221'); + assert.equals(obj.list_of_list_of_list_field[2][2][2], 'm222'); +} + +function createTestCases(serialize, deserialize) { + + var cases = { + + "Serialize/deserialize should return equal object": function(assert){ + var tObj = createThriftObj(); + var received = deserialize(serialize(tObj), ttypes.Complex); + assert.ok(tObj !== received, 'not the same object'); + assert.deepEqual(tObj, received); + assert.end(); + }, + + "Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) { + var tObj1 = createThriftObj(); + var tObj2 = new ttypes.Complex(createJsObj()); + assertValues(tObj2, assert); + var s1 = serialize(tObj1); + var s2 = serialize(tObj2); + assert.ok(bufferEquals(s1, s2)); + assert.end(); + }, + + "Modifications to args object should not affect constructed Thrift object": function (assert) { + + var args = createJsObj(); + assertValues(args, assert); + + var tObj = new ttypes.Complex(args); + assertValues(tObj, assert); + + args.struct_field.value = 'ZZZ'; + args.struct_list_field[0].value = 'ZZZ'; + args.struct_list_field[1].value = 'ZZZ'; + args.struct_set_field[0].value = 'ZZZ'; + args.struct_set_field[1].value = 'ZZZ'; + args.struct_map_field.A.value = 'ZZZ'; + args.struct_map_field.B.value = 'ZZZ'; + args.struct_nested_containers_field[0][0].C[0] = 'ZZZ'; + args.struct_nested_containers_field[0][0].C[1] = 'ZZZ'; + args.struct_nested_containers_field2.D[0].DA = 'ZZZ'; + args.struct_nested_containers_field2.D[0].DB = 'ZZZ'; + + assertValues(tObj, assert); + assert.end(); + }, + + "nulls are ok": function(assert) { + var tObj = new ttypes.Complex({ + struct_field: null, + struct_list_field: null, + struct_set_field: null, + struct_map_field: null, + struct_nested_containers_field: null, + struct_nested_containers_field2: null + }); + var received = deserialize(serialize(tObj), ttypes.Complex); + assert.strictEqual(tObj.struct_field, null); + assert.ok(tObj !== received); + assert.deepEqual(tObj, received); + assert.end(); + }, + + "Can make list with objects": function(assert) { + var tObj = new ttypes.ComplexList({ + "struct_list_field": [new ttypes.Complex({})] + }); + var innerObj = tObj.struct_list_field[0]; + assert.ok(innerObj instanceof ttypes.Complex) + assert.strictEqual(innerObj.struct_field, null); + assert.strictEqual(innerObj.struct_list_field, null); + assert.strictEqual(innerObj.struct_set_field, null); + assert.strictEqual(innerObj.struct_map_field, null); + assert.strictEqual(innerObj.struct_nested_containers_field, null); + assert.strictEqual(innerObj.struct_nested_containers_field2, null); + assert.end(); + } + + }; + return cases; +} + + +function run(name, cases){ + Object.keys(cases).forEach(function(caseName) { + test(name + ': ' + caseName, cases[caseName]); + }); +} + +run('binary', createTestCases(serializeBinary, deserializeBinary)); +run('json', createTestCases(serializeJSON, deserializeJSON)); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/exceptions.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/exceptions.js new file mode 100644 index 00000000..c6f2e4d2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/exceptions.js @@ -0,0 +1,55 @@ +'use strict'; +var test = require('tape'); +var thrift = require('../lib/thrift/thrift.js'); +var InputBufferUnderrunError = require('../lib/thrift/input_buffer_underrun_error'); + +test('TApplicationException', function t(assert) { + var e = new thrift.TApplicationException(1, 'foo'); + assert.ok(e instanceof thrift.TApplicationException, 'is instanceof TApplicationException'); + assert.ok(e instanceof thrift.TException, 'is instanceof TException'); + assert.ok(e instanceof Error, 'is instanceof Error'); + assert.equal(typeof e.stack, 'string', 'has stack trace'); + assert.ok(/^TApplicationException: foo/.test(e.stack), 'Stack trace has correct error name and message'); + assert.ok(e.stack.indexOf('test/exceptions.js:7:11') !== -1, 'stack trace starts on correct line and column'); + assert.equal(e.name, 'TApplicationException', 'has function name TApplicationException'); + assert.equal(e.message, 'foo', 'has error message "foo"'); + assert.equal(e.type, 1, 'has type 1'); + assert.end(); +}); + +test('TException', function t(assert) { + var e = new thrift.TException('foo'); + assert.ok(e instanceof thrift.TException, 'is instanceof TException'); + assert.ok(e instanceof Error, 'is instanceof Error'); + assert.equal(typeof e.stack, 'string', 'has stack trace'); + assert.ok(/^TException: foo/.test(e.stack), 'Stack trace has correct error name and message'); + assert.ok(e.stack.indexOf('test/exceptions.js:21:11') !== -1, 'stack trace starts on correct line and column'); + assert.equal(e.name, 'TException', 'has function name TException'); + assert.equal(e.message, 'foo', 'has error message "foo"'); + assert.end(); +}); + +test('TProtocolException', function t(assert) { + var e = new thrift.TProtocolException(1, 'foo'); + assert.ok(e instanceof thrift.TProtocolException, 'is instanceof TProtocolException'); + assert.ok(e instanceof Error, 'is instanceof Error'); + assert.equal(typeof e.stack, 'string', 'has stack trace'); + assert.ok(/^TProtocolException: foo/.test(e.stack), 'Stack trace has correct error name and message'); + assert.ok(e.stack.indexOf('test/exceptions.js:33:11') !== -1, 'stack trace starts on correct line and column'); + assert.equal(e.name, 'TProtocolException', 'has function name TProtocolException'); + assert.equal(e.message, 'foo', 'has error message "foo"'); + assert.equal(e.type, 1, 'has type 1'); + assert.end(); +}); + +test('InputBufferUnderrunError', function t(assert) { + var e = new InputBufferUnderrunError('foo'); + assert.ok(e instanceof InputBufferUnderrunError, 'is instanceof InputBufferUnderrunError'); + assert.ok(e instanceof Error, 'is instanceof Error'); + assert.equal(typeof e.stack, 'string', 'has stack trace'); + assert.ok(/^InputBufferUnderrunError: foo/.test(e.stack), 'Stack trace has correct error name and message'); + assert.ok(e.stack.indexOf('test/exceptions.js:46:11') !== -1, 'stack trace starts on correct line and column'); + assert.equal(e.name, 'InputBufferUnderrunError', 'has function name InputBufferUnderrunError'); + assert.equal(e.message, 'foo', 'has error message "foo"'); + assert.end(); +}); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/helpers.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/helpers.js new file mode 100644 index 00000000..c850c46c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/helpers.js @@ -0,0 +1,13 @@ +'use strict'; +var thrift = require('../lib/thrift'); + +module.exports.transports = { + 'buffered': thrift.TBufferedTransport, + 'framed': thrift.TFramedTransport +}; + +module.exports.protocols = { + 'json': thrift.TJSONProtocol, + 'binary': thrift.TBinaryProtocol, + 'compact': thrift.TCompactProtocol +}; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.crt b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.crt new file mode 100644 index 00000000..8a5ef3c3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD +VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs +MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV +BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3 +DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy +MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU +MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh +cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ +bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ +GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6 +L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg +2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw +AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX +wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n +AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME +GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5 +U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm +T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD +1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I +p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO +r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0 +-----END CERTIFICATE----- diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.js new file mode 100644 index 00000000..6e5cdfa7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.js @@ -0,0 +1,101 @@ +#!/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. + */ + +var fs = require('fs'); +var path = require('path'); +var thrift = require('../lib/thrift'); +var program = require('commander'); +var helpers = require('./helpers'); + +var ThriftTest = require('./gen-nodejs/ThriftTest'); +var SecondService = require('./gen-nodejs/SecondService'); +var ThriftTestHandler = require('./test_handler').AsyncThriftTestHandler; +var ThriftTestHandlerPromise = require('./test_handler').SyncThriftTestHandler; +var ttypes = require('./gen-nodejs/ThriftTest_types'); + +program + .option('-p, --protocol ', 'Set thrift protocol (binary|json|compact)', 'binary') + .option('-t, --transport ', 'Set thrift transport (buffered|framed)', 'buffered') + .option('--ssl', 'use ssl transport') + .option('--port ', 'Set thrift server port', 9090) + .option('--promise', 'test with promise style functions') + .option('-t, --type ', 'Select server type (tcp|multiplex|http)', 'tcp') + .parse(process.argv); + +var port = program.port; +var type = program.type; +var ssl = program.ssl; +var promise = program.promise; + +var handler = program.promise ? ThriftTestHandler : ThriftTestHandlerPromise; + +var options = { + transport: helpers.transports[program.transport], + protocol: helpers.protocols[program.protocol] +}; + +if (type === 'http' || type ==='websocket') { + options.handler = handler; + options.processor = ThriftTest; + + options = { + services: { "/test": options }, + cors: { + '*': true + } + } +} + +if (type === 'multiplex') { + var SecondServiceHandler = { + secondtestString: function(thing, result) { + console.log('testString(\'' + thing + '\')'); + result(null, thing); + } + }; + + var processor = new thrift.MultiplexedProcessor(); + + processor.registerProcessor("ThriftTest", + new ThriftTest.Processor(ThriftTestHandler)); + + processor.registerProcessor("SecondService", + new SecondService.Processor(SecondServiceHandler)); + +} + +if (ssl) { + options.tls = { + key: fs.readFileSync(path.resolve(__dirname, 'server.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'server.crt')) + }; +} + +var server; +if (type === 'tcp') { + server = thrift.createServer(ThriftTest, handler, options); +} else if (type === 'multiplex') { + server = thrift.createMultiplexServer(processor, options); +} else if (type === 'http' || type === 'websocket') { + server = thrift.createWebServer(options); +} + +server.listen(port); diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.key b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.key new file mode 100644 index 00000000..263cfce5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR +tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy +SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H +jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB +GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk +u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm +trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt +fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR +xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD +bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck +Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq +57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9 +7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU +8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE +jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj +V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ +HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/ +LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3 +SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791 +Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS +y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc +ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW +cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE +c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1 +Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc +rdPUV9/uQkdx8VrShxlD8A== +-----END PRIVATE KEY----- diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/test-cases.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test-cases.js new file mode 100644 index 00000000..13722be8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test-cases.js @@ -0,0 +1,144 @@ +'use strict'; + +var ttypes = require('./gen-nodejs/ThriftTest_types'); +var Int64 = require('node-int64'); + +//all Languages in UTF-8 +/*jshint -W100 */ +var stringTest = module.exports.stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " + + "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " + + "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " + + "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, " + + "Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, " + + "Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, " + + "Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, " + + "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " + + "Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, " + + "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " + + "Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, " + + "ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, " + + "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " + + "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " + + "Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa " + + "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" + + "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " + + "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " + + "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " + + "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " + + "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, " + + "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " + + "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " + + "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " + + "Bân-lâm-gú, 粵語"; +/*jshint +W100 */ + +var specialCharacters = module.exports.specialCharacters = 'quote: \" backslash:' + + ' forwardslash-escaped: \/ ' + + ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + + ' now-all-of-them-together: "\\\/\b\n\r\t' + + ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><' + + ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ '; + +var mapTestInput = module.exports.mapTestInput = { + "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key", + "longValue":stringTest, stringTest:"long key" +}; + +var simple = [ + ['testVoid', undefined], + ['testString', 'Test'], + ['testString', ''], + ['testString', stringTest], + ['testString', specialCharacters], + ['testBool', true], + ['testBool', false], + ['testByte', 1], + ['testByte', 0], + ['testByte', -1], + ['testByte', -127], + ['testI32', -1], + ['testDouble', -5.2098523], + ['testDouble', 7.012052175215044], + ['testEnum', ttypes.Numberz.ONE], + ['testI64', 5], + ['testI64', -5], + ['testI64', 734359738368], + ['testI64', -734359738368], + ['testI64', new Int64(new Buffer([0, 0x20, 0, 0, 0, 0, 0, 1]))], // 2^53+1 + ['testI64', new Int64( + new Buffer([0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))], // -2^53-1 + ['testTypedef', 69] +] + +var mapout = {}; +for (var i = 0; i < 5; ++i) { + mapout[i] = i-10; +} + +var deep = [ + ['testList', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]], +]; + +var deepUnordered = [ + ['testMap', mapout], + ['testSet', [1,2,3]], + ['testStringMap', mapTestInput] +]; + +var out = new ttypes.Xtruct({ + string_thing: 'Zero', + byte_thing: 1, + i32_thing: -3, + i64_thing: 1000000 +}); + +var out2 = new ttypes.Xtruct2(); +out2.byte_thing = 1; +out2.struct_thing = out; +out2.i32_thing = 5; + +var crazy = new ttypes.Insanity({ + "userMap":{ "5":5, "8":8 }, + "xtructs":[new ttypes.Xtruct({ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }), new ttypes.Xtruct({ + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + })] +}); + +var crazy2 = new ttypes.Insanity({ + "userMap":{ "5":5, "8":8 }, + "xtructs":[{ + "string_thing":"Goodbye4", + "byte_thing":4, + "i32_thing":4, + "i64_thing":4 + }, { + "string_thing":"Hello2", + "byte_thing":2, + "i32_thing":2, + "i64_thing":2 + }] +}); + + +var insanity = { + "1":{ "2": crazy, "3": crazy }, + "2":{ "6":{ "userMap":{}, "xtructs":[] } } +}; + +module.exports.simple = simple; +module.exports.deep = deep; +module.exports.deepUnordered = deepUnordered; + +module.exports.out = out; +module.exports.out2 = out2; +module.exports.crazy = crazy; +module.exports.crazy2 = crazy2; +module.exports.insanity = insanity; diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/testAll.sh b/vendor/src/github.com/apache/thrift/lib/nodejs/test/testAll.sh new file mode 100644 index 00000000..38b284ac --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/testAll.sh @@ -0,0 +1,107 @@ +#! /bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +if [ -n "${1}" ]; then + COVER=${1}; +fi + +DIR="$( cd "$( dirname "$0" )" && pwd )" + +ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js" +RUNBROWSER="$DIR/../../../node_modules/run-browser/bin/cli.js" + +REPORT_PREFIX="${DIR}/../coverage/report" + +COUNT=0 + +export NODE_PATH="${DIR}:${DIR}/../lib:${NODE_PATH}" + +testServer() +{ + echo " Testing $1 Client/Server with protocol $2 and transport $3 $4"; + RET=0 + if [ -n "${COVER}" ]; then + ${ISTANBUL} cover ${DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint -- --type $1 -p $2 -t $3 $4 & + COUNT=$((COUNT+1)) + else + node ${DIR}/server.js --type $1 -p $2 -t $3 $4 & + fi + SERVERPID=$! + sleep 1 + if [ -n "${COVER}" ]; then + ${ISTANBUL} cover ${DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} -- --type $1 -p $2 -t $3 $4 || RET=1 + COUNT=$((COUNT+1)) + else + node ${DIR}/client.js --type $1 -p $2 -t $3 $4 || RET=1 + fi + kill -2 $SERVERPID || RET=1 + return $RET +} + +testBrowser() +{ + echo " Testing browser client with http server with json protocol and buffered transport"; + RET=0 + node ${DIR}/server.js --type http -p json -t buffered & + SERVERPID=$! + sleep 1 + ${RUNBROWSER} ${DIR}/browser_client.js --phantom || RET=1 + kill -2 $SERVERPID || RET=1 + return $RET +} + +TESTOK=0 + +#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 + +#unit tests + +node ${DIR}/binary.test.js || TESTOK=1 +node ${DIR}/deep-constructor.test.js || TESTOK=1 + +#integration tests + +for type in tcp multiplex websocket http +do + + for protocol in compact binary json + do + + for transport in buffered framed + do + testServer $type $protocol $transport || TESTOK=1 + testServer $type $protocol $transport --ssl || TESTOK=1 + testServer $type $protocol $transport --promise || TESTOK=1 + done + done +done + +# XHR only until phantomjs 2 is released. +testBrowser + +if [ -n "${COVER}" ]; then + ${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html + rm -r ${DIR}/../coverage/report*/* + rmdir ${DIR}/../coverage/report* +fi + +exit $TESTOK diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_driver.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_driver.js new file mode 100644 index 00000000..03ec5138 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_driver.js @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * 'License'); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + // This is the Node.js test driver for the standard Apache Thrift + // test service. The driver invokes every function defined in the + // Thrift Test service with a representative range of parameters. + // + // The ThriftTestDriver function requires a client object + // connected to a server hosting the Thrift Test service and + // supports an optional callback function which is called with + // a status message when the test is complete. + +var test = require('tape'); +//var assert = require('assert'); +var ttypes = require('./gen-nodejs/ThriftTest_types'); +var TException = require('thrift').Thrift.TException; +var Int64 = require('node-int64'); +var testCases = require('./test-cases'); + +exports.ThriftTestDriver = function(client, callback) { + + test('NodeJS Style Callback Client Tests', function(assert) { + + var checkRecursively = makeRecursiveCheck(assert); + + function makeAsserter(assertionFn) { + return function(c) { + var fnName = c[0]; + var expected = c[1]; + client[fnName](expected, function(err, actual) { + assert.error(err, fnName + ': no callback error'); + assertionFn(actual, expected, fnName); + }) + }; + } + + testCases.simple.forEach(makeAsserter(function(a, e, m){ + if (a instanceof Int64) { + var e64 = e instanceof Int64 ? e : new Int64(e); + assert.deepEqual(a.buffer, e64.buffer, m); + } else { + assert.equal(a, e, m); + } + })); + testCases.deep.forEach(makeAsserter(assert.deepEqual)); + testCases.deepUnordered.forEach(makeAsserter(makeUnorderedDeepEqual(assert))); + + var arr = []; + for (var i = 0; i < 256; ++i) { + arr[i] = 255 - i; + } + var buf = new Buffer(arr); + client.testBinary(buf, function(err, response) { + assert.error(err, 'testBinary: no callback error'); + assert.equal(response.length, 256, 'testBinary'); + assert.deepEqual(response, buf, 'testBinary(Buffer)'); + }); + var buf = new Buffer(arr); + client.testBinary(buf.toString('binary'), function(err, response) { + assert.error(err, 'testBinary: no callback error'); + assert.equal(response.length, 256, 'testBinary'); + assert.deepEqual(response, buf, 'testBinary(string)'); + }); + + client.testMapMap(42, function(err, response) { + var expected = { + "4": {"1":1, "2":2, "3":3, "4":4}, + "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1} + }; + assert.error(err, 'testMapMap: no callback error'); + assert.deepEqual(expected, response, 'testMapMap'); + }); + + client.testStruct(testCases.out, function(err, response) { + assert.error(err, 'testStruct: no callback error'); + checkRecursively(testCases.out, response, 'testStruct'); + }); + + client.testNest(testCases.out2, function(err, response) { + assert.error(err, 'testNest: no callback error'); + checkRecursively(testCases.out2, response, 'testNest'); + }); + + client.testInsanity(testCases.crazy, function(err, response) { + assert.error(err, 'testInsanity: no callback error'); + checkRecursively(testCases.insanity, response, 'testInsanity'); + }); + + client.testInsanity(testCases.crazy2, function(err, response) { + assert.error(err, 'testInsanity2: no callback error'); + checkRecursively(testCases.insanity, response, 'testInsanity2'); + }); + + client.testException('TException', function(err, response) { + assert.ok(err instanceof TException, 'testException: correct error type'); + assert.ok(!response, 'testException: no response'); + }); + + client.testException('Xception', function(err, response) { + assert.ok(err instanceof ttypes.Xception, 'testException: correct error type'); + assert.ok(!response, 'testException: no response'); + assert.equal(err.errorCode, 1001, 'testException: correct error code'); + assert.equal('Xception', err.message, 'testException: correct error message'); + }); + + client.testException('no Exception', function(err, response) { + assert.error(err, 'testException: no callback error'); + assert.ok(!response, 'testException: no response'); + }); + + client.testOneway(0, function(err, response) { + assert.fail('testOneway should not answer'); + }); + + checkOffByOne(function(done) { + client.testI32(-1, function(err, response) { + assert.error(err, 'checkOffByOne: no callback error'); + assert.equal(-1, response); + assert.end(); + done(); + }); + }, callback); + + }); +}; + +exports.ThriftTestDriverPromise = function(client, callback) { + + test('Q Promise Client Tests', function(assert) { + + var checkRecursively = makeRecursiveCheck(assert); + + function fail(msg) { + return function() { + assert.fail(msg); + } + } + + function makeAsserter(assertionFn) { + return function(c) { + var fnName = c[0]; + var expected = c[1]; + client[fnName](expected) + .then(function(actual) { + assertionFn(actual, expected, fnName); + }) + .fail(fail('fnName')); + }; + } + + testCases.simple.forEach(makeAsserter(function(a, e, m){ + if (a instanceof Int64) { + var e64 = e instanceof Int64 ? e : new Int64(e); + assert.deepEqual(a.buffer, e64.buffer, m); + } else { + assert.equal(a, e, m); + } + })); + testCases.deep.forEach(makeAsserter(assert.deepEqual)); + testCases.deepUnordered.forEach(makeAsserter(makeUnorderedDeepEqual(assert))); + + client.testStruct(testCases.out) + .then(function(response) { + checkRecursively(testCases.out, response, 'testStruct'); + }) + .fail(fail('testStruct')); + + client.testNest(testCases.out2) + .then(function(response) { + checkRecursively(testCases.out2, response, 'testNest'); + }) + .fail(fail('testNest')); + + client.testInsanity(testCases.crazy) + .then(function(response) { + checkRecursively(testCases.insanity, response, 'testInsanity'); + }) + .fail(fail('testInsanity')); + + client.testInsanity(testCases.crazy2) + .then(function(response) { + checkRecursively(testCases.insanity, response, 'testInsanity2'); + }) + .fail(fail('testInsanity2')); + + client.testException('TException') + .then(function(response) { + fail('testException: TException'); + }) + .fail(function(err) { + assert.ok(err instanceof TException); + }); + + client.testException('Xception') + .then(function(response) { + fail('testException: Xception'); + }) + .fail(function(err) { + assert.ok(err instanceof ttypes.Xception); + assert.equal(err.errorCode, 1001); + assert.equal('Xception', err.message); + }); + + client.testException('no Exception') + .then(function(response) { + assert.equal(undefined, response); //void + }) + .fail(fail('testException')); + + client.testOneway(0, fail('testOneway: should not answer')); + + checkOffByOne(function(done) { + client.testI32(-1) + .then(function(response) { + assert.equal(-1, response); + assert.end(); + done(); + }) + .fail(fail('checkOffByOne')); + }, callback); + }); +}; + + +// Helper Functions +// ========================================================= + +function makeRecursiveCheck(assert) { + + return function (map1, map2, msg) { + var equal = true; + + var equal = checkRecursively(map1, map2); + + assert.ok(equal, msg); + + // deepEqual doesn't work with fields using node-int64 + function checkRecursively(map1, map2) { + if (typeof map1 !== 'function' && typeof map2 !== 'function') { + if (!map1 || typeof map1 !== 'object') { + //Handle int64 types (which use node-int64 in Node.js JavaScript) + if ((typeof map1 === "number") && (typeof map2 === "object") && + (map2.buffer) && (map2.buffer instanceof Buffer) && (map2.buffer.length === 8)) { + var n = new Int64(map2.buffer); + return map1 === n.toNumber(); + } else { + return map1 == map2; + } + } else { + return Object.keys(map1).every(function(key) { + return checkRecursively(map1[key], map2[key]); + }); + } + } + } + } +} + +function checkOffByOne(done, callback) { + + var retry_limit = 30; + var retry_interval = 100; + var test_complete = false; + var retrys = 0; + + /** + * redo a simple test after the oneway to make sure we aren't "off by one" -- + * if the server treated oneway void like normal void, this next test will + * fail since it will get the void confirmation rather than the correct + * result. In this circumstance, the client will throw the exception: + * + * Because this is the last test against the server, when it completes + * the entire suite is complete by definition (the tests run serially). + */ + done(function() { + test_complete = true; + }); + + //We wait up to retry_limit * retry_interval for the test suite to complete + function TestForCompletion() { + if(test_complete && callback) { + callback("Server successfully tested!"); + } else { + if (++retrys < retry_limit) { + setTimeout(TestForCompletion, retry_interval); + } else if (callback) { + callback("Server test failed to complete after " + + (retry_limit * retry_interval / 1000) + " seconds"); + } + } + } + + setTimeout(TestForCompletion, retry_interval); +} + +function makeUnorderedDeepEqual(assert) { + return function(actual, expected, name) { + assert.equal(actual.length, expected.length, name); + for (var k in actual) { + var found = false; + for (var k2 in expected) { + if (actual[k] === expected[k2]) { + found = true; + } + } + if (!found) { + assert.fail('Unexpected value ' + actual[k] + ' with key ' + k); + } + } + }; +} diff --git a/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_handler.js b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_handler.js new file mode 100644 index 00000000..5c89f7ac --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/nodejs/test/test_handler.js @@ -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. + */ + +//This is the server side Node test handler for the standard +// Apache Thrift test service. + +var ttypes = require('./gen-nodejs/ThriftTest_types'); +var TException = require('thrift').Thrift.TException; + +function makeSyncHandler(label) { + return function(thing) { + //console.log(label + '(\'' + thing + '\')'); + return thing; + } +} + +var syncHandlers = { + testVoid: testVoid, + testMapMap: testMapMap, + testInsanity: testInsanity, + testMulti: testMulti, + testException: testException, + testMultiException: testMultiException, + testOneway: testOneway +}; + +function makeAsyncHandler(label) { + return function(thing, result) { + thing = syncHandlers[label](thing); + result(null, thing); + } +} + +var asyncHandlers = { + testVoid: testVoidAsync, + testMulti: testMultiAsync, + testException: testExceptionAsync, + testMultiException: testMultiExceptionAsync, + testOneway: testOnewayAsync +}; + +var identityHandlers = [ + 'testString', + 'testBool', + 'testByte', + 'testI32', + 'testI64', + 'testDouble', + 'testBinary', + 'testStruct', + 'testNest', + 'testMap', + 'testStringMap', + 'testSet', + 'testList', + 'testEnum', + 'testTypedef' +]; + +function testVoid() { + //console.log('testVoid()'); +} + +function testVoidAsync(result) { + result(testVoid()); +} + +function testMapMap(hello) { + //console.log('testMapMap(' + hello + ')'); + + var mapmap = []; + var pos = []; + var neg = []; + for (var i = 1; i < 5; i++) { + pos[i] = i; + neg[-i] = -i; + } + mapmap[4] = pos; + mapmap[-4] = neg; + + return mapmap; +} + +function testInsanity(argument) { + //console.log('testInsanity('); + //console.log(argument); + //console.log(')'); + + var first_map = []; + var second_map = []; + + first_map[ttypes.Numberz.TWO] = argument; + first_map[ttypes.Numberz.THREE] = argument; + + var looney = new ttypes.Insanity(); + second_map[ttypes.Numberz.SIX] = looney; + + var insane = []; + insane[1] = first_map; + insane[2] = second_map; + + //console.log('insane result:'); + //console.log(insane); + return insane; +} + +function testMulti(arg0, arg1, arg2, arg3, arg4, arg5) { + //console.log('testMulti()'); + + var hello = new ttypes.Xtruct(); + hello.string_thing = 'Hello2'; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = arg2; + return hello; +} + +function testMultiAsync(arg0, arg1, arg2, arg3, arg4, arg5, result) { + var hello = testMulti(arg0, arg1, arg2, arg3, arg4, arg5); + result(null, hello); +} + +function testException(arg) { + //console.log('testException('+arg+')'); + if (arg === 'Xception') { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + throw x; + } else if (arg === 'TException') { + throw new TException(arg); + } else { + return; + } +} + +function testExceptionAsync(arg, result) { + //console.log('testException('+arg+')'); + if (arg === 'Xception') { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = arg; + result(x); + } else if (arg === 'TException') { + result(new TException(arg)); + } else { + result(null); + } +} + +function testMultiException(arg0, arg1) { + //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')'); + if (arg0 === ('Xception')) { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + throw x; + } else if (arg0 === ('Xception2')) { + var x2 = new ttypes.Xception2(); + x2.errorCode = 2002; + x2.struct_thing = new ttypes.Xtruct(); + x2.struct_thing.string_thing = 'This is an Xception2'; + throw x2; + } + + var res = new ttypes.Xtruct(); + res.string_thing = arg1; + return res; +} + +function testMultiExceptionAsync(arg0, arg1, result) { + //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')'); + if (arg0 === ('Xception')) { + var x = new ttypes.Xception(); + x.errorCode = 1001; + x.message = 'This is an Xception'; + result(x); + } else if (arg0 === ('Xception2')) { + var x2 = new ttypes.Xception2(); + x2.errorCode = 2002; + x2.struct_thing = new ttypes.Xtruct(); + x2.struct_thing.string_thing = 'This is an Xception2'; + result(x2); + } else { + var res = new ttypes.Xtruct(); + res.string_thing = arg1; + result(null, res); + } +} + +function testOneway(sleepFor) { + //console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!'); +} + +function testOnewayAsync(sleepFor, result) { + testOneway(sleepFor); +} + +identityHandlers.forEach(function(label) { + syncHandlers[label] = makeSyncHandler(label); + asyncHandlers[label] = makeAsyncHandler(label); +}); + +['testMapMap', 'testInsanity'].forEach(function(label) { + asyncHandlers[label] = makeAsyncHandler(label); +}); + +exports.ThriftTestHandler = asyncHandlers; + +exports.AsyncThriftTestHandler = asyncHandlers; +exports.SyncThriftTestHandler = asyncHandlers; diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/Makefile b/vendor/src/github.com/apache/thrift/lib/ocaml/Makefile new file mode 100644 index 00000000..6abeee71 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/Makefile @@ -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. +# + +all: + cd src; make; cd .. +clean: + cd src; make clean; cd .. diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/OCamlMakefile b/vendor/src/github.com/apache/thrift/lib/ocaml/OCamlMakefile new file mode 100644 index 00000000..b0b9252c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/OCamlMakefile @@ -0,0 +1,1231 @@ +########################################################################### +# OCamlMakefile +# Copyright (C) 1999-2007 Markus Mottl +# +# For updates see: +# http://www.ocaml.info/home/ocaml_sources.html +# +########################################################################### + +# Modified by damien for .glade.ml compilation + +# Set these variables to the names of the sources to be processed and +# the result variable. Order matters during linkage! + +ifndef SOURCES + SOURCES := foo.ml +endif +export SOURCES + +ifndef RES_CLIB_SUF + RES_CLIB_SUF := _stubs +endif +export RES_CLIB_SUF + +ifndef RESULT + RESULT := foo +endif +export RESULT := $(strip $(RESULT)) + +export LIB_PACK_NAME + +ifndef DOC_FILES + DOC_FILES := $(filter %.mli, $(SOURCES)) +endif +export DOC_FILES +FIRST_DOC_FILE := $(firstword $(DOC_FILES)) + +export BCSUFFIX +export NCSUFFIX + +ifndef TOPSUFFIX + TOPSUFFIX := .top +endif +export TOPSUFFIX + +# Eventually set include- and library-paths, libraries to link, +# additional compilation-, link- and ocamlyacc-flags +# Path- and library information needs not be written with "-I" and such... +# Define THREADS if you need it, otherwise leave it unset (same for +# USE_CAMLP4)! + +export THREADS +export VMTHREADS +export ANNOTATE +export USE_CAMLP4 + +export INCDIRS +export LIBDIRS +export EXTLIBDIRS +export RESULTDEPS +export OCAML_DEFAULT_DIRS + +export LIBS +export CLIBS +export CFRAMEWORKS + +export OCAMLFLAGS +export OCAMLNCFLAGS +export OCAMLBCFLAGS + +export OCAMLLDFLAGS +export OCAMLNLDFLAGS +export OCAMLBLDFLAGS + +export OCAMLMKLIB_FLAGS + +ifndef OCAMLCPFLAGS + OCAMLCPFLAGS := a +endif +export OCAMLCPFLAGS + +ifndef DOC_DIR + DOC_DIR := doc +endif +export DOC_DIR + +export PPFLAGS + +export LFLAGS +export YFLAGS +export IDLFLAGS + +export OCAMLDOCFLAGS + +export OCAMLFIND_INSTFLAGS + +export DVIPSFLAGS + +export STATIC + +# Add a list of optional trash files that should be deleted by "make clean" +export TRASH + +ECHO := echo + +ifdef REALLY_QUIET + export REALLY_QUIET + ECHO := true + LFLAGS := $(LFLAGS) -q + YFLAGS := $(YFLAGS) -q +endif + +#################### variables depending on your OCaml-installation + +ifdef MINGW + export MINGW + WIN32 := 1 + CFLAGS_WIN32 := -mno-cygwin +endif +ifdef MSVC + export MSVC + WIN32 := 1 + ifndef STATIC + CPPFLAGS_WIN32 := -DCAML_DLL + endif + CFLAGS_WIN32 += -nologo + EXT_OBJ := obj + EXT_LIB := lib + ifeq ($(CC),gcc) + # work around GNU Make default value + ifdef THREADS + CC := cl -MT + else + CC := cl + endif + endif + ifeq ($(CXX),g++) + # work around GNU Make default value + CXX := $(CC) + endif + CFLAG_O := -Fo +endif +ifdef WIN32 + EXT_CXX := cpp + EXE := .exe +endif + +ifndef EXT_OBJ + EXT_OBJ := o +endif +ifndef EXT_LIB + EXT_LIB := a +endif +ifndef EXT_CXX + EXT_CXX := cc +endif +ifndef EXE + EXE := # empty +endif +ifndef CFLAG_O + CFLAG_O := -o # do not delete this comment (preserves trailing whitespace)! +endif + +export CC +export CXX +export CFLAGS +export CXXFLAGS +export LDFLAGS +export CPPFLAGS + +ifndef RPATH_FLAG + ifdef ELF_RPATH_FLAG + RPATH_FLAG := $(ELF_RPATH_FLAG) + else + RPATH_FLAG := -R + endif +endif +export RPATH_FLAG + +ifndef MSVC +ifndef PIC_CFLAGS + PIC_CFLAGS := -fPIC +endif +ifndef PIC_CPPFLAGS + PIC_CPPFLAGS := -DPIC +endif +endif + +export PIC_CFLAGS +export PIC_CPPFLAGS + +BCRESULT := $(addsuffix $(BCSUFFIX), $(RESULT)) +NCRESULT := $(addsuffix $(NCSUFFIX), $(RESULT)) +TOPRESULT := $(addsuffix $(TOPSUFFIX), $(RESULT)) + +ifndef OCAMLFIND + OCAMLFIND := ocamlfind +endif +export OCAMLFIND + +ifndef OCAMLC + OCAMLC := ocamlc +endif +export OCAMLC + +ifndef OCAMLOPT + OCAMLOPT := ocamlopt +endif +export OCAMLOPT + +ifndef OCAMLMKTOP + OCAMLMKTOP := ocamlmktop +endif +export OCAMLMKTOP + +ifndef OCAMLCP + OCAMLCP := ocamlcp +endif +export OCAMLCP + +ifndef OCAMLDEP + OCAMLDEP := ocamldep +endif +export OCAMLDEP + +ifndef OCAMLLEX + OCAMLLEX := ocamllex +endif +export OCAMLLEX + +ifndef OCAMLYACC + OCAMLYACC := ocamlyacc +endif +export OCAMLYACC + +ifndef OCAMLMKLIB + OCAMLMKLIB := ocamlmklib +endif +export OCAMLMKLIB + +ifndef OCAML_GLADECC + OCAML_GLADECC := lablgladecc2 +endif +export OCAML_GLADECC + +ifndef OCAML_GLADECC_FLAGS + OCAML_GLADECC_FLAGS := +endif +export OCAML_GLADECC_FLAGS + +ifndef CAMELEON_REPORT + CAMELEON_REPORT := report +endif +export CAMELEON_REPORT + +ifndef CAMELEON_REPORT_FLAGS + CAMELEON_REPORT_FLAGS := +endif +export CAMELEON_REPORT_FLAGS + +ifndef CAMELEON_ZOGGY + CAMELEON_ZOGGY := camlp4o pa_zog.cma pr_o.cmo +endif +export CAMELEON_ZOGGY + +ifndef CAMELEON_ZOGGY_FLAGS + CAMELEON_ZOGGY_FLAGS := +endif +export CAMELEON_ZOGGY_FLAGS + +ifndef OXRIDL + OXRIDL := oxridl +endif +export OXRIDL + +ifndef CAMLIDL + CAMLIDL := camlidl +endif +export CAMLIDL + +ifndef CAMLIDLDLL + CAMLIDLDLL := camlidldll +endif +export CAMLIDLDLL + +ifndef NOIDLHEADER + MAYBE_IDL_HEADER := -header +endif +export NOIDLHEADER + +export NO_CUSTOM + +ifndef CAMLP4 + CAMLP4 := camlp4 +endif +export CAMLP4 + +ifndef REAL_OCAMLFIND + ifdef PACKS + ifndef CREATE_LIB + ifdef THREADS + PACKS += threads + endif + endif + empty := + space := $(empty) $(empty) + comma := , + ifdef PREDS + PRE_OCAML_FIND_PREDICATES := $(subst $(space),$(comma),$(PREDS)) + PRE_OCAML_FIND_PACKAGES := $(subst $(space),$(comma),$(PACKS)) + OCAML_FIND_PREDICATES := -predicates $(PRE_OCAML_FIND_PREDICATES) + # OCAML_DEP_PREDICATES := -syntax $(PRE_OCAML_FIND_PREDICATES) + OCAML_FIND_PACKAGES := $(OCAML_FIND_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) + OCAML_DEP_PACKAGES := $(OCAML_DEP_PREDICATES) -package $(PRE_OCAML_FIND_PACKAGES) + else + OCAML_FIND_PACKAGES := -package $(subst $(space),$(comma),$(PACKS)) + OCAML_DEP_PACKAGES := + endif + OCAML_FIND_LINKPKG := -linkpkg + REAL_OCAMLFIND := $(OCAMLFIND) + endif +endif + +export OCAML_FIND_PACKAGES +export OCAML_DEP_PACKAGES +export OCAML_FIND_LINKPKG +export REAL_OCAMLFIND + +ifndef OCAMLDOC + OCAMLDOC := ocamldoc +endif +export OCAMLDOC + +ifndef LATEX + LATEX := latex +endif +export LATEX + +ifndef DVIPS + DVIPS := dvips +endif +export DVIPS + +ifndef PS2PDF + PS2PDF := ps2pdf +endif +export PS2PDF + +ifndef OCAMLMAKEFILE + OCAMLMAKEFILE := OCamlMakefile +endif +export OCAMLMAKEFILE + +ifndef OCAMLLIBPATH + OCAMLLIBPATH := \ + $(shell $(OCAMLC) 2>/dev/null -where || echo /usr/local/lib/ocaml) +endif +export OCAMLLIBPATH + +ifndef OCAML_LIB_INSTALL + OCAML_LIB_INSTALL := $(OCAMLLIBPATH)/contrib +endif +export OCAML_LIB_INSTALL + +########################################################################### + +#################### change following sections only if +#################### you know what you are doing! + +# delete target files when a build command fails +.PHONY: .DELETE_ON_ERROR +.DELETE_ON_ERROR: + +# for pedants using "--warn-undefined-variables" +export MAYBE_IDL +export REAL_RESULT +export CAMLIDLFLAGS +export THREAD_FLAG +export RES_CLIB +export MAKEDLL +export ANNOT_FLAG +export C_OXRIDL +export SUBPROJS +export CFLAGS_WIN32 +export CPPFLAGS_WIN32 + +INCFLAGS := + +SHELL := /bin/sh + +MLDEPDIR := ._d +BCDIDIR := ._bcdi +NCDIDIR := ._ncdi + +FILTER_EXTNS := %.mli %.ml %.mll %.mly %.idl %.oxridl %.c %.m %.$(EXT_CXX) %.rep %.zog %.glade + +FILTERED := $(filter $(FILTER_EXTNS), $(SOURCES)) +SOURCE_DIRS := $(filter-out ./, $(sort $(dir $(FILTERED)))) + +FILTERED_REP := $(filter %.rep, $(FILTERED)) +DEP_REP := $(FILTERED_REP:%.rep=$(MLDEPDIR)/%.d) +AUTO_REP := $(FILTERED_REP:.rep=.ml) + +FILTERED_ZOG := $(filter %.zog, $(FILTERED)) +DEP_ZOG := $(FILTERED_ZOG:%.zog=$(MLDEPDIR)/%.d) +AUTO_ZOG := $(FILTERED_ZOG:.zog=.ml) + +FILTERED_GLADE := $(filter %.glade, $(FILTERED)) +DEP_GLADE := $(FILTERED_GLADE:%.glade=$(MLDEPDIR)/%.d) +AUTO_GLADE := $(FILTERED_GLADE:.glade=.ml) + +FILTERED_ML := $(filter %.ml, $(FILTERED)) +DEP_ML := $(FILTERED_ML:%.ml=$(MLDEPDIR)/%.d) + +FILTERED_MLI := $(filter %.mli, $(FILTERED)) +DEP_MLI := $(FILTERED_MLI:.mli=.di) + +FILTERED_MLL := $(filter %.mll, $(FILTERED)) +DEP_MLL := $(FILTERED_MLL:%.mll=$(MLDEPDIR)/%.d) +AUTO_MLL := $(FILTERED_MLL:.mll=.ml) + +FILTERED_MLY := $(filter %.mly, $(FILTERED)) +DEP_MLY := $(FILTERED_MLY:%.mly=$(MLDEPDIR)/%.d) $(FILTERED_MLY:.mly=.di) +AUTO_MLY := $(FILTERED_MLY:.mly=.mli) $(FILTERED_MLY:.mly=.ml) + +FILTERED_IDL := $(filter %.idl, $(FILTERED)) +DEP_IDL := $(FILTERED_IDL:%.idl=$(MLDEPDIR)/%.d) $(FILTERED_IDL:.idl=.di) +C_IDL := $(FILTERED_IDL:%.idl=%_stubs.c) +ifndef NOIDLHEADER + C_IDL += $(FILTERED_IDL:.idl=.h) +endif +OBJ_C_IDL := $(FILTERED_IDL:%.idl=%_stubs.$(EXT_OBJ)) +AUTO_IDL := $(FILTERED_IDL:.idl=.mli) $(FILTERED_IDL:.idl=.ml) $(C_IDL) + +FILTERED_OXRIDL := $(filter %.oxridl, $(FILTERED)) +DEP_OXRIDL := $(FILTERED_OXRIDL:%.oxridl=$(MLDEPDIR)/%.d) $(FILTERED_OXRIDL:.oxridl=.di) +AUTO_OXRIDL := $(FILTERED_OXRIDL:.oxridl=.mli) $(FILTERED_OXRIDL:.oxridl=.ml) $(C_OXRIDL) + +FILTERED_C_CXX := $(filter %.c %.m %.$(EXT_CXX), $(FILTERED)) +OBJ_C_CXX := $(FILTERED_C_CXX:.c=.$(EXT_OBJ)) +OBJ_C_CXX := $(OBJ_C_CXX:.m=.$(EXT_OBJ)) +OBJ_C_CXX := $(OBJ_C_CXX:.$(EXT_CXX)=.$(EXT_OBJ)) + +PRE_TARGETS += $(AUTO_MLL) $(AUTO_MLY) $(AUTO_IDL) $(AUTO_OXRIDL) $(AUTO_ZOG) $(AUTO_REP) $(AUTO_GLADE) + +ALL_DEPS := $(DEP_ML) $(DEP_MLI) $(DEP_MLL) $(DEP_MLY) $(DEP_IDL) $(DEP_OXRIDL) $(DEP_ZOG) $(DEP_REP) $(DEP_GLADE) + +MLDEPS := $(filter %.d, $(ALL_DEPS)) +MLIDEPS := $(filter %.di, $(ALL_DEPS)) +BCDEPIS := $(MLIDEPS:%.di=$(BCDIDIR)/%.di) +NCDEPIS := $(MLIDEPS:%.di=$(NCDIDIR)/%.di) + +ALLML := $(filter %.mli %.ml %.mll %.mly %.idl %.oxridl %.rep %.zog %.glade, $(FILTERED)) + +IMPLO_INTF := $(ALLML:%.mli=%.mli.__) +IMPLO_INTF := $(foreach file, $(IMPLO_INTF), \ + $(basename $(file)).cmi $(basename $(file)).cmo) +IMPLO_INTF := $(filter-out %.mli.cmo, $(IMPLO_INTF)) +IMPLO_INTF := $(IMPLO_INTF:%.mli.cmi=%.cmi) + +IMPLX_INTF := $(IMPLO_INTF:.cmo=.cmx) + +INTF := $(filter %.cmi, $(IMPLO_INTF)) +IMPL_CMO := $(filter %.cmo, $(IMPLO_INTF)) +IMPL_CMX := $(IMPL_CMO:.cmo=.cmx) +IMPL_ASM := $(IMPL_CMO:.cmo=.asm) +IMPL_S := $(IMPL_CMO:.cmo=.s) + +OBJ_LINK := $(OBJ_C_IDL) $(OBJ_C_CXX) +OBJ_FILES := $(IMPL_CMO:.cmo=.$(EXT_OBJ)) $(OBJ_LINK) + +EXECS := $(addsuffix $(EXE), \ + $(sort $(TOPRESULT) $(BCRESULT) $(NCRESULT))) +ifdef WIN32 + EXECS += $(BCRESULT).dll $(NCRESULT).dll +endif + +CLIB_BASE := $(RESULT)$(RES_CLIB_SUF) +ifneq ($(strip $(OBJ_LINK)),) + RES_CLIB := lib$(CLIB_BASE).$(EXT_LIB) +endif + +ifdef WIN32 +DLLSONAME := $(CLIB_BASE).dll +else +DLLSONAME := dll$(CLIB_BASE).so +endif + +NONEXECS := $(INTF) $(IMPL_CMO) $(IMPL_CMX) $(IMPL_ASM) $(IMPL_S) \ + $(OBJ_FILES) $(PRE_TARGETS) $(BCRESULT).cma $(NCRESULT).cmxa \ + $(NCRESULT).$(EXT_LIB) $(BCRESULT).cmi $(BCRESULT).cmo \ + $(NCRESULT).cmi $(NCRESULT).cmx $(NCRESULT).o \ + $(RES_CLIB) $(IMPL_CMO:.cmo=.annot) \ + $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(LIB_PACK_NAME).cmx $(LIB_PACK_NAME).o + +ifndef STATIC + NONEXECS += $(DLLSONAME) +endif + +ifndef LIBINSTALL_FILES + LIBINSTALL_FILES := $(RESULT).mli $(RESULT).cmi $(RESULT).cma \ + $(RESULT).cmxa $(RESULT).$(EXT_LIB) $(RES_CLIB) + ifndef STATIC + ifneq ($(strip $(OBJ_LINK)),) + LIBINSTALL_FILES += $(DLLSONAME) + endif + endif +endif + +export LIBINSTALL_FILES + +ifdef WIN32 + # some extra stuff is created while linking DLLs + NONEXECS += $(BCRESULT).$(EXT_LIB) $(BCRESULT).exp $(NCRESULT).exp $(CLIB_BASE).exp $(CLIB_BASE).lib +endif + +TARGETS := $(EXECS) $(NONEXECS) + +# If there are IDL-files +ifneq ($(strip $(FILTERED_IDL)),) + MAYBE_IDL := -cclib -lcamlidl +endif + +ifdef USE_CAMLP4 + CAMLP4PATH := \ + $(shell $(CAMLP4) -where 2>/dev/null || echo /usr/local/lib/camlp4) + INCFLAGS := -I $(CAMLP4PATH) + CINCFLAGS := -I$(CAMLP4PATH) +endif + +DINCFLAGS := $(INCFLAGS) $(SOURCE_DIRS:%=-I %) $(OCAML_DEFAULT_DIRS:%=-I %) +INCFLAGS := $(DINCFLAGS) $(INCDIRS:%=-I %) +CINCFLAGS += $(SOURCE_DIRS:%=-I%) $(INCDIRS:%=-I%) $(OCAML_DEFAULT_DIRS:%=-I%) + +ifndef MSVC + CLIBFLAGS += $(SOURCE_DIRS:%=-L%) $(LIBDIRS:%=-L%) \ + $(EXTLIBDIRS:%=-L%) $(OCAML_DEFAULT_DIRS:%=-L%) + + ifeq ($(ELF_RPATH), yes) + CLIBFLAGS += $(EXTLIBDIRS:%=-Wl,$(RPATH_FLAG)%) + endif +endif + +ifndef PROFILING + INTF_OCAMLC := $(OCAMLC) +else + ifndef THREADS + INTF_OCAMLC := $(OCAMLCP) -p $(OCAMLCPFLAGS) + else + # OCaml does not support profiling byte code + # with threads (yet), therefore we force an error. + ifndef REAL_OCAMLC + $(error Profiling of multithreaded byte code not yet supported by OCaml) + endif + INTF_OCAMLC := $(OCAMLC) + endif +endif + +ifndef MSVC + COMMON_LDFLAGS := $(LDFLAGS:%=-ccopt %) $(SOURCE_DIRS:%=-ccopt -L%) \ + $(LIBDIRS:%=-ccopt -L%) $(EXTLIBDIRS:%=-ccopt -L%) \ + $(EXTLIBDIRS:%=-ccopt -Wl $(OCAML_DEFAULT_DIRS:%=-ccopt -L%)) + + ifeq ($(ELF_RPATH),yes) + COMMON_LDFLAGS += $(EXTLIBDIRS:%=-ccopt -Wl,$(RPATH_FLAG)%) + endif +else + COMMON_LDFLAGS := -ccopt "/link -NODEFAULTLIB:LIBC $(LDFLAGS:%=%) $(SOURCE_DIRS:%=-LIBPATH:%) \ + $(LIBDIRS:%=-LIBPATH:%) $(EXTLIBDIRS:%=-LIBPATH:%) \ + $(OCAML_DEFAULT_DIRS:%=-LIBPATH:%) " +endif + +CLIBS_OPTS := $(CLIBS:%=-cclib -l%) $(CFRAMEWORKS:%=-cclib '-framework %') +ifdef MSVC + ifndef STATIC + # MSVC libraries do not have 'lib' prefix + CLIBS_OPTS := $(CLIBS:%=-cclib %.lib) + endif +endif + +ifneq ($(strip $(OBJ_LINK)),) + ifdef CREATE_LIB + OBJS_LIBS := -cclib -l$(CLIB_BASE) $(CLIBS_OPTS) $(MAYBE_IDL) + else + OBJS_LIBS := $(OBJ_LINK) $(CLIBS_OPTS) $(MAYBE_IDL) + endif +else + OBJS_LIBS := $(CLIBS_OPTS) $(MAYBE_IDL) +endif + +# If we have to make byte-code +ifndef REAL_OCAMLC + BYTE_OCAML := y + + # EXTRADEPS is added dependencies we have to insert for all + # executable files we generate. Ideally it should be all of the + # libraries we use, but it's hard to find the ones that get searched on + # the path since I don't know the paths built into the compiler, so + # just include the ones with slashes in their names. + EXTRADEPS := $(addsuffix .cma,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) + SPECIAL_OCAMLFLAGS := $(OCAMLBCFLAGS) + + REAL_OCAMLC := $(INTF_OCAMLC) + + REAL_IMPL := $(IMPL_CMO) + REAL_IMPL_INTF := $(IMPLO_INTF) + IMPL_SUF := .cmo + + DEPFLAGS := + MAKE_DEPS := $(MLDEPS) $(BCDEPIS) + + ifdef CREATE_LIB + override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) + override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) + ifndef STATIC + ifneq ($(strip $(OBJ_LINK)),) + MAKEDLL := $(DLLSONAME) + ALL_LDFLAGS := -dllib $(DLLSONAME) + endif + endif + endif + + ifndef NO_CUSTOM + ifneq "$(strip $(OBJ_LINK) $(THREADS) $(MAYBE_IDL) $(CLIBS) $(CFRAMEWORKS))" "" + ALL_LDFLAGS += -custom + endif + endif + + ALL_LDFLAGS += $(INCFLAGS) $(OCAMLLDFLAGS) $(OCAMLBLDFLAGS) \ + $(COMMON_LDFLAGS) $(LIBS:%=%.cma) + CAMLIDLDLLFLAGS := + + ifdef THREADS + ifdef VMTHREADS + THREAD_FLAG := -vmthread + else + THREAD_FLAG := -thread + endif + ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) + ifndef CREATE_LIB + ifndef REAL_OCAMLFIND + ALL_LDFLAGS := unix.cma threads.cma $(ALL_LDFLAGS) + endif + endif + endif + +# we have to make native-code +else + EXTRADEPS := $(addsuffix .cmxa,$(foreach i,$(LIBS),$(if $(findstring /,$(i)),$(i)))) + ifndef PROFILING + SPECIAL_OCAMLFLAGS := $(OCAMLNCFLAGS) + PLDFLAGS := + else + SPECIAL_OCAMLFLAGS := -p $(OCAMLNCFLAGS) + PLDFLAGS := -p + endif + + REAL_IMPL := $(IMPL_CMX) + REAL_IMPL_INTF := $(IMPLX_INTF) + IMPL_SUF := .cmx + + override CPPFLAGS := -DNATIVE_CODE $(CPPFLAGS) + + DEPFLAGS := -native + MAKE_DEPS := $(MLDEPS) $(NCDEPIS) + + ALL_LDFLAGS := $(PLDFLAGS) $(INCFLAGS) $(OCAMLLDFLAGS) \ + $(OCAMLNLDFLAGS) $(COMMON_LDFLAGS) + CAMLIDLDLLFLAGS := -opt + + ifndef CREATE_LIB + ALL_LDFLAGS += $(LIBS:%=%.cmxa) + else + override CFLAGS := $(PIC_CFLAGS) $(CFLAGS) + override CPPFLAGS := $(PIC_CPPFLAGS) $(CPPFLAGS) + endif + + ifdef THREADS + THREAD_FLAG := -thread + ALL_LDFLAGS := $(THREAD_FLAG) $(ALL_LDFLAGS) + ifndef CREATE_LIB + ifndef REAL_OCAMLFIND + ALL_LDFLAGS := unix.cmxa threads.cmxa $(ALL_LDFLAGS) + endif + endif + endif +endif + +export MAKE_DEPS + +ifdef ANNOTATE + ANNOT_FLAG := -dtypes +else +endif + +ALL_OCAMLCFLAGS := $(THREAD_FLAG) $(ANNOT_FLAG) $(OCAMLFLAGS) \ + $(INCFLAGS) $(SPECIAL_OCAMLFLAGS) + +ifdef make_deps + -include $(MAKE_DEPS) + PRE_TARGETS := +endif + +########################################################################### +# USER RULES + +# Call "OCamlMakefile QUIET=" to get rid of all of the @'s. +QUIET=@ + +# generates byte-code (default) +byte-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ + REAL_RESULT="$(BCRESULT)" make_deps=yes +bc: byte-code + +byte-code-nolink: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ + REAL_RESULT="$(BCRESULT)" make_deps=yes +bcnl: byte-code-nolink + +top: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(TOPRESULT) \ + REAL_RESULT="$(BCRESULT)" make_deps=yes + +# generates native-code + +native-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + make_deps=yes +nc: native-code + +native-code-nolink: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + make_deps=yes +ncnl: native-code-nolink + +# generates byte-code libraries +byte-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(BCRESULT).cma \ + REAL_RESULT="$(BCRESULT)" \ + CREATE_LIB=yes \ + make_deps=yes +bcl: byte-code-library + +# generates native-code libraries +native-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(NCRESULT).cmxa \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + CREATE_LIB=yes \ + make_deps=yes +ncl: native-code-library + +ifdef WIN32 +# generates byte-code dll +byte-code-dll: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(BCRESULT).dll \ + REAL_RESULT="$(BCRESULT)" \ + make_deps=yes +bcd: byte-code-dll + +# generates native-code dll +native-code-dll: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(NCRESULT).dll \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + make_deps=yes +ncd: native-code-dll +endif + +# generates byte-code with debugging information +debug-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ + REAL_RESULT="$(BCRESULT)" make_deps=yes \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dc: debug-code + +debug-code-nolink: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ + REAL_RESULT="$(BCRESULT)" make_deps=yes \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dcnl: debug-code-nolink + +# generates byte-code with debugging information (native code) +debug-native-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ + REAL_RESULT="$(NCRESULT)" make_deps=yes \ + REAL_OCAMLC="$(OCAMLOPT)" \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dnc: debug-native-code + +debug-native-code-nolink: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) nolink \ + REAL_RESULT="$(NCRESULT)" make_deps=yes \ + REAL_OCAMLC="$(OCAMLOPT)" \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dncnl: debug-native-code-nolink + +# generates byte-code libraries with debugging information +debug-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(BCRESULT).cma \ + REAL_RESULT="$(BCRESULT)" make_deps=yes \ + CREATE_LIB=yes \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dcl: debug-code-library + +# generates byte-code libraries with debugging information (native code) +debug-native-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(NCRESULT).cma \ + REAL_RESULT="$(NCRESULT)" make_deps=yes \ + REAL_OCAMLC="$(OCAMLOPT)" \ + CREATE_LIB=yes \ + OCAMLFLAGS="-g $(OCAMLFLAGS)" \ + OCAMLLDFLAGS="-g $(OCAMLLDFLAGS)" +dncl: debug-native-code-library + +# generates byte-code for profiling +profiling-byte-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT) \ + REAL_RESULT="$(BCRESULT)" PROFILING="y" \ + make_deps=yes +pbc: profiling-byte-code + +# generates native-code + +profiling-native-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(NCRESULT) \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + PROFILING="y" \ + make_deps=yes +pnc: profiling-native-code + +# generates byte-code libraries +profiling-byte-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(BCRESULT).cma \ + REAL_RESULT="$(BCRESULT)" PROFILING="y" \ + CREATE_LIB=yes \ + make_deps=yes +pbcl: profiling-byte-code-library + +# generates native-code libraries +profiling-native-code-library: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(RES_CLIB) $(NCRESULT).cmxa \ + REAL_RESULT="$(NCRESULT)" PROFILING="y" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + CREATE_LIB=yes \ + make_deps=yes +pncl: profiling-native-code-library + +# packs byte-code objects +pack-byte-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) $(BCRESULT).cmo \ + REAL_RESULT="$(BCRESULT)" \ + PACK_LIB=yes make_deps=yes +pabc: pack-byte-code + +# packs native-code objects +pack-native-code: $(PRE_TARGETS) + $(QUIET)$(MAKE) -r -f $(OCAMLMAKEFILE) \ + $(NCRESULT).cmx $(NCRESULT).o \ + REAL_RESULT="$(NCRESULT)" \ + REAL_OCAMLC="$(OCAMLOPT)" \ + PACK_LIB=yes make_deps=yes +panc: pack-native-code + +# generates HTML-documentation +htdoc: $(DOC_DIR)/$(RESULT)/html/index.html + +# generates Latex-documentation +ladoc: $(DOC_DIR)/$(RESULT)/latex/doc.tex + +# generates PostScript-documentation +psdoc: $(DOC_DIR)/$(RESULT)/latex/doc.ps + +# generates PDF-documentation +pdfdoc: $(DOC_DIR)/$(RESULT)/latex/doc.pdf + +# generates all supported forms of documentation +doc: htdoc ladoc psdoc pdfdoc + +########################################################################### +# LOW LEVEL RULES + +$(REAL_RESULT): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) $(RESULTDEPS) + $(REAL_OCAMLFIND) $(REAL_OCAMLC) \ + $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ + $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ + $(REAL_IMPL) + +nolink: $(REAL_IMPL_INTF) $(OBJ_LINK) + +ifdef WIN32 +$(REAL_RESULT).dll: $(REAL_IMPL_INTF) $(OBJ_LINK) + $(CAMLIDLDLL) $(CAMLIDLDLLFLAGS) $(OBJ_LINK) $(CLIBS) \ + -o $@ $(REAL_IMPL) +endif + +%$(TOPSUFFIX): $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) + $(REAL_OCAMLFIND) $(OCAMLMKTOP) \ + $(OCAML_FIND_PACKAGES) $(OCAML_FIND_LINKPKG) \ + $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@$(EXE) \ + $(REAL_IMPL) + +.SUFFIXES: .mli .ml .cmi .cmo .cmx .cma .cmxa .$(EXT_OBJ) \ + .mly .di .d .$(EXT_LIB) .idl %.oxridl .c .m .$(EXT_CXX) .h .so \ + .rep .zog .glade + +ifndef STATIC +ifdef MINGW +$(DLLSONAME): $(OBJ_LINK) + $(CC) $(CFLAGS) $(CFLAGS_WIN32) $(OBJ_LINK) -shared -o $@ \ + -Wl,--whole-archive $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/lib%.a))) \ + $(OCAMLLIBPATH)/ocamlrun.a \ + -Wl,--export-all-symbols \ + -Wl,--no-whole-archive +else +ifdef MSVC +$(DLLSONAME): $(OBJ_LINK) + link /NOLOGO /DLL /OUT:$@ $(OBJ_LINK) \ + $(wildcard $(foreach dir,$(LIBDIRS),$(CLIBS:%=$(dir)/%.lib))) \ + $(OCAMLLIBPATH)/ocamlrun.lib + +else +$(DLLSONAME): $(OBJ_LINK) + $(OCAMLMKLIB) $(INCFLAGS) $(CLIBFLAGS) \ + -o $(CLIB_BASE) $(OBJ_LINK) $(CLIBS:%=-l%) $(CFRAMEWORKS:%=-framework %) \ + $(OCAMLMKLIB_FLAGS) +endif +endif +endif + +ifndef LIB_PACK_NAME +$(RESULT).cma: $(REAL_IMPL_INTF) $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) + $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) + +$(RESULT).cmxa $(RESULT).$(EXT_LIB): $(REAL_IMPL_INTF) $(EXTRADEPS) $(RESULTDEPS) + $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(REAL_IMPL) +else +ifdef BYTE_OCAML +$(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo: $(REAL_IMPL_INTF) + $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack -o $(LIB_PACK_NAME).cmo $(OCAMLLDFLAGS) $(REAL_IMPL) +else +$(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx: $(REAL_IMPL_INTF) + $(REAL_OCAMLFIND) $(OCAMLOPT) -pack -o $(LIB_PACK_NAME).cmx $(OCAMLLDFLAGS) $(REAL_IMPL) +endif + +$(RESULT).cma: $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmo $(MAKEDLL) $(EXTRADEPS) $(RESULTDEPS) + $(REAL_OCAMLFIND) $(REAL_OCAMLC) -a $(ALL_LDFLAGS) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmo + +$(RESULT).cmxa $(RESULT).$(EXT_LIB): $(LIB_PACK_NAME).cmi $(LIB_PACK_NAME).cmx $(EXTRADEPS) $(RESULTDEPS) + $(REAL_OCAMLFIND) $(OCAMLOPT) -a $(filter-out -custom, $(ALL_LDFLAGS)) $(OBJS_LIBS) -o $@ $(LIB_PACK_NAME).cmx +endif + +$(RES_CLIB): $(OBJ_LINK) +ifndef MSVC + ifneq ($(strip $(OBJ_LINK)),) + $(AR) rcs $@ $(OBJ_LINK) + endif +else + ifneq ($(strip $(OBJ_LINK)),) + lib -nologo -debugtype:cv -out:$(RES_CLIB) $(OBJ_LINK) + endif +endif + +.mli.cmi: $(EXTRADEPS) + $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ + if [ -z "$$pp" ]; then \ + $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c $(THREAD_FLAG) $(ANNOT_FLAG) \ + $(OCAMLFLAGS) $(INCFLAGS) $<; \ + $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c $(THREAD_FLAG) $(ANNOT_FLAG) \ + $(OCAMLFLAGS) $(INCFLAGS) $<; \ + else \ + $(ECHO) $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c -pp \"$$pp $(PPFLAGS)\" $(THREAD_FLAG) $(ANNOT_FLAG) \ + $(OCAMLFLAGS) $(INCFLAGS) $<; \ + $(REAL_OCAMLFIND) $(INTF_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c -pp "$$pp $(PPFLAGS)" $(THREAD_FLAG) $(ANNOT_FLAG) \ + $(OCAMLFLAGS) $(INCFLAGS) $<; \ + fi + +.ml.cmi .ml.$(EXT_OBJ) .ml.cmx .ml.cmo: $(EXTRADEPS) + $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ + if [ -z "$$pp" ]; then \ + $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c $(ALL_OCAMLCFLAGS) $<; \ + $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c $(ALL_OCAMLCFLAGS) $<; \ + else \ + $(ECHO) $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c -pp \"$$pp $(PPFLAGS)\" $(ALL_OCAMLCFLAGS) $<; \ + $(REAL_OCAMLFIND) $(REAL_OCAMLC) $(OCAML_FIND_PACKAGES) \ + -c -pp "$$pp $(PPFLAGS)" $(ALL_OCAMLCFLAGS) $<; \ + fi + +ifdef PACK_LIB +$(REAL_RESULT).cmo $(REAL_RESULT).cmx $(REAL_RESULT).o: $(REAL_IMPL_INTF) $(OBJ_LINK) $(EXTRADEPS) + $(REAL_OCAMLFIND) $(REAL_OCAMLC) -pack $(ALL_LDFLAGS) \ + $(OBJS_LIBS) -o $@ $(REAL_IMPL) +endif + +.PRECIOUS: %.ml +%.ml: %.mll + $(OCAMLLEX) $(LFLAGS) $< + +.PRECIOUS: %.ml %.mli +%.ml %.mli: %.mly + $(OCAMLYACC) $(YFLAGS) $< + $(QUIET)pp=`sed -n -e 's/.*(\*pp \([^*]*\) \*).*/\1/p;q' $<`; \ + if [ ! -z "$$pp" ]; then \ + mv $*.ml $*.ml.temporary; \ + echo "(*pp $$pp $(PPFLAGS)*)" > $*.ml; \ + cat $*.ml.temporary >> $*.ml; \ + rm $*.ml.temporary; \ + mv $*.mli $*.mli.temporary; \ + echo "(*pp $$pp $(PPFLAGS)*)" > $*.mli; \ + cat $*.mli.temporary >> $*.mli; \ + rm $*.mli.temporary; \ + fi + + +.PRECIOUS: %.ml +%.ml: %.rep + $(CAMELEON_REPORT) $(CAMELEON_REPORT_FLAGS) -gen $< + +.PRECIOUS: %.ml +%.ml: %.zog + $(CAMELEON_ZOGGY) $(CAMELEON_ZOGGY_FLAGS) -impl $< > $@ + +.PRECIOUS: %.ml +%.ml: %.glade + $(OCAML_GLADECC) $(OCAML_GLADECC_FLAGS) $< > $@ + +.PRECIOUS: %.ml %.mli +%.ml %.mli: %.oxridl + $(OXRIDL) $< + +.PRECIOUS: %.ml %.mli %_stubs.c %.h +%.ml %.mli %_stubs.c %.h: %.idl + $(CAMLIDL) $(MAYBE_IDL_HEADER) $(IDLFLAGS) \ + $(CAMLIDLFLAGS) $< + $(QUIET)if [ $(NOIDLHEADER) ]; then touch $*.h; fi + +.c.$(EXT_OBJ): + $(OCAMLC) -c -cc "$(CC)" -ccopt "$(CFLAGS) \ + $(CPPFLAGS) $(CPPFLAGS_WIN32) \ + $(CFLAGS_WIN32) $(CINCFLAGS) $(CFLAG_O)$@ " $< + +.m.$(EXT_OBJ): + $(CC) -c $(CFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ + -I'$(OCAMLLIBPATH)' \ + $< $(CFLAG_O)$@ + +.$(EXT_CXX).$(EXT_OBJ): + $(CXX) -c $(CXXFLAGS) $(CINCFLAGS) $(CPPFLAGS) \ + -I'$(OCAMLLIBPATH)' \ + $< $(CFLAG_O)$@ + +$(MLDEPDIR)/%.d: %.ml + $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi + $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ + if [ -z "$$pp" ]; then \ + $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ + $(DINCFLAGS) $< \> $@; \ + $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ + $(DINCFLAGS) $< > $@; \ + else \ + $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ + -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ + $(REAL_OCAMLFIND) $(OCAMLDEP) $(OCAML_DEP_PACKAGES) \ + -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ + fi + +$(BCDIDIR)/%.di $(NCDIDIR)/%.di: %.mli + $(QUIET)if [ ! -d $(@D) ]; then mkdir -p $(@D); fi + $(QUIET)pp=`sed -n -e '/^#/d' -e 's/(\*pp \([^*]*\) \*)/\1/p;q' $<`; \ + if [ -z "$$pp" ]; then \ + $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< \> $@; \ + $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) $(DINCFLAGS) $< > $@; \ + else \ + $(ECHO) $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ + -pp \"$$pp $(PPFLAGS)\" $(DINCFLAGS) $< \> $@; \ + $(REAL_OCAMLFIND) $(OCAMLDEP) $(DEPFLAGS) \ + -pp "$$pp $(PPFLAGS)" $(DINCFLAGS) $< > $@; \ + fi + +$(DOC_DIR)/$(RESULT)/html: + mkdir -p $@ + +$(DOC_DIR)/$(RESULT)/html/index.html: $(DOC_DIR)/$(RESULT)/html $(DOC_FILES) + rm -rf $3.80 *** + +--------------------------------------------------------------------------- + + Contents of this distribution + +Changes - guess what? ;-) + +OCamlMakefile - Makefile for easy handling of compilation of not so easy + OCaml-projects. It generates dependencies of OCaml-files + automatically, is able to handle "ocamllex"-, + "ocamlyacc"-, IDL- and C-files, knows how to run + preprocessors and generates native- or byte-code, as + executable or as library - with thread-support if you + want! Profiling and debugging support can be added on + the fly! There is also support for installing libraries. + Ah, yes, and you can also create toplevels from any + sources: this allows you immediate interactive testing. + Automatic generation of documentation is easy due to + integration of support for OCamldoc. + +README - this file + +calc/ - Directory containing a quite fully-featured example + of what "OCamlMakefile" can do for you. This example + makes use of "ocamllex", "ocamlyacc", IDL + C and + threads. + +camlp4/ - This simple example demonstrates how to automatically + preprocess files with the camlp4-preprocessor. + +gtk/ - Demonstration of how to use OCamlMakefile with GTK + and threads. Courtesy of Tim Freeman . + +idl/ - Contains a very small example of how to use + "camlidl" together with "OCamlMakefile". Also intended + to show, how easy it is to interface OCaml and C. + +threads/ - Two examples of how to use threads (originally + posted by Xavier Leroy some time ago). Shows the use of + "OCamlMakefile" in an environment of multiple compilation + targets. + +--------------------------------------------------------------------------- + + Why should you use it? + +For several reasons: + + * It is well-tested (I use it in all of my projects). + + * In contrast to most other approaches it generates dependencies + correctly by ensuring that all automatically generated OCaml-files + exist before dependency calculation. This is the only way to + guarantee that "ocamldep" works correctly. + + * It is extremely convenient (at least I think so ;-). + Even quite complex compilation processes (see example "calc.ml") + need very little information to work correctly - actually just about + the minimum (file names of sources). + +--------------------------------------------------------------------------- + + When you shouldn't use it... + +In projects where every compilation unit needs different flags - but +in such complicated cases you will be on your own anyway. Luckily, +this doesn't happen too frequently... + +--------------------------------------------------------------------------- + + How to use "OCamlMakefile" in your own project + (Take a look at the examples for a quick introduction!) + +Create your project-specific "Makefile" in the appropriate directory. + +Now there are two ways of making use of "OCamlMakefile": + + 1) Have a look at the default settings in "OCamlMakefile" and set + them to the values that are vaild on your system - whether the + path to the standard libraries is ok, what executables shall be + used, etc... + + 2) Copy it into the directory of the project to be compiled. + Add "-include OCamlMakefile" as a last line of your "Makefile". + + 3) Put it somewhere else on the system. In this case you will have to + set a variable "OCAMLMAKEFILE" in your project-specific "Makefile". + This is the way in which the examples are written: so you need + only one version of "OCamlMakefile" to manage all your projects! + See the examples for details. + +You should usually specify two further variables for your project: + + * SOURCES (default: foo.ml) + * RESULT (default: foo) + +Put all the sources necessary for a target into variable "SOURCES". +Then set "RESULT" to the name of the target. If you want to generate +libraries, you should *not* specify the suffix (".cma", ".cmxa", ".a") +- it will be added automatically if you specify that you want to build +a library. + + ** Don't forget to add the ".mli"-files, too! ** + ** Don't forget that order of the source files matters! ** + +The order is important, because it matters during linking anyway +due to potential side effects caused at program startup. This is +why OCamlMakefile does not attempt to partially order dependencies by +itself, which might confuse users even more. It just compiles and links +OCaml-sources in the order specified by the user, even if it could +determine automatically that the order cannot be correct. + +The minimum of your "Makefile" looks like this (assuming that +"OCamlMakefile" is in the search path of "make"): + + -include OCamlMakefile + +This will assume that you want to compile a file "foo.ml" to a binary +"foo". + +Otherwise, your Makefile will probably contain something like this: + + SOURCES = foo.ml + RESULT = foo + -include OCamlMakefile + +Be careful with the names you put into these variables: if they are wrong, +a "make clean" might erase the wrong files - but I know you will not do +that ;-) + +A simple "make" will generate a byte-code executable. If you want to +change this, you may add an "all"-rule that generates something else. + +E.g.: + + SOURCES = foo.ml + RESULT = foo + all: native-code-library + -include OCamlMakefile + +This will build a native-code library "foo.cmxa" (+ "foo.a") from file +"foo.ml". + +You may even build several targets at once. To produce byte- and native-code +executables with one "make", add the following rule: + + all: byte-code native-code + +You will probably want to use a different suffix for each of these targets +so that the result will not be overwritten (see optional variables below +for details). + +You may also tell "make" at the command-line what kind of target to +produce (e.g. "make nc"). Here all the possibilities with shortcuts +between parenthesis: + + * byte-code (bc) + * byte-code-nolink (bcnl) - no linking stage + * byte-code-library (bcl) + * native-code (nc) + * native-code-nolink (ncnl) - no linking stage + * native-code-library (ncl) + * debug-code (dc) + * debug-code-nolink (dcnl) - no linking stage + * debug-code-library (dcl) + * profiling-byte-code (pbc) + * profiling-byte-code-library (pbcl) + * profiling-native-code (pnc) + * profiling-native-code-library (pncl) + * byte-code-dll (bcd) + * native-code-dll (ncd) + * pack-byte-code (pabc) + * pack-native-code (panc) + * toplevel interpreter (top) + * subprojs + +Here a short note concerning building and linking byte code libraries +with C-files: + + OCaml links C-object files only when they are used in an executable. + After compilation they should be placed in some directory that is in + your include path if you link your library against an executable. + + It is sometimes more convenient to link all C-object files into a + single C-library. Then you have to override the automatic link flags + of your library using "-noautolink" and add another linkflag that + links in your C-library explicitly. + +What concerns maintenance: + + "make clean" removes all (all!) automatically generated files - so + again: make sure your variables are ok! + + "make cleanup" is similar to "make clean" but leaves executables. + +Another way to destroy some important files is by having "OCamlMakefile" +automatically generate files with the same name. Read the documentation +about the tools in the OCaml-distribution to see what kind of files are +generated. "OCamlMakefile" additionally generates ('%' is basename of +source file): + + %_idl.c - "camlidl" generates a file "%.c" from "%.idl", but this is + not such a good idea, because when generating native-code, + both the file "%.c" and "%.ml" would generate files "%.o" + which would overwrite each other. Thus, "OCamlMakefile" + renames "%.c" to "%_idl.c" to work around this problem. + +The dependencies are stored in three different subdirectories (dot dirs): + + ._d - contains dependencies for .ml-files + ._bcdi - contains byte code dependencies for .mli-files + ._ncdi - contains native code dependencies for .mli-files + +The endings of the dependency files are: "%.d" for those generated from +"%.ml"-files, "%.di" for ones derived from "%.mli"-files. + +--------------------------------------------------------------------------- + + Debugging + + This is easy: if you discover a bug, just do a "make clean; make dc" + to recompile your project with debugging information. Then you can + immediately apply "ocamldebug" to the executable. + +--------------------------------------------------------------------------- + + Profiling + + For generating code that can be profiled with "ocamlprof" (byte code) + or "gprof" (native code), compile your project with one of the profiling + targets (see targets above). E.g.: + + * "make pbc" will build byte code that can be profiled with + "ocamlprof". + + * "make pnc" will build native code that can be profiled with + "gprof". + + Please note that it is not currently possible to profile byte code with + threads. OCamlMakefile will force an error if you try to do this. + + A short hint for DEC Alpha-users (under Digital Unix): you may also + compile your sources to native code without any further profiling + options/targets. Then call "pixie my_exec", "my_exec" being your + executable. This will produce (among other files) an executable + "my_exec.pixie". Call it and it will produce profiling information which + can be analysed using "prof -pixie my_exec". The resulting information + is extremely detailed and allows analysis up to the clock cycle level... + +--------------------------------------------------------------------------- + + Using Preprocessors + + Because one could employ any kind of program that reads from standard + input and prints to standard output as preprocessor, there cannot be any + default way to handle all of them correctly without further knowledge. + + Therefore you have to cooperate a bit with OCamlMakefile to let + preprocessing happen automatically. Basically, this only requires + that you put a comment into the first line of files that should be + preprocessed, e.g.: + + (*pp cat *) + ... rest of program ... + + OCamlMakefile looks at the first line of your files, and if it finds + a comment that starts with "(*pp", then it will assume that the + rest of the comment tells it how to correctly call the appropriate + preprocessor. In this case the program "cat" will be called, which will, + of course, just output the source text again without changing it. + + If you are, for example, an advocate of the new "revised syntax", + which is supported by the camlp4 preprocessor, you could simply write: + + (*pp camlp4r *) + ... rest of program in revised syntax ... + + Simple, isn't it? + + If you want to write your own syntax extensions, just take a look at the + example in the directory "camlp4": it implements the "repeat ... until" + extension as described in the camlp4-tutorial. + +--------------------------------------------------------------------------- + + Library (Un-)Installation Support + + OCamlMakefile contains two targets using "ocamlfind" for this purpose: + + * libinstall + * libuninstall + + These two targets require the existence of the variable + "LIBINSTALL_FILES", which should be set to all the files that you + want to install in the library directory (usually %.mli, %.cmi, %.cma, + %.cmxa, %.a and possibly further C-libraries). The target "libinstall" + has the dependency "all" to force compilation of the library so make + sure you define target "all" in your Makefile appropriately. + + The targets inform the user about the configured install path and ask + for confirmation to (un)install there. If you want to use them, it + is often a good idea to just alias them in your Makefile to "install" + and "uninstall" respectively. + + Two other targets allow installation of files into a particular + directory (without using ocamlfind): + + * rawinstall + * rawuninstall + +--------------------------------------------------------------------------- + + Building toplevels + + There is just one target for this: + + * top + + The generated file can be used immediately for interactive sessions - + even with scanners, parsers, C-files, etc.! + +--------------------------------------------------------------------------- + + Generating documentation + + The following targets are supported: + + * htdoc - generates HTML-documentation + * ladoc - generates Latex-documentation + * psdoc - generates PostScript-documentation + * pdfdoc - generates PDF-documentation + * doc - generates all supported forms of documentation + * clean-doc - generates all supported forms of documentation + + All of them generate a sub-directory "doc". More precisely, for HTML it + is "doc/$(RESULT)/html" and for Latex, PostScript and PDF the directory + "doc/$(RESULT)/latex". See the OCamldoc-manual for details and the + optional variables below for settings you can control. + +--------------------------------------------------------------------------- + + Handling subprojects + + You can have several targets in the same directory and manage them + from within an single Makefile. + + Give each subproject a name, e.g. "p1", "p2", etc. Then you export + settings specific to each project by using variables of the form + "PROJ_p1", "PROJ_p2", etc. E.g.: + + define PROJ_p1 + SOURCES="foo.ml main.ml" + RESULT="p1" + OCAMLFLAGS="-unsafe" + endef + export PROJ_p1 + + define PROJ_p2 + ... + endef + export PROJ_p2 + + You may also export common settings used by all projects directly, e.g. + "export THREADS = y". + + Now it is a good idea to define, which projects should be affected by + commands by default. E.g.: + + ifndef SUBPROJS + export SUBPROJS = p1 p2 + endif + + This will automatically generate a given target for all those + subprojects if this variable has not been defined in the shell + environment or in the command line of the make-invocation by the user. + E.g., "make dc" will generate debug code for all subprojects. + + Then you need to define a default action for your subprojects if "make" + has been called without arguments: + + all: bc + + This will build byte code by default for all subprojects. + + Finally, you'll have to define a catch-all target that uses the target + provided by the user for all subprojects. Just add (assuming that + OCAMLMAKEFILE has been defined appropriately): + + %: + @make -f $(OCAMLMAKEFILE) subprojs SUBTARGET=$@ + + See the "threads"-directory in the distribution for a short example! + +--------------------------------------------------------------------------- + + Optional variables that may be passed to "OCamlMakefile" + + * LIB_PACK_NAME - packs all modules of a library into a module whose + name is given in variable "LIB_PACK_NAME". + + * RES_CLIB_SUF - when building a library that contains C-stubs, this + variable controls the suffix appended to the name + of the C-library (default: "_stubs"). + + * THREADS - say "THREADS = yes" if you need thread support compiled in, + otherwise leave it away. + + * VMTHREADS - say "VMTHREADS = yes" if you want to force VM-level + scheduling of threads (byte-code only). + + * ANNOTATE - say "ANNOTATE = yes" to generate type annotation files + (.annot) to support displaying of type information + in editors. + + * USE_CAMLP4 - say "USE_CAMLP4 = yes" in your "Makefile" if you + want to include the camlp4 directory during the + build process, otherwise leave it away. + + * INCDIRS - directories that should be searched for ".cmi"- and + ".cmo"-files. You need not write "-I ..." - just the + plain names. + * LIBDIRS - directories that should be searched for libraries + Also just put the plain paths into this variable + * EXTLIBDIRS - Same as "LIBDIRS", but paths in this variable are + also added to the binary via the "-R"-flag so that + dynamic libraries in non-standard places can be found. + * RESULTDEPS - Targets on which results (executables or libraries) + should additionally depend. + + * PACKS - adds packages under control of "findlib". + + * PREDS - specifies "findlib"-predicates. + + * LIBS - OCaml-libraries that should be linked (just plain names). + E.g. if you want to link the Str-library, just write + "str" (without quotes). + The new OCaml-compiler handles libraries in such + a way that they "remember" whether they have to + be linked against a C-library and it gets linked + in automatically. + If there is a slash in the library name (such as + "./str" or "lib/foo") then make is told that the + generated files depend on the library. This + helps to ensure that changes to your libraries are + taken into account, which is important if you are + regenerating your libraries frequently. + * CLIBS - C-libraries that should be linked (just plain names). + + * PRE_TARGETS - set this to a list of target files that you want + to have buildt before dependency calculation actually + takes place. E.g. use this to automatically compile + modules needed by camlp4, which have to be available + before other modules can be parsed at all. + + ** WARNING **: the files mentioned in this variable + will be removed when "make clean" is executed! + + * LIBINSTALL_FILES - the files of a library that should be installed + using "findlib". Default: + + $(RESULT).mli $(RESULT).cmi $(RESULT).cma + $(RESULT).cmxa $(RESULT).a lib$(RESULT).a + + * OCAML_LIB_INSTALL - target directory for "rawinstall/rawuninstall". + (default: $(OCAMLLIBPATH)/contrib) + + * DOC_FILES - names of files from which documentation is generated. + (default: all .mli-files in your $(SOURCES)). + + * DOC_DIR - name of directory where documentation should be stored. + + * OCAMLFLAGS - flags passed to the compilers + * OCAMLBCFLAGS - flags passed to the byte code compiler only + * OCAMLNCFLAGS - flags passed to the native code compiler only + + * OCAMLLDFLAGS - flags passed to the OCaml-linker + * OCAMLBLDFLAGS - flags passed to the OCaml-linker when linking byte code + * OCAMLNLDFLAGS - flags passed to the OCaml-linker when linking + native code + + * OCAMLMKLIB_FLAGS - flags passed to the OCaml library tool + + * OCAMLCPFLAGS - profiling flags passed to "ocamlcp" (default: "a") + + * PPFLAGS - additional flags passed to the preprocessor (default: none) + + * LFLAGS - flags passed to "ocamllex" + * YFLAGS - flags passed to "ocamlyacc" + * IDLFLAGS - flags passed to "camlidl" + + * OCAMLDOCFLAGS - flags passed to "ocamldoc" + + * OCAMLFIND_INSTFLAGS - flags passed to "ocamlfind" during installation + (default: none) + + * DVIPSFLAGS - flags passed to dvips + (when generating documentation in PostScript). + + * STATIC - set this variable if you want to force creation + of static libraries + + * CC - the C-compiler to be used + * CXX - the C++-compiler to be used + + * CFLAGS - additional flags passed to the C-compiler. + The flag "-DNATIVE_CODE" will be passed automatically + if you choose to build native code. This allows you + to compile your C-files conditionally. But please + note: You should do a "make clean" or remove the + object files manually or touch the %.c-files: + otherwise, they may not be correctly recompiled + between different builds. + + * CXXFLAGS - additional flags passed to the C++-compiler. + + * CPPFLAGS - additional flags passed to the C-preprocessor. + + * CFRAMEWORKS - Objective-C framework to pass to linker on MacOS X. + + * LDFLAGS - additional flags passed to the C-linker + + * RPATH_FLAG - flag passed through to the C-linker to set a path for + dynamic libraries. May need to be set by user on + exotic platforms. (default: "-R"). + + * ELF_RPATH_FLAG - this flag is used to set the rpath on ELF-platforms. + (default: "-R") + + * ELF_RPATH - if this flag is "yes", then the RPATH_FLAG will be + passed by "-Wl" to the linker as normal on + ELF-platforms. + + * OCAMLLIBPATH - path to the OCaml-standard-libraries + (first default: `$(OCAMLC) -where`) + (second default: "/usr/local/lib/ocaml") + + * OCAML_DEFAULT_DIRS - additional path in which the user can supply + default directories to his own collection of + libraries. The idea is to pass this as an environment + variable so that the Makefiles do not have to contain + this path all the time. + + * OCAMLFIND - ocamlfind from findlib (default: "ocamlfind") + * OCAMLC - byte-code compiler (default: "ocamlc") + * OCAMLOPT - native-code compiler (default: "ocamlopt") + * OCAMLMKTOP - top-level compiler (default: "ocamlmktop") + * OCAMLCP - profiling byte-code compiler (default: "ocamlcp") + * OCAMLDEP - dependency generator (default: "ocamldep") + * OCAMLLEX - scanner generator (default: "ocamllex") + * OCAMLYACC - parser generator (default: "ocamlyacc") + * OCAMLMKLIB - tool to create libraries (default: "ocamlmklib") + * CAMLIDL - IDL-code generator (default: "camlidl") + * CAMLIDLDLL - IDL-utility (default: "camlidldll") + * CAMLP4 - camlp4 preprocessor (default: "camlp4") + * OCAMLDOC - OCamldoc-command (default: "ocamldoc") + + * LATEX - Latex-processor (default: "latex") + * DVIPS - dvips-command (default: "dvips") + * PS2PDF - PostScript-to-PDF converter (default: "ps2pdf") + + * CAMELEON_REPORT - report tool of Cameleon (default: "report") + * CAMELEON_REPORT_FLAGS - flags for the report tool of Cameleon + + * CAMELEON_ZOGGY - zoggy tool of Cameleon + (default: "camlp4o pa_zog.cma pr_o.cmo") + * CAMELEON_ZOGGY_FLAGS - flags for the zoggy tool of Cameleon + + * OCAML_GLADECC - Glade compiler for OCaml (default: "lablgladecc2") + * OCAML_GLADECC_FLAGS - flags for the Glade compiler + + * OXRIDL - OXRIDL-generator (default: "oxridl") + + * NOIDLHEADER - set to "yes" to prohibit "OCamlMakefile" from using + the default camlidl-flag "-header". + + * NO_CUSTOM - Prevent linking in custom mode. + + * QUIET - unsetting this variable (e.g. "make QUIET=") + will print all executed commands, including + intermediate ones. This allows more comfortable + debugging when things go wrong during a build. + + * REALLY_QUIET - when set this flag turns off output from some commands. + + * OCAMLMAKEFILE - location of (=path to) this "OCamlMakefile". + Because it calles itself recursively, it has to + know where it is. (default: "OCamlMakefile" = + local directory) + + * BCSUFFIX - Suffix for all byte-code files. E.g.: + + RESULT = foo + BCSUFFIX = _bc + + This will produce byte-code executables/libraries + with basename "foo_bc". + + * NCSUFFIX - Similar to "BCSUFFIX", but for native-code files. + * TOPSUFFIX - Suffix added to toplevel interpreters (default: ".top") + + * SUBPROJS - variable containing the names of subprojects to be + compiled. + + * SUBTARGET - target to be built for all projects in variable + SUBPROJS. + +--------------------------------------------------------------------------- + + Optional variables for Windows users + + * MINGW - variable to detect the MINGW-environment + * MSVC - variable to detect the MSVC-compiler + +--------------------------------------------------------------------------- + +Up-to-date information (newest release of distribution) can always be +found at: + + http://www.ocaml.info/home/ocaml_sources.html + +--------------------------------------------------------------------------- + +Enjoy! + +New York, 2007-04-22 +Markus Mottl + +e-mail: markus.mottl@gmail.com +WWW: http://www.ocaml.info diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/README.md b/vendor/src/github.com/apache/thrift/lib/ocaml/README.md new file mode 100644 index 00000000..5a47a424 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/README.md @@ -0,0 +1,119 @@ +Thrift OCaml Software Library + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + + +Library +======= + +The library abstract classes, exceptions, and general use functions +are mostly jammed in Thrift.ml (an exception being +TServer). + +Generally, classes are used, however they are often put in their own +module along with other relevant types and functions. The classes +often called t, exceptions are called E. + +Implementations live in their own files. There is TBinaryProtocol, +TSocket, TThreadedServer, TSimpleServer, and TServerSocket. + +A note on making the library: Running make should create native, debug +code libraries, and a toplevel. + + +Struct format +------------- +Structs are turned into classes. The fields are all option types and +are initially None. Write is a method, but reading is done by a +separate function (since there is no such thing as a static +class). The class type is t and is in a module with the name of the +struct. + + +enum format +----------- +Enums are put in their own module along with +functions to_i and of_i which convert the ocaml types into ints. For +example: + +enum Numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +==> + +module Numberz = +struct +type t = +| ONE +| TWO +| THREE +| FIVE +| SIX +| EIGHT + +let of_i = ... +let to_i = ... +end + +typedef format +-------------- +Typedef turns into the type declaration: +typedef i64 UserId + +==> + +type userid Int64.t + +exception format +---------------- +The same as structs except that the module also has an exception type +E of t that is raised/caught. + +For example, with an exception Xception, +raise (Xception.E (new Xception.t)) +and +try + ... +with Xception.E e -> ... + +list format +----------- +Lists are turned into OCaml native lists. + +Map/Set formats +--------------- +These are both turned into Hashtbl.t's. Set values are bool. + +Services +-------- +The client is a class "client" parametrized on input and output +protocols. The processor is a class parametrized on a handler. A +handler is a class inheriting the iface abstract class. Unlike other +implementations, client does not implement iface since iface functions +must take option arguments so as to deal with the case where a client +does not send all the arguments. diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/TODO b/vendor/src/github.com/apache/thrift/lib/ocaml/TODO new file mode 100644 index 00000000..4d1dc771 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/TODO @@ -0,0 +1,5 @@ +Write interfaces +Clean up the code generator +Avoid capture properly instead of relying on the user not to use _ + + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/_oasis b/vendor/src/github.com/apache/thrift/lib/ocaml/_oasis new file mode 100644 index 00000000..95536f01 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/_oasis @@ -0,0 +1,19 @@ +Name: libthrift-ocaml +Version: 0.10.0 +OASISFormat: 0.3 +Synopsis: OCaml bindings for the Apache Thrift RPC system +Authors: Apache Thrift Developers +License: Apache-2.0 +Homepage: http://thrift.apache.org +BuildTools: ocamlbuild +Plugins: META (0.3), + DevFiles (0.3) + +Library "libthrift-ocaml" + Path: src + FindlibName: thrift + buildTools: ocamlbuild + BuildDepends: threads + Modules: Thrift,TBinaryProtocol,TSocket,TFramedTransport,TChannelTransport,TServer,TSimpleServer,TServerSocket,TThreadedServer + XMETARequires: threads + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/ocaml/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/Makefile b/vendor/src/github.com/apache/thrift/lib/ocaml/src/Makefile new file mode 100644 index 00000000..a97ade5e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/Makefile @@ -0,0 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SOURCES = Thrift.ml TBinaryProtocol.ml TSocket.ml TFramedTransport.ml TChannelTransport.ml TServer.ml TSimpleServer.ml TServerSocket.ml TThreadedServer.ml +RESULT = thrift +LIBS = unix threads +THREADS = yes +all: native-code-library debug-code-library top +OCAMLMAKEFILE = ../OCamlMakefile +include $(OCAMLMAKEFILE) diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TBinaryProtocol.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TBinaryProtocol.ml new file mode 100644 index 00000000..6d7500e9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TBinaryProtocol.ml @@ -0,0 +1,171 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift + +module P = Protocol + +let get_byte i b = 255 land (i lsr (8*b)) +let get_byte32 i b = 255 land (Int32.to_int (Int32.shift_right i (8*b))) +let get_byte64 i b = 255 land (Int64.to_int (Int64.shift_right i (8*b))) + + +let tv = P.t_type_to_i +let vt = P.t_type_of_i + + +let comp_int b n = + let s = ref 0l in + let sb = 32 - 8*n in + for i=0 to (n-1) do + s:= Int32.logor !s (Int32.shift_left (Int32.of_int (int_of_char b.[i])) (8*(n-1-i))) + done; + Int32.shift_right (Int32.shift_left !s sb) sb + +let comp_int64 b n = + let s = ref 0L in + for i=0 to (n-1) do + s:=Int64.logor !s (Int64.shift_left (Int64.of_int (int_of_char b.[i])) (8*(n-1-i))) + done; + !s + +let version_mask = 0xffff0000l +let version_1 = 0x80010000l + +class t trans = +object (self) + inherit P.t trans + val ibyte = String.create 8 + method writeBool b = + ibyte.[0] <- char_of_int (if b then 1 else 0); + trans#write ibyte 0 1 + method writeByte i = + ibyte.[0] <- char_of_int (get_byte i 0); + trans#write ibyte 0 1 + method writeI16 i = + let gb = get_byte i in + ibyte.[1] <- char_of_int (gb 0); + ibyte.[0] <- char_of_int (gb 1); + trans#write ibyte 0 2 + method writeI32 i = + let gb = get_byte32 i in + for i=0 to 3 do + ibyte.[3-i] <- char_of_int (gb i) + done; + trans#write ibyte 0 4 + method writeI64 i= + let gb = get_byte64 i in + for i=0 to 7 do + ibyte.[7-i] <- char_of_int (gb i) + done; + trans#write ibyte 0 8 + method writeDouble d = + self#writeI64 (Int64.bits_of_float d) + method writeString s= + let n = String.length s in + self#writeI32 (Int32.of_int n); + trans#write s 0 n + method writeBinary a = self#writeString a + method writeMessageBegin (n,t,s) = + self#writeI32 (Int32.logor version_1 (Int32.of_int (P.message_type_to_i t))); + self#writeString n; + self#writeI32 (Int32.of_int s) + method writeMessageEnd = () + method writeStructBegin s = () + method writeStructEnd = () + method writeFieldBegin (n,t,i) = + self#writeByte (tv t); + self#writeI16 i + method writeFieldEnd = () + method writeFieldStop = + self#writeByte (tv (P.T_STOP)) + method writeMapBegin (k,v,s) = + self#writeByte (tv k); + self#writeByte (tv v); + self#writeI32 (Int32.of_int s) + method writeMapEnd = () + method writeListBegin (t,s) = + self#writeByte (tv t); + self#writeI32 (Int32.of_int s) + method writeListEnd = () + method writeSetBegin (t,s) = + self#writeByte (tv t); + self#writeI32 (Int32.of_int s) + method writeSetEnd = () + method readByte = + ignore (trans#readAll ibyte 0 1); + Int32.to_int (comp_int ibyte 1) + method readI16 = + ignore (trans#readAll ibyte 0 2); + Int32.to_int (comp_int ibyte 2) + method readI32 = + ignore (trans#readAll ibyte 0 4); + comp_int ibyte 4 + method readI64 = + ignore (trans#readAll ibyte 0 8); + comp_int64 ibyte 8 + method readDouble = + Int64.float_of_bits (self#readI64) + method readBool = + self#readByte = 1 + method readString = + let sz = Int32.to_int (self#readI32) in + let buf = String.create sz in + ignore (trans#readAll buf 0 sz); + buf + method readBinary = self#readString + method readMessageBegin = + let ver = self#readI32 in + if Int32.compare (Int32.logand ver version_mask) version_1 != 0 then + raise (P.E (P.BAD_VERSION, "Missing version identifier")) + else + let s = self#readString in + let mt = P.message_type_of_i (Int32.to_int (Int32.logand ver 0xFFl)) in + (s,mt, Int32.to_int self#readI32) + method readMessageEnd = () + method readStructBegin = + "" + method readStructEnd = () + method readFieldBegin = + let t = (vt (self#readByte)) + in + if t != P.T_STOP then + ("",t,self#readI16) + else ("",t,0); + method readFieldEnd = () + method readMapBegin = + let kt = vt (self#readByte) in + let vt = vt (self#readByte) in + (kt,vt, Int32.to_int self#readI32) + method readMapEnd = () + method readListBegin = + let t = vt (self#readByte) in + (t, Int32.to_int self#readI32) + method readListEnd = () + method readSetBegin = + let t = vt (self#readByte) in + (t, Int32.to_int self#readI32); + method readSetEnd = () +end + +class factory = +object + inherit P.factory + method getProtocol tr = new t tr +end diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TChannelTransport.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TChannelTransport.ml new file mode 100644 index 00000000..0f7d616f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TChannelTransport.ml @@ -0,0 +1,39 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift +module T = Transport + +class t (i,o) = +object (self) + val mutable opened = true + inherit Transport.t + method isOpen = opened + method opn = () + method close = close_in i; opened <- false + method read buf off len = + if opened then + try + really_input i buf off len; len + with _ -> raise (T.E (T.UNKNOWN, ("TChannelTransport: Could not read "^(string_of_int len)))) + else + raise (T.E (T.NOT_OPEN, "TChannelTransport: Channel was closed")) + method write buf off len = output o buf off len + method flush = flush o +end diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TFramedTransport.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TFramedTransport.ml new file mode 100644 index 00000000..1be51e76 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TFramedTransport.ml @@ -0,0 +1,93 @@ +open Thrift + +module T = Transport + +let c_0xff_32 = Int32.of_string "0xff" + +(* Copied from OCamlnet rtypes.ml *) +let encode_frame_size x = + let s = String.create 4 in + let n3 = Int32.to_int (Int32.shift_right_logical x 24) land 0xff in + let n2 = Int32.to_int (Int32.shift_right_logical x 16) land 0xff in + let n1 = Int32.to_int (Int32.shift_right_logical x 8) land 0xff in + let n0 = Int32.to_int (Int32.logand x c_0xff_32) in + String.unsafe_set s 0 (Char.unsafe_chr n3); + String.unsafe_set s 1 (Char.unsafe_chr n2); + String.unsafe_set s 2 (Char.unsafe_chr n1); + String.unsafe_set s 3 (Char.unsafe_chr n0); + s + +let decode_frame_size s = + let n3 = Int32.of_int (Char.code s.[0]) in + let n2 = Int32.of_int (Char.code s.[1]) in + let n1 = Int32.of_int (Char.code s.[2]) in + let n0 = Int32.of_int (Char.code s.[3]) in + Int32.logor + (Int32.shift_left n3 24) + (Int32.logor + (Int32.shift_left n2 16) + (Int32.logor + (Int32.shift_left n1 8) + n0)) + +class t ?(max_length=Sys.max_string_length) (transport: T.t) = +object (self) + inherit T.t + + method isOpen = transport#isOpen + method opn = transport#opn + method close = transport#close + + val mutable read_buf = None + val mutable read_buf_offset = 0 + val mutable write_buf = "" + + method private read_frame = + let len_buf = String.create 4 in + assert (transport#readAll len_buf 0 4 = 4); + + let size = Int32.to_int (decode_frame_size len_buf) in + + (if size < 0 + then failwith (Printf.sprintf "Read a negative frame size (%i)!" size)); + + (if size > max_length + then failwith (Printf.sprintf "Frame size (%i) larger than max length (%i)!" size max_length)); + + let buf = String.create size in + assert (transport#readAll buf 0 size = size); + read_buf <- Some buf; + read_buf_offset <- 0 + + method private read_from_frame frame buf off len = + let to_copy = min len ((String.length frame) - read_buf_offset) in + String.blit frame read_buf_offset buf off to_copy; + read_buf_offset <- read_buf_offset + to_copy; + to_copy + + method read buf off len = + match read_buf with + | Some frame -> + let i = self#read_from_frame frame buf off len in + if i > 0 + then i + else begin + self#read_frame; + self#read_from_frame frame buf off len + end + | None -> + self#read_frame; + self#read buf off len + + method write buf off len = + write_buf <- write_buf ^ (String.sub buf off len) + + method flush = + let encoded_size = encode_frame_size (Int32.of_int (String.length write_buf)) in + transport#write encoded_size 0 (String.length encoded_size); + transport#write write_buf 0 (String.length write_buf); + transport#flush; + write_buf <- "" +end + + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServer.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServer.ml new file mode 100644 index 00000000..fc51efa8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServer.ml @@ -0,0 +1,42 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift + +class virtual t + (pf : Processor.t) + (st : Transport.server_t) + (tf : Transport.factory) + (ipf : Protocol.factory) + (opf : Protocol.factory)= +object + method virtual serve : unit +end;; + + + +let run_basic_server proc port = + Unix.establish_server (fun inp -> fun out -> + let trans = new TChannelTransport.t (inp,out) in + let proto = new TBinaryProtocol.t (trans :> Transport.t) in + try + while proc#process proto proto do () done; () + with e -> ()) (Unix.ADDR_INET (Unix.inet_addr_of_string "127.0.0.1",port)) + + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServerSocket.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServerSocket.ml new file mode 100644 index 00000000..405ef82c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TServerSocket.ml @@ -0,0 +1,41 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift + +class t port = +object + inherit Transport.server_t + val mutable sock = None + method listen = + let s = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in + sock <- Some s; + Unix.bind s (Unix.ADDR_INET (Unix.inet_addr_any, port)); + Unix.listen s 256 + method close = + match sock with + Some s -> Unix.shutdown s Unix.SHUTDOWN_ALL; Unix.close s; + sock <- None + | _ -> () + method acceptImpl = + match sock with + Some s -> let (fd,_) = Unix.accept s in + new TChannelTransport.t (Unix.in_channel_of_descr fd,Unix.out_channel_of_descr fd) + | _ -> raise (Transport.E (Transport.NOT_OPEN,"TServerSocket: Not listening but tried to accept")) +end diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSimpleServer.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSimpleServer.ml new file mode 100644 index 00000000..2927c08f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSimpleServer.ml @@ -0,0 +1,40 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift +module S = TServer + +class t pf st tf ipf opf = +object + inherit S.t pf st tf ipf opf + method serve = + try + st#listen; + while true do + let c = st#accept in + let trans = tf#getTransport c in + let inp = ipf#getProtocol trans in + let op = opf#getProtocol trans in + try + while (pf#process inp op) do () done; + trans#close + with e -> trans#close; raise e + done + with _ -> () +end diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSocket.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSocket.ml new file mode 100644 index 00000000..109e11c5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TSocket.ml @@ -0,0 +1,59 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift + +module T = Transport + +class t host port= +object (self) + inherit T.t + val mutable chans = None + method isOpen = chans != None + method opn = + try + let addr = (let {Unix.h_addr_list=x} = Unix.gethostbyname host in x.(0)) in + chans <- Some(Unix.open_connection (Unix.ADDR_INET (addr,port))) + with + Unix.Unix_error (e,fn,_) -> raise (T.E (T.NOT_OPEN, ("TSocket: Could not connect to "^host^":"^(string_of_int port)^" because: "^fn^":"^(Unix.error_message e)))) + | _ -> raise (T.E (T.NOT_OPEN, ("TSocket: Could not connect to "^host^":"^(string_of_int port)))) + + method close = + match chans with + None -> () + | Some(inc,out) -> (Unix.shutdown_connection inc; + close_in inc; + chans <- None) + method read buf off len = match chans with + None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open")) + | Some(i,o) -> + try + really_input i buf off len; len + with + Unix.Unix_error (e,fn,_) -> raise (T.E (T.UNKNOWN, ("TSocket: Could not read "^(string_of_int len)^" from "^host^":"^(string_of_int port)^" because: "^fn^":"^(Unix.error_message e)))) + | _ -> raise (T.E (T.UNKNOWN, ("TSocket: Could not read "^(string_of_int len)^" from "^host^":"^(string_of_int port)))) + method write buf off len = match chans with + None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open")) + | Some(i,o) -> output o buf off len + method flush = match chans with + None -> raise (T.E (T.NOT_OPEN, "TSocket: Socket not open")) + | Some(i,o) -> flush o +end + + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/TThreadedServer.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TThreadedServer.ml new file mode 100644 index 00000000..4462dbd7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/TThreadedServer.ml @@ -0,0 +1,45 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Thrift + +class t + (pf : Processor.t) + (st : Transport.server_t) + (tf : Transport.factory) + (ipf : Protocol.factory) + (opf : Protocol.factory)= +object + inherit TServer.t pf st tf ipf opf + method serve = + st#listen; + while true do + let tr = tf#getTransport (st#accept) in + ignore (Thread.create + (fun _ -> + let ip = ipf#getProtocol tr in + let op = opf#getProtocol tr in + try + while pf#process ip op do + () + done + with _ -> ()) ()) + done +end + diff --git a/vendor/src/github.com/apache/thrift/lib/ocaml/src/Thrift.ml b/vendor/src/github.com/apache/thrift/lib/ocaml/src/Thrift.ml new file mode 100644 index 00000000..f0d7a429 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ocaml/src/Thrift.ml @@ -0,0 +1,383 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +exception Break;; +exception Thrift_error;; +exception Field_empty of string;; + +class t_exn = +object + val mutable message = "" + method get_message = message + method set_message s = message <- s +end;; + +module Transport = +struct + type exn_type = + | UNKNOWN + | NOT_OPEN + | ALREADY_OPEN + | TIMED_OUT + | END_OF_FILE;; + + exception E of exn_type * string + + class virtual t = + object (self) + method virtual isOpen : bool + method virtual opn : unit + method virtual close : unit + method virtual read : string -> int -> int -> int + method readAll buf off len = + let got = ref 0 in + let ret = ref 0 in + while !got < len do + ret := self#read buf (off+(!got)) (len - (!got)); + if !ret <= 0 then + raise (E (UNKNOWN, "Cannot read. Remote side has closed.")); + got := !got + !ret + done; + !got + method virtual write : string -> int -> int -> unit + method virtual flush : unit + end + + class factory = + object + method getTransport (t : t) = t + end + + class virtual server_t = + object (self) + method virtual listen : unit + method accept = self#acceptImpl + method virtual close : unit + method virtual acceptImpl : t + end + +end;; + + + +module Protocol = +struct + type t_type = + | T_STOP + | T_VOID + | T_BOOL + | T_BYTE + | T_I08 + | T_I16 + | T_I32 + | T_U64 + | T_I64 + | T_DOUBLE + | T_STRING + | T_UTF7 + | T_STRUCT + | T_MAP + | T_SET + | T_LIST + | T_UTF8 + | T_UTF16 + + let t_type_to_i = function + T_STOP -> 0 + | T_VOID -> 1 + | T_BOOL -> 2 + | T_BYTE -> 3 + | T_I08 -> 3 + | T_I16 -> 6 + | T_I32 -> 8 + | T_U64 -> 9 + | T_I64 -> 10 + | T_DOUBLE -> 4 + | T_STRING -> 11 + | T_UTF7 -> 11 + | T_STRUCT -> 12 + | T_MAP -> 13 + | T_SET -> 14 + | T_LIST -> 15 + | T_UTF8 -> 16 + | T_UTF16 -> 17 + + let t_type_of_i = function + 0 -> T_STOP + | 1 -> T_VOID + | 2 -> T_BOOL + | 3 -> T_BYTE + | 6-> T_I16 + | 8 -> T_I32 + | 9 -> T_U64 + | 10 -> T_I64 + | 4 -> T_DOUBLE + | 11 -> T_STRING + | 12 -> T_STRUCT + | 13 -> T_MAP + | 14 -> T_SET + | 15 -> T_LIST + | 16 -> T_UTF8 + | 17 -> T_UTF16 + | _ -> raise Thrift_error + + type message_type = + | CALL + | REPLY + | EXCEPTION + | ONEWAY + + let message_type_to_i = function + | CALL -> 1 + | REPLY -> 2 + | EXCEPTION -> 3 + | ONEWAY -> 4 + + let message_type_of_i = function + | 1 -> CALL + | 2 -> REPLY + | 3 -> EXCEPTION + | 4 -> ONEWAY + | _ -> raise Thrift_error + + class virtual t (trans: Transport.t) = + object (self) + val mutable trans_ = trans + method getTransport = trans_ + (* writing methods *) + method virtual writeMessageBegin : string * message_type * int -> unit + method virtual writeMessageEnd : unit + method virtual writeStructBegin : string -> unit + method virtual writeStructEnd : unit + method virtual writeFieldBegin : string * t_type * int -> unit + method virtual writeFieldEnd : unit + method virtual writeFieldStop : unit + method virtual writeMapBegin : t_type * t_type * int -> unit + method virtual writeMapEnd : unit + method virtual writeListBegin : t_type * int -> unit + method virtual writeListEnd : unit + method virtual writeSetBegin : t_type * int -> unit + method virtual writeSetEnd : unit + method virtual writeBool : bool -> unit + method virtual writeByte : int -> unit + method virtual writeI16 : int -> unit + method virtual writeI32 : Int32.t -> unit + method virtual writeI64 : Int64.t -> unit + method virtual writeDouble : float -> unit + method virtual writeString : string -> unit + method virtual writeBinary : string -> unit + (* reading methods *) + method virtual readMessageBegin : string * message_type * int + method virtual readMessageEnd : unit + method virtual readStructBegin : string + method virtual readStructEnd : unit + method virtual readFieldBegin : string * t_type * int + method virtual readFieldEnd : unit + method virtual readMapBegin : t_type * t_type * int + method virtual readMapEnd : unit + method virtual readListBegin : t_type * int + method virtual readListEnd : unit + method virtual readSetBegin : t_type * int + method virtual readSetEnd : unit + method virtual readBool : bool + method virtual readByte : int + method virtual readI16 : int + method virtual readI32: Int32.t + method virtual readI64 : Int64.t + method virtual readDouble : float + method virtual readString : string + method virtual readBinary : string + (* skippage *) + method skip typ = + match typ with + | T_STOP -> () + | T_VOID -> () + | T_BOOL -> ignore self#readBool + | T_BYTE + | T_I08 -> ignore self#readByte + | T_I16 -> ignore self#readI16 + | T_I32 -> ignore self#readI32 + | T_U64 + | T_I64 -> ignore self#readI64 + | T_DOUBLE -> ignore self#readDouble + | T_STRING -> ignore self#readString + | T_UTF7 -> () + | T_STRUCT -> ignore ((ignore self#readStructBegin); + (try + while true do + let (_,t,_) = self#readFieldBegin in + if t = T_STOP then + raise Break + else + (self#skip t; + self#readFieldEnd) + done + with Break -> ()); + self#readStructEnd) + | T_MAP -> ignore (let (k,v,s) = self#readMapBegin in + for i=0 to s do + self#skip k; + self#skip v; + done; + self#readMapEnd) + | T_SET -> ignore (let (t,s) = self#readSetBegin in + for i=0 to s do + self#skip t + done; + self#readSetEnd) + | T_LIST -> ignore (let (t,s) = self#readListBegin in + for i=0 to s do + self#skip t + done; + self#readListEnd) + | T_UTF8 -> () + | T_UTF16 -> () + end + + class virtual factory = + object + method virtual getProtocol : Transport.t -> t + end + + type exn_type = + | UNKNOWN + | INVALID_DATA + | NEGATIVE_SIZE + | SIZE_LIMIT + | BAD_VERSION + | NOT_IMPLEMENTED + | DEPTH_LIMIT + + exception E of exn_type * string;; + +end;; + + +module Processor = +struct + class virtual t = + object + method virtual process : Protocol.t -> Protocol.t -> bool + end;; + + class factory (processor : t) = + object + val processor_ = processor + method getProcessor (trans : Transport.t) = processor_ + end;; +end + + +(* Ugly *) +module Application_Exn = +struct + type typ= + | UNKNOWN + | UNKNOWN_METHOD + | INVALID_MESSAGE_TYPE + | WRONG_METHOD_NAME + | BAD_SEQUENCE_ID + | MISSING_RESULT + | INTERNAL_ERROR + | PROTOCOL_ERROR + | INVALID_TRANSFORM + | INVALID_PROTOCOL + | UNSUPPORTED_CLIENT_TYPE + + let typ_of_i = function + 0l -> UNKNOWN + | 1l -> UNKNOWN_METHOD + | 2l -> INVALID_MESSAGE_TYPE + | 3l -> WRONG_METHOD_NAME + | 4l -> BAD_SEQUENCE_ID + | 5l -> MISSING_RESULT + | 6l -> INTERNAL_ERROR + | 7l -> PROTOCOL_ERROR + | 8l -> INVALID_TRANSFORM + | 9l -> INVALID_PROTOCOL + | 10l -> UNSUPPORTED_CLIENT_TYPE + | _ -> raise Thrift_error;; + let typ_to_i = function + | UNKNOWN -> 0l + | UNKNOWN_METHOD -> 1l + | INVALID_MESSAGE_TYPE -> 2l + | WRONG_METHOD_NAME -> 3l + | BAD_SEQUENCE_ID -> 4l + | MISSING_RESULT -> 5l + | INTERNAL_ERROR -> 6l + | PROTOCOL_ERROR -> 7l + | INVALID_TRANSFORM -> 8l + | INVALID_PROTOCOL -> 9l + | UNSUPPORTED_CLIENT_TYPE -> 10l + + class t = + object (self) + inherit t_exn + val mutable typ = UNKNOWN + method get_type = typ + method set_type t = typ <- t + method write (oprot : Protocol.t) = + oprot#writeStructBegin "TApplicationExeception"; + if self#get_message != "" then + (oprot#writeFieldBegin ("message",Protocol.T_STRING, 1); + oprot#writeString self#get_message; + oprot#writeFieldEnd) + else (); + oprot#writeFieldBegin ("type",Protocol.T_I32,2); + oprot#writeI32 (typ_to_i typ); + oprot#writeFieldEnd; + oprot#writeFieldStop; + oprot#writeStructEnd + end;; + + let create typ msg = + let e = new t in + e#set_type typ; + e#set_message msg; + e + + let read (iprot : Protocol.t) = + let msg = ref "" in + let typ = ref 0l in + ignore iprot#readStructBegin; + (try + while true do + let (name,ft,id) =iprot#readFieldBegin in + if ft = Protocol.T_STOP + then raise Break + else (); + (match id with + | 1 -> (if ft = Protocol.T_STRING + then msg := (iprot#readString) + else iprot#skip ft) + | 2 -> (if ft = Protocol.T_I32 + then typ := iprot#readI32 + else iprot#skip ft) + | _ -> iprot#skip ft); + iprot#readFieldEnd + done + with Break -> ()); + iprot#readStructEnd; + let e = new t in + e#set_type (typ_of_i !typ); + e#set_message !msg; + e;; + + exception E of t +end;; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/Makefile.PL b/vendor/src/github.com/apache/thrift/lib/perl/Makefile.PL new file mode 100644 index 00000000..ceca86f7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/Makefile.PL @@ -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. +# + +use ExtUtils::MakeMaker; +WriteMakefile( 'NAME' => 'Thrift', + 'VERSION_FROM' => 'lib/Thrift.pm', + 'PREREQ_PM' => { + 'Bit::Vector' => 0, + 'Class::Accessor' => 0 + }, + ($] >= 5.005 ? + ( AUTHOR => 'Apache Thrift ') : ()), + ); diff --git a/vendor/src/github.com/apache/thrift/lib/perl/Makefile.am b/vendor/src/github.com/apache/thrift/lib/perl/Makefile.am new file mode 100644 index 00000000..0d2c8d3e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/Makefile.am @@ -0,0 +1,105 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = test + +Makefile-perl.mk : Makefile.PL + $(PERL) Makefile.PL MAKEFILE=Makefile-perl.mk INSTALLDIRS=$(INSTALLDIRS) INSTALL_BASE=$(PERL_PREFIX) + +all-local: Makefile-perl.mk + $(MAKE) -f $< + find blib -name 'Makefile*' -exec rm -f {} \; + +install-exec-local: Makefile-perl.mk + $(MAKE) -f $< install DESTDIR=$(DESTDIR)/ + +clean-local: + if test -f Makefile-perl.mk ; then \ + $(MAKE) -f Makefile-perl.mk clean ; \ + fi + $(RM) Makefile-perl.mk.old + $(RM) -r gen-perl gen-perl2 + +EXTRA_DIST = \ + coding_standards.md \ + Makefile.PL \ + test.pl \ + lib/Thrift.pm \ + lib/Thrift.pm \ + lib/Thrift/BinaryProtocol.pm \ + lib/Thrift/BufferedTransport.pm \ + lib/Thrift/FramedTransport.pm \ + lib/Thrift/HttpClient.pm \ + lib/Thrift/MemoryBuffer.pm \ + lib/Thrift/MessageType.pm \ + lib/Thrift/MultiplexedProcessor.pm \ + lib/Thrift/MultiplexedProtocol.pm \ + lib/Thrift/Protocol.pm \ + lib/Thrift/ProtocolDecorator.pm \ + lib/Thrift/Server.pm \ + lib/Thrift/ServerSocket.pm \ + lib/Thrift/Socket.pm \ + lib/Thrift/SSLSocket.pm \ + lib/Thrift/SSLServerSocket.pm \ + lib/Thrift/UnixServerSocket.pm \ + lib/Thrift/UnixSocket.pm \ + lib/Thrift/Transport.pm \ + README.md + +THRIFT = @top_builddir@/compiler/cpp/thrift +THRIFT_IF = @top_srcdir@/test/ThriftTest.thrift +NAME_BENCHMARKSERVICE = @top_srcdir@/lib/rb/benchmark/Benchmark.thrift +NAME_AGGR = @top_srcdir@/contrib/async-test/aggr.thrift + +THRIFTTEST_GEN = \ + gen-perl/ThriftTest/Constants.pm \ + gen-perl/ThriftTest/SecondService.pm \ + gen-perl/ThriftTest/ThriftTest.pm \ + gen-perl/ThriftTest/Types.pm + +BENCHMARK_GEN = \ + gen-perl/BenchmarkService.pm \ + gen-perl/Constants.pm \ + gen-perl/Types.pm + +AGGR_GEN = \ + gen-perl2/Aggr.pm \ + gen-perl2/Constants.pm \ + gen-perl2/Types.pm + +PERL_GEN = \ + $(THRIFTTEST_GEN) \ + $(BENCHMARK_GEN) \ + $(AGGR_GEN) + +BUILT_SOURCES = $(PERL_GEN) + +check-local: $(PERL_GEN) + $(PERL) -Iblib/lib -I@abs_srcdir@ -I@builddir@/gen-perl2 -I@builddir@/gen-perl \ + @abs_srcdir@/test.pl @abs_srcdir@/test/*.t + +$(THRIFTTEST_GEN): $(THRIFT_IF) $(THRIFT) + $(THRIFT) --gen perl $< + +$(BENCHMARK_GEN): $(NAME_BENCHMARKSERVICE) $(THRIFT) + $(THRIFT) --gen perl $< + +$(AGGR_GEN): $(NAME_AGGR) $(THRIFT) + $(MKDIR_P) gen-perl2 + $(THRIFT) -out gen-perl2 --gen perl $< diff --git a/vendor/src/github.com/apache/thrift/lib/perl/README.md b/vendor/src/github.com/apache/thrift/lib/perl/README.md new file mode 100644 index 00000000..51247e04 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/README.md @@ -0,0 +1,45 @@ +Thrift Perl 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 Perl +===================== + +Thrift requires Perl >= 5.6.0 + +Unexpected exceptions in a service handler are converted to +TApplicationException with type INTERNAL ERROR and the string +of the exception is delivered as the message. + +On the client side, exceptions are thrown with die, so be sure +to wrap eval{} statments around any code that contains exceptions. + +Please see tutoral and test dirs for examples. + +Dependencies +============ + +Bit::Vector - comes with modern perl installations. +Class::Accessor +IO::Socket::INET - comes with modern perl installations. +IO::Socket::SSL - required if using SSL/TLS. +NET::SSLeay +Crypt::SSLeay - for make cross diff --git a/vendor/src/github.com/apache/thrift/lib/perl/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/perl/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift.pm new file mode 100644 index 00000000..152d2239 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift.pm @@ -0,0 +1,193 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES 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; + +our $VERSION = '0.10.0'; + +require 5.6.0; +use strict; +use warnings; + +# +# Data types that can be sent via Thrift +# +package TType; +use constant STOP => 0; +use constant VOID => 1; +use constant BOOL => 2; +use constant BYTE => 3; +use constant I08 => 3; +use constant DOUBLE => 4; +use constant I16 => 6; +use constant I32 => 8; +use constant I64 => 10; +use constant STRING => 11; +use constant UTF7 => 11; +use constant STRUCT => 12; +use constant MAP => 13; +use constant SET => 14; +use constant LIST => 15; +use constant UTF8 => 16; +use constant UTF16 => 17; +1; + +# +# Message types for RPC +# +package TMessageType; +use constant CALL => 1; +use constant REPLY => 2; +use constant EXCEPTION => 3; +use constant ONEWAY => 4; +1; + +package Thrift::TException; + +use overload '""' => sub { + return + ref( $_[0] ) + . " error: " + . ( $_[0]->{message} || 'empty message' ) + . " (code " + . ( defined $_[0]->{code} ? $_[0]->{code} : 'undefined' ) . ")"; + }; + +sub new { + my $classname = shift; + my $self = {message => shift, code => shift || 0}; + + return bless($self,$classname); +} +1; + +package TApplicationException; +use base('Thrift::TException'); + +use constant UNKNOWN => 0; +use constant UNKNOWN_METHOD => 1; +use constant INVALID_MESSAGE_TYPE => 2; +use constant WRONG_METHOD_NAME => 3; +use constant BAD_SEQUENCE_ID => 4; +use constant MISSING_RESULT => 5; +use constant INTERNAL_ERROR => 6; +use constant PROTOCOL_ERROR => 7; +use constant INVALID_TRANSFORM => 8; +use constant INVALID_PROTOCOL => 9; +use constant UNSUPPORTED_CLIENT_TYPE => 10; + +sub new { + my $classname = shift; + + my $self = $classname->SUPER::new(@_); + + return bless($self,$classname); +} + +sub read { + my $self = shift; + my $input = shift; + + my $xfer = 0; + my $fname = undef; + my $ftype = 0; + my $fid = 0; + + $xfer += $input->readStructBegin(\$fname); + + while (1) + { + $xfer += $input->readFieldBegin(\$fname, \$ftype, \$fid); + if ($ftype == TType::STOP) { + last; next; + } + + SWITCH: for($fid) + { + /1/ && do{ + + if ($ftype == TType::STRING) { + $xfer += $input->readString(\$self->{message}); + } else { + $xfer += $input->skip($ftype); + } + + last; + }; + + /2/ && do{ + if ($ftype == TType::I32) { + $xfer += $input->readI32(\$self->{code}); + } else { + $xfer += $input->skip($ftype); + } + last; + }; + + $xfer += $input->skip($ftype); + } + + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + + return $xfer; +} + +sub write { + my $self = shift; + my $output = shift; + + my $xfer = 0; + + $xfer += $output->writeStructBegin('TApplicationException'); + + if ($self->getMessage()) { + $xfer += $output->writeFieldBegin('message', TType::STRING, 1); + $xfer += $output->writeString($self->getMessage()); + $xfer += $output->writeFieldEnd(); + } + + if ($self->getCode()) { + $xfer += $output->writeFieldBegin('type', TType::I32, 2); + $xfer += $output->writeI32($self->getCode()); + $xfer += $output->writeFieldEnd(); + } + + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; +} + +sub getMessage +{ + my $self = shift; + + return $self->{message}; +} + +sub getCode +{ + my $self = shift; + + return $self->{code}; +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm new file mode 100644 index 00000000..c638ead1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BinaryProtocol.pm @@ -0,0 +1,511 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; + +use strict; +use warnings; + +use utf8; +use Encode; + +use Thrift; +use Thrift::Protocol; + +use Bit::Vector; + +# +# Binary implementation of the Thrift protocol. +# +package Thrift::BinaryProtocol; +use base('Thrift::Protocol'); + +use constant VERSION_MASK => 0xffff0000; +use constant VERSION_1 => 0x80010000; +use constant IS_BIG_ENDIAN => unpack("h*", pack("s", 1)) =~ /01/; + +sub new +{ + my $classname = shift; + my $trans = shift; + my $self = $classname->SUPER::new($trans); + + return bless($self,$classname); +} + +sub writeMessageBegin +{ + my $self = shift; + my ($name, $type, $seqid) = @_; + + return + $self->writeI32(VERSION_1 | $type) + + $self->writeString($name) + + $self->writeI32($seqid); +} + +sub writeMessageEnd +{ + my $self = shift; + return 0; +} + +sub writeStructBegin{ + my $self = shift; + my $name = shift; + return 0; +} + +sub writeStructEnd +{ + my $self = shift; + return 0; +} + +sub writeFieldBegin +{ + my $self = shift; + my ($fieldName, $fieldType, $fieldId) = @_; + + return + $self->writeByte($fieldType) + + $self->writeI16($fieldId); +} + +sub writeFieldEnd +{ + my $self = shift; + return 0; +} + +sub writeFieldStop +{ + my $self = shift; + return $self->writeByte(TType::STOP); +} + +sub writeMapBegin +{ + my $self = shift; + my ($keyType, $valType, $size) = @_; + + return + $self->writeByte($keyType) + + $self->writeByte($valType) + + $self->writeI32($size); +} + +sub writeMapEnd +{ + my $self = shift; + return 0; +} + +sub writeListBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return + $self->writeByte($elemType) + + $self->writeI32($size); +} + +sub writeListEnd +{ + my $self = shift; + return 0; +} + +sub writeSetBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return + $self->writeByte($elemType) + + $self->writeI32($size); +} + +sub writeSetEnd +{ + my $self = shift; + return 0; +} + +sub writeBool +{ + my $self = shift; + my $value = shift; + + my $data = pack('c', $value ? 1 : 0); + $self->{trans}->write($data, 1); + return 1; +} + +sub writeByte +{ + my $self = shift; + my $value= shift; + + my $data = pack('c', $value); + $self->{trans}->write($data, 1); + return 1; +} + +sub writeI16 +{ + my $self = shift; + my $value= shift; + + my $data = pack('n', $value); + $self->{trans}->write($data, 2); + return 2; +} + +sub writeI32 +{ + my $self = shift; + my $value= shift; + + my $data = pack('N', $value); + $self->{trans}->write($data, 4); + return 4; +} + +sub writeI64 +{ + my $self = shift; + my $value= shift; + my $data; + + my $vec; + #stop annoying error + $vec = Bit::Vector->new_Dec(64, $value); + $data = pack 'NN', $vec->Chunk_Read(32, 32), $vec->Chunk_Read(32, 0); + + $self->{trans}->write($data, 8); + + return 8; +} + + +sub writeDouble +{ + my $self = shift; + my $value= shift; + + my $data = pack('d', $value); + if (IS_BIG_ENDIAN) { + $self->{trans}->write($data, 8); + } + else { + $self->{trans}->write(scalar reverse($data), 8); + } + return 8; +} + +sub writeString{ + my $self = shift; + my $value= shift; + + if( utf8::is_utf8($value) ){ + $value = Encode::encode_utf8($value); + } + + my $len = length($value); + + my $result = $self->writeI32($len); + + if ($len) { + $self->{trans}->write($value,$len); + } + return $result + $len; + } + + +# +#All references +# +sub readMessageBegin +{ + my $self = shift; + my ($name, $type, $seqid) = @_; + + my $version = 0; + my $result = $self->readI32(\$version); + if (($version & VERSION_MASK) > 0) { + if (($version & VERSION_MASK) != VERSION_1) { + die new Thrift::TException('Missing version identifier') + } + $$type = $version & 0x000000ff; + return + $result + + $self->readString($name) + + $self->readI32($seqid); + } else { # old client support code + return + $result + + $self->readStringBody($name, $version) + # version here holds the size of the string + $self->readByte($type) + + $self->readI32($seqid); + } +} + +sub readMessageEnd +{ + my $self = shift; + return 0; +} + +sub readStructBegin +{ + my $self = shift; + my $name = shift; + + $$name = ''; + + return 0; +} + +sub readStructEnd +{ + my $self = shift; + return 0; +} + +sub readFieldBegin +{ + my $self = shift; + my ($name, $fieldType, $fieldId) = @_; + + my $result = $self->readByte($fieldType); + + if ($$fieldType == TType::STOP) { + $$fieldId = 0; + return $result; + } + + $result += $self->readI16($fieldId); + + return $result; +} + +sub readFieldEnd() { + my $self = shift; + return 0; +} + +sub readMapBegin +{ + my $self = shift; + my ($keyType, $valType, $size) = @_; + + return + $self->readByte($keyType) + + $self->readByte($valType) + + $self->readI32($size); +} + +sub readMapEnd() +{ + my $self = shift; + return 0; +} + +sub readListBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return + $self->readByte($elemType) + + $self->readI32($size); +} + +sub readListEnd +{ + my $self = shift; + return 0; +} + +sub readSetBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return + $self->readByte($elemType) + + $self->readI32($size); +} + +sub readSetEnd +{ + my $self = shift; + return 0; +} + +sub readBool +{ + my $self = shift; + my $value = shift; + + my $data = $self->{trans}->readAll(1); + my @arr = unpack('c', $data); + $$value = $arr[0] == 1; + return 1; +} + +sub readByte +{ + my $self = shift; + my $value = shift; + + my $data = $self->{trans}->readAll(1); + my @arr = unpack('c', $data); + $$value = $arr[0]; + return 1; +} + +sub readI16 +{ + my $self = shift; + my $value = shift; + + my $data = $self->{trans}->readAll(2); + + my @arr = unpack('n', $data); + + $$value = $arr[0]; + + if ($$value > 0x7fff) { + $$value = 0 - (($$value - 1) ^ 0xffff); + } + + return 2; +} + +sub readI32 +{ + my $self = shift; + my $value= shift; + + my $data = $self->{trans}->readAll(4); + my @arr = unpack('N', $data); + + $$value = $arr[0]; + if ($$value > 0x7fffffff) { + $$value = 0 - (($$value - 1) ^ 0xffffffff); + } + return 4; +} + +sub readI64 +{ + my $self = shift; + my $value = shift; + + my $data = $self->{trans}->readAll(8); + + my ($hi,$lo)=unpack('NN',$data); + + my $vec = new Bit::Vector(64); + + $vec->Chunk_Store(32,32,$hi); + $vec->Chunk_Store(32,0,$lo); + + $$value = $vec->to_Dec(); + + return 8; +} + +sub readDouble +{ + my $self = shift; + my $value = shift; + + my $data; + if (IS_BIG_ENDIAN) { + $data = $self->{trans}->readAll(8); + } + else { + $data = scalar reverse($self->{trans}->readAll(8)); + } + + my @arr = unpack('d', $data); + + $$value = $arr[0]; + + return 8; +} + +sub readString +{ + my $self = shift; + my $value = shift; + + my $len; + my $result = $self->readI32(\$len); + + if ($len) { + $$value = $self->{trans}->readAll($len); + } else { + $$value = ''; + } + + return $result + $len; +} + +sub readStringBody +{ + my $self = shift; + my $value = shift; + my $len = shift; + + if ($len) { + $$value = $self->{trans}->readAll($len); + } else { + $$value = ''; + } + + return $len; +} + +# +# Binary Protocol Factory +# +package Thrift::BinaryProtocolFactory; +use base('TProtocolFactory'); + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::new(); + + return bless($self,$classname); +} + +sub getProtocol{ + my $self = shift; + my $trans = shift; + + return new Thrift::BinaryProtocol($trans); +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BufferedTransport.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BufferedTransport.pm new file mode 100644 index 00000000..3868ca2d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/BufferedTransport.pm @@ -0,0 +1,136 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +package Thrift::BufferedTransport; +use base('Thrift::Transport'); + +sub new +{ + my $classname = shift; + my $transport = shift; + my $rBufSize = shift || 512; + my $wBufSize = shift || 512; + + my $self = { + transport => $transport, + rBufSize => $rBufSize, + wBufSize => $wBufSize, + wBuf => '', + rBuf => '', + }; + + return bless($self,$classname); +} + +sub isOpen +{ + my $self = shift; + + return $self->{transport}->isOpen(); +} + +sub open +{ + my $self = shift; + $self->{transport}->open(); +} + +sub close() +{ + my $self = shift; + $self->{transport}->close(); +} + +sub readAll +{ + my $self = shift; + my $len = shift; + + return $self->{transport}->readAll($len); +} + +sub read +{ + my $self = shift; + my $len = shift; + my $ret; + + # Methinks Perl is already buffering these for us + return $self->{transport}->read($len); +} + +sub write +{ + my $self = shift; + my $buf = shift; + + $self->{wBuf} .= $buf; + if (length($self->{wBuf}) >= $self->{wBufSize}) { + $self->{transport}->write($self->{wBuf}); + $self->{wBuf} = ''; + } +} + +sub flush +{ + my $self = shift; + + if (length($self->{wBuf}) > 0) { + $self->{transport}->write($self->{wBuf}); + $self->{wBuf} = ''; + } + $self->{transport}->flush(); +} + + +# +# BufferedTransport factory creates buffered transport objects from transports +# +package Thrift::BufferedTransportFactory; + +sub new { + my $classname = shift; + my $self = {}; + + return bless($self,$classname); +} + +# +# Build a buffered transport from the base transport +# +# @return Thrift::BufferedTransport transport +# +sub getTransport +{ + my $self = shift; + my $trans = shift; + + my $buffered = Thrift::BufferedTransport->new($trans); + return $buffered; +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/FramedTransport.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/FramedTransport.pm new file mode 100644 index 00000000..6f2d2cf7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/FramedTransport.pm @@ -0,0 +1,193 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +# +# Framed transport. Writes and reads data in chunks that are stamped with +# their length. +# +# @package thrift.transport +# +package Thrift::FramedTransport; + +use base('Thrift::Transport'); + +sub new +{ + my $classname = shift; + my $transport = shift; + my $read = shift || 1; + my $write = shift || 1; + + my $self = { + transport => $transport, + read => $read, + write => $write, + wBuf => '', + rBuf => '', + }; + + return bless($self,$classname); +} + +sub isOpen +{ + my $self = shift; + return $self->{transport}->isOpen(); +} + +sub open +{ + my $self = shift; + + $self->{transport}->open(); +} + +sub close +{ + my $self = shift; + + if (defined $self->{transport}) { + $self->{transport}->close(); + } +} + +# +# Reads from the buffer. When more data is required reads another entire +# chunk and serves future reads out of that. +# +# @param int $len How much data +# +sub read +{ + + my $self = shift; + my $len = shift; + + if (!$self->{read}) { + return $self->{transport}->read($len); + } + + if (length($self->{rBuf}) == 0) { + $self->_readFrame(); + } + + + # Just return full buff + if ($len > length($self->{rBuf})) { + my $out = $self->{rBuf}; + $self->{rBuf} = ''; + return $out; + } + + # Return substr + my $out = substr($self->{rBuf}, 0, $len); + $self->{rBuf} = substr($self->{rBuf}, $len); + return $out; +} + +# +# Reads a chunk of data into the internal read buffer. +# (private) +sub _readFrame +{ + my $self = shift; + my $buf = $self->{transport}->readAll(4); + my @val = unpack('N', $buf); + my $sz = $val[0]; + + $self->{rBuf} = $self->{transport}->readAll($sz); +} + +# +# Writes some data to the pending output buffer. +# +# @param string $buf The data +# @param int $len Limit of bytes to write +# +sub write +{ + my $self = shift; + my $buf = shift; + my $len = shift; + + unless($self->{write}) { + return $self->{transport}->write($buf, $len); + } + + if ( defined $len && $len < length($buf)) { + $buf = substr($buf, 0, $len); + } + + $self->{wBuf} .= $buf; + } + +# +# Writes the output buffer to the stream in the format of a 4-byte length +# followed by the actual data. +# +sub flush +{ + my $self = shift; + + unless ($self->{write}) { + return $self->{transport}->flush(); + } + + my $out = pack('N', length($self->{wBuf})); + $out .= $self->{wBuf}; + $self->{transport}->write($out); + $self->{transport}->flush(); + $self->{wBuf} = ''; + +} + +# +# FramedTransport factory creates framed transport objects from transports +# +package Thrift::FramedTransportFactory; + +sub new { + my $classname = shift; + my $self = {}; + + return bless($self, $classname); +} + +# +# Build a framed transport from the base transport +# +# @return Thrift::FramedTransport transport +# +sub getTransport +{ + my $self = shift; + my $trans = shift; + + my $buffered = Thrift::FramedTransport->new($trans); + return $buffered; +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/HttpClient.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/HttpClient.pm new file mode 100644 index 00000000..d6fc8be3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/HttpClient.pm @@ -0,0 +1,200 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +use HTTP::Request; +use LWP::UserAgent; +use IO::String; + +package Thrift::HttpClient; + +use base('Thrift::Transport'); + +sub new +{ + my $classname = shift; + my $url = shift || 'http://localhost:9090'; + my $debugHandler = shift; + + my $out = IO::String->new; + binmode($out); + + my $self = { + url => $url, + out => $out, + debugHandler => $debugHandler, + debug => 0, + sendTimeout => 100, + recvTimeout => 750, + handle => undef, + }; + + return bless($self,$classname); +} + +sub setSendTimeout +{ + my $self = shift; + my $timeout = shift; + + $self->{sendTimeout} = $timeout; +} + +sub setRecvTimeout +{ + my $self = shift; + my $timeout = shift; + + $self->{recvTimeout} = $timeout; +} + + +# +#Sets debugging output on or off +# +# @param bool $debug +# +sub setDebug +{ + my $self = shift; + my $debug = shift; + + $self->{debug} = $debug; +} + +# +# Tests whether this is open +# +# @return bool true if the socket is open +# +sub isOpen +{ + return 1; +} + +sub open {} + +# +# Cleans up the buffer. +# +sub close +{ + my $self = shift; + if (defined($self->{io})) { + close($self->{io}); + $self->{io} = undef; + } +} + +# +# Guarantees that the full amount of data is read. +# +# @return string The data, of exact length +# @throws TTransportException if cannot read data +# +sub readAll +{ + my $self = shift; + my $len = shift; + + my $buf = $self->read($len); + + if (!defined($buf)) { + die new Thrift::TException('TSocket: Could not read '.$len.' bytes from input buffer'); + } + return $buf; +} + +# +# Read and return string +# +sub read +{ + my $self = shift; + my $len = shift; + + my $buf; + + my $in = $self->{in}; + + if (!defined($in)) { + die new Thrift::TException("Response buffer is empty, no request."); + } + eval { + my $ret = sysread($in, $buf, $len); + if (! defined($ret)) { + die new Thrift::TException("No more data available."); + } + }; if($@){ + die new Thrift::TException($@); + } + + return $buf; +} + +# +# Write string +# +sub write +{ + my $self = shift; + my $buf = shift; + $self->{out}->print($buf); +} + +# +# Flush output (do the actual HTTP/HTTPS request) +# +sub flush +{ + my $self = shift; + + my $ua = LWP::UserAgent->new('timeout' => ($self->{sendTimeout} / 1000), + 'agent' => 'Perl/THttpClient' + ); + $ua->default_header('Accept' => 'application/x-thrift'); + $ua->default_header('Content-Type' => 'application/x-thrift'); + $ua->cookie_jar({}); # hash to remember cookies between redirects + + my $out = $self->{out}; + $out->setpos(0); # rewind + my $buf = join('', <$out>); + + my $request = new HTTP::Request(POST => $self->{url}, undef, $buf); + my $response = $ua->request($request); + my $content_ref = $response->content_ref; + + my $in = IO::String->new($content_ref); + binmode($in); + $self->{in} = $in; + $in->setpos(0); # rewind + + # reset write buffer + $out = IO::String->new; + binmode($out); + $self->{out} = $out; +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm new file mode 100644 index 00000000..0b286878 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MemoryBuffer.pm @@ -0,0 +1,146 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +package Thrift::MemoryBuffer; +use base('Thrift::Transport'); + +sub new +{ + my $classname = shift; + + my $bufferSize= shift || 1024; + + my $self = { + buffer => '', + bufferSize=> $bufferSize, + wPos => 0, + rPos => 0, + }; + + return bless($self,$classname); +} + +sub isOpen +{ + return 1; +} + +sub open +{ + +} + +sub close +{ + +} + +sub peek +{ + my $self = shift; + return($self->{rPos} < $self->{wPos}); +} + + +sub getBuffer +{ + my $self = shift; + return $self->{buffer}; +} + +sub resetBuffer +{ + my $self = shift; + + my $new_buffer = shift || ''; + + $self->{buffer} = $new_buffer; + $self->{bufferSize} = length($new_buffer); + $self->{wPos} = length($new_buffer); + $self->{rPos} = 0; +} + +sub available +{ + my $self = shift; + return ($self->{wPos} - $self->{rPos}); +} + +sub read +{ + my $self = shift; + my $len = shift; + my $ret; + + my $avail = ($self->{wPos} - $self->{rPos}); + return '' if $avail == 0; + + #how much to give + my $give = $len; + $give = $avail if $avail < $len; + + $ret = substr($self->{buffer},$self->{rPos},$give); + + $self->{rPos} += $give; + + return $ret; +} + +sub readAll +{ + my $self = shift; + my $len = shift; + + my $avail = ($self->{wPos} - $self->{rPos}); + if ($avail < $len) { + die new TTransportException("Attempt to readAll($len) found only $avail available"); + } + + my $data = ''; + my $got = 0; + + while (($got = length($data)) < $len) { + $data .= $self->read($len - $got); + } + + return $data; +} + +sub write +{ + my $self = shift; + my $buf = shift; + + $self->{buffer} .= $buf; + $self->{wPos} += length($buf); +} + +sub flush +{ + +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MessageType.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MessageType.pm new file mode 100644 index 00000000..c8902cc5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MessageType.pm @@ -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. +# + +use strict; +use warnings; + +package Thrift::MessageType; + +use strict; + +use constant CALL => 1; +use constant REPLY => 2; +use constant EXCEPTION => 3; +use constant ONEWAY => 4; + +1; \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm new file mode 100644 index 00000000..421bf73f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProcessor.pm @@ -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. +# + +use strict; +use warnings; + +use Thrift; +use Thrift::Protocol; +use Thrift::MultiplexedProtocol; +use Thrift::ProtocolDecorator; +use Thrift::MessageType; + +package Thrift::StoredMessageProtocol; +use base qw(Thrift::ProtocolDecorator); + +use strict; + +sub new { + my $classname = shift; + my $protocol = shift; + my $fname = shift; + my $mtype = shift; + my $rseqid = shift; + my $self = $classname->SUPER::new($protocol); + + $self->{fname} = $fname; + $self->{mtype} = $mtype; + $self->{rseqid} = $rseqid; + + return bless($self,$classname); +} + +sub readMessageBegin +{ + my $self = shift; + my $name = shift; + my $type = shift; + my $seqid = shift; + + $$name = $self->{fname}; + $$type = $self->{mtype}; + $$seqid = $self->{rseqid}; +} + +package Thrift::MultiplexedProcessor; + +use strict; + +sub new { + my $classname = shift; + my $self = {}; + + $self->{serviceProcessorMap} = {}; + + return bless($self,$classname); +} + +sub registerProcessor { + my $self = shift; + my $serviceName = shift; + my $processor = shift; + + $self->{serviceProcessorMap}->{$serviceName} = $processor; +} + +sub process{ + my $self = shift; + my $input = shift; + my $output = shift; + + # + # Use the actual underlying protocol (e.g. BinaryProtocol) to read the + # message header. This pulls the message "off the wire", which we'll + # deal with at the end of this method. + # + + my ($fname, $mtype, $rseqid); + $input->readMessageBegin(\$fname, \$mtype, \$rseqid); + + + if ($mtype ne Thrift::MessageType::CALL && $mtype ne Thrift::MessageType::ONEWAY) { + die new Thrift::TException("This should not have happened!?"); + } + + # Extract the service name and the new Message name. + if (index($fname, Thrift::MultiplexedProtocol::SEPARATOR) == -1) { + die new Thrift::TException("Service name not found in message name: {$fname}. Did you " . + "forget to use a MultiplexProtocol in your client?"); + } + + (my $serviceName, my $messageName) = split(':', $fname, 2); + + if (!exists($self->{serviceProcessorMap}->{$serviceName})) { + die new Thrift::TException("Service name not found: {$serviceName}. Did you forget " . + "to call registerProcessor()?"); + } + + #Dispatch processing to the stored processor + my $processor = $self->{serviceProcessorMap}->{$serviceName}; + return $processor->process( + new Thrift::StoredMessageProtocol($input, $messageName, $mtype, $rseqid), $output + ); +} + +1; \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm new file mode 100644 index 00000000..83a4eaf3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/MultiplexedProtocol.pm @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use warnings; + +use Thrift::Protocol; +use Thrift::ProtocolDecorator; +use Thrift::MessageType; + +package Thrift::MultiplexedProtocol; +use base qw(Thrift::ProtocolDecorator); + +use strict; + +use constant SEPARATOR => ':'; + +sub new { + my $classname = shift; + my $protocol = shift; + my $serviceName = shift; + my $self = $classname->SUPER::new($protocol); + + $self->{serviceName} = $serviceName; + + return bless($self,$classname); +} + +# +# Writes the message header. +# Prepends the service name to the function name, separated by MultiplexedProtocol::SEPARATOR. +# +# @param string $name Function name. +# @param int $type Message type. +# @param int $seqid The sequence id of this message. +# +sub writeMessageBegin +{ + my $self = shift; + my ($name, $type, $seqid) = @_; + + if ($type == Thrift::MessageType::CALL || $type == Thrift::MessageType::ONEWAY) { + my $nameWithService = $self->{serviceName}.SEPARATOR.$name; + $self->SUPER::writeMessageBegin($nameWithService, $type, $seqid); + } + else { + $self->SUPER::writeMessageBegin($name, $type, $seqid); + } +} + +1; \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Protocol.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Protocol.pm new file mode 100644 index 00000000..3e9f0dd1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Protocol.pm @@ -0,0 +1,544 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; + +# +# Protocol exceptions +# +package TProtocolException; +use base('Thrift::TException'); + +use constant UNKNOWN => 0; +use constant INVALID_DATA => 1; +use constant NEGATIVE_SIZE => 2; +use constant SIZE_LIMIT => 3; +use constant BAD_VERSION => 4; +use constant NOT_IMPLEMENTED => 5; +use constant DEPTH_LIMIT => 6; + + +sub new { + my $classname = shift; + + my $self = $classname->SUPER::new(); + + return bless($self,$classname); +} + +# +# Protocol base class module. +# +package Thrift::Protocol; + +sub new { + my $classname = shift; + my $self = {}; + + my $trans = shift; + $self->{trans}= $trans; + + return bless($self,$classname); +} + +sub getTransport +{ + my $self = shift; + + return $self->{trans}; +} + +# +# Writes the message header +# +# @param string $name Function name +# @param int $type message type TMessageType::CALL or TMessageType::REPLY +# @param int $seqid The sequence id of this message +# +sub writeMessageBegin +{ + my ($name, $type, $seqid); + die "abstract"; +} + +# +# Close the message +# +sub writeMessageEnd { + die "abstract"; +} + +# +# Writes a struct header. +# +# @param string $name Struct name +# @throws TException on write error +# @return int How many bytes written +# +sub writeStructBegin { + my ($name); + + die "abstract"; +} + +# +# Close a struct. +# +# @throws TException on write error +# @return int How many bytes written +# +sub writeStructEnd { + die "abstract"; +} + +# +# Starts a field. +# +# @param string $name Field name +# @param int $type Field type +# @param int $fid Field id +# @throws TException on write error +# @return int How many bytes written +# +sub writeFieldBegin { + my ($fieldName, $fieldType, $fieldId); + + die "abstract"; +} + +sub writeFieldEnd { + die "abstract"; +} + +sub writeFieldStop { + die "abstract"; +} + +sub writeMapBegin { + my ($keyType, $valType, $size); + + die "abstract"; +} + +sub writeMapEnd { + die "abstract"; +} + +sub writeListBegin { + my ($elemType, $size); + die "abstract"; +} + +sub writeListEnd { + die "abstract"; +} + +sub writeSetBegin { + my ($elemType, $size); + die "abstract"; +} + +sub writeSetEnd { + die "abstract"; +} + +sub writeBool { + my ($bool); + die "abstract"; +} + +sub writeByte { + my ($byte); + die "abstract"; +} + +sub writeI16 { + my ($i16); + die "abstract"; +} + +sub writeI32 { + my ($i32); + die "abstract"; +} + +sub writeI64 { + my ($i64); + die "abstract"; +} + +sub writeDouble { + my ($dub); + die "abstract"; +} + +sub writeString +{ + my ($str); + die "abstract"; +} + +# +# Reads the message header +# +# @param string $name Function name +# @param int $type message type TMessageType::CALL or TMessageType::REPLY +# @parem int $seqid The sequence id of this message +# +sub readMessageBegin +{ + my ($name, $type, $seqid); + die "abstract"; +} + +# +# Read the close of message +# +sub readMessageEnd +{ + die "abstract"; +} + +sub readStructBegin +{ + my($name); + + die "abstract"; +} + +sub readStructEnd +{ + die "abstract"; +} + +sub readFieldBegin +{ + my ($name, $fieldType, $fieldId); + die "abstract"; +} + +sub readFieldEnd +{ + die "abstract"; +} + +sub readMapBegin +{ + my ($keyType, $valType, $size); + die "abstract"; +} + +sub readMapEnd +{ + die "abstract"; +} + +sub readListBegin +{ + my ($elemType, $size); + die "abstract"; +} + +sub readListEnd +{ + die "abstract"; +} + +sub readSetBegin +{ + my ($elemType, $size); + die "abstract"; +} + +sub readSetEnd +{ + die "abstract"; +} + +sub readBool +{ + my ($bool); + die "abstract"; +} + +sub readByte +{ + my ($byte); + die "abstract"; +} + +sub readI16 +{ + my ($i16); + die "abstract"; +} + +sub readI32 +{ + my ($i32); + die "abstract"; +} + +sub readI64 +{ + my ($i64); + die "abstract"; +} + +sub readDouble +{ + my ($dub); + die "abstract"; +} + +sub readString +{ + my ($str); + die "abstract"; +} + +# +# The skip function is a utility to parse over unrecognized data without +# causing corruption. +# +# @param TType $type What type is it +# +sub skip +{ + my $self = shift; + my $type = shift; + + my $ref; + my $result; + my $i; + + if($type == TType::BOOL) + { + return $self->readBool(\$ref); + } + elsif($type == TType::BYTE){ + return $self->readByte(\$ref); + } + elsif($type == TType::I16){ + return $self->readI16(\$ref); + } + elsif($type == TType::I32){ + return $self->readI32(\$ref); + } + elsif($type == TType::I64){ + return $self->readI64(\$ref); + } + elsif($type == TType::DOUBLE){ + return $self->readDouble(\$ref); + } + elsif($type == TType::STRING) + { + return $self->readString(\$ref); + } + elsif($type == TType::STRUCT) + { + $result = $self->readStructBegin(\$ref); + while (1) { + my ($ftype,$fid); + $result += $self->readFieldBegin(\$ref, \$ftype, \$fid); + if ($ftype == TType::STOP) { + last; + } + $result += $self->skip($ftype); + $result += $self->readFieldEnd(); + } + $result += $self->readStructEnd(); + return $result; + } + elsif($type == TType::MAP) + { + my($keyType,$valType,$size); + $result = $self->readMapBegin(\$keyType, \$valType, \$size); + for ($i = 0; $i < $size; $i++) { + $result += $self->skip($keyType); + $result += $self->skip($valType); + } + $result += $self->readMapEnd(); + return $result; + } + elsif($type == TType::SET) + { + my ($elemType,$size); + $result = $self->readSetBegin(\$elemType, \$size); + for ($i = 0; $i < $size; $i++) { + $result += $self->skip($elemType); + } + $result += $self->readSetEnd(); + return $result; + } + elsif($type == TType::LIST) + { + my ($elemType,$size); + $result = $self->readListBegin(\$elemType, \$size); + for ($i = 0; $i < $size; $i++) { + $result += $self->skip($elemType); + } + $result += $self->readListEnd(); + return $result; + } + + die new Thrift::TException("Type $type not recognised --- corrupt data?"); + + } + +# +# Utility for skipping binary data +# +# @param TTransport $itrans TTransport object +# @param int $type Field type +# +sub skipBinary +{ + my $self = shift; + my $itrans = shift; + my $type = shift; + + if($type == TType::BOOL) + { + return $itrans->readAll(1); + } + elsif($type == TType::BYTE) + { + return $itrans->readAll(1); + } + elsif($type == TType::I16) + { + return $itrans->readAll(2); + } + elsif($type == TType::I32) + { + return $itrans->readAll(4); + } + elsif($type == TType::I64) + { + return $itrans->readAll(8); + } + elsif($type == TType::DOUBLE) + { + return $itrans->readAll(8); + } + elsif( $type == TType::STRING ) + { + my @len = unpack('N', $itrans->readAll(4)); + my $len = $len[0]; + if ($len > 0x7fffffff) { + $len = 0 - (($len - 1) ^ 0xffffffff); + } + return 4 + $itrans->readAll($len); + } + elsif( $type == TType::STRUCT ) + { + my $result = 0; + while (1) { + my $ftype = 0; + my $fid = 0; + my $data = $itrans->readAll(1); + my @arr = unpack('c', $data); + $ftype = $arr[0]; + if ($ftype == TType::STOP) { + last; + } + # I16 field id + $result += $itrans->readAll(2); + $result += $self->skipBinary($itrans, $ftype); + } + return $result; + } + elsif($type == TType::MAP) + { + # Ktype + my $data = $itrans->readAll(1); + my @arr = unpack('c', $data); + my $ktype = $arr[0]; + # Vtype + $data = $itrans->readAll(1); + @arr = unpack('c', $data); + my $vtype = $arr[0]; + # Size + $data = $itrans->readAll(4); + @arr = unpack('N', $data); + my $size = $arr[0]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + my $result = 6; + for (my $i = 0; $i < $size; $i++) { + $result += $self->skipBinary($itrans, $ktype); + $result += $self->skipBinary($itrans, $vtype); + } + return $result; + } + elsif($type == TType::SET || $type == TType::LIST) + { + # Vtype + my $data = $itrans->readAll(1); + my @arr = unpack('c', $data); + my $vtype = $arr[0]; + # Size + $data = $itrans->readAll(4); + @arr = unpack('N', $data); + my $size = $arr[0]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + my $result = 5; + for (my $i = 0; $i < $size; $i++) { + $result += $self->skipBinary($itrans, $vtype); + } + return $result; + } + + die new Thrift::TException("Type $type not recognised --- corrupt data?"); +} + +# +# Protocol factory creates protocol objects from transports +# +package TProtocolFactory; + + +sub new { + my $classname = shift; + my $self = {}; + + return bless($self,$classname); +} + +# +# Build a protocol from the base transport +# +# @return TProtcol protocol +# +sub getProtocol +{ + my ($trans); + die "interface"; +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm new file mode 100644 index 00000000..81202007 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ProtocolDecorator.pm @@ -0,0 +1,360 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use warnings; + +use Thrift::Protocol; + +package Thrift::ProtocolDecorator; +use base qw(Thrift::Protocol); + +sub new { + my $classname = shift; + my $protocol = shift; + my $self = $classname->SUPER::new($protocol->getTransport()); + + $self->{concreteProtocol} = $protocol; + + return bless($self,$classname); +} + +# +# Writes the message header +# +# @param string $name Function name +# @param int $type message type TMessageType::CALL or TMessageType::REPLY +# @param int $seqid The sequence id of this message +# +sub writeMessageBegin { + my $self = shift; + my ($name, $type, $seqid) = @_; + + return $self->{concreteProtocol}->writeMessageBegin($name, $type, $seqid); +} + +# +# Close the message +# +sub writeMessageEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeMessageEnd(); +} + +# +# Writes a struct header. +# +# @param string $name Struct name +# @throws TException on write error +# @return int How many bytes written +# +sub writeStructBegin { + my $self = shift; + my ($name) = @_; + + return $self->{concreteProtocol}->writeStructBegin($name); +} + +# +# Close a struct. +# +# @throws TException on write error +# @return int How many bytes written +# +sub writeStructEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeStructEnd(); +} + +# +# Starts a field. +# +# @param string $name Field name +# @param int $type Field type +# @param int $fid Field id +# @throws TException on write error +# @return int How many bytes written +# +sub writeFieldBegin { + my $self = shift; + my ($fieldName, $fieldType, $fieldId) = @_; + + return $self->{concreteProtocol}->writeFieldBegin($fieldName, $fieldType, $fieldId); +} + +sub writeFieldEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeFieldEnd(); +} + +sub writeFieldStop { + my $self = shift; + + return $self->{concreteProtocol}->writeFieldStop(); +} + +sub writeMapBegin { + my $self = shift; + my ($keyType, $valType, $size) = @_; + + return $self->{concreteProtocol}->writeMapBegin($keyType, $valType, $size); +} + +sub writeMapEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeMapEnd(); +} + +sub writeListBegin { + my $self = shift; + my ($elemType, $size) = @_; + + return $self->{concreteProtocol}->writeListBegin($elemType, $size); +} + +sub writeListEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeListEnd(); +} + +sub writeSetBegin { + my $self = shift; + my ($elemType, $size) = @_; + + return $self->{concreteProtocol}->writeSetBegin($elemType, $size); +} + +sub writeSetEnd { + my $self = shift; + + return $self->{concreteProtocol}->writeListEnd(); +} + +sub writeBool { + my $self = shift; + my $bool = shift; + + return $self->{concreteProtocol}->writeBool($bool); +} + +sub writeByte { + my $self = shift; + my $byte = shift; + + return $self->{concreteProtocol}->writeByte($byte); +} + +sub writeI16 { + my $self = shift; + my $i16 = shift; + + return $self->{concreteProtocol}->writeI16($i16); +} + +sub writeI32 { + my $self = shift; + my ($i32) = @_; + + return $self->{concreteProtocol}->writeI32($i32); + +} + +sub writeI64 { + my $self = shift; + my $i64 = shift; + + return $self->{concreteProtocol}->writeI64($i64); +} + +sub writeDouble { + my $self = shift; + my $dub = shift; + + return $self->{concreteProtocol}->writeDouble($dub); +} + +sub writeString { + my $self = shift; + my $str = shift; + + return $self->{concreteProtocol}->writeString($str); +} + +# +# Reads the message header +# +# @param string $name Function name +# @param int $type message type TMessageType::CALL or TMessageType::REPLY +# @parem int $seqid The sequence id of this message +# +sub readMessageBegin +{ + my $self = shift; + my ($name, $type, $seqid) = @_; + + return $self->{concreteProtocol}->readMessageBegin($name, $type, $seqid); +} + +# +# Read the close of message +# +sub readMessageEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readMessageEnd(); +} + +sub readStructBegin +{ + my $self = shift; + my $name = shift; + + return $self->{concreteProtocol}->readStructBegin($name); +} + +sub readStructEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readStructEnd(); +} + +sub readFieldBegin +{ + my $self = shift; + my ($name, $fieldType, $fieldId) = @_; + + return $self->{concreteProtocol}->readFieldBegin($name, $fieldType, $fieldId); +} + +sub readFieldEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readFieldEnd(); +} + +sub readMapBegin +{ + my $self = shift; + my ($keyType, $valType, $size) = @_; + + return $self->{concreteProtocol}->readMapBegin($keyType, $valType, $size); +} + +sub readMapEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readMapEnd(); +} + +sub readListBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return $self->{concreteProtocol}->readListBegin($elemType, $size); +} + +sub readListEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readListEnd(); +} + +sub readSetBegin +{ + my $self = shift; + my ($elemType, $size) = @_; + + return $self->{concreteProtocol}->readSetBegin($elemType, $size); +} + +sub readSetEnd +{ + my $self = shift; + + return $self->{concreteProtocol}->readSetEnd(); +} + +sub readBool +{ + my $self = shift; + my $bool = shift; + + return $self->{concreteProtocol}->readBool($bool); +} + +sub readByte +{ + my $self = shift; + my $byte = shift; + + return $self->{concreteProtocol}->readByte($byte); +} + +sub readI16 +{ + my $self = shift; + my $i16 = shift; + + return $self->{concreteProtocol}->readI16($i16); +} + +sub readI32 +{ + my $self = shift; + my $i32 = shift; + + return $self->{concreteProtocol}->readI32($i32); +} + +sub readI64 +{ + my $self = shift; + my $i64 = shift; + + return $self->{concreteProtocol}->readI64($i64); +} + +sub readDouble +{ + my $self = shift; + my $dub = shift; + + return $self->{concreteProtocol}->readDouble($dub); +} + +sub readString +{ + my $self = shift; + my $str = shift; + + return $self->{concreteProtocol}->readString($str); +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm new file mode 100644 index 00000000..2efdfffe --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLServerSocket.pm @@ -0,0 +1,68 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::SSLSocket; + +use IO::Socket::SSL; +use IO::Select; + +package Thrift::SSLServerSocket; + +use base qw( Thrift::ServerSocket ); + +# +# Constructor. +# Takes a hash: +# See Thirft::Socket for base class parameters. +# @param[in] ca certificate authority filename - not required +# @param[in] cert certificate filename; may contain key in which case key is not required +# @param[in] key private key filename for the certificate if it is not inside the cert file +# +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::new(@_); + return bless($self, $classname); +} + +sub __client +{ + return new Thrift::SSLSocket(); +} + +sub __listen +{ + my $self = shift; + return IO::Socket::SSL->new(LocalAddr => $self->{host}, + LocalPort => $self->{port}, + Proto => 'tcp', + Listen => $self->{queue}, + ReuseAddr => 1, + SSL_cert_file => $self->{cert}, + SSL_key_file => $self->{key}, + SSL_ca_file => $self->{ca}); +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLSocket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLSocket.pm new file mode 100644 index 00000000..0d2edf85 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/SSLSocket.pm @@ -0,0 +1,93 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +use IO::Socket::SSL; +use IO::Select; + +package Thrift::SSLSocket; + +# TODO: Does not provide cipher selection or authentication hooks yet. + +use base qw( Thrift::Socket ); + +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::new(@_); + + return bless($self, $classname); +} + +sub __open +{ + my $self = shift; + return IO::Socket::SSL->new(PeerAddr => $self->{host}, + PeerPort => $self->{port}, + Proto => 'tcp', + Timeout => $self->{sendTimeout} / 1000); +} + +sub __close +{ + my $self = shift; + my $sock = ($self->{handle}->handles())[0]; + if ($sock) { + $sock->close(SSL_no_shutdown => 1); + } +} + +sub __recv +{ + my $self = shift; + my $sock = shift; + my $len = shift; + my $buf = undef; + if ($sock) { + sysread($sock, $buf, $len); + } + return $buf; +} + +sub __send +{ + my $self = shift; + my $sock = shift; + my $buf = shift; + return syswrite($sock, $buf); +} + +sub __wait +{ + my $self = shift; + my $sock = ($self->{handle}->handles())[0]; + if ($sock and $sock->pending() eq 0) { + return $self->SUPER::__wait(); + } + return $sock; +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Server.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Server.pm new file mode 100644 index 00000000..ac1e17dd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Server.pm @@ -0,0 +1,315 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::BufferedTransport; +use Thrift::BinaryProtocol; + +# +# Server base class module +# +package Thrift::Server; + +# 3 possible constructors: +# 1. (processor, serverTransport) +# 2. (processor, serverTransport, transportFactory, protocolFactory) +# 3. (processor, serverTransport, +# inputTransportFactory, outputTransportFactory, +# inputProtocolFactory, outputProtocolFactory) +sub new +{ + my $classname = shift; + my @args = @_; + + my $self; + + if (scalar @args == 2) + { + $self = _init($args[0], $args[1], + Thrift::BufferedTransportFactory->new(), + Thrift::BufferedTransportFactory->new(), + Thrift::BinaryProtocolFactory->new(), + Thrift::BinaryProtocolFactory->new()); + } + elsif (scalar @args == 4) + { + $self = _init($args[0], $args[1], $args[2], $args[2], $args[3], $args[3]); + } + elsif (scalar @args == 6) + { + $self = _init($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]); + } + else + { + die "Thrift::Server expects exactly 2, 4, or 6 args"; + } + + return bless($self,$classname); +} + +sub _init +{ + my $processor = shift; + my $serverTransport = shift; + my $inputTransportFactory = shift; + my $outputTransportFactory = shift; + my $inputProtocolFactory = shift; + my $outputProtocolFactory = shift; + + my $self = { + processor => $processor, + serverTransport => $serverTransport, + inputTransportFactory => $inputTransportFactory, + outputTransportFactory => $outputTransportFactory, + inputProtocolFactory => $inputProtocolFactory, + outputProtocolFactory => $outputProtocolFactory, + }; +} + +sub serve +{ + die "abstract"; +} + +sub _clientBegin +{ + my $self = shift; + my $iprot = shift; + my $oprot = shift; + + if (exists $self->{serverEventHandler} and + defined $self->{serverEventHandler}) + { + $self->{serverEventHandler}->clientBegin($iprot, $oprot); + } +} + +sub _handleException +{ + my $self = shift; + my $e = shift; + + if ($e =~ m/TException/ and exists $e->{message}) { + my $message = $e->{message}; + my $code = $e->{code}; + my $out = $code . ':' . $message; + + $message =~ m/TTransportException/ and die $out; + if ($message =~ m/Socket/) { + # suppress Socket messages + } else { + warn $out; + } + } else { + warn $e; + } +} + +# +# SimpleServer from the Server base class that handles one connection at a time +# +package Thrift::SimpleServer; +use base qw( Thrift::Server ); + +sub new +{ + my $classname = shift; + my @args = @_; + + my $self = $classname->SUPER::new(@args); + return bless($self,$classname); +} + +sub serve +{ + my $self = shift; + + $self->{serverTransport}->listen(); + while (1) + { + my $client = $self->{serverTransport}->accept(); + my $itrans = $self->{inputTransportFactory}->getTransport($client); + my $otrans = $self->{outputTransportFactory}->getTransport($client); + my $iprot = $self->{inputProtocolFactory}->getProtocol($itrans); + my $oprot = $self->{outputProtocolFactory}->getProtocol($otrans); + eval { + $self->_clientBegin($iprot, $oprot); + while (1) + { + $self->{processor}->process($iprot, $oprot); + } + }; if($@) { + $self->_handleException($@); + } + + $itrans->close(); + $otrans->close(); + } +} + + +# +# ForkingServer that forks a new process for each request +# +package Thrift::ForkingServer; +use base qw( Thrift::Server ); + +use POSIX ":sys_wait_h"; + +sub new +{ + my $classname = shift; + my @args = @_; + + my $self = $classname->SUPER::new(@args); + return bless($self,$classname); +} + + +sub serve +{ + my $self = shift; + + # THRIFT-3848: without ignoring SIGCHLD, perl ForkingServer goes into a tight loop + $SIG{CHLD} = 'IGNORE'; + + $self->{serverTransport}->listen(); + while (1) + { + my $client = $self->{serverTransport}->accept(); + $self->_client($client); + } +} + +sub _client +{ + my $self = shift; + my $client = shift; + + eval { + my $itrans = $self->{inputTransportFactory}->getTransport($client); + my $otrans = $self->{outputTransportFactory}->getTransport($client); + + my $iprot = $self->{inputProtocolFactory}->getProtocol($itrans); + my $oprot = $self->{outputProtocolFactory}->getProtocol($otrans); + + $self->_clientBegin($iprot, $oprot); + + my $pid = fork(); + + if ($pid) #parent + { + $self->_parent($pid, $itrans, $otrans); + } else { + $self->_child($itrans, $otrans, $iprot, $oprot); + } + }; if($@) { + $self->_handleException($@); + } +} + +sub _parent +{ + my $self = shift; + my $pid = shift; + my $itrans = shift; + my $otrans = shift; + + # add before collect, otherwise you race w/ waitpid + $self->{children}->{$pid} = 1; + $self->_collectChildren(); + + # Parent must close socket or the connection may not get closed promptly + $self->tryClose($itrans); + $self->tryClose($otrans); +} + +sub _child +{ + my $self = shift; + my $itrans = shift; + my $otrans = shift; + my $iprot = shift; + my $oprot = shift; + + my $ecode = 0; + eval { + while (1) + { + $self->{processor}->process($iprot, $oprot); + } + }; if($@) { + $ecode = 1; + $self->_handleException($@); + } + + $self->tryClose($itrans); + $self->tryClose($otrans); + + exit($ecode); +} + +sub tryClose +{ + my $self = shift; + my $file = shift; + + eval { + if (defined $file) + { + $file->close(); + } + }; if($@) { + if ($@ =~ m/TException/ and exists $@->{message}) { + my $message = $@->{message}; + my $code = $@->{code}; + my $out = $code . ':' . $message; + + warn $out; + } else { + warn $@; + } + } +} + +sub _collectChildren +{ + my $self = shift; + + while (scalar keys %{$self->{children}}) + { + my $pid = waitpid(-1, WNOHANG); + + if ($pid>0) + { + delete $self->{children}->{$pid}; + } + else + { + last; + } + } +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ServerSocket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ServerSocket.pm new file mode 100644 index 00000000..89664f69 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/ServerSocket.pm @@ -0,0 +1,114 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use IO::Socket::INET; +use IO::Select; +use Thrift; +use Thrift::Socket; + +package Thrift::ServerSocket; + +use base qw( Thrift::ServerTransport ); + +# +# Constructor. +# Legacy construction takes one argument, port number. +# New construction takes a hash: +# @param[in] host host interface to listen on (undef = all interfaces) +# @param[in] port port number to listen on (required) +# @param[in] queue the listen queue size (default if not specified is 128) +# @example my $serversock = new Thrift::ServerSocket(host => undef, port => port) +# +sub new +{ + my $classname = shift; + my $args = shift; + my $self; + + # Support both old-style "port number" construction and newer... + if (ref($args) eq 'HASH') { + $self = $args; + } else { + $self = { port => $args }; + } + + if (not defined $self->{queue}) { + $self->{queue} = 128; + } + + return bless($self, $classname); +} + +sub listen +{ + my $self = shift; + + my $sock = $self->__listen() || do { + my $error = ref($self) . ': Could not bind to ' . '*:' . $self->{port} . ' (' . $! . ')'; + + if ($self->{debug}) { + $self->{debugHandler}->($error); + } + + die new Thrift::TException($error); + }; + + $self->{handle} = $sock; +} + +sub accept +{ + my $self = shift; + + if ( exists $self->{handle} and defined $self->{handle} ) + { + my $client = $self->{handle}->accept(); + my $result = $self->__client(); + $result->{handle} = new IO::Select($client); + return $result; + } + + return 0; +} + +### +### Overridable methods +### + +sub __client +{ + return new Thrift::Socket(); +} + +sub __listen +{ + my $self = shift; + return IO::Socket::INET->new(LocalAddr => $self->{host}, + LocalPort => $self->{port}, + Proto => 'tcp', + Listen => $self->{queue}, + ReuseAddr => 1); +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Socket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Socket.pm new file mode 100644 index 00000000..eaf8b9e2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Socket.pm @@ -0,0 +1,317 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +use IO::Socket::INET; +use IO::Select; + +package Thrift::Socket; + +use base qw( Thrift::Transport ); + +sub new +{ + my $classname = shift; + my $host = shift || "localhost"; + my $port = shift || 9090; + my $debugHandler = shift; + + my $self = { + host => $host, + port => $port, + debugHandler => $debugHandler, + debug => 0, + sendTimeout => 10000, + recvTimeout => 10000, + handle => undef, + }; + + return bless($self,$classname); +} + + +sub setSendTimeout +{ + my $self = shift; + my $timeout = shift; + + $self->{sendTimeout} = $timeout; +} + +sub setRecvTimeout +{ + my $self = shift; + my $timeout = shift; + + $self->{recvTimeout} = $timeout; +} + + +# +#Sets debugging output on or off +# +# @param bool $debug +# +sub setDebug +{ + my $self = shift; + my $debug = shift; + + $self->{debug} = $debug; +} + +# +# Tests whether this is open +# +# @return bool true if the socket is open +# +sub isOpen +{ + my $self = shift; + + if( defined $self->{handle} ){ + return ($self->{handle}->handles())[0]->connected; + } + + return 0; +} + +# +# Connects the socket. +# +sub open +{ + my $self = shift; + + my $sock = $self->__open() || do { + my $error = ref($self).': Could not connect to '.$self->{host}.':'.$self->{port}.' ('.$!.')'; + + if ($self->{debug}) { + $self->{debugHandler}->($error); + } + + die new Thrift::TException($error); + }; + + $self->{handle} = new IO::Select( $sock ); +} + +# +# Closes the socket. +# +sub close +{ + my $self = shift; + if( defined $self->{handle} ) { + $self->__close(); + } +} + +# +# Uses stream get contents to do the reading +# +# @param int $len How many bytes +# @return string Binary data +# +sub readAll +{ + my $self = shift; + my $len = shift; + + + return unless defined $self->{handle}; + + my $pre = ""; + while (1) { + + my $sock = $self->__wait(); + my $buf = $self->__recv($sock, $len); + + if (!defined $buf || $buf eq '') { + + die new Thrift::TException(ref($self).': Could not read '.$len.' bytes from '. + $self->{host}.':'.$self->{port}); + + } elsif ((my $sz = length($buf)) < $len) { + + $pre .= $buf; + $len -= $sz; + + } else { + return $pre.$buf; + } + } +} + +# +# Read from the socket +# +# @param int $len How many bytes +# @return string Binary data +# +sub read +{ + my $self = shift; + my $len = shift; + + return unless defined $self->{handle}; + + my $sock = $self->__wait(); + my $buf = $self->__recv($sock, $len); + + if (!defined $buf || $buf eq '') { + + die new TException(ref($self).': Could not read '.$len.' bytes from '. + $self->{host}.':'.$self->{port}); + + } + + return $buf; +} + + +# +# Write to the socket. +# +# @param string $buf The data to write +# +sub write +{ + my $self = shift; + my $buf = shift; + + return unless defined $self->{handle}; + + while (length($buf) > 0) { + #check for timeout + my @sockets = $self->{handle}->can_write( $self->{sendTimeout} / 1000 ); + + if(@sockets == 0){ + die new Thrift::TException(ref($self).': timed out writing to bytes from '. + $self->{host}.':'.$self->{port}); + } + + my $sent = $self->__send($sockets[0], $buf); + + if (!defined $sent || $sent == 0 ) { + + die new Thrift::TException(ref($self).': Could not write '.length($buf).' bytes '. + $self->{host}.':'.$self->{host}); + + } + + $buf = substr($buf, $sent); + } +} + +# +# Flush output to the socket. +# +sub flush +{ + my $self = shift; + + return unless defined $self->{handle}; + + my $ret = ($self->{handle}->handles())[0]->flush; +} + +### +### Overridable methods +### + +# +# Open a connection to a server. +# +sub __open +{ + my $self = shift; + return IO::Socket::INET->new(PeerAddr => $self->{host}, + PeerPort => $self->{port}, + Proto => 'tcp', + Timeout => $self->{sendTimeout} / 1000); +} + +# +# Close the connection +# +sub __close +{ + my $self = shift; + CORE::close(($self->{handle}->handles())[0]); +} + +# +# Read data +# +# @param[in] $sock the socket +# @param[in] $len the length to read +# @returns the data buffer that was read +# +sub __recv +{ + my $self = shift; + my $sock = shift; + my $len = shift; + my $buf = undef; + $sock->recv($buf, $len); + return $buf; +} + +# +# Send data +# +# @param[in] $sock the socket +# @param[in] $buf the data buffer +# @returns the number of bytes written +# +sub __send +{ + my $self = shift; + my $sock = shift; + my $buf = shift; + return $sock->send($buf); +} + +# +# Wait for data to be readable +# +# @returns a socket that can be read +# +sub __wait +{ + my $self = shift; + my @sockets = $self->{handle}->can_read( $self->{recvTimeout} / 1000 ); + + if (@sockets == 0) { + die new Thrift::TException(ref($self).': timed out reading from '. + $self->{host}.':'.$self->{port}); + } + + return $sockets[0]; +} + + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Transport.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Transport.pm new file mode 100644 index 00000000..5ec6feee --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/Transport.pm @@ -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. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; + +# +# Transport exceptions +# +package TTransportException; +use base('Thrift::TException'); + +use constant UNKNOWN => 0; +use constant NOT_OPEN => 1; +use constant ALREADY_OPEN => 2; +use constant TIMED_OUT => 3; +use constant END_OF_FILE => 4; + +sub new{ + my $classname = shift; + my $self = $classname->SUPER::new(@_); + + return bless($self,$classname); +} + +package Thrift::Transport; + +# +# Whether this transport is open. +# +# @return boolean true if open +# +sub isOpen +{ + die "abstract"; +} + +# +# Open the transport for reading/writing +# +# @throws TTransportException if cannot open +# +sub open +{ + die "abstract"; +} + +# +# Close the transport. +# +sub close +{ + die "abstract"; +} + +# +# Read some data into the array. +# +# @param int $len How much to read +# @return string The data that has been read +# @throws TTransportException if cannot read any more data +# +sub read +{ + my ($len); + die("abstract"); +} + +# +# Guarantees that the full amount of data is read. +# +# @return string The data, of exact length +# @throws TTransportException if cannot read data +# +sub readAll +{ + my $self = shift; + my $len = shift; + + my $data = ''; + my $got = 0; + + while (($got = length($data)) < $len) { + $data .= $self->read($len - $got); + } + + return $data; +} + +# +# Writes the given data out. +# +# @param string $buf The data to write +# @throws TTransportException if writing fails +# +sub write +{ + my ($buf); + die "abstract"; +} + +# +# Flushes any pending data out of a buffer +# +# @throws TTransportException if a writing error occurs +# +sub flush {} + + +# +# TransportFactory creates transport objects from transports +# +package Thrift::TransportFactory; + +sub new { + my $classname = shift; + my $self = {}; + + return bless($self,$classname); +} + +# +# Build a transport from the base transport +# +# @return Thrift::Transport transport +# +sub getTransport +{ + my $self = shift; + my $trans = shift; + + return $trans; +} + + +# +# ServerTransport base class module +# +package Thrift::ServerTransport; + +sub listen +{ + die "abstract"; +} + +sub accept +{ + die "abstract"; +} + +sub close +{ + die "abstract"; +} + + +1; + diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm new file mode 100644 index 00000000..3251a00e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixServerSocket.pm @@ -0,0 +1,84 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::UnixSocket; + +use IO::Socket::UNIX; +use IO::Select; + +package Thrift::UnixServerSocket; + +use base qw( Thrift::ServerSocket ); + +# +# Constructor. +# If a single argument is given that is not a hash, that is the unix domain socket path. +# If a single argument is given that is a hash: +# @param[in] path unix domain socket file name +# @param[in] queue the listen queue size (default is not specified is supplied by ServerSocket) +# @example my $serversock = new Thrift::UnixServerSocket($path); +# @example my $serversock = new Thrift::UnixServerSocket(path => "somepath", queue => 64); +# +sub new +{ + my $classname = shift; + my $args = shift; + my $self; + + if (ref($args) eq 'HASH') { + $self = $classname->SUPER::new($args); + } else { + $self = $classname->SUPER::new(); + $self->{path} = $args; + } + + return bless($self, $classname); +} + +sub __client +{ + return new Thrift::UnixSocket(); +} + +sub __listen +{ + my $self = shift; + + my $sock = IO::Socket::UNIX->new( + Type => IO::Socket::SOCK_STREAM, + Local => $self->{path}, + Listen => $self->{queue}) + || do { + my $error = 'UnixServerSocket: Could not bind to ' . + $self->{path} . ' (' . $! . ')'; + if ($self->{debug}) { + $self->{debugHandler}->($error); + } + die new Thrift::TException($error); + }; + + return $sock; +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixSocket.pm b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixSocket.pm new file mode 100644 index 00000000..e8317b62 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/lib/Thrift/UnixSocket.pm @@ -0,0 +1,68 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 5.6.0; +use strict; +use warnings; + +use Thrift; +use Thrift::Transport; + +use IO::Socket::UNIX; +use IO::Select; + +package Thrift::UnixSocket; + +use base qw( Thrift::Socket ); + +# +# Constructor. +# Takes a unix domain socket filename. +# See Thirft::Socket for base class parameters. +# @param[in] path path to unix socket file +# @example my $sock = new Thrift::UnixSocket($path); +# +sub new +{ + my $classname = shift; + my $self = $classname->SUPER::new(); + $self->{path} = shift; + return bless($self, $classname); +} + +sub __open +{ + my $self = shift; + + my $sock = IO::Socket::UNIX->new( + Type => IO::Socket::SOCK_STREAM, + Peer => $self->{path}) + || do { + my $error = 'UnixSocket: Could not connect to ' . + $self->{path} . ' (' . $! . ')'; + if ($self->{debug}) { + $self->{debugHandler}->($error); + } + die new Thrift::TException($error); + }; + + return $sock; +} + +1; diff --git a/vendor/src/github.com/apache/thrift/lib/perl/test.pl b/vendor/src/github.com/apache/thrift/lib/perl/test.pl new file mode 100644 index 00000000..7e068402 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/test.pl @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use warnings; + +use Test::Harness; + +runtests(@ARGV); diff --git a/vendor/src/github.com/apache/thrift/lib/perl/test/Makefile.am b/vendor/src/github.com/apache/thrift/lib/perl/test/Makefile.am new file mode 100644 index 00000000..de039718 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/test/Makefile.am @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +EXTRA_DIST = memory_buffer.t processor.t multiplex.t diff --git a/vendor/src/github.com/apache/thrift/lib/perl/test/memory_buffer.t b/vendor/src/github.com/apache/thrift/lib/perl/test/memory_buffer.t new file mode 100644 index 00000000..8fa9fd72 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/test/memory_buffer.t @@ -0,0 +1,53 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use Test::More tests => 6; + +use strict; +use warnings; + +use Data::Dumper; + +use Thrift::BinaryProtocol; +use Thrift::MemoryBuffer; + +use ThriftTest::Types; + + +my $transport = Thrift::MemoryBuffer->new(); +my $protocol = Thrift::BinaryProtocol->new($transport); + +my $a = ThriftTest::Xtruct->new(); +$a->i32_thing(10); +$a->i64_thing(30); +$a->string_thing('Hello, world!'); +$a->write($protocol); + +my $b = ThriftTest::Xtruct->new(); +$b->read($protocol); +is($b->i32_thing, $a->i32_thing); +is($b->i64_thing, $a->i64_thing); +is($b->string_thing, $a->string_thing); + +$b->write($protocol); +my $c = ThriftTest::Xtruct->new(); +$c->read($protocol); +is($c->i32_thing, $a->i32_thing); +is($c->i64_thing, $a->i64_thing); +is($c->string_thing, $a->string_thing); diff --git a/vendor/src/github.com/apache/thrift/lib/perl/test/multiplex.t b/vendor/src/github.com/apache/thrift/lib/perl/test/multiplex.t new file mode 100644 index 00000000..76f2706f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/test/multiplex.t @@ -0,0 +1,203 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use Test::More tests => 6; + +use strict; +use warnings; + +use Thrift; +use Thrift::Socket; +use Thrift::Server; +use Thrift::MultiplexedProcessor; +use Thrift::BinaryProtocol; +use Thrift::MemoryBuffer; +use Thrift::FramedTransport; +use Thrift::MemoryBuffer; + + +use BenchmarkService; +use Aggr; + +use constant NAME_BENCHMARKSERVICE => 'BenchmarkService'; +use constant NAME_AGGR => 'Aggr'; + +my $buffer = Thrift::MemoryBuffer->new(1024); +my $aggr_protocol = Thrift::MultiplexedProtocol->new(Thrift::BinaryProtocol->new($buffer), NAME_AGGR); +my $aggr_client = AggrClient->new($aggr_protocol); +my $benchmark_protocol = Thrift::MultiplexedProtocol->new(Thrift::BinaryProtocol->new($buffer), NAME_BENCHMARKSERVICE); +my $benchmark_client = BenchmarkServiceClient->new($benchmark_protocol); + +$buffer->open(); + +for(my $i = 1; $i <= 5; $i++) { + $aggr_client->send_addValue($i); + $aggr_client->{seqid}++; +} + +$aggr_client->send_getValues(); + +for(my $i = 1; $i <= 5; $i++) { + $benchmark_client->send_fibonacci($i); + $benchmark_client->{seqid}++; +} +$benchmark_client->{seqid}--; + +my $client_command_binary = $buffer->getBuffer; +$buffer->resetBuffer; + + +# Process by server +my $server_output_binary; +{ + my $benchmark_handler = My::BenchmarkService->new(); + my $benchmark_processor = BenchmarkServiceProcessor->new($benchmark_handler); + my $aggr_handler = My::Aggr->new(); + my $aggr_processor = AggrProcessor->new($aggr_handler); + + my $protocol_factory = Thrift::BinaryProtocolFactory->new(); + + my $input_buffer = Thrift::MemoryBuffer->new(); + $input_buffer->write($client_command_binary); + + my $input_protocol = $protocol_factory->getProtocol($input_buffer); + + my $output_buffer = Thrift::MemoryBuffer->new(); + my $output_protocol = $protocol_factory->getProtocol($output_buffer); + + my $processor = Thrift::MultiplexedProcessor->new(); + + $processor->registerProcessor(NAME_BENCHMARKSERVICE, $benchmark_processor); + $processor->registerProcessor(NAME_AGGR, $aggr_processor); + my $result; + for(my $i = 1; $i <= 11; $i++) { + $result = $processor->process($input_protocol, $output_protocol); + print "process resulted in $result\n"; + } + + $server_output_binary = $output_buffer->getBuffer(); +} + +$buffer->write($server_output_binary); + + + +for(my $i = 1; $i <= 5; $i++) { + my ($function_name, $message_type, $sequence_id); + + $aggr_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id); + + if ($message_type == TMessageType::EXCEPTION) { + die; + } + + my $aggr_result = Aggr_addValue_result->new(); + $aggr_result->read($aggr_protocol); + $aggr_protocol->readMessageEnd(); +} + +my ($function_name, $message_type, $sequence_id); + +$aggr_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id); + +if ($message_type == TMessageType::EXCEPTION) { + die; +} + +my $aggr_result = Aggr_getValues_result->new(); +$aggr_result->read($aggr_protocol); +$aggr_protocol->readMessageEnd(); + +is_deeply($aggr_result->success(), [1,2,3,4,5]); + + +foreach my $val((1,2,3,5,8)) { + my ($function_name, $message_type, $sequence_id); + + $benchmark_protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id); + + if ($message_type == TMessageType::EXCEPTION) { + die; + } + my $benchmark_result = BenchmarkService_fibonacci_result->new(); + $benchmark_result->read($benchmark_protocol); + $benchmark_protocol->readMessageEnd(); + + is($benchmark_result->success(), $val); +} + + +package My::Aggr; +use base qw(AggrIf); + +use strict; +use warnings; + +sub new { + my $classname = shift; + my $self = {}; + + $self->{values} = (); + + return bless($self,$classname); +} + +sub addValue{ + my $self = shift; + my $value = shift; + + push (@{$self->{values}}, $value); +} + +sub getValues{ + my $self = shift; + + return $self->{values}; +} + + + +package My::BenchmarkService; +use base qw(BenchmarkServiceIf); + +use strict; +use warnings; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub fibonacci { + my ($self, $n) = @_; + + my $prev = 0; + my $next; + my $result = 1; + + while ($n > 0) { + $next = $result + $prev; + $prev = $result; + $result = $next; + --$n; + } + + return $result; +} + diff --git a/vendor/src/github.com/apache/thrift/lib/perl/test/processor.t b/vendor/src/github.com/apache/thrift/lib/perl/test/processor.t new file mode 100644 index 00000000..1d8be73b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/perl/test/processor.t @@ -0,0 +1,104 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use Test::More tests => 2; + +use strict; +use warnings; + +use Thrift; +use Thrift::BinaryProtocol; +use Thrift::MemoryBuffer; + +use ThriftTest::ThriftTest; +use ThriftTest::Types; + +use Data::Dumper; + +my $buffer = Thrift::MemoryBuffer->new(1024); +my $protocol = Thrift::BinaryProtocol->new($buffer); +my $client = ThriftTest::ThriftTestClient->new($protocol); + +$buffer->open(); +$client->send_testString("foo"); +$client->{seqid}++; +$client->send_testString("bar"); + +my $client_command_binary = $buffer->getBuffer; +$buffer->resetBuffer; + +# Process by server + +my $server_output_binary; +{ + my $protocol_factory = Thrift::BinaryProtocolFactory->new(); + + my $input_buffer = Thrift::MemoryBuffer->new(); + $input_buffer->write($client_command_binary); + my $input_protocol = $protocol_factory->getProtocol($input_buffer); + + my $output_buffer = Thrift::MemoryBuffer->new(); + my $output_protocol = $protocol_factory->getProtocol($output_buffer); + + my $processor = ThriftTest::ThriftTestProcessor->new( My::ThriftTest->new() ); + my $result = $processor->process($input_protocol, $output_protocol); + print "process resulted in $result\n"; + $result = $processor->process($input_protocol, $output_protocol); + print "process resulted in $result\n"; + $server_output_binary = $output_buffer->getBuffer(); +} + +$buffer->write($server_output_binary); + +foreach my $val (("got foo","got bar")){ + my ($function_name, $message_type, $sequence_id); + + $protocol->readMessageBegin(\$function_name, \$message_type, \$sequence_id); + print " $function_name, $message_type, $sequence_id\n"; + + if ($message_type == TMessageType::EXCEPTION) { + die; + } + + my $result = ThriftTest::ThriftTest_testString_result->new(); + $result->read($protocol); + $protocol->readMessageEnd(); + + is($result->success(),$val); +} + + +package My::ThriftTest; + +use strict; +use warnings; +use Data::Dumper; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub testString { + my ($self, $string) = @_; + + print __PACKAGE__ . "->testString()\n"; + + return "got ".$string; +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/Makefile.am b/vendor/src/github.com/apache/thrift/lib/php/Makefile.am new file mode 100644 index 00000000..8e629600 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/Makefile.am @@ -0,0 +1,150 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +if WITH_TESTS +SUBDIRS = test +endif + +if WITH_PHP_EXTENSION +%.so: + cd src/ext/thrift_protocol/ && $(MAKE) + +phpconfdir=$(PHP_CONFIG_PREFIX) +phpconf_DATA=thrift_protocol.ini + +phpmoduledir = `php-config --extension-dir` +phpmodule_SCRIPTS = src/ext/thrift_protocol/modules/thrift_protocol.so + +distclean-local: + cd $(phpmodule_SCRIPTS) && $(PHPIZE) --clean + +endif + +phpdir = $(PHP_PREFIX)/Thrift +php_DATA = \ + lib/Thrift/TMultiplexedProcessor.php + +phpbasedir = $(phpdir)/Base +phpbase_DATA = \ + lib/Thrift/Base/TBase.php + +phpclassloaderdir = $(phpdir)/ClassLoader +phpclassloader_DATA = \ + lib/Thrift/ClassLoader/ThriftClassLoader.php + +phpexceptiondir = $(phpdir)/Exception +phpexception_DATA = \ + lib/Thrift/Exception/TApplicationException.php \ + lib/Thrift/Exception/TException.php \ + lib/Thrift/Exception/TProtocolException.php \ + lib/Thrift/Exception/TTransportException.php + +phpfactorydir = $(phpdir)/Factory +phpfactory_DATA = \ + lib/Thrift/Factory/TBinaryProtocolFactory.php \ + lib/Thrift/Factory/TCompactProtocolFactory.php \ + lib/Thrift/Factory/TJSONProtocolFactory.php \ + lib/Thrift/Factory/TProtocolFactory.php \ + lib/Thrift/Factory/TStringFuncFactory.php \ + lib/Thrift/Factory/TTransportFactory.php + +phpprotocoldir = $(phpdir)/Protocol +phpprotocol_DATA = \ + lib/Thrift/Protocol/TBinaryProtocolAccelerated.php \ + lib/Thrift/Protocol/TBinaryProtocol.php \ + lib/Thrift/Protocol/TCompactProtocol.php \ + lib/Thrift/Protocol/TJSONProtocol.php \ + lib/Thrift/Protocol/TMultiplexedProtocol.php \ + lib/Thrift/Protocol/TProtocol.php \ + lib/Thrift/Protocol/TProtocolDecorator.php \ + lib/Thrift/Protocol/TSimpleJSONProtocol.php + +phpprotocoljsondir = $(phpprotocoldir)/JSON +phpprotocoljson_DATA = \ + lib/Thrift/Protocol/JSON/BaseContext.php \ + lib/Thrift/Protocol/JSON/ListContext.php \ + lib/Thrift/Protocol/JSON/LookaheadReader.php \ + lib/Thrift/Protocol/JSON/PairContext.php + +phpprotocolsimplejsondir = $(phpprotocoldir)/SimpleJSON +phpprotocolsimplejson_DATA = \ + lib/Thrift/Protocol/SimpleJSON/CollectionMapKeyException.php \ + lib/Thrift/Protocol/SimpleJSON/Context.php \ + lib/Thrift/Protocol/SimpleJSON/ListContext.php \ + lib/Thrift/Protocol/SimpleJSON/MapContext.php \ + lib/Thrift/Protocol/SimpleJSON/StructContext.php + +phpserializerdir = $(phpdir)/Serializer +phpserializer_DATA = \ + lib/Thrift/Serializer/TBinarySerializer.php + +phpserverdir = $(phpdir)/Server +phpserver_DATA = \ + lib/Thrift/Server/TServerSocket.php \ + lib/Thrift/Server/TForkingServer.php \ + lib/Thrift/Server/TServer.php \ + lib/Thrift/Server/TServerTransport.php \ + lib/Thrift/Server/TSimpleServer.php + +phpstringfuncdir = $(phpdir)/StringFunc +phpstringfunc_DATA = \ + lib/Thrift/StringFunc/Mbstring.php \ + lib/Thrift/StringFunc/Core.php \ + lib/Thrift/StringFunc/TStringFunc.php + +phptransportdir = $(phpdir)/Transport +phptransport_DATA = \ + lib/Thrift/Transport/TBufferedTransport.php \ + lib/Thrift/Transport/TCurlClient.php \ + lib/Thrift/Transport/TFramedTransport.php \ + lib/Thrift/Transport/THttpClient.php \ + lib/Thrift/Transport/TMemoryBuffer.php \ + lib/Thrift/Transport/TNullTransport.php \ + lib/Thrift/Transport/TPhpStream.php \ + lib/Thrift/Transport/TSocket.php \ + lib/Thrift/Transport/TSocketPool.php \ + lib/Thrift/Transport/TTransport.php + +phptypedir = $(phpdir)/Type +phptype_DATA = \ + lib/Thrift/Type/TMessageType.php \ + lib/Thrift/Type/TType.php \ + lib/Thrift/Type/TConstant.php + +EXTRA_DIST = \ + lib \ + src/autoload.php \ + src/ext/thrift_protocol/config.m4 \ + src/ext/thrift_protocol/config.w32 \ + src/ext/thrift_protocol/php_thrift_protocol7.cpp \ + 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 + +MAINTAINERCLEANFILES = \ + Makefile \ + Makefile.in + diff --git a/vendor/src/github.com/apache/thrift/lib/php/README.apache.md b/vendor/src/github.com/apache/thrift/lib/php/README.apache.md new file mode 100644 index 00000000..5e925897 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/README.apache.md @@ -0,0 +1,74 @@ +Thrift PHP/Apache Integration + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Building PHP Thrift Services with Apache +======================================== + +Thrift can be embedded in the Apache webserver with PHP installed. Sample +code is provided below. Note that to make requests to this type of server +you must use a THttpClient transport. + +Sample Code +=========== + +registerNamespace('Thrift', $THRIFT_ROOT); +$loader->registerDefinition('Thrift', $THRIFT_ROOT . '/packages'); +$loader->register(); + +use Thrift\Transport\TPhpStream; +use Thrift\Protocol\TBinaryProtocol; + +/** + * Example of how to build a Thrift server in Apache/PHP + */ + +class ServiceHandler implements ServiceIf { + // Implement your interface and methods here +} + +header('Content-Type: application/x-thrift'); + +$handler = new ServiceHandler(); +$processor = new ServiceProcessor($handler); + +// Use the TPhpStream transport to read/write directly from HTTP +$transport = new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W); +$protocol = new TBinaryProtocol($transport); + +$transport->open(); +$processor->process($protocol, $protocol); +$transport->close(); diff --git a/vendor/src/github.com/apache/thrift/lib/php/README.md b/vendor/src/github.com/apache/thrift/lib/php/README.md new file mode 100644 index 00000000..c24ee2c0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/README.md @@ -0,0 +1,53 @@ +Thrift PHP Software Library + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Using Thrift with PHP +===================== + +Thrift requires PHP 5. Thrift makes as few assumptions about your PHP +environment as possible while trying to make some more advanced PHP +features (i.e. APC cacheing using asbolute path URLs) as simple as possible. + +To use Thrift in your PHP codebase, take the following steps: + +#1) Copy all of thrift/lib/php/lib into your PHP codebase +#2) Configure Symfony Autoloader (or whatever you usually use) + +After that, you have to manually include the Thrift package +created by the compiler: + +require_once 'packages/Service/Service.php'; +require_once 'packages/Service/Types.php'; + +Dependencies +============ + +PHP_INT_SIZE + + This built-in signals whether your architecture is 32 or 64 bit and is + used by the TBinaryProtocol to properly use pack() and unpack() to + serialize data. + +apc_fetch(), apc_store() + + APC cache is used by the TSocketPool class. If you do not have APC installed, + Thrift will fill in null stub function definitions. diff --git a/vendor/src/github.com/apache/thrift/lib/php/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/php/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Base/TBase.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Base/TBase.php new file mode 100644 index 00000000..4195f75d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Base/TBase.php @@ -0,0 +1,380 @@ + 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + abstract public function read($input); + + abstract public function write($output); + + public function __construct($spec=null, $vals=null) + { + if (is_array($spec) && is_array($vals)) { + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } + } + + public function __wakeup() + { + $this->__construct(get_object_vars($this)); + } + + private function _readMap(&$var, $spec, $input) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + + return $xfer; + } + + protected function _read($class, $spec, $input) + { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + + return $xfer; + } + + private function _writeMap($var, $spec, $output) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + + return $xfer; + } + + protected function _write($class, $spec, $output) + { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php new file mode 100644 index 00000000..67575ce1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php @@ -0,0 +1,210 @@ +apc = $apc; + $this->apc_prefix = $apc_prefix; + } + + /** + * Registers a namespace. + * + * @param string $namespace The namespace + * @param array|string $paths The location(s) of the namespace + */ + public function registerNamespace($namespace, $paths) + { + $this->namespaces[$namespace] = (array) $paths; + } + + /** + * Registers a Thrift definition namespace. + * + * @param string $namespace The definition namespace + * @param array|string $paths The location(s) of the definition namespace + */ + public function registerDefinition($namespace, $paths) + { + $this->definitions[$namespace] = (array) $paths; + } + + /** + * Registers this instance as an autoloader. + * + * @param Boolean $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Loads the given class, definition or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) + { + if ( + (true === $this->apc && ($file = $this->findFileInApc($class))) or + ($file = $this->findFile($class)) + ) + { + require_once $file; + } + } + + /** + * Loads the given class or interface in APC. + * @param string $class The name of the class + * @return string + */ + protected function findFileInApc($class) + { + if (false === $file = apc_fetch($this->apc_prefix.$class)) { + apc_store($this->apc_prefix.$class, $file = $this->findFile($class)); + } + + return $file; + } + + /** + * Find class in namespaces or definitions directories + * @param string $class + * @return string + */ + public function findFile($class) + { + // Remove first backslash + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + if (false !== $pos = strrpos($class, '\\')) { + // Namespaced class name + $namespace = substr($class, 0, $pos); + + // Iterate in normal namespaces + foreach ($this->namespaces as $ns => $dirs) { + //Don't interfere with other autoloaders + if (0 !== strpos($namespace, $ns)) { + continue; + } + + foreach ($dirs as $dir) { + $className = substr($class, $pos + 1); + + $file = $dir.DIRECTORY_SEPARATOR. + str_replace('\\', DIRECTORY_SEPARATOR, $namespace). + DIRECTORY_SEPARATOR. + $className.'.php'; + + if (file_exists($file)) { + return $file; + } + } + } + + // Iterate in Thrift namespaces + + // Remove first part of namespace + $m = explode('\\', $class); + + // Ignore wrong call + if (count($m) <= 1) { + return; + } + + $class = array_pop($m); + $namespace = implode('\\', $m); + + foreach ($this->definitions as $ns => $dirs) { + //Don't interfere with other autoloaders + if (0 !== strpos($namespace, $ns)) { + continue; + } + + foreach ($dirs as $dir) { + /** + * Available in service: Interface, Client, Processor, Rest + * And every service methods (_.+) + */ + if( + 0 === preg_match('#(.+)(if|client|processor|rest)$#i', $class, $n) and + 0 === preg_match('#(.+)_[a-z0-9]+_(args|result)$#i', $class, $n) + ) + { + $className = 'Types'; + } else { + $className = $n[1]; + } + + $file = $dir.DIRECTORY_SEPARATOR . + str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . + DIRECTORY_SEPARATOR . + $className . '.php'; + + if (file_exists($file)) { + return $file; + } + } + } + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TApplicationException.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TApplicationException.php new file mode 100644 index 00000000..b1689fc3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TApplicationException.php @@ -0,0 +1,76 @@ + array('var' => 'message', + 'type' => TType::STRING), + 2 => array('var' => 'code', + 'type' => TType::I32)); + + const UNKNOWN = 0; + const UNKNOWN_METHOD = 1; + const INVALID_MESSAGE_TYPE = 2; + const WRONG_METHOD_NAME = 3; + const BAD_SEQUENCE_ID = 4; + const MISSING_RESULT = 5; + const INTERNAL_ERROR = 6; + const PROTOCOL_ERROR = 7; + const INVALID_TRANSFORM = 8; + const INVALID_PROTOCOL = 9; + const UNSUPPORTED_CLIENT_TYPE = 10; + + public function __construct($message=null, $code=0) + { + parent::__construct($message, $code); + } + + public function read($output) + { + return $this->_read('TApplicationException', self::$_TSPEC, $output); + } + + public function write($output) + { + $xfer = 0; + $xfer += $output->writeStructBegin('TApplicationException'); + if ($message = $this->getMessage()) { + $xfer += $output->writeFieldBegin('message', TType::STRING, 1); + $xfer += $output->writeString($message); + $xfer += $output->writeFieldEnd(); + } + if ($code = $this->getCode()) { + $xfer += $output->writeFieldBegin('type', TType::I32, 2); + $xfer += $output->writeI32($code); + $xfer += $output->writeFieldEnd(); + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TException.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TException.php new file mode 100644 index 00000000..5c068438 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TException.php @@ -0,0 +1,383 @@ + $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } else { + parent::__construct($p1, $p2); + } + } + + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + private function _readMap(&$var, $spec, $input) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + + return $xfer; + } + + protected function _read($class, $spec, $input) + { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + + return $xfer; + } + + private function _writeMap($var, $spec, $output) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + + return $xfer; + } + + protected function _write($class, $spec, $output) + { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TProtocolException.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TProtocolException.php new file mode 100644 index 00000000..ba7135c2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Exception/TProtocolException.php @@ -0,0 +1,50 @@ +strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function getProtocol($trans) + { + return new TBinaryProtocol($trans, $this->strictRead_, $this->strictWrite_); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php new file mode 100644 index 00000000..f4b4fe3e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Factory/TCompactProtocolFactory.php @@ -0,0 +1,40 @@ +p_ = $p; + } + + public function write() + { + if ($this->first_) { + $this->first_ = false; + } else { + $this->p_->getTransport()->write(TJSONProtocol::COMMA); + } + } + + public function read() + { + if ($this->first_) { + $this->first_ = false; + } else { + $this->p_->readJSONSyntaxChar(TJSONProtocol::COMMA); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php new file mode 100644 index 00000000..0b18c40d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/LookaheadReader.php @@ -0,0 +1,57 @@ +p_ = $p; + } + + public function read() + { + if ($this->hasData_) { + $this->hasData_ = false; + } else { + $this->data_ = $this->p_->getTransport()->readAll(1); + } + + return substr($this->data_, 0, 1); + } + + public function peek() + { + if (!$this->hasData_) { + $this->data_ = $this->p_->getTransport()->readAll(1); + } + + $this->hasData_ = true; + + return substr($this->data_, 0, 1); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/PairContext.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/PairContext.php new file mode 100644 index 00000000..7b353c4a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/JSON/PairContext.php @@ -0,0 +1,64 @@ +p_ = $p; + } + + public function write() + { + if ($this->first_) { + $this->first_ = false; + $this->colon_ = true; + } else { + $this->p_->getTransport()->write($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); + $this->colon_ = !$this->colon_; + } + } + + public function read() + { + if ($this->first_) { + $this->first_ = false; + $this->colon_ = true; + } else { + $this->p_->readJSONSyntaxChar($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); + $this->colon_ = !$this->colon_; + } + } + + public function escapeNum() + { + return $this->colon_; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/CollectionMapKeyException.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/CollectionMapKeyException.php new file mode 100644 index 00000000..522b85a5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/CollectionMapKeyException.php @@ -0,0 +1,33 @@ +p_ = $p; + } + + public function write() + { + if ($this->first_) { + $this->first_ = false; + } else { + $this->p_->getTransport()->write(TSimpleJSONProtocol::COMMA); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/MapContext.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/MapContext.php new file mode 100644 index 00000000..bb9a04d2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/MapContext.php @@ -0,0 +1,51 @@ +isKey = !$this->isKey; + } + + public function isMapKey() + { + // we want to coerce map keys to json strings regardless + // of their type + return $this->isKey; + } +} + + diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/StructContext.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/StructContext.php new file mode 100644 index 00000000..8162f2bc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/SimpleJSON/StructContext.php @@ -0,0 +1,53 @@ +p_ = $p; + } + + public function write() + { + if ($this->first_) { + $this->first_ = false; + $this->colon_ = true; + } else { + $this->p_->getTransport()->write( + $this->colon_ ? + TSimpleJSONProtocol::COLON : + TSimpleJSONProtocol::COMMA + ); + $this->colon_ = !$this->colon_; + } + } +} + diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php new file mode 100644 index 00000000..fe6a1034 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocol.php @@ -0,0 +1,453 @@ +strictRead_ = $strictRead; + $this->strictWrite_ = $strictWrite; + } + + public function writeMessageBegin($name, $type, $seqid) + { + if ($this->strictWrite_) { + $version = self::VERSION_1 | $type; + + return + $this->writeI32($version) + + $this->writeString($name) + + $this->writeI32($seqid); + } else { + return + $this->writeString($name) + + $this->writeByte($type) + + $this->writeI32($seqid); + } + } + + public function writeMessageEnd() + { + return 0; + } + + public function writeStructBegin($name) + { + return 0; + } + + public function writeStructEnd() + { + return 0; + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) + { + return + $this->writeByte($fieldType) + + $this->writeI16($fieldId); + } + + public function writeFieldEnd() + { + return 0; + } + + public function writeFieldStop() + { + return + $this->writeByte(TType::STOP); + } + + public function writeMapBegin($keyType, $valType, $size) + { + return + $this->writeByte($keyType) + + $this->writeByte($valType) + + $this->writeI32($size); + } + + public function writeMapEnd() + { + return 0; + } + + public function writeListBegin($elemType, $size) + { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeListEnd() + { + return 0; + } + + public function writeSetBegin($elemType, $size) + { + return + $this->writeByte($elemType) + + $this->writeI32($size); + } + + public function writeSetEnd() + { + return 0; + } + + public function writeBool($value) + { + $data = pack('c', $value ? 1 : 0); + $this->trans_->write($data, 1); + + return 1; + } + + public function writeByte($value) + { + $data = pack('c', $value); + $this->trans_->write($data, 1); + + return 1; + } + + public function writeI16($value) + { + $data = pack('n', $value); + $this->trans_->write($data, 2); + + return 2; + } + + public function writeI32($value) + { + $data = pack('N', $value); + $this->trans_->write($data, 4); + + return 4; + } + + public function writeI64($value) + { + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int) ($value / 4294967296); + $lo = (int) $value; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int) 0xffffffff) == (int) 0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + $data = pack('N2', $hi, $lo); + + } else { + $hi = $value >> 32; + $lo = $value & 0xFFFFFFFF; + $data = pack('N2', $hi, $lo); + } + + $this->trans_->write($data, 8); + + return 8; + } + + public function writeDouble($value) + { + $data = pack('d', $value); + $this->trans_->write(strrev($data), 8); + + return 8; + } + + public function writeString($value) + { + $len = TStringFuncFactory::create()->strlen($value); + $result = $this->writeI32($len); + if ($len) { + $this->trans_->write($value, $len); + } + + return $result + $len; + } + + public function readMessageBegin(&$name, &$type, &$seqid) + { + $result = $this->readI32($sz); + if ($sz < 0) { + $version = (int) ($sz & self::VERSION_MASK); + if ($version != (int) self::VERSION_1) { + throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION); + } + $type = $sz & 0x000000ff; + $result += + $this->readString($name) + + $this->readI32($seqid); + } else { + if ($this->strictRead_) { + throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION); + } else { + // Handle pre-versioned input + $name = $this->trans_->readAll($sz); + $result += + $sz + + $this->readByte($type) + + $this->readI32($seqid); + } + } + + return $result; + } + + public function readMessageEnd() + { + return 0; + } + + public function readStructBegin(&$name) + { + $name = ''; + + return 0; + } + + public function readStructEnd() + { + return 0; + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) + { + $result = $this->readByte($fieldType); + if ($fieldType == TType::STOP) { + $fieldId = 0; + + return $result; + } + $result += $this->readI16($fieldId); + + return $result; + } + + public function readFieldEnd() + { + return 0; + } + + public function readMapBegin(&$keyType, &$valType, &$size) + { + return + $this->readByte($keyType) + + $this->readByte($valType) + + $this->readI32($size); + } + + public function readMapEnd() + { + return 0; + } + + public function readListBegin(&$elemType, &$size) + { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readListEnd() + { + return 0; + } + + public function readSetBegin(&$elemType, &$size) + { + return + $this->readByte($elemType) + + $this->readI32($size); + } + + public function readSetEnd() + { + return 0; + } + + public function readBool(&$value) + { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1] == 1; + + return 1; + } + + public function readByte(&$value) + { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1]; + + return 1; + } + + public function readI16(&$value) + { + $data = $this->trans_->readAll(2); + $arr = unpack('n', $data); + $value = $arr[1]; + if ($value > 0x7fff) { + $value = 0 - (($value - 1) ^ 0xffff); + } + + return 2; + } + + public function readI32(&$value) + { + $data = $this->trans_->readAll(4); + $arr = unpack('N', $data); + $value = $arr[1]; + if ($value > 0x7fffffff) { + $value = 0 - (($value - 1) ^ 0xffffffff); + } + + return 4; + } + + public function readI64(&$value) + { + $data = $this->trans_->readAll(8); + + $arr = unpack('N2', $data); + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + if (PHP_INT_SIZE == 4) { + + $hi = $arr[1]; + $lo = $arr[2]; + $isNeg = $hi < 0; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int) 0xffffffff; + $lo = ~$lo & (int) 0xffffffff; + + if ($lo == (int) 0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to pe positive - we deal wigh sign + // explicitly below + + if ($hi & (int) 0x80000000) { + $hi &= (int) 0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int) 0x80000000) { + $lo &= (int) 0x7fffffff; + $lo += 0x80000000; + } + + $value = $hi * 4294967296 + $lo; + + if ($isNeg) { + $value = 0 - $value; + } + } else { + + // Upcast negatives in LSB bit + if ($arr[2] & 0x80000000) { + $arr[2] = $arr[2] & 0xffffffff; + } + + // Check for a negative + if ($arr[1] & 0x80000000) { + $arr[1] = $arr[1] & 0xffffffff; + $arr[1] = $arr[1] ^ 0xffffffff; + $arr[2] = $arr[2] ^ 0xffffffff; + $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; + } else { + $value = $arr[1]*4294967296 + $arr[2]; + } + } + + return 8; + } + + public function readDouble(&$value) + { + $data = strrev($this->trans_->readAll(8)); + $arr = unpack('d', $data); + $value = $arr[1]; + + return 8; + } + + public function readString(&$value) + { + $result = $this->readI32($len); + if ($len) { + $value = $this->trans_->readAll($len); + } else { + $value = ''; + } + + return $result + $len; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php new file mode 100644 index 00000000..f0e0bb99 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TBinaryProtocolAccelerated.php @@ -0,0 +1,65 @@ +strictRead_; + } + public function isStrictWrite() + { + return $this->strictWrite_; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TCompactProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TCompactProtocol.php new file mode 100644 index 00000000..c25b0501 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TCompactProtocol.php @@ -0,0 +1,739 @@ + TCompactProtocol::COMPACT_STOP, + TType::BOOL => TCompactProtocol::COMPACT_TRUE, // used for collection + TType::BYTE => TCompactProtocol::COMPACT_BYTE, + TType::I16 => TCompactProtocol::COMPACT_I16, + TType::I32 => TCompactProtocol::COMPACT_I32, + TType::I64 => TCompactProtocol::COMPACT_I64, + TType::DOUBLE => TCompactProtocol::COMPACT_DOUBLE, + TType::STRING => TCompactProtocol::COMPACT_BINARY, + TType::STRUCT => TCompactProtocol::COMPACT_STRUCT, + TType::LST => TCompactProtocol::COMPACT_LIST, + TType::SET => TCompactProtocol::COMPACT_SET, + TType::MAP => TCompactProtocol::COMPACT_MAP, + ); + + protected static $ttypes = array( + TCompactProtocol::COMPACT_STOP => TType::STOP , + TCompactProtocol::COMPACT_TRUE => TType::BOOL, // used for collection + TCompactProtocol::COMPACT_FALSE => TType::BOOL, + TCompactProtocol::COMPACT_BYTE => TType::BYTE, + TCompactProtocol::COMPACT_I16 => TType::I16, + TCompactProtocol::COMPACT_I32 => TType::I32, + TCompactProtocol::COMPACT_I64 => TType::I64, + TCompactProtocol::COMPACT_DOUBLE => TType::DOUBLE, + TCompactProtocol::COMPACT_BINARY => TType::STRING, + TCompactProtocol::COMPACT_STRUCT => TType::STRUCT, + TCompactProtocol::COMPACT_LIST => TType::LST, + TCompactProtocol::COMPACT_SET => TType::SET, + TCompactProtocol::COMPACT_MAP => TType::MAP, + ); + + protected $state = TCompactProtocol::STATE_CLEAR; + protected $lastFid = 0; + protected $boolFid = null; + protected $boolValue = null; + protected $structs = array(); + protected $containers = array(); + + // Some varint / zigzag helper methods + public function toZigZag($n, $bits) + { + return ($n << 1) ^ ($n >> ($bits - 1)); + } + + public function fromZigZag($n) + { + return ($n >> 1) ^ -($n & 1); + } + + public function getVarint($data) + { + $out = ""; + while (true) { + if (($data & ~0x7f) === 0) { + $out .= chr($data); + break; + } else { + $out .= chr(($data & 0xff) | 0x80); + $data = $data >> 7; + } + } + + return $out; + } + + public function writeVarint($data) + { + $out = $this->getVarint($data); + $result = TStringFuncFactory::create()->strlen($out); + $this->trans_->write($out, $result); + + return $result; + } + + public function readVarint(&$result) + { + $idx = 0; + $shift = 0; + $result = 0; + while (true) { + $x = $this->trans_->readAll(1); + $arr = unpack('C', $x); + $byte = $arr[1]; + $idx += 1; + $result |= ($byte & 0x7f) << $shift; + if (($byte >> 7) === 0) { + return $idx; + } + $shift += 7; + } + + return $idx; + } + + public function __construct($trans) + { + parent::__construct($trans); + } + + public function writeMessageBegin($name, $type, $seqid) + { + $written = + $this->writeUByte(TCompactProtocol::PROTOCOL_ID) + + $this->writeUByte(TCompactProtocol::VERSION | + ($type << TCompactProtocol::TYPE_SHIFT_AMOUNT)) + + $this->writeVarint($seqid) + + $this->writeString($name); + $this->state = TCompactProtocol::STATE_VALUE_WRITE; + + return $written; + } + + public function writeMessageEnd() + { + $this->state = TCompactProtocol::STATE_CLEAR; + + return 0; + } + + public function writeStructBegin($name) + { + $this->structs[] = array($this->state, $this->lastFid); + $this->state = TCompactProtocol::STATE_FIELD_WRITE; + $this->lastFid = 0; + + return 0; + } + + public function writeStructEnd() + { + $old_values = array_pop($this->structs); + $this->state = $old_values[0]; + $this->lastFid = $old_values[1]; + + return 0; + } + + public function writeFieldStop() + { + return $this->writeByte(0); + } + + public function writeFieldHeader($type, $fid) + { + $written = 0; + $delta = $fid - $this->lastFid; + if (0 < $delta && $delta <= 15) { + $written = $this->writeUByte(($delta << 4) | $type); + } else { + $written = $this->writeByte($type) + + $this->writeI16($fid); + } + $this->lastFid = $fid; + + return $written; + } + + public function writeFieldBegin($field_name, $field_type, $field_id) + { + if ($field_type == TTYPE::BOOL) { + $this->state = TCompactProtocol::STATE_BOOL_WRITE; + $this->boolFid = $field_id; + + return 0; + } else { + $this->state = TCompactProtocol::STATE_VALUE_WRITE; + + return $this->writeFieldHeader(self::$ctypes[$field_type], $field_id); + } + } + + public function writeFieldEnd() + { + $this->state = TCompactProtocol::STATE_FIELD_WRITE; + + return 0; + } + + public function writeCollectionBegin($etype, $size) + { + $written = 0; + if ($size <= 14) { + $written = $this->writeUByte($size << 4 | + self::$ctypes[$etype]); + } else { + $written = $this->writeUByte(0xf0 | + self::$ctypes[$etype]) + + $this->writeVarint($size); + } + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_WRITE; + + return $written; + } + + public function writeMapBegin($key_type, $val_type, $size) + { + $written = 0; + if ($size == 0) { + $written = $this->writeByte(0); + } else { + $written = $this->writeVarint($size) + + $this->writeUByte(self::$ctypes[$key_type] << 4 | + self::$ctypes[$val_type]); + } + $this->containers[] = $this->state; + + return $written; + } + + public function writeCollectionEnd() + { + $this->state = array_pop($this->containers); + + return 0; + } + + public function writeMapEnd() + { + return $this->writeCollectionEnd(); + } + + public function writeListBegin($elem_type, $size) + { + return $this->writeCollectionBegin($elem_type, $size); + } + + public function writeListEnd() + { + return $this->writeCollectionEnd(); + } + + public function writeSetBegin($elem_type, $size) + { + return $this->writeCollectionBegin($elem_type, $size); + } + + public function writeSetEnd() + { + return $this->writeCollectionEnd(); + } + + public function writeBool($value) + { + if ($this->state == TCompactProtocol::STATE_BOOL_WRITE) { + $ctype = TCompactProtocol::COMPACT_FALSE; + if ($value) { + $ctype = TCompactProtocol::COMPACT_TRUE; + } + + return $this->writeFieldHeader($ctype, $this->boolFid); + } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_WRITE) { + return $this->writeByte($value ? 1 : 0); + } else { + throw new TProtocolException('Invalid state in compact protocol'); + } + } + + public function writeByte($value) + { + $data = pack('c', $value); + $this->trans_->write($data, 1); + + return 1; + } + + public function writeUByte($byte) + { + $this->trans_->write(pack('C', $byte), 1); + + return 1; + } + + public function writeI16($value) + { + $thing = $this->toZigZag($value, 16); + + return $this->writeVarint($thing); + } + + public function writeI32($value) + { + $thing = $this->toZigZag($value, 32); + + return $this->writeVarint($thing); + } + + public function writeDouble($value) + { + $data = pack('d', $value); + $this->trans_->write($data, 8); + + return 8; + } + + public function writeString($value) + { + $len = TStringFuncFactory::create()->strlen($value); + $result = $this->writeVarint($len); + if ($len) { + $this->trans_->write($value, $len); + } + + return $result + $len; + } + + public function readFieldBegin(&$name, &$field_type, &$field_id) + { + $result = $this->readUByte($compact_type_and_delta); + + $compact_type = $compact_type_and_delta & 0x0f; + + if ($compact_type == TType::STOP) { + $field_type = $compact_type; + $field_id = 0; + + return $result; + } + $delta = $compact_type_and_delta >> 4; + if ($delta == 0) { + $result += $this->readI16($field_id); + } else { + $field_id = $this->lastFid + $delta; + } + $this->lastFid = $field_id; + $field_type = $this->getTType($compact_type); + + if ($compact_type == TCompactProtocol::COMPACT_TRUE) { + $this->state = TCompactProtocol::STATE_BOOL_READ; + $this->boolValue = true; + } elseif ($compact_type == TCompactProtocol::COMPACT_FALSE) { + $this->state = TCompactProtocol::STATE_BOOL_READ; + $this->boolValue = false; + } else { + $this->state = TCompactProtocol::STATE_VALUE_READ; + } + + return $result; + } + + public function readFieldEnd() + { + $this->state = TCompactProtocol::STATE_FIELD_READ; + + return 0; + } + + public function readUByte(&$value) + { + $data = $this->trans_->readAll(1); + $arr = unpack('C', $data); + $value = $arr[1]; + + return 1; + } + + public function readByte(&$value) + { + $data = $this->trans_->readAll(1); + $arr = unpack('c', $data); + $value = $arr[1]; + + return 1; + } + + public function readZigZag(&$value) + { + $result = $this->readVarint($value); + $value = $this->fromZigZag($value); + + return $result; + } + + public function readMessageBegin(&$name, &$type, &$seqid) + { + $protoId = 0; + $result = $this->readUByte($protoId); + if ($protoId != TCompactProtocol::PROTOCOL_ID) { + throw new TProtocolException('Bad protocol id in TCompact message'); + } + $verType = 0; + $result += $this->readUByte($verType); + $type = ($verType >> TCompactProtocol::TYPE_SHIFT_AMOUNT) & TCompactProtocol::TYPE_BITS; + $version = $verType & TCompactProtocol::VERSION_MASK; + if ($version != TCompactProtocol::VERSION) { + throw new TProtocolException('Bad version in TCompact message'); + } + $result += $this->readVarint($seqid); + $result += $this->readString($name); + + return $result; + } + + public function readMessageEnd() + { + return 0; + } + + public function readStructBegin(&$name) + { + $name = ''; // unused + $this->structs[] = array($this->state, $this->lastFid); + $this->state = TCompactProtocol::STATE_FIELD_READ; + $this->lastFid = 0; + + return 0; + } + + public function readStructEnd() + { + $last = array_pop($this->structs); + $this->state = $last[0]; + $this->lastFid = $last[1]; + + return 0; + } + + public function readCollectionBegin(&$type, &$size) + { + $sizeType = 0; + $result = $this->readUByte($sizeType); + $size = $sizeType >> 4; + $type = $this->getTType($sizeType); + if ($size == 15) { + $result += $this->readVarint($size); + } + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_READ; + + return $result; + } + + public function readMapBegin(&$key_type, &$val_type, &$size) + { + $result = $this->readVarint($size); + $types = 0; + if ($size > 0) { + $result += $this->readUByte($types); + } + $val_type = $this->getTType($types); + $key_type = $this->getTType($types >> 4); + $this->containers[] = $this->state; + $this->state = TCompactProtocol::STATE_CONTAINER_READ; + + return $result; + } + + public function readCollectionEnd() + { + $this->state = array_pop($this->containers); + + return 0; + } + + public function readMapEnd() + { + return $this->readCollectionEnd(); + } + + public function readListBegin(&$elem_type, &$size) + { + return $this->readCollectionBegin($elem_type, $size); + } + + public function readListEnd() + { + return $this->readCollectionEnd(); + } + + public function readSetBegin(&$elem_type, &$size) + { + return $this->readCollectionBegin($elem_type, $size); + } + + public function readSetEnd() + { + return $this->readCollectionEnd(); + } + + public function readBool(&$value) + { + if ($this->state == TCompactProtocol::STATE_BOOL_READ) { + $value = $this->boolValue; + + return 0; + } elseif ($this->state == TCompactProtocol::STATE_CONTAINER_READ) { + return $this->readByte($value); + } else { + throw new TProtocolException('Invalid state in compact protocol'); + } + } + + public function readI16(&$value) + { + return $this->readZigZag($value); + } + + public function readI32(&$value) + { + return $this->readZigZag($value); + } + + public function readDouble(&$value) + { + $data = $this->trans_->readAll(8); + $arr = unpack('d', $data); + $value = $arr[1]; + + return 8; + } + + public function readString(&$value) + { + $result = $this->readVarint($len); + if ($len) { + $value = $this->trans_->readAll($len); + } else { + $value = ''; + } + + return $result + $len; + } + + public function getTType($byte) + { + return self::$ttypes[$byte & 0x0f]; + } + + // If we are on a 32bit architecture we have to explicitly deal with + // 64-bit twos-complement arithmetic since PHP wants to treat all ints + // as signed and any int over 2^31 - 1 as a float + + // Read and write I64 as two 32 bit numbers $hi and $lo + + public function readI64(&$value) + { + // Read varint from wire + $hi = 0; + $lo = 0; + + $idx = 0; + $shift = 0; + + while (true) { + $x = $this->trans_->readAll(1); + $arr = unpack('C', $x); + $byte = $arr[1]; + $idx += 1; + // Shift hi and lo together. + if ($shift < 28) { + $lo |= (($byte & 0x7f) << $shift); + } elseif ($shift == 28) { + $lo |= (($byte & 0x0f) << 28); + $hi |= (($byte & 0x70) >> 4); + } else { + $hi |= (($byte & 0x7f) << ($shift - 32)); + } + if (($byte >> 7) === 0) { + break; + } + $shift += 7; + } + + // Now, unzig it. + $xorer = 0; + if ($lo & 1) { + $xorer = 0xffffffff; + } + $lo = ($lo >> 1) & 0x7fffffff; + $lo = $lo | (($hi & 1) << 31); + $hi = ($hi >> 1) ^ $xorer; + $lo = $lo ^ $xorer; + + // Now put $hi and $lo back together + $isNeg = $hi < 0 || $hi & 0x80000000; + + // Check for a negative + if ($isNeg) { + $hi = ~$hi & (int) 0xffffffff; + $lo = ~$lo & (int) 0xffffffff; + + if ($lo == (int) 0xffffffff) { + $hi++; + $lo = 0; + } else { + $lo++; + } + } + + // Force 32bit words in excess of 2G to be positive - we deal with sign + // explicitly below + + if ($hi & (int) 0x80000000) { + $hi &= (int) 0x7fffffff; + $hi += 0x80000000; + } + + if ($lo & (int) 0x80000000) { + $lo &= (int) 0x7fffffff; + $lo += 0x80000000; + } + + // Create as negative value first, since we can store -2^63 but not 2^63 + $value = -$hi * 4294967296 - $lo; + + if (!$isNeg) { + $value = -$value; + } + + return $idx; + } + + public function writeI64($value) + { + // If we are in an I32 range, use the easy method below. + if (($value > 4294967296) || ($value < -4294967296)) { + // Convert $value to $hi and $lo + $neg = $value < 0; + + if ($neg) { + $value *= -1; + } + + $hi = (int) $value >> 32; + $lo = (int) $value & 0xffffffff; + + if ($neg) { + $hi = ~$hi; + $lo = ~$lo; + if (($lo & (int) 0xffffffff) == (int) 0xffffffff) { + $lo = 0; + $hi++; + } else { + $lo++; + } + } + + // Now do the zigging and zagging. + $xorer = 0; + if ($neg) { + $xorer = 0xffffffff; + } + $lowbit = ($lo >> 31) & 1; + $hi = ($hi << 1) | $lowbit; + $lo = ($lo << 1); + $lo = ($lo ^ $xorer) & 0xffffffff; + $hi = ($hi ^ $xorer) & 0xffffffff; + + // now write out the varint, ensuring we shift both hi and lo + $out = ""; + while (true) { + if (($lo & ~0x7f) === 0 && + $hi === 0) { + $out .= chr($lo); + break; + } else { + $out .= chr(($lo & 0xff) | 0x80); + $lo = $lo >> 7; + $lo = $lo | ($hi << 25); + $hi = $hi >> 7; + // Right shift carries sign, but we don't want it to. + $hi = $hi & (127 << 25); + } + } + + $ret = TStringFuncFactory::create()->strlen($out); + $this->trans_->write($out, $ret); + + return $ret; + } else { + return $this->writeVarint($this->toZigZag($value, 64)); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TJSONProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TJSONProtocol.php new file mode 100644 index 00000000..6d8e81fa --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TJSONProtocol.php @@ -0,0 +1,807 @@ + 1) { + switch (substr($name, 0, 1)) { + case 'd': + $result = TType::DOUBLE; + break; + case 'i': + switch (substr($name, 1, 1)) { + case '8': + $result = TType::BYTE; + break; + case '1': + $result = TType::I16; + break; + case '3': + $result = TType::I32; + break; + case '6': + $result = TType::I64; + break; + } + break; + case 'l': + $result = TType::LST; + break; + case 'm': + $result = TType::MAP; + break; + case 'r': + $result = TType::STRUCT; + break; + case 's': + if (substr($name, 1, 1) == 't') { + $result = TType::STRING; + } elseif (substr($name, 1, 1) == 'e') { + $result = TType::SET; + } + break; + case 't': + $result = TType::BOOL; + break; + } + } + if ($result == TType::STOP) { + throw new TProtocolException("Unrecognized type", TProtocolException::INVALID_DATA); + } + + return $result; + } + + public $contextStack_ = array(); + public $context_; + public $reader_; + + private function pushContext($c) + { + array_push($this->contextStack_, $this->context_); + $this->context_ = $c; + } + + private function popContext() + { + $this->context_ = array_pop($this->contextStack_); + } + + public function __construct($trans) + { + parent::__construct($trans); + $this->context_ = new BaseContext(); + $this->reader_ = new LookaheadReader($this); + } + + public function reset() + { + $this->contextStack_ = array(); + $this->context_ = new BaseContext(); + $this->reader_ = new LookaheadReader($this); + } + + private $tmpbuf_ = array(4); + + public function readJSONSyntaxChar($b) + { + $ch = $this->reader_->read(); + + if (substr($ch, 0, 1) != $b) { + throw new TProtocolException("Unexpected character: " . $ch, TProtocolException::INVALID_DATA); + } + } + + private function hexVal($s) + { + for ($i = 0; $i < strlen($s); $i++) { + $ch = substr($s, $i, 1); + + if (!($ch >= "a" && $ch <= "f") && !($ch >= "0" && $ch <= "9")) { + throw new TProtocolException("Expected hex character " . $ch, TProtocolException::INVALID_DATA); + } + } + + return hexdec($s); + } + + private function hexChar($val) + { + return dechex($val); + } + + private function hasJSONUnescapedUnicode() + { + if (PHP_MAJOR_VERSION > 5 + || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4)) + return true; + + return false; + } + + private function unescapedUnicode($str) + { + if ($this->hasJSONUnescapedUnicode()) { + return json_encode($str, JSON_UNESCAPED_UNICODE); + } + + $json = json_encode($str); + + /* + * Unescaped character outside the Basic Multilingual Plane + * High surrogate: 0xD800 - 0xDBFF + * Low surrogate: 0xDC00 - 0xDFFF + */ + $json = preg_replace_callback('/\\\\u(d[89ab][0-9a-f]{2})\\\\u(d[cdef][0-9a-f]{2})/i', + function ($matches) { + return mb_convert_encoding(pack('H*', $matches[1].$matches[2]), 'UTF-8', 'UTF-16BE'); + }, $json); + + /* + * Unescaped characters within the Basic Multilingual Plane + */ + $json = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', + function ($matches) { + return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UTF-16BE'); + }, $json); + + return $json; + } + + private function writeJSONString($b) + { + $this->context_->write(); + + if (is_numeric($b) && $this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write($this->unescapedUnicode($b)); + + if (is_numeric($b) && $this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONInteger($num) + { + $this->context_->write(); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write($num); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONDouble($num) + { + $this->context_->write(); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write(json_encode($num)); + + if ($this->context_->escapeNum()) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONBase64($data) + { + $this->context_->write(); + $this->trans_->write(self::QUOTE); + $this->trans_->write(json_encode(base64_encode($data))); + $this->trans_->write(self::QUOTE); + } + + private function writeJSONObjectStart() + { + $this->context_->write(); + $this->trans_->write(self::LBRACE); + $this->pushContext(new PairContext($this)); + } + + private function writeJSONObjectEnd() + { + $this->popContext(); + $this->trans_->write(self::RBRACE); + } + + private function writeJSONArrayStart() + { + $this->context_->write(); + $this->trans_->write(self::LBRACKET); + $this->pushContext(new ListContext($this)); + } + + private function writeJSONArrayEnd() + { + $this->popContext(); + $this->trans_->write(self::RBRACKET); + } + + private function readJSONString($skipContext) + { + if (!$skipContext) { + $this->context_->read(); + } + + $jsonString = ''; + $lastChar = null; + while (true) { + $ch = $this->reader_->read(); + $jsonString .= $ch; + if ($ch == self::QUOTE && + $lastChar !== NULL && + $lastChar !== self::ESCSEQ) { + break; + } + if ($ch == self::ESCSEQ && $lastChar == self::ESCSEQ) { + $lastChar = self::DOUBLEESC; + } else { + $lastChar = $ch; + } + } + + return json_decode($jsonString); + } + + private function isJSONNumeric($b) + { + switch ($b) { + case '+': + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'E': + case 'e': + return true; + } + + return false; + } + + private function readJSONNumericChars() + { + $strbld = array(); + + while (true) { + $ch = $this->reader_->peek(); + + if (!$this->isJSONNumeric($ch)) { + break; + } + + $strbld[] = $this->reader_->read(); + } + + return implode("", $strbld); + } + + private function readJSONInteger() + { + $this->context_->read(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + $str = $this->readJSONNumericChars(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + if (!is_numeric($str)) { + throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); + } + + return intval($str); + } + + /** + * Identical to readJSONInteger but without the final cast. + * Needed for proper handling of i64 on 32 bit machines. Why a + * separate function? So we don't have to force the rest of the + * use cases through the extra conditional. + */ + private function readJSONIntegerAsString() + { + $this->context_->read(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + $str = $this->readJSONNumericChars(); + + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + if (!is_numeric($str)) { + throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA); + } + + return $str; + } + + private function readJSONDouble() + { + $this->context_->read(); + + if (substr($this->reader_->peek(), 0, 1) == self::QUOTE) { + $arr = $this->readJSONString(true); + + if ($arr == "NaN") { + return NAN; + } elseif ($arr == "Infinity") { + return INF; + } elseif (!$this->context_->escapeNum()) { + throw new TProtocolException("Numeric data unexpectedly quoted " . $arr, + TProtocolException::INVALID_DATA); + } + + return floatval($arr); + } else { + if ($this->context_->escapeNum()) { + $this->readJSONSyntaxChar(self::QUOTE); + } + + return floatval($this->readJSONNumericChars()); + } + } + + private function readJSONBase64() + { + $arr = $this->readJSONString(false); + $data = base64_decode($arr, true); + + if ($data === false) { + throw new TProtocolException("Invalid base64 data " . $arr, TProtocolException::INVALID_DATA); + } + + return $data; + } + + private function readJSONObjectStart() + { + $this->context_->read(); + $this->readJSONSyntaxChar(self::LBRACE); + $this->pushContext(new PairContext($this)); + } + + private function readJSONObjectEnd() + { + $this->readJSONSyntaxChar(self::RBRACE); + $this->popContext(); + } + + private function readJSONArrayStart() + { + $this->context_->read(); + $this->readJSONSyntaxChar(self::LBRACKET); + $this->pushContext(new ListContext($this)); + } + + private function readJSONArrayEnd() + { + $this->readJSONSyntaxChar(self::RBRACKET); + $this->popContext(); + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public function writeMessageBegin($name, $type, $seqid) + { + $this->writeJSONArrayStart(); + $this->writeJSONInteger(self::VERSION); + $this->writeJSONString($name); + $this->writeJSONInteger($type); + $this->writeJSONInteger($seqid); + } + + /** + * Close the message + */ + public function writeMessageEnd() + { + $this->writeJSONArrayEnd(); + } + + /** + * Writes a struct header. + * + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructBegin($name) + { + $this->writeJSONObjectStart(); + } + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructEnd() + { + $this->writeJSONObjectEnd(); + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) + { + $this->writeJSONInteger($fieldId); + $this->writeJSONObjectStart(); + $this->writeJSONString($this->getTypeNameForTypeID($fieldType)); + } + + public function writeFieldEnd() + { + $this->writeJsonObjectEnd(); + } + + public function writeFieldStop() + { + } + + public function writeMapBegin($keyType, $valType, $size) + { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($keyType)); + $this->writeJSONString($this->getTypeNameForTypeID($valType)); + $this->writeJSONInteger($size); + $this->writeJSONObjectStart(); + } + + public function writeMapEnd() + { + $this->writeJSONObjectEnd(); + $this->writeJSONArrayEnd(); + } + + public function writeListBegin($elemType, $size) + { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($elemType)); + $this->writeJSONInteger($size); + } + + public function writeListEnd() + { + $this->writeJSONArrayEnd(); + } + + public function writeSetBegin($elemType, $size) + { + $this->writeJSONArrayStart(); + $this->writeJSONString($this->getTypeNameForTypeID($elemType)); + $this->writeJSONInteger($size); + } + + public function writeSetEnd() + { + $this->writeJSONArrayEnd(); + } + + public function writeBool($bool) + { + $this->writeJSONInteger($bool ? 1 : 0); + } + + public function writeByte($byte) + { + $this->writeJSONInteger($byte); + } + + public function writeI16($i16) + { + $this->writeJSONInteger($i16); + } + + public function writeI32($i32) + { + $this->writeJSONInteger($i32); + } + + public function writeI64($i64) + { + $this->writeJSONInteger($i64); + } + + public function writeDouble($dub) + { + $this->writeJSONDouble($dub); + } + + public function writeString($str) + { + $this->writeJSONString($str); + } + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @parem int $seqid The sequence id of this message + */ + public function readMessageBegin(&$name, &$type, &$seqid) + { + $this->readJSONArrayStart(); + + if ($this->readJSONInteger() != self::VERSION) { + throw new TProtocolException("Message contained bad version", TProtocolException::BAD_VERSION); + } + + $name = $this->readJSONString(false); + $type = $this->readJSONInteger(); + $seqid = $this->readJSONInteger(); + + return true; + } + + /** + * Read the close of message + */ + public function readMessageEnd() + { + $this->readJSONArrayEnd(); + } + + public function readStructBegin(&$name) + { + $this->readJSONObjectStart(); + + return 0; + } + + public function readStructEnd() + { + $this->readJSONObjectEnd(); + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) + { + $ch = $this->reader_->peek(); + $name = ""; + + if (substr($ch, 0, 1) == self::RBRACE) { + $fieldType = TType::STOP; + } else { + $fieldId = $this->readJSONInteger(); + $this->readJSONObjectStart(); + $fieldType = $this->getTypeIDForTypeName($this->readJSONString(false)); + } + } + + public function readFieldEnd() + { + $this->readJSONObjectEnd(); + } + + public function readMapBegin(&$keyType, &$valType, &$size) + { + $this->readJSONArrayStart(); + $keyType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $valType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + $this->readJSONObjectStart(); + } + + public function readMapEnd() + { + $this->readJSONObjectEnd(); + $this->readJSONArrayEnd(); + } + + public function readListBegin(&$elemType, &$size) + { + $this->readJSONArrayStart(); + $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + + return true; + } + + public function readListEnd() + { + $this->readJSONArrayEnd(); + } + + public function readSetBegin(&$elemType, &$size) + { + $this->readJSONArrayStart(); + $elemType = $this->getTypeIDForTypeName($this->readJSONString(false)); + $size = $this->readJSONInteger(); + + return true; + } + + public function readSetEnd() + { + $this->readJSONArrayEnd(); + } + + public function readBool(&$bool) + { + $bool = $this->readJSONInteger() == 0 ? false : true; + + return true; + } + + public function readByte(&$byte) + { + $byte = $this->readJSONInteger(); + + return true; + } + + public function readI16(&$i16) + { + $i16 = $this->readJSONInteger(); + + return true; + } + + public function readI32(&$i32) + { + $i32 = $this->readJSONInteger(); + + return true; + } + + public function readI64(&$i64) + { + if (PHP_INT_SIZE === 4) { + $i64 = $this->readJSONIntegerAsString(); + } else { + $i64 = $this->readJSONInteger(); + } + + return true; + } + + public function readDouble(&$dub) + { + $dub = $this->readJSONDouble(); + + return true; + } + + public function readString(&$str) + { + $str = $this->readJSONString(false); + + return true; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TMultiplexedProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TMultiplexedProtocol.php new file mode 100644 index 00000000..d579c099 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TMultiplexedProtocol.php @@ -0,0 +1,85 @@ +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. + * + * @package Thrift\Protocol + */ +class TMultiplexedProtocol extends TProtocolDecorator +{ + /** + * Separator between service name and function name. + * Should be the same as used at multiplexed Thrift server. + * + * @var string + */ + const SEPARATOR = ":"; + + /** + * The name of service. + * + * @var string + */ + private $serviceName_; + + /** + * Constructor of TMultiplexedProtocol class. + * + * Wrap the specified protocol, allowing it to be used to communicate with a + * multiplexing server. The $serviceName is required as it is + * prepended to the message header so that the multiplexing server can broker + * the function call to the proper service. + * + * @param TProtocol $protocol + * @param string $serviceName The name of service. + */ + public function __construct(TProtocol $protocol, $serviceName) + { + parent::__construct($protocol); + $this->serviceName_ = $serviceName; + } + + /** + * Writes the message header. + * Prepends the service name to the function name, separated by TMultiplexedProtocol::SEPARATOR. + * + * @param string $name Function name. + * @param int $type Message type. + * @param int $seqid The sequence id of this message. + */ + public function writeMessageBegin($name, $type, $seqid) + { + if ($type == TMessageType::CALL || $type == TMessageType::ONEWAY) { + $nameWithService = $this->serviceName_ . self::SEPARATOR . $name; + parent::writeMessageBegin($nameWithService, $type, $seqid); + } else { + parent::writeMessageBegin($name, $type, $seqid); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocol.php new file mode 100644 index 00000000..0e3bc0d0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocol.php @@ -0,0 +1,352 @@ +trans_ = $trans; + } + + /** + * Accessor for transport + * + * @return TTransport + */ + public function getTransport() + { + return $this->trans_; + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + abstract public function writeMessageBegin($name, $type, $seqid); + + /** + * Close the message + */ + abstract public function writeMessageEnd(); + + /** + * Writes a struct header. + * + * @param string $name Struct name + * @throws TException on write error + * @return int How many bytes written + */ + abstract public function writeStructBegin($name); + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + abstract public function writeStructEnd(); + + /* + * Starts a field. + * + * @param string $name Field name + * @param int $type Field type + * @param int $fid Field id + * @throws TException on write error + * @return int How many bytes written + */ + abstract public function writeFieldBegin($fieldName, $fieldType, $fieldId); + + abstract public function writeFieldEnd(); + + abstract public function writeFieldStop(); + + abstract public function writeMapBegin($keyType, $valType, $size); + + abstract public function writeMapEnd(); + + abstract public function writeListBegin($elemType, $size); + + abstract public function writeListEnd(); + + abstract public function writeSetBegin($elemType, $size); + + abstract public function writeSetEnd(); + + abstract public function writeBool($bool); + + abstract public function writeByte($byte); + + abstract public function writeI16($i16); + + abstract public function writeI32($i32); + + abstract public function writeI64($i64); + + abstract public function writeDouble($dub); + + abstract public function writeString($str); + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @parem int $seqid The sequence id of this message + */ + abstract public function readMessageBegin(&$name, &$type, &$seqid); + + /** + * Read the close of message + */ + abstract public function readMessageEnd(); + + abstract public function readStructBegin(&$name); + + abstract public function readStructEnd(); + + abstract public function readFieldBegin(&$name, &$fieldType, &$fieldId); + + abstract public function readFieldEnd(); + + abstract public function readMapBegin(&$keyType, &$valType, &$size); + + abstract public function readMapEnd(); + + abstract public function readListBegin(&$elemType, &$size); + + abstract public function readListEnd(); + + abstract public function readSetBegin(&$elemType, &$size); + + abstract public function readSetEnd(); + + abstract public function readBool(&$bool); + + abstract public function readByte(&$byte); + + abstract public function readI16(&$i16); + + abstract public function readI32(&$i32); + + abstract public function readI64(&$i64); + + abstract public function readDouble(&$dub); + + abstract public function readString(&$str); + + /** + * The skip function is a utility to parse over unrecognized date without + * causing corruption. + * + * @param TType $type What type is it + */ + public function skip($type) + { + switch ($type) { + case TType::BOOL: + return $this->readBool($bool); + case TType::BYTE: + return $this->readByte($byte); + case TType::I16: + return $this->readI16($i16); + case TType::I32: + return $this->readI32($i32); + case TType::I64: + return $this->readI64($i64); + case TType::DOUBLE: + return $this->readDouble($dub); + case TType::STRING: + return $this->readString($str); + case TType::STRUCT: + { + $result = $this->readStructBegin($name); + while (true) { + $result += $this->readFieldBegin($name, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + $result += $this->skip($ftype); + $result += $this->readFieldEnd(); + } + $result += $this->readStructEnd(); + + return $result; + } + case TType::MAP: + { + $result = $this->readMapBegin($keyType, $valType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($keyType); + $result += $this->skip($valType); + } + $result += $this->readMapEnd(); + + return $result; + } + case TType::SET: + { + $result = $this->readSetBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readSetEnd(); + + return $result; + } + case TType::LST: + { + $result = $this->readListBegin($elemType, $size); + for ($i = 0; $i < $size; $i++) { + $result += $this->skip($elemType); + } + $result += $this->readListEnd(); + + return $result; + } + default: + throw new TProtocolException('Unknown field type: '.$type, + TProtocolException::INVALID_DATA); + } + } + + /** + * Utility for skipping binary data + * + * @param TTransport $itrans TTransport object + * @param int $type Field type + */ + public static function skipBinary($itrans, $type) + { + switch ($type) { + case TType::BOOL: + return $itrans->readAll(1); + case TType::BYTE: + return $itrans->readAll(1); + case TType::I16: + return $itrans->readAll(2); + case TType::I32: + return $itrans->readAll(4); + case TType::I64: + return $itrans->readAll(8); + case TType::DOUBLE: + return $itrans->readAll(8); + case TType::STRING: + $len = unpack('N', $itrans->readAll(4)); + $len = $len[1]; + if ($len > 0x7fffffff) { + $len = 0 - (($len - 1) ^ 0xffffffff); + } + + return 4 + $itrans->readAll($len); + case TType::STRUCT: + { + $result = 0; + while (true) { + $ftype = 0; + $fid = 0; + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ftype = $arr[1]; + if ($ftype == TType::STOP) { + break; + } + // I16 field id + $result += $itrans->readAll(2); + $result += self::skipBinary($itrans, $ftype); + } + + return $result; + } + case TType::MAP: + { + // Ktype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $ktype = $arr[1]; + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 6; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $ktype); + $result += self::skipBinary($itrans, $vtype); + } + + return $result; + } + case TType::SET: + case TType::LST: + { + // Vtype + $data = $itrans->readAll(1); + $arr = unpack('c', $data); + $vtype = $arr[1]; + // Size + $data = $itrans->readAll(4); + $arr = unpack('N', $data); + $size = $arr[1]; + if ($size > 0x7fffffff) { + $size = 0 - (($size - 1) ^ 0xffffffff); + } + $result = 5; + for ($i = 0; $i < $size; $i++) { + $result += self::skipBinary($itrans, $vtype); + } + + return $result; + } + default: + throw new TProtocolException('Unknown field type: '.$type, + TProtocolException::INVALID_DATA); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocolDecorator.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocolDecorator.php new file mode 100644 index 00000000..c08c4d50 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TProtocolDecorator.php @@ -0,0 +1,284 @@ +TProtocolDecorator forwards all requests to an enclosed + * TProtocol instance, providing a way to author concise + * concrete decorator subclasses. While it has no abstract methods, it + * is marked abstract as a reminder that by itself, it does not modify + * the behaviour of the enclosed TProtocol. + * + * @package Thrift\Protocol + */ +abstract class TProtocolDecorator extends TProtocol +{ + /** + * Instance of protocol, to which all operations will be forwarded. + * + * @var TProtocol + */ + private $concreteProtocol_; + + /** + * Constructor of TProtocolDecorator class. + * Encloses the specified protocol. + * + * @param TProtocol $protocol All operations will be forward to this instance. Must be non-null. + */ + protected function __construct(TProtocol $protocol) + { + parent::__construct($protocol->getTransport()); + $this->concreteProtocol_ = $protocol; + } + + /** + * Writes the message header. + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public function writeMessageBegin($name, $type, $seqid) + { + return $this->concreteProtocol_->writeMessageBegin($name, $type, $seqid); + } + + /** + * Closes the message. + */ + public function writeMessageEnd() + { + return $this->concreteProtocol_->writeMessageEnd(); + } + + /** + * Writes a struct header. + * + * @param string $name Struct name + * + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructBegin($name) + { + return $this->concreteProtocol_->writeStructBegin($name); + } + + /** + * Close a struct. + * + * @throws TException on write error + * @return int How many bytes written + */ + public function writeStructEnd() + { + return $this->concreteProtocol_->writeStructEnd(); + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) + { + return $this->concreteProtocol_->writeFieldBegin($fieldName, $fieldType, $fieldId); + } + + public function writeFieldEnd() + { + return $this->concreteProtocol_->writeFieldEnd(); + } + + public function writeFieldStop() + { + return $this->concreteProtocol_->writeFieldStop(); + } + + public function writeMapBegin($keyType, $valType, $size) + { + return $this->concreteProtocol_->writeMapBegin($keyType, $valType, $size); + } + + public function writeMapEnd() + { + return $this->concreteProtocol_->writeMapEnd(); + } + + public function writeListBegin($elemType, $size) + { + return $this->concreteProtocol_->writeListBegin($elemType, $size); + } + + public function writeListEnd() + { + return $this->concreteProtocol_->writeListEnd(); + } + + public function writeSetBegin($elemType, $size) + { + return $this->concreteProtocol_->writeSetBegin($elemType, $size); + } + + public function writeSetEnd() + { + return $this->concreteProtocol_->writeSetEnd(); + } + + public function writeBool($bool) + { + return $this->concreteProtocol_->writeBool($bool); + } + + public function writeByte($byte) + { + return $this->concreteProtocol_->writeByte($byte); + } + + public function writeI16($i16) + { + return $this->concreteProtocol_->writeI16($i16); + } + + public function writeI32($i32) + { + return $this->concreteProtocol_->writeI32($i32); + } + + public function writeI64($i64) + { + return $this->concreteProtocol_->writeI64($i64); + } + + public function writeDouble($dub) + { + return $this->concreteProtocol_->writeDouble($dub); + } + + public function writeString($str) + { + return $this->concreteProtocol_->writeString($str); + } + + /** + * Reads the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public function readMessageBegin(&$name, &$type, &$seqid) + { + return $this->concreteProtocol_->readMessageBegin($name, $type, $seqid); + } + + /** + * Read the close of message + */ + public function readMessageEnd() + { + return $this->concreteProtocol_->readMessageEnd(); + } + + public function readStructBegin(&$name) + { + return $this->concreteProtocol_->readStructBegin($name); + } + + public function readStructEnd() + { + return $this->concreteProtocol_->readStructEnd(); + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) + { + return $this->concreteProtocol_->readFieldBegin($name, $fieldType, $fieldId); + } + + public function readFieldEnd() + { + return $this->concreteProtocol_->readFieldEnd(); + } + + public function readMapBegin(&$keyType, &$valType, &$size) + { + $this->concreteProtocol_->readMapBegin($keyType, $valType, $size); + } + + public function readMapEnd() + { + return $this->concreteProtocol_->readMapEnd(); + } + + public function readListBegin(&$elemType, &$size) + { + $this->concreteProtocol_->readListBegin($elemType, $size); + } + + public function readListEnd() + { + return $this->concreteProtocol_->readListEnd(); + } + + public function readSetBegin(&$elemType, &$size) + { + return $this->concreteProtocol_->readSetBegin($elemType, $size); + } + + public function readSetEnd() + { + return $this->concreteProtocol_->readSetEnd(); + } + + public function readBool(&$bool) + { + return $this->concreteProtocol_->readBool($bool); + } + + public function readByte(&$byte) + { + return $this->concreteProtocol_->readByte($byte); + } + + public function readI16(&$i16) + { + return $this->concreteProtocol_->readI16($i16); + } + + public function readI32(&$i32) + { + return $this->concreteProtocol_->readI32($i32); + } + + public function readI64(&$i64) + { + return $this->concreteProtocol_->readI64($i64); + } + + public function readDouble(&$dub) + { + return $this->concreteProtocol_->readDouble($dub); + } + + public function readString(&$str) + { + return $this->concreteProtocol_->readString($str); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TSimpleJSONProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TSimpleJSONProtocol.php new file mode 100644 index 00000000..9cf90bda --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Protocol/TSimpleJSONProtocol.php @@ -0,0 +1,371 @@ +writeContextStack_[] = $this->writeContext_; + $this->writeContext_ = $c; + } + + /** + * Pop the last write context off the stack + */ + protected function popWriteContext() { + $this->writeContext_ = array_pop($this->writeContextStack_); + } + + /** + * Used to make sure that we are not encountering a map whose keys are containers + */ + protected function assertContextIsNotMapKey($invalidKeyType) { + if ($this->writeContext_->isMapKey()) { + throw new CollectionMapKeyException( + "Cannot serialize a map with keys that are of type " . + $invalidKeyType + ); + } + } + + private function writeJSONString($b) + { + $this->writeContext_->write(); + + $this->trans_->write(json_encode((string)$b)); + } + + private function writeJSONInteger($num) + { + $isMapKey = $this->writeContext_->isMapKey(); + + $this->writeContext_->write(); + + if ($isMapKey) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write((int)$num); + + if ($isMapKey) { + $this->trans_->write(self::QUOTE); + } + } + + private function writeJSONDouble($num) + { + $isMapKey = $this->writeContext_->isMapKey(); + + $this->writeContext_->write(); + + if ($isMapKey) { + $this->trans_->write(self::QUOTE); + } + + $this->trans_->write(json_encode((float)$num)); + + if ($isMapKey) { + $this->trans_->write(self::QUOTE); + } + } + + /** + * Constructor + */ + public function __construct($trans) + { + parent::__construct($trans); + $this->writeContext_ = new Context(); + } + + /** + * Writes the message header + * + * @param string $name Function name + * @param int $type message type TMessageType::CALL or TMessageType::REPLY + * @param int $seqid The sequence id of this message + */ + public function writeMessageBegin($name, $type, $seqid) + { + $this->trans_->write(self::LBRACKET); + $this->pushWriteContext(new ListContext($this)); + $this->writeJSONString($name); + $this->writeJSONInteger($type); + $this->writeJSONInteger($seqid); + } + + /** + * Close the message + */ + public function writeMessageEnd() + { + $this->popWriteContext(); + $this->trans_->write(self::RBRACKET); + } + + /** + * Writes a struct header. + * + * @param string $name Struct name + */ + public function writeStructBegin($name) + { + $this->writeContext_->write(); + $this->trans_->write(self::LBRACE); + $this->pushWriteContext(new StructContext($this)); + } + + /** + * Close a struct. + */ + public function writeStructEnd() + { + $this->popWriteContext(); + $this->trans_->write(self::RBRACE); + } + + public function writeFieldBegin($fieldName, $fieldType, $fieldId) + { + $this->writeJSONString($fieldName); + } + + public function writeFieldEnd() + { + } + + public function writeFieldStop() + { + } + + public function writeMapBegin($keyType, $valType, $size) + { + $this->assertContextIsNotMapKey(self::NAME_MAP); + $this->writeContext_->write(); + $this->trans_->write(self::LBRACE); + $this->pushWriteContext(new MapContext($this)); + } + + public function writeMapEnd() + { + $this->popWriteContext(); + $this->trans_->write(self::RBRACE); + } + + public function writeListBegin($elemType, $size) + { + $this->assertContextIsNotMapKey(self::NAME_LIST); + $this->writeContext_->write(); + $this->trans_->write(self::LBRACKET); + $this->pushWriteContext(new ListContext($this)); + // No metadata! + } + + public function writeListEnd() + { + $this->popWriteContext(); + $this->trans_->write(self::RBRACKET); + } + + public function writeSetBegin($elemType, $size) + { + $this->assertContextIsNotMapKey(self::NAME_SET); + $this->writeContext_->write(); + $this->trans_->write(self::LBRACKET); + $this->pushWriteContext(new ListContext($this)); + // No metadata! + } + + public function writeSetEnd() + { + $this->popWriteContext(); + $this->trans_->write(self::RBRACKET); + } + + public function writeBool($bool) + { + $this->writeJSONInteger($bool ? 1 : 0); + } + + public function writeByte($byte) + { + $this->writeJSONInteger($byte); + } + + public function writeI16($i16) + { + $this->writeJSONInteger($i16); + } + + public function writeI32($i32) + { + $this->writeJSONInteger($i32); + } + + public function writeI64($i64) + { + $this->writeJSONInteger($i64); + } + + public function writeDouble($dub) + { + $this->writeJSONDouble($dub); + } + + public function writeString($str) + { + $this->writeJSONString($str); + } + + /** + * Reading methods. + * + * simplejson is not meant to be read back into thrift + * - see http://wiki.apache.org/thrift/ThriftUsageJava + * - use JSON instead + */ + + public function readMessageBegin(&$name, &$type, &$seqid) + { + throw new TException("Not implemented"); + } + + public function readMessageEnd() + { + throw new TException("Not implemented"); + } + + public function readStructBegin(&$name) + { + throw new TException("Not implemented"); + } + + public function readStructEnd() + { + throw new TException("Not implemented"); + } + + public function readFieldBegin(&$name, &$fieldType, &$fieldId) + { + throw new TException("Not implemented"); + } + + public function readFieldEnd() + { + throw new TException("Not implemented"); + } + + public function readMapBegin(&$keyType, &$valType, &$size) + { + throw new TException("Not implemented"); + } + + public function readMapEnd() + { + throw new TException("Not implemented"); + } + + public function readListBegin(&$elemType, &$size) + { + throw new TException("Not implemented"); + } + + public function readListEnd() + { + throw new TException("Not implemented"); + } + + public function readSetBegin(&$elemType, &$size) + { + throw new TException("Not implemented"); + } + + public function readSetEnd() + { + throw new TException("Not implemented"); + } + + public function readBool(&$bool) + { + throw new TException("Not implemented"); + } + + public function readByte(&$byte) + { + throw new TException("Not implemented"); + } + + public function readI16(&$i16) + { + throw new TException("Not implemented"); + } + + public function readI32(&$i32) + { + throw new TException("Not implemented"); + } + + public function readI64(&$i64) + { + throw new TException("Not implemented"); + } + + public function readDouble(&$dub) + { + throw new TException("Not implemented"); + } + + public function readString(&$str) + { + throw new TException("Not implemented"); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Serializer/TBinarySerializer.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Serializer/TBinarySerializer.php new file mode 100644 index 00000000..aa2f71b4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Serializer/TBinarySerializer.php @@ -0,0 +1,85 @@ +getName(), + TMessageType::REPLY, $object, + 0, $protocol->isStrictWrite()); + + $protocol->readMessageBegin($unused_name, $unused_type, + $unused_seqid); + } else { + $object->write($protocol); + } + $protocol->getTransport()->flush(); + + return $transport->getBuffer(); + } + + public static function deserialize($string_object, $class_name, $buffer_size = 8192) + { + $transport = new TMemoryBuffer(); + $protocol = new TBinaryProtocolAccelerated($transport); + if (function_exists('thrift_protocol_read_binary')) { + // NOTE (t.heintz) TBinaryProtocolAccelerated internally wraps our TMemoryBuffer in a + // TBufferedTransport, so we have to retrieve it again or risk losing data when writing + // less than 512 bytes to the transport (see the comment there as well). + // @see THRIFT-1579 + $protocol->writeMessageBegin('', TMessageType::REPLY, 0); + $protocolTransport = $protocol->getTransport(); + $protocolTransport->write($string_object); + $protocolTransport->flush(); + + return thrift_protocol_read_binary($protocol, $class_name, + $protocol->isStrictRead(), + $buffer_size); + } else { + $transport->write($string_object); + $object = new $class_name(); + $object->read($protocol); + + return $object; + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TForkingServer.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TForkingServer.php new file mode 100644 index 00000000..7f6e541c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TForkingServer.php @@ -0,0 +1,120 @@ +transport_->listen(); + + while (!$this->stop_) { + try { + $transport = $this->transport_->accept(); + + if ($transport != null) { + $pid = pcntl_fork(); + + if ($pid > 0) { + $this->handleParent($transport, $pid); + } elseif ($pid === 0) { + $this->handleChild($transport); + } else { + throw new TException('Failed to fork'); + } + } + } catch (TTransportException $e) { } + + $this->collectChildren(); + } + } + + /** + * Code run by the parent + * + * @param TTransport $transport + * @param int $pid + * @return void + */ + private function handleParent(TTransport $transport, $pid) + { + $this->children_[$pid] = $transport; + } + + /** + * Code run by the child. + * + * @param TTransport $transport + * @return void + */ + private function handleChild(TTransport $transport) + { + try { + $inputTransport = $this->inputTransportFactory_->getTransport($transport); + $outputTransport = $this->outputTransportFactory_->getTransport($transport); + $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); + $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); + while ($this->processor_->process($inputProtocol, $outputProtocol)) { } + @$transport->close(); + } catch (TTransportException $e) { } + + exit(0); + } + + /** + * Collects any children we may have + * + * @return void + */ + private function collectChildren() + { + foreach ($this->children_ as $pid => $transport) { + if (pcntl_waitpid($pid, $status, WNOHANG) > 0) { + unset($this->children_[$pid]); + if ($transport) @$transport->close(); + } + } + } + + /** + * Stops the server running. Kills the transport + * and then stops the main serving loop + * + * @return void + */ + public function stop() + { + $this->transport_->close(); + $this->stop_ = true; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSSLServerSocket.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSSLServerSocket.php new file mode 100644 index 00000000..dfc47043 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSSLServerSocket.php @@ -0,0 +1,94 @@ +getSSLHost($host); + parent::__construct($ssl_host, $port); + $this->context_ = $context; + } + + public function getSSLHost($host) + { + $transport_protocol_loc = strpos($host, "://"); + if ($transport_protocol_loc === false) { + $host = 'ssl://'.$host; + } + return $host; + } + + /** + * Opens a new socket server handle + * + * @return void + */ + public function listen() + { + $this->listener_ = @stream_socket_server( + $this->host_ . ':' . $this->port_, + $errno, + $errstr, + STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, + $this->context_); + } + + /** + * Implementation of accept. If not client is accepted in the given time + * + * @return TSocket + */ + protected function acceptImpl() + { + $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); + if(!$handle) return null; + + $socket = new TSSLSocket(); + $socket->setHandle($handle); + + return $socket; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServer.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServer.php new file mode 100644 index 00000000..f4d76cc1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServer.php @@ -0,0 +1,100 @@ +processor_ = $processor; + $this->transport_ = $transport; + $this->inputTransportFactory_ = $inputTransportFactory; + $this->outputTransportFactory_ = $outputTransportFactory; + $this->inputProtocolFactory_ = $inputProtocolFactory; + $this->outputProtocolFactory_ = $outputProtocolFactory; + } + + /** + * Serves the server. This should never return + * unless a problem permits it to do so or it + * is interrupted intentionally + * + * @abstract + * @return void + */ + abstract public function serve(); + + /** + * Stops the server serving + * + * @abstract + * @return void + */ + abstract public function stop(); +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerSocket.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerSocket.php new file mode 100644 index 00000000..da8e2268 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerSocket.php @@ -0,0 +1,122 @@ +host_ = $host; + $this->port_ = $port; + } + + /** + * Sets the accept timeout + * + * @param int $acceptTimeout + * @return void + */ + public function setAcceptTimeout($acceptTimeout) + { + $this->acceptTimeout_ = $acceptTimeout; + } + + /** + * Opens a new socket server handle + * + * @return void + */ + public function listen() + { + $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_); + } + + /** + * Closes the socket server handle + * + * @return void + */ + public function close() + { + @fclose($this->listener_); + $this->listener_ = null; + } + + /** + * Implementation of accept. If not client is accepted in the given time + * + * @return TSocket + */ + protected function acceptImpl() + { + $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); + if(!$handle) return null; + + $socket = new TSocket(); + $socket->setHandle($handle); + + return $socket; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerTransport.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerTransport.php new file mode 100644 index 00000000..f82d06d1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TServerTransport.php @@ -0,0 +1,56 @@ +acceptImpl(); + + if ($transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + + return $transport; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSimpleServer.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSimpleServer.php new file mode 100644 index 00000000..e277700e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Server/TSimpleServer.php @@ -0,0 +1,58 @@ +transport_->listen(); + + while (!$this->stop_) { + try { + $transport = $this->transport_->accept(); + + if ($transport != null) { + $inputTransport = $this->inputTransportFactory_->getTransport($transport); + $outputTransport = $this->outputTransportFactory_->getTransport($transport); + $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); + $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); + while ($this->processor_->process($inputProtocol, $outputProtocol)) { } + } + } catch (TTransportException $e) { } + } + } + + /** + * Stops the server running. Kills the transport + * and then stops the main serving loop + * + * @return void + */ + public function stop() + { + $this->transport_->close(); + $this->stop_ = true; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/Core.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/Core.php new file mode 100644 index 00000000..39a75b3a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/Core.php @@ -0,0 +1,40 @@ +strlen($str) - $start; + } + + return mb_substr($str, $start, $length, '8bit'); + } + + public function strlen($str) + { + return mb_strlen($str, '8bit'); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/TStringFunc.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/TStringFunc.php new file mode 100644 index 00000000..dea497f2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/StringFunc/TStringFunc.php @@ -0,0 +1,28 @@ +TMultiplexedProcessor is a Processor 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:

+ * + *
+ * $processor = new TMultiplexedProcessor(); + * + * processor->registerProcessor( + * "Calculator", + * new \tutorial\CalculatorProcessor(new CalculatorHandler())); + * + * processor->registerProcessor( + * "WeatherReport", + * new \tutorial\WeatherReportProcessor(new WeatherReportHandler())); + * + * $processor->process($protocol, $protocol); + *
+ */ + +class TMultiplexedProcessor +{ + private $serviceProcessorMap_; + + /** + * '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. + * + * @param serviceName Name of a service, has to be identical to the name + * declared in the Thrift IDL, e.g. "WeatherReport". + * @param processor Implementation of a service, usually referred to + * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface. + */ + public function registerProcessor($serviceName, $processor) + { + $this->serviceProcessorMap_[$serviceName] = $processor; + } + + /** + * This implementation of process performs the following steps: + * + *
    + *
  1. Read the beginning of the message.
  2. + *
  3. Extract the service name from the message.
  4. + *
  5. Using the service name to locate the appropriate processor.
  6. + *
  7. Dispatch to the processor, with a decorated instance of TProtocol + * that allows readMessageBegin() to return the original Message.
  8. + *
+ * + * @throws TException If the message type is not CALL or ONEWAY, if + * the service name was not found in the message, or if the service + * name was not found in the service map. + */ + public function process(TProtocol $input, TProtocol $output) + { + /* + Use the actual underlying protocol (e.g. TBinaryProtocol) to read the + message header. This pulls the message "off the wire", which we'll + deal with at the end of this method. + */ + $input->readMessageBegin($fname, $mtype, $rseqid); + + if ($mtype !== TMessageType::CALL && $mtype != TMessageType::ONEWAY) { + throw new TException("This should not have happened!?"); + } + + // Extract the service name and the new Message name. + if (strpos($fname, TMultiplexedProtocol::SEPARATOR) === false) { + throw new TException("Service name not found in message name: {$fname}. Did you " . + "forget to use a TMultiplexProtocol in your client?"); + } + list($serviceName, $messageName) = explode(':', $fname, 2); + if (!array_key_exists($serviceName, $this->serviceProcessorMap_)) { + throw new TException("Service name not found: {$serviceName}. Did you forget " . + "to call registerProcessor()?"); + } + + // Dispatch processing to the stored processor + $processor = $this->serviceProcessorMap_[$serviceName]; + + return $processor->process( + new StoredMessageProtocol($input, $messageName, $mtype, $rseqid), $output + ); + } +} + +/** + * Our goal was to work with any protocol. In order to do that, we needed + * to allow them to call readMessageBegin() and get the Message in exactly + * the standard format, without the service name prepended to the Message name. + */ +class StoredMessageProtocol extends TProtocolDecorator +{ + private $fname_, $mtype_, $rseqid_; + + public function __construct(TProtocol $protocol, $fname, $mtype, $rseqid) + { + parent::__construct($protocol); + $this->fname_ = $fname; + $this->mtype_ = $mtype; + $this->rseqid_ = $rseqid; + } + + public function readMessageBegin(&$name, &$type, &$seqid) + { + $name = $this->fname_; + $type = $this->mtype_; + $seqid = $this->rseqid_; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TBufferedTransport.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TBufferedTransport.php new file mode 100644 index 00000000..f654ad3e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TBufferedTransport.php @@ -0,0 +1,181 @@ +transport_ = $transport; + $this->rBufSize_ = $rBufSize; + $this->wBufSize_ = $wBufSize; + } + + /** + * The underlying transport + * + * @var TTransport + */ + protected $transport_ = null; + + /** + * The receive buffer size + * + * @var int + */ + protected $rBufSize_ = 512; + + /** + * The write buffer size + * + * @var int + */ + protected $wBufSize_ = 512; + + /** + * The write buffer. + * + * @var string + */ + protected $wBuf_ = ''; + + /** + * The read buffer. + * + * @var string + */ + protected $rBuf_ = ''; + + public function isOpen() + { + return $this->transport_->isOpen(); + } + + public function open() + { + $this->transport_->open(); + } + + public function close() + { + $this->transport_->close(); + } + + public function putBack($data) + { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * The reason that we customize readAll here is that the majority of PHP + * streams are already internally buffered by PHP. The socket stream, for + * example, buffers internally and blocks if you call read with $len greater + * than the amount of data available, unlike recv() in C. + * + * Therefore, use the readAll method of the wrapped transport inside + * the buffered readAll. + */ + public function readAll($len) + { + $have = TStringFuncFactory::create()->strlen($this->rBuf_); + if ($have == 0) { + $data = $this->transport_->readAll($len); + } elseif ($have < $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + $data .= $this->transport_->readAll($len - $have); + } elseif ($have == $len) { + $data = $this->rBuf_; + $this->rBuf_ = ''; + } elseif ($have > $len) { + $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + } + + return $data; + } + + public function read($len) + { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $this->transport_->read($this->rBufSize_); + } + + if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) { + $ret = $this->rBuf_; + $this->rBuf_ = ''; + + return $ret; + } + + $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + + return $ret; + } + + public function write($buf) + { + $this->wBuf_ .= $buf; + if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) { + $out = $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + } + } + + public function flush() + { + if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) { + $out = $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + } + $this->transport_->flush(); + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TCurlClient.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TCurlClient.php new file mode 100644 index 00000000..4b3e694c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TCurlClient.php @@ -0,0 +1,231 @@ +strlen($uri) > 0) && ($uri{0} != '/')) { + $uri = '/'.$uri; + } + $this->scheme_ = $scheme; + $this->host_ = $host; + $this->port_ = $port; + $this->uri_ = $uri; + $this->request_ = ''; + $this->response_ = null; + $this->timeout_ = null; + } + + /** + * Set read timeout + * + * @param float $timeout + */ + public function setTimeoutSecs($timeout) + { + $this->timeout_ = $timeout; + } + + /** + * Whether this transport is open. + * + * @return boolean true if open + */ + public function isOpen() + { + return true; + } + + /** + * Open the transport for reading/writing + * + * @throws TTransportException if cannot open + */ + public function open() + { + } + + /** + * Close the transport. + */ + public function close() + { + $this->request_ = ''; + $this->response_ = null; + } + + /** + * Read some data into the array. + * + * @param int $len How much to read + * @return string The data that has been read + * @throws TTransportException if cannot read any more data + */ + public function read($len) + { + if ($len >= strlen($this->response_)) { + return $this->response_; + } else { + $ret = substr($this->response_, 0, $len); + $this->response_ = substr($this->response_, $len); + + return $ret; + } + } + + /** + * Writes some data into the pending buffer + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public function write($buf) + { + $this->request_ .= $buf; + } + + /** + * Opens and sends the actual request over the HTTP connection + * + * @throws TTransportException if a writing error occurs + */ + public function flush() + { + if (!self::$curlHandle) { + register_shutdown_function(array('Thrift\\Transport\\TCurlClient', 'closeCurlHandle')); + self::$curlHandle = curl_init(); + curl_setopt(self::$curlHandle, CURLOPT_RETURNTRANSFER, true); + curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true); + curl_setopt(self::$curlHandle, CURLOPT_USERAGENT, 'PHP/TCurlClient'); + curl_setopt(self::$curlHandle, CURLOPT_CUSTOMREQUEST, 'POST'); + curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true); + curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 1); + } + // God, PHP really has some esoteric ways of doing simple things. + $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); + $fullUrl = $this->scheme_."://".$host.$this->uri_; + + $headers = array('Accept: application/x-thrift', + 'Content-Type: application/x-thrift', + 'Content-Length: '.TStringFuncFactory::create()->strlen($this->request_)); + curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers); + + if ($this->timeout_ > 0) { + curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_); + } + curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_); + $this->request_ = ''; + + curl_setopt(self::$curlHandle, CURLOPT_URL, $fullUrl); + $this->response_ = curl_exec(self::$curlHandle); + + // Connect failed? + if (!$this->response_) { + curl_close(self::$curlHandle); + self::$curlHandle = null; + $error = 'TCurlClient: Could not connect to '.$fullUrl; + throw new TTransportException($error, TTransportException::NOT_OPEN); + } + } + + public static function closeCurlHandle() + { + try { + if (self::$curlHandle) { + curl_close(self::$curlHandle); + self::$curlHandle = null; + } + } catch (\Exception $x) { + error_log('There was an error closing the curl handle: ' . $x->getMessage()); + } + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TFramedTransport.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TFramedTransport.php new file mode 100644 index 00000000..b8a64a9a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TFramedTransport.php @@ -0,0 +1,193 @@ +transport_ = $transport; + $this->read_ = $read; + $this->write_ = $write; + } + + public function isOpen() + { + return $this->transport_->isOpen(); + } + + public function open() + { + $this->transport_->open(); + } + + public function close() + { + $this->transport_->close(); + } + + /** + * Reads from the buffer. When more data is required reads another entire + * chunk and serves future reads out of that. + * + * @param int $len How much data + */ + public function read($len) + { + if (!$this->read_) { + return $this->transport_->read($len); + } + + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->readFrame(); + } + + // Just return full buff + if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) { + $out = $this->rBuf_; + $this->rBuf_ = null; + + return $out; + } + + // Return TStringFuncFactory::create()->substr + $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); + $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); + + return $out; + } + + /** + * Put previously read data back into the buffer + * + * @param string $data data to return + */ + public function putBack($data) + { + if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { + $this->rBuf_ = $data; + } else { + $this->rBuf_ = ($data . $this->rBuf_); + } + } + + /** + * Reads a chunk of data into the internal read buffer. + */ + private function readFrame() + { + $buf = $this->transport_->readAll(4); + $val = unpack('N', $buf); + $sz = $val[1]; + + $this->rBuf_ = $this->transport_->readAll($sz); + } + + /** + * Writes some data to the pending output buffer. + * + * @param string $buf The data + * @param int $len Limit of bytes to write + */ + public function write($buf, $len=null) + { + if (!$this->write_) { + return $this->transport_->write($buf, $len); + } + + if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) { + $buf = TStringFuncFactory::create()->substr($buf, 0, $len); + } + $this->wBuf_ .= $buf; + } + + /** + * Writes the output buffer to the stream in the format of a 4-byte length + * followed by the actual data. + */ + public function flush() + { + if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) { + return $this->transport_->flush(); + } + + $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_)); + $out .= $this->wBuf_; + + // Note that we clear the internal wBuf_ prior to the underlying write + // to ensure we're in a sane state (i.e. internal buffer cleaned) + // if the underlying write throws up an exception + $this->wBuf_ = ''; + $this->transport_->write($out); + $this->transport_->flush(); + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/THttpClient.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/THttpClient.php new file mode 100644 index 00000000..b372ab74 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/THttpClient.php @@ -0,0 +1,229 @@ +strlen($uri) > 0) && ($uri{0} != '/')) { + $uri = '/'.$uri; + } + $this->scheme_ = $scheme; + $this->host_ = $host; + $this->port_ = $port; + $this->uri_ = $uri; + $this->buf_ = ''; + $this->handle_ = null; + $this->timeout_ = null; + $this->headers_ = array(); + } + + /** + * Set read timeout + * + * @param float $timeout + */ + public function setTimeoutSecs($timeout) + { + $this->timeout_ = $timeout; + } + + /** + * Whether this transport is open. + * + * @return boolean true if open + */ + public function isOpen() + { + return true; + } + + /** + * Open the transport for reading/writing + * + * @throws TTransportException if cannot open + */ + public function open() {} + + /** + * Close the transport. + */ + public function close() + { + if ($this->handle_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * Read some data into the array. + * + * @param int $len How much to read + * @return string The data that has been read + * @throws TTransportException if cannot read any more data + */ + public function read($len) + { + $data = @fread($this->handle_, $len); + if ($data === FALSE || $data === '') { + $md = stream_get_meta_data($this->handle_); + if ($md['timed_out']) { + throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::TIMED_OUT); + } else { + throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::UNKNOWN); + } + } + + return $data; + } + + /** + * Writes some data into the pending buffer + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + public function write($buf) + { + $this->buf_ .= $buf; + } + + /** + * Opens and sends the actual request over the HTTP connection + * + * @throws TTransportException if a writing error occurs + */ + public function flush() + { + // God, PHP really has some esoteric ways of doing simple things. + $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); + + $headers = array(); + $defaultHeaders = array('Host' => $host, + 'Accept' => 'application/x-thrift', + 'User-Agent' => 'PHP/THttpClient', + 'Content-Type' => 'application/x-thrift', + 'Content-Length' => TStringFuncFactory::create()->strlen($this->buf_)); + foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) { + $headers[] = "$key: $value"; + } + + $options = array('method' => 'POST', + 'header' => implode("\r\n", $headers), + 'max_redirects' => 1, + 'content' => $this->buf_); + if ($this->timeout_ > 0) { + $options['timeout'] = $this->timeout_; + } + $this->buf_ = ''; + + $contextid = stream_context_create(array('http' => $options)); + $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid); + + // Connect failed? + if ($this->handle_ === FALSE) { + $this->handle_ = null; + $error = 'THttpClient: Could not connect to '.$host.$this->uri_; + throw new TTransportException($error, TTransportException::NOT_OPEN); + } + } + + public function addHeaders($headers) + { + $this->headers_ = array_merge($this->headers_, $headers); + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TMemoryBuffer.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TMemoryBuffer.php new file mode 100644 index 00000000..ca31c579 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TMemoryBuffer.php @@ -0,0 +1,100 @@ +buf_ = $buf; + } + + protected $buf_ = ''; + + public function isOpen() + { + return true; + } + + public function open() {} + + public function close() {} + + public function write($buf) + { + $this->buf_ .= $buf; + } + + public function read($len) + { + $bufLength = TStringFuncFactory::create()->strlen($this->buf_); + + if ($bufLength === 0) { + throw new TTransportException('TMemoryBuffer: Could not read ' . + $len . ' bytes from buffer.', + TTransportException::UNKNOWN); + } + + if ($bufLength <= $len) { + $ret = $this->buf_; + $this->buf_ = ''; + + return $ret; + } + + $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len); + $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len); + + return $ret; + } + + public function getBuffer() + { + return $this->buf_; + } + + public function available() + { + return TStringFuncFactory::create()->strlen($this->buf_); + } + + public function putBack($data) + { + $this->buf_ = $data.$this->buf_; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TNullTransport.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TNullTransport.php new file mode 100644 index 00000000..feeb7a46 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TNullTransport.php @@ -0,0 +1,51 @@ +read_ = $mode & self::MODE_R; + $this->write_ = $mode & self::MODE_W; + } + + public function open() + { + if ($this->read_) { + $this->inStream_ = @fopen(self::inStreamName(), 'r'); + if (!is_resource($this->inStream_)) { + throw new TException('TPhpStream: Could not open php://input'); + } + } + if ($this->write_) { + $this->outStream_ = @fopen('php://output', 'w'); + if (!is_resource($this->outStream_)) { + throw new TException('TPhpStream: Could not open php://output'); + } + } + } + + public function close() + { + if ($this->read_) { + @fclose($this->inStream_); + $this->inStream_ = null; + } + if ($this->write_) { + @fclose($this->outStream_); + $this->outStream_ = null; + } + } + + public function isOpen() + { + return + (!$this->read_ || is_resource($this->inStream_)) && + (!$this->write_ || is_resource($this->outStream_)); + } + + public function read($len) + { + $data = @fread($this->inStream_, $len); + if ($data === FALSE || $data === '') { + throw new TException('TPhpStream: Could not read '.$len.' bytes'); + } + + return $data; + } + + public function write($buf) + { + while (TStringFuncFactory::create()->strlen($buf) > 0) { + $got = @fwrite($this->outStream_, $buf); + if ($got === 0 || $got === FALSE) { + throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes'); + } + $buf = TStringFuncFactory::create()->substr($buf, $got); + } + } + + public function flush() + { + @fflush($this->outStream_); + } + + private static function inStreamName() + { + if (php_sapi_name() == 'cli') { + return 'php://stdin'; + } + + return 'php://input'; + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSSLSocket.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSSLSocket.php new file mode 100644 index 00000000..533b7bbf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSSLSocket.php @@ -0,0 +1,112 @@ +host_ = $this->getSSLHost($host); + $this->port_ = $port; + $this->context_ = $context; + $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; + } + + /** + * Creates a host name with SSL transport protocol + * if no transport protocol already specified in + * the host name. + * + * @param string $host Host to listen on + * @return string $host Host name with transport protocol + */ + private function getSSLHost($host) + { + $transport_protocol_loc = strpos($host, "://"); + if ($transport_protocol_loc === false) { + $host = 'ssl://'.$host; + } + return $host; + } + + /** + * Connects the socket. + */ + public function open() + { + if ($this->isOpen()) { + throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); + } + + if (empty($this->host_)) { + throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); + } + + if ($this->port_ <= 0) { + throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); + } + + $this->handle_ = @stream_socket_client($this->host_.':'.$this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000), + STREAM_CLIENT_CONNECT, + $this->context_); + + // Connect failed? + if ($this->handle_ === FALSE) { + $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocket.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocket.php new file mode 100644 index 00000000..10d5115d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocket.php @@ -0,0 +1,337 @@ +host_ = $host; + $this->port_ = $port; + $this->persist_ = $persist; + $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; + } + + /** + * @param resource $handle + * @return void + */ + public function setHandle($handle) + { + $this->handle_ = $handle; + } + + /** + * Sets the send timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setSendTimeout($timeout) + { + $this->sendTimeoutSec_ = floor($timeout / 1000); + $this->sendTimeoutUsec_ = + ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets the receive timeout. + * + * @param int $timeout Timeout in milliseconds. + */ + public function setRecvTimeout($timeout) + { + $this->recvTimeoutSec_ = floor($timeout / 1000); + $this->recvTimeoutUsec_ = + ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000; + } + + /** + * Sets debugging output on or off + * + * @param bool $debug + */ + public function setDebug($debug) + { + $this->debug_ = $debug; + } + + /** + * Get the host that this socket is connected to + * + * @return string host + */ + public function getHost() + { + return $this->host_; + } + + /** + * Get the remote port that this socket is connected to + * + * @return int port + */ + public function getPort() + { + return $this->port_; + } + + /** + * Tests whether this is open + * + * @return bool true if the socket is open + */ + public function isOpen() + { + return is_resource($this->handle_); + } + + /** + * Connects the socket. + */ + public function open() + { + if ($this->isOpen()) { + throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); + } + + if (empty($this->host_)) { + throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); + } + + if ($this->port_ <= 0) { + throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); + } + + if ($this->persist_) { + $this->handle_ = @pfsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } else { + $this->handle_ = @fsockopen($this->host_, + $this->port_, + $errno, + $errstr, + $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); + } + + // Connect failed? + if ($this->handle_ === FALSE) { + $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } + } + + /** + * Closes the socket. + */ + public function close() + { + if (!$this->persist_) { + @fclose($this->handle_); + $this->handle_ = null; + } + } + + /** + * Read from the socket at most $len bytes. + * + * This method will not wait for all the requested data, it will return as + * soon as any data is received. + * + * @param int $len Maximum number of bytes to read. + * @return string Binary data + */ + public function read($len) + { + $null = null; + $read = array($this->handle_); + $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_); + + if ($readable > 0) { + $data = fread($this->handle_, $len); + if ($data === false) { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } elseif ($data == '' && feof($this->handle_)) { + throw new TTransportException('TSocket read 0 bytes'); + } + + return $data; + } elseif ($readable === 0) { + throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. + $this->host_.':'.$this->port_); + } + } + + /** + * Write to the socket. + * + * @param string $buf The data to write + */ + public function write($buf) + { + $null = null; + $write = array($this->handle_); + + // keep writing until all the data has been written + while (TStringFuncFactory::create()->strlen($buf) > 0) { + // wait for stream to become available for writing + $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_); + if ($writable > 0) { + // write buffer to stream + $written = fwrite($this->handle_, $buf); + if ($written === -1 || $written === false) { + throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + // determine how much of the buffer is left to write + $buf = TStringFuncFactory::create()->substr($buf, $written); + } elseif ($writable === 0) { + throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '. + $this->host_.':'.$this->port_); + } else { + throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. + $this->host_.':'.$this->port_); + } + } + } + + /** + * Flush output to the socket. + * + * Since read(), readAll() and write() operate on the sockets directly, + * this is a no-op + * + * If you wish to have flushable buffering behaviour, wrap this TSocket + * in a TBufferedTransport. + */ + public function flush() + { + // no-op + } + } diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocketPool.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocketPool.php new file mode 100644 index 00000000..18ffd8d9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TSocketPool.php @@ -0,0 +1,300 @@ + $val) { + $ports[$key] = $port; + } + } + + foreach ($hosts as $key => $host) { + $this->servers_ []= array('host' => $host, + 'port' => $ports[$key]); + } + } + + /** + * Add a server to the pool + * + * This function does not prevent you from adding a duplicate server entry. + * + * @param string $host hostname or IP + * @param int $port port + */ + public function addServer($host, $port) + { + $this->servers_[] = array('host' => $host, 'port' => $port); + } + + /** + * Sets how many time to keep retrying a host in the connect function. + * + * @param int $numRetries + */ + public function setNumRetries($numRetries) + { + $this->numRetries_ = $numRetries; + } + + /** + * Sets how long to wait until retrying a host if it was marked down + * + * @param int $numRetries + */ + public function setRetryInterval($retryInterval) + { + $this->retryInterval_ = $retryInterval; + } + + /** + * Sets how many time to keep retrying a host before marking it as down. + * + * @param int $numRetries + */ + public function setMaxConsecutiveFailures($maxConsecutiveFailures) + { + $this->maxConsecutiveFailures_ = $maxConsecutiveFailures; + } + + /** + * Turns randomization in connect order on or off. + * + * @param bool $randomize + */ + public function setRandomize($randomize) + { + $this->randomize_ = $randomize; + } + + /** + * Whether to always try the last server. + * + * @param bool $alwaysTryLast + */ + public function setAlwaysTryLast($alwaysTryLast) + { + $this->alwaysTryLast_ = $alwaysTryLast; + } + + /** + * Connects the socket by iterating through all the servers in the pool + * and trying to find one that works. + */ + public function open() + { + // Check if we want order randomization + if ($this->randomize_) { + shuffle($this->servers_); + } + + // Count servers to identify the "last" one + $numServers = count($this->servers_); + + for ($i = 0; $i < $numServers; ++$i) { + + // This extracts the $host and $port variables + extract($this->servers_[$i]); + + // Check APC cache for a record of this server being down + $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~'; + + // Cache miss? Assume it's OK + $lastFailtime = apc_fetch($failtimeKey); + if ($lastFailtime === FALSE) { + $lastFailtime = 0; + } + + $retryIntervalPassed = false; + + // Cache hit...make sure enough the retry interval has elapsed + if ($lastFailtime > 0) { + $elapsed = time() - $lastFailtime; + if ($elapsed > $this->retryInterval_) { + $retryIntervalPassed = true; + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: retryInterval '. + '('.$this->retryInterval_.') '. + 'has passed for host '.$host.':'.$port); + } + } + } + + // Only connect if not in the middle of a fail interval, OR if this + // is the LAST server we are trying, just hammer away on it + $isLastServer = false; + if ($this->alwaysTryLast_) { + $isLastServer = ($i == ($numServers - 1)); + } + + if (($lastFailtime === 0) || + ($isLastServer) || + ($lastFailtime > 0 && $retryIntervalPassed)) { + + // Set underlying TSocket params to this one + $this->host_ = $host; + $this->port_ = $port; + + // Try up to numRetries_ connections per server + for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { + try { + // Use the underlying TSocket open function + parent::open(); + + // Only clear the failure counts if required to do so + if ($lastFailtime > 0) { + apc_store($failtimeKey, 0); + } + + // Successful connection, return now + return; + + } catch (TException $tx) { + // Connection failed + } + } + + // Mark failure of this host in the cache + $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~'; + + // Ignore cache misses + $consecfails = apc_fetch($consecfailsKey); + if ($consecfails === FALSE) { + $consecfails = 0; + } + + // Increment by one + $consecfails++; + + // Log and cache this failure + if ($consecfails >= $this->maxConsecutiveFailures_) { + if ($this->debug_) { + call_user_func($this->debugHandler_, + 'TSocketPool: marking '.$host.':'.$port. + ' as down for '.$this->retryInterval_.' secs '. + 'after '.$consecfails.' failed attempts.'); + } + // Store the failure time + apc_store($failtimeKey, time()); + + // Clear the count of consecutive failures + apc_store($consecfailsKey, 0); + } else { + apc_store($consecfailsKey, $consecfails); + } + } + } + + // Oh no; we failed them all. The system is totally ill! + $error = 'TSocketPool: All hosts in pool are down. '; + $hosts = array(); + foreach ($this->servers_ as $server) { + $hosts []= $server['host'].':'.$server['port']; + } + $hostlist = implode(',', $hosts); + $error .= '('.$hostlist.')'; + if ($this->debug_) { + call_user_func($this->debugHandler_, $error); + } + throw new TException($error); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TTransport.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TTransport.php new file mode 100644 index 00000000..99c39ff3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Transport/TTransport.php @@ -0,0 +1,95 @@ +read($len); + + $data = ''; + $got = 0; + while (($got = TStringFuncFactory::create()->strlen($data)) < $len) { + $data .= $this->read($len - $got); + } + + return $data; + } + + /** + * Writes the given data out. + * + * @param string $buf The data to write + * @throws TTransportException if writing fails + */ + abstract public function write($buf); + + /** + * Flushes any pending data out of a buffer + * + * @throws TTransportException if a writing error occurs + */ + public function flush() {} +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Type/TConstant.php b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Type/TConstant.php new file mode 100644 index 00000000..7c8eceb0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/lib/Thrift/Type/TConstant.php @@ -0,0 +1,50 @@ +strlen($str) - $start; + } + + return mb_substr($str, $start, $length, '8bit'); + } + + public function strlen($str) + { + return mb_strlen($str, '8bit'); + } +} + +class TStringFuncFactory +{ + private static $_instance; + + /** + * Get the Singleton instance of TStringFunc implementation that is + * compatible with the current system's mbstring.func_overload settings. + * + * @return TStringFunc + */ + public static function create() + { + if (!self::$_instance) { + self::_setInstance(); + } + + return self::$_instance; + } + + private static function _setInstance() + { + /** + * Cannot use str* functions for byte counting because multibyte + * characters will be read a single bytes. + * + * See: http://us.php.net/manual/en/mbstring.overload.php + */ + if (ini_get('mbstring.func_overload') & 2) { + self::$_instance = new TStringFunc_Mbstring(); + } + /** + * mbstring is not installed or does not have function overloading + * of the str* functions enabled so use PHP core str* functions for + * byte counting. + */ + else { + self::$_instance = new TStringFunc_Core(); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/src/Thrift.php b/vendor/src/github.com/apache/thrift/lib/php/src/Thrift.php new file mode 100644 index 00000000..4fe43927 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/src/Thrift.php @@ -0,0 +1,821 @@ + $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } else { + parent::__construct($p1, $p2); + } + } + + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + private function _readMap(&$var, $spec, $input) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + + return $xfer; + } + + protected function _read($class, $spec, $input) + { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + + return $xfer; + } + + private function _writeMap($var, $spec, $output) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + + return $xfer; + } + + protected function _write($class, $spec, $output) + { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } + +} + +/** + * Base class from which other Thrift structs extend. This is so that we can + * cut back on the size of the generated code which is turning out to have a + * nontrivial cost just to load thanks to the wondrously abysmal implementation + * of PHP. Note that code is intentionally duplicated in here to avoid making + * function calls for every field or member of a container.. + */ +abstract class TBase +{ + static $tmethod = array(TType::BOOL => 'Bool', + TType::BYTE => 'Byte', + TType::I16 => 'I16', + TType::I32 => 'I32', + TType::I64 => 'I64', + TType::DOUBLE => 'Double', + TType::STRING => 'String'); + + abstract public function read($input); + + abstract public function write($output); + + public function __construct($spec=null, $vals=null) + { + if (is_array($spec) && is_array($vals)) { + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if (isset($vals[$var])) { + $this->$var = $vals[$var]; + } + } + } + } + + private function _readMap(&$var, $spec, $input) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kread = $vread = null; + if (isset(TBase::$tmethod[$ktype])) { + $kread = 'read'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vread = 'read'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $var = array(); + $_ktype = $_vtype = $size = 0; + $xfer += $input->readMapBegin($_ktype, $_vtype, $size); + for ($i = 0; $i < $size; ++$i) { + $key = $val = null; + if ($kread !== null) { + $xfer += $input->$kread($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $class = $kspec['class']; + $key = new $class(); + $xfer += $key->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($key, $kspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($key, $kspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($key, $kspec, $input, true); + break; + } + } + if ($vread !== null) { + $xfer += $input->$vread($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $class = $vspec['class']; + $val = new $class(); + $xfer += $val->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($val, $vspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($val, $vspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($val, $vspec, $input, true); + break; + } + } + $var[$key] = $val; + } + $xfer += $input->readMapEnd(); + + return $xfer; + } + + private function _readList(&$var, $spec, $input, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $eread = $vread = null; + if (isset(TBase::$tmethod[$etype])) { + $eread = 'read'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + $var = array(); + $_etype = $size = 0; + if ($set) { + $xfer += $input->readSetBegin($_etype, $size); + } else { + $xfer += $input->readListBegin($_etype, $size); + } + for ($i = 0; $i < $size; ++$i) { + $elem = null; + if ($eread !== null) { + $xfer += $input->$eread($elem); + } else { + $espec = $spec['elem']; + switch ($etype) { + case TType::STRUCT: + $class = $espec['class']; + $elem = new $class(); + $xfer += $elem->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($elem, $espec, $input); + break; + case TType::LST: + $xfer += $this->_readList($elem, $espec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($elem, $espec, $input, true); + break; + } + } + if ($set) { + $var[$elem] = true; + } else { + $var []= $elem; + } + } + if ($set) { + $xfer += $input->readSetEnd(); + } else { + $xfer += $input->readListEnd(); + } + + return $xfer; + } + + protected function _read($class, $spec, $input) + { + $xfer = 0; + $fname = null; + $ftype = 0; + $fid = 0; + $xfer += $input->readStructBegin($fname); + while (true) { + $xfer += $input->readFieldBegin($fname, $ftype, $fid); + if ($ftype == TType::STOP) { + break; + } + if (isset($spec[$fid])) { + $fspec = $spec[$fid]; + $var = $fspec['var']; + if ($ftype == $fspec['type']) { + $xfer = 0; + if (isset(TBase::$tmethod[$ftype])) { + $func = 'read'.TBase::$tmethod[$ftype]; + $xfer += $input->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $class = $fspec['class']; + $this->$var = new $class(); + $xfer += $this->$var->read($input); + break; + case TType::MAP: + $xfer += $this->_readMap($this->$var, $fspec, $input); + break; + case TType::LST: + $xfer += $this->_readList($this->$var, $fspec, $input, false); + break; + case TType::SET: + $xfer += $this->_readList($this->$var, $fspec, $input, true); + break; + } + } + } else { + $xfer += $input->skip($ftype); + } + } else { + $xfer += $input->skip($ftype); + } + $xfer += $input->readFieldEnd(); + } + $xfer += $input->readStructEnd(); + + return $xfer; + } + + private function _writeMap($var, $spec, $output) + { + $xfer = 0; + $ktype = $spec['ktype']; + $vtype = $spec['vtype']; + $kwrite = $vwrite = null; + if (isset(TBase::$tmethod[$ktype])) { + $kwrite = 'write'.TBase::$tmethod[$ktype]; + } else { + $kspec = $spec['key']; + } + if (isset(TBase::$tmethod[$vtype])) { + $vwrite = 'write'.TBase::$tmethod[$vtype]; + } else { + $vspec = $spec['val']; + } + $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); + foreach ($var as $key => $val) { + if (isset($kwrite)) { + $xfer += $output->$kwrite($key); + } else { + switch ($ktype) { + case TType::STRUCT: + $xfer += $key->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($key, $kspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($key, $kspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($key, $kspec, $output, true); + break; + } + } + if (isset($vwrite)) { + $xfer += $output->$vwrite($val); + } else { + switch ($vtype) { + case TType::STRUCT: + $xfer += $val->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($val, $vspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($val, $vspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($val, $vspec, $output, true); + break; + } + } + } + $xfer += $output->writeMapEnd(); + + return $xfer; + } + + private function _writeList($var, $spec, $output, $set=false) + { + $xfer = 0; + $etype = $spec['etype']; + $ewrite = null; + if (isset(TBase::$tmethod[$etype])) { + $ewrite = 'write'.TBase::$tmethod[$etype]; + } else { + $espec = $spec['elem']; + } + if ($set) { + $xfer += $output->writeSetBegin($etype, count($var)); + } else { + $xfer += $output->writeListBegin($etype, count($var)); + } + foreach ($var as $key => $val) { + $elem = $set ? $key : $val; + if (isset($ewrite)) { + $xfer += $output->$ewrite($elem); + } else { + switch ($etype) { + case TType::STRUCT: + $xfer += $elem->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($elem, $espec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($elem, $espec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($elem, $espec, $output, true); + break; + } + } + } + if ($set) { + $xfer += $output->writeSetEnd(); + } else { + $xfer += $output->writeListEnd(); + } + + return $xfer; + } + + protected function _write($class, $spec, $output) + { + $xfer = 0; + $xfer += $output->writeStructBegin($class); + foreach ($spec as $fid => $fspec) { + $var = $fspec['var']; + if ($this->$var !== null) { + $ftype = $fspec['type']; + $xfer += $output->writeFieldBegin($var, $ftype, $fid); + if (isset(TBase::$tmethod[$ftype])) { + $func = 'write'.TBase::$tmethod[$ftype]; + $xfer += $output->$func($this->$var); + } else { + switch ($ftype) { + case TType::STRUCT: + $xfer += $this->$var->write($output); + break; + case TType::MAP: + $xfer += $this->_writeMap($this->$var, $fspec, $output); + break; + case TType::LST: + $xfer += $this->_writeList($this->$var, $fspec, $output, false); + break; + case TType::SET: + $xfer += $this->_writeList($this->$var, $fspec, $output, true); + break; + } + } + $xfer += $output->writeFieldEnd(); + } + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } +} + +class TApplicationException extends TException +{ + static $_TSPEC = + array(1 => array('var' => 'message', + 'type' => TType::STRING), + 2 => array('var' => 'code', + 'type' => TType::I32)); + + const UNKNOWN = 0; + const UNKNOWN_METHOD = 1; + const INVALID_MESSAGE_TYPE = 2; + const WRONG_METHOD_NAME = 3; + const BAD_SEQUENCE_ID = 4; + const MISSING_RESULT = 5; + const INTERNAL_ERROR = 6; + const PROTOCOL_ERROR = 7; + + public function __construct($message=null, $code=0) + { + parent::__construct($message, $code); + } + + public function read($output) + { + return $this->_read('TApplicationException', self::$_TSPEC, $output); + } + + public function write($output) + { + $xfer = 0; + $xfer += $output->writeStructBegin('TApplicationException'); + if ($message = $this->getMessage()) { + $xfer += $output->writeFieldBegin('message', TType::STRING, 1); + $xfer += $output->writeString($message); + $xfer += $output->writeFieldEnd(); + } + if ($code = $this->getCode()) { + $xfer += $output->writeFieldBegin('type', TType::I32, 2); + $xfer += $output->writeI32($code); + $xfer += $output->writeFieldEnd(); + } + $xfer += $output->writeFieldStop(); + $xfer += $output->writeStructEnd(); + + return $xfer; + } +} + +/** + * Set global THRIFT ROOT automatically via inclusion here + */ +if (!isset($GLOBALS['THRIFT_ROOT'])) { + $GLOBALS['THRIFT_ROOT'] = dirname(__FILE__); +} +include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php'; +include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php'; +include_once $GLOBALS['THRIFT_ROOT'].'/TStringUtils.php'; diff --git a/vendor/src/github.com/apache/thrift/lib/php/src/autoload.php b/vendor/src/github.com/apache/thrift/lib/php/src/autoload.php new file mode 100644 index 00000000..85bd797a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/src/autoload.php @@ -0,0 +1,51 @@ + 50000 + +#include +#if defined( WIN32 ) || defined( _WIN64 ) +typedef int int32_t; +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef long long int64_t; +typedef unsigned uint32_t; +typedef short int16_t; +typedef unsigned long long uint64_t; +#else +#include +#endif +#include + +#ifndef bswap_64 +#define bswap_64(x) (((uint64_t)(x) << 56) | \ + (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \ + (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \ + (((uint64_t)(x) << 8) & 0xff00000000ULL) | \ + (((uint64_t)(x) >> 8) & 0xff000000ULL) | \ + (((uint64_t)(x) >> 24) & 0xff0000ULL) | \ + (((uint64_t)(x) >> 40) & 0xff00ULL) | \ + ((uint64_t)(x) >> 56)) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htonll(x) bswap_64(x) +#define ntohll(x) bswap_64(x) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htonll(x) x +#define ntohll(x) x +#else +#error Unknown __BYTE_ORDER +#endif + +enum TType { + T_STOP = 0, + T_VOID = 1, + T_BOOL = 2, + T_BYTE = 3, + T_I08 = 3, + T_I16 = 6, + T_I32 = 8, + T_U64 = 9, + T_I64 = 10, + T_DOUBLE = 4, + T_STRING = 11, + T_UTF7 = 11, + T_STRUCT = 12, + T_MAP = 13, + T_SET = 14, + T_LIST = 15, + T_UTF8 = 16, + T_UTF16 = 17 +}; + +const int32_t VERSION_MASK = 0xffff0000; +const int32_t VERSION_1 = 0x80010000; +const int8_t T_CALL = 1; +const int8_t T_REPLY = 2; +const int8_t T_EXCEPTION = 3; +// tprotocolexception +const int INVALID_DATA = 1; +const int BAD_VERSION = 4; + +static zend_function_entry thrift_protocol_functions[] = { + PHP_FE(thrift_protocol_write_binary, NULL) + PHP_FE(thrift_protocol_read_binary, NULL) + {NULL, NULL, NULL} +} ; + +zend_module_entry thrift_protocol_module_entry = { + STANDARD_MODULE_HEADER, + "thrift_protocol", + thrift_protocol_functions, + NULL, + NULL, + NULL, + NULL, + NULL, + "1.0", + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_THRIFT_PROTOCOL +ZEND_GET_MODULE(thrift_protocol) +#endif + +class PHPExceptionWrapper : public std::exception { +public: + PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) { + snprintf(_what, 40, "PHP exception zval=%p", ex); + } + const char* what() const throw() { return _what; } + ~PHPExceptionWrapper() throw() {} + operator zval*() const throw() { return const_cast(ex); } // Zend API doesn't do 'const'... +protected: + zval* ex; + char _what[40]; +} ; + +class PHPTransport { +public: + zval* protocol() { return p; } + zval* transport() { return t; } +protected: + PHPTransport() {} + + void construct_with_zval(zval* _p, size_t _buffer_size) { + buffer = reinterpret_cast(emalloc(_buffer_size)); + buffer_ptr = buffer; + buffer_used = 0; + buffer_size = _buffer_size; + p = _p; + + // Get the transport for the passed protocol + zval gettransport; + ZVAL_STRING(&gettransport, "getTransport", 0); + MAKE_STD_ZVAL(t); + ZVAL_NULL(t); + TSRMLS_FETCH(); + call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC); + } + ~PHPTransport() { + efree(buffer); + zval_ptr_dtor(&t); + } + + char* buffer; + char* buffer_ptr; + size_t buffer_used; + size_t buffer_size; + + zval* p; + zval* t; +}; + + +class PHPOutputTransport : public PHPTransport { +public: + PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) { + construct_with_zval(_p, _buffer_size); + } + + ~PHPOutputTransport() { + //flush(); + } + + void write(const char* data, size_t len) { + if ((len + buffer_used) > buffer_size) { + internalFlush(); + } + if (len > buffer_size) { + directWrite(data, len); + } else { + memcpy(buffer_ptr, data, len); + buffer_used += len; + buffer_ptr += len; + } + } + + void writeI64(int64_t i) { + i = htonll(i); + write((const char*)&i, 8); + } + + void writeU32(uint32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI32(int32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI16(int16_t i) { + i = htons(i); + write((const char*)&i, 2); + } + + void writeI8(int8_t i) { + write((const char*)&i, 1); + } + + void writeString(const char* str, size_t len) { + writeU32(len); + write(str, len); + } + + void flush() { + internalFlush(); + directFlush(); + } + +protected: + void internalFlush() { + if (buffer_used) { + directWrite(buffer, buffer_used); + buffer_ptr = buffer; + buffer_used = 0; + } + } + void directFlush() { + zval ret; + ZVAL_NULL(&ret); + zval flushfn; + ZVAL_STRING(&flushfn, "flush", 0); + TSRMLS_FETCH(); + call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC); + zval_dtor(&ret); + } + void directWrite(const char* data, size_t len) { + zval writefn; + ZVAL_STRING(&writefn, "write", 0); + char* newbuf = (char*)emalloc(len + 1); + memcpy(newbuf, data, len); + newbuf[len] = '\0'; + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_STRINGL(args[0], newbuf, len, 0); + TSRMLS_FETCH(); + zval ret; + ZVAL_NULL(&ret); + call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + zval_dtor(&ret); + if (EG(exception)) { + zval* ex = EG(exception); + EG(exception) = NULL; + throw PHPExceptionWrapper(ex); + } + } +}; + +class PHPInputTransport : public PHPTransport { +public: + PHPInputTransport(zval* _p, size_t _buffer_size = 8192) { + construct_with_zval(_p, _buffer_size); + } + + ~PHPInputTransport() { + put_back(); + } + + void put_back() { + if (buffer_used) { + zval putbackfn; + ZVAL_STRING(&putbackfn, "putBack", 0); + + char* newbuf = (char*)emalloc(buffer_used + 1); + memcpy(newbuf, buffer_ptr, buffer_used); + newbuf[buffer_used] = '\0'; + + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_STRINGL(args[0], newbuf, buffer_used, 0); + + TSRMLS_FETCH(); + + zval ret; + ZVAL_NULL(&ret); + call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + zval_dtor(&ret); + } + buffer_used = 0; + buffer_ptr = buffer; + } + + void skip(size_t len) { + while (len) { + size_t chunk_size = MIN(len, buffer_used); + if (chunk_size) { + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + void readBytes(void* buf, size_t len) { + while (len) { + size_t chunk_size = MIN(len, buffer_used); + if (chunk_size) { + memcpy(buf, buffer_ptr, chunk_size); + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + buf = reinterpret_cast(buf) + chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + int8_t readI8() { + int8_t c; + readBytes(&c, 1); + return c; + } + + int16_t readI16() { + int16_t c; + readBytes(&c, 2); + return (int16_t)ntohs(c); + } + + uint32_t readU32() { + uint32_t c; + readBytes(&c, 4); + return (uint32_t)ntohl(c); + } + + int32_t readI32() { + int32_t c; + readBytes(&c, 4); + return (int32_t)ntohl(c); + } + +protected: + void refill() { + assert(buffer_used == 0); + zval retval; + ZVAL_NULL(&retval); + + zval *args[1]; + MAKE_STD_ZVAL(args[0]); + ZVAL_LONG(args[0], buffer_size); + + TSRMLS_FETCH(); + + zval funcname; + ZVAL_STRING(&funcname, "read", 0); + + call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC); + zval_ptr_dtor(args); + + if (EG(exception)) { + zval_dtor(&retval); + zval* ex = EG(exception); + EG(exception) = NULL; + throw PHPExceptionWrapper(ex); + } + + buffer_used = Z_STRLEN(retval); + memcpy(buffer, Z_STRVAL(retval), buffer_used); + zval_dtor(&retval); + + buffer_ptr = buffer; + } + +}; + +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec); +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec); +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec); +void skip_element(long thrift_typeID, PHPInputTransport& transport); +void protocol_writeMessageBegin(zval *transport, const char* method_name, int32_t msgtype, int32_t seqID); + + +// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments +void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) { + TSRMLS_FETCH(); + size_t obj_typename_len = strlen(obj_typename); + zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC); + if (! ce) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename); + RETURN_NULL(); + } + + object_and_properties_init(return_value, ce, NULL); + zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC); + zval* ctor_rv = NULL; + zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC); + zval_ptr_dtor(&ctor_rv); +} + +void throw_tprotocolexception(const char* what, long errorcode) { + TSRMLS_FETCH(); + + zval *zwhat, *zerrorcode; + MAKE_STD_ZVAL(zwhat); + MAKE_STD_ZVAL(zerrorcode); + + ZVAL_STRING(zwhat, what, 1); + ZVAL_LONG(zerrorcode, errorcode); + + zval* ex; + MAKE_STD_ZVAL(ex); + createObject("\\Thrift\\Exception\\TProtocolException", ex, 2, zwhat, zerrorcode); + zval_ptr_dtor(&zwhat); + zval_ptr_dtor(&zerrorcode); + throw PHPExceptionWrapper(ex); +} + +// Sets EG(exception), call this and then RETURN_NULL(); +void throw_zend_exception_from_std_exception(const std::exception& ex TSRMLS_DC) { + zend_throw_exception(zend_exception_get_default(TSRMLS_C), const_cast(ex.what()), 0 TSRMLS_CC); +} + + +void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) { + zval** val_ptr; + Z_TYPE_P(return_value) = IS_NULL; // just in case + + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + RETURN_NULL(); + return; + case T_STRUCT: { + if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) { + throw_tprotocolexception("no class type in spec", INVALID_DATA); + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + char* structType = Z_STRVAL_PP(val_ptr); + createObject(structType, return_value); + if (Z_TYPE_P(return_value) == IS_NULL) { + // unable to create class entry + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + TSRMLS_FETCH(); + zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + char errbuf[128]; + snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec)); + throw_tprotocolexception(errbuf, INVALID_DATA); + RETURN_NULL(); + } + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + return; + } break; + case T_BOOL: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_BOOL(c != 0); + } + //case T_I08: // same numeric value as T_BYTE + case T_BYTE: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_LONG((int8_t)c); + } + case T_I16: { + uint16_t c; + transport.readBytes(&c, 2); + RETURN_LONG((int16_t)ntohs(c)); + } + case T_I32: { + uint32_t c; + transport.readBytes(&c, 4); + RETURN_LONG((int32_t)ntohl(c)); + } + case T_U64: + case T_I64: { + uint64_t c; + transport.readBytes(&c, 8); + RETURN_LONG((int64_t)ntohll(c)); + } + case T_DOUBLE: { + union { + uint64_t c; + double d; + } a; + transport.readBytes(&(a.c), 8); + a.c = ntohll(a.c); + RETURN_DOUBLE(a.d); + } + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t size = transport.readU32(); + if (size) { + char* strbuf = (char*) emalloc(size + 1); + transport.readBytes(strbuf, size); + strbuf[size] = '\0'; + ZVAL_STRINGL(return_value, strbuf, size, 0); + } else { + ZVAL_EMPTY_STRING(return_value); + } + return; + } + case T_MAP: { // array of key -> value + uint8_t types[2]; + transport.readBytes(types, 2); + uint32_t size = transport.readU32(); + array_init(return_value); + + zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr); + HashTable* keyspec = Z_ARRVAL_PP(val_ptr); + zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + for (uint32_t s = 0; s < size; ++s) { + zval *value; + MAKE_STD_ZVAL(value); + + zval* key; + MAKE_STD_ZVAL(key); + + binary_deserialize(types[0], transport, key, keyspec); + binary_deserialize(types[1], transport, value, valspec); + if (Z_TYPE_P(key) == IS_LONG) { + zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); + } + else { + if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); + zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); + } + zval_ptr_dtor(&key); + } + return; // return_value already populated + } + case T_LIST: { // array with autogenerated numeric keys + int8_t type = transport.readI8(); + uint32_t size = transport.readU32(); + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* elemspec = Z_ARRVAL_PP(val_ptr); + + array_init(return_value); + for (uint32_t s = 0; s < size; ++s) { + zval *value; + MAKE_STD_ZVAL(value); + binary_deserialize(type, transport, value, elemspec); + zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL); + } + return; + } + case T_SET: { // array of key -> TRUE + uint8_t type; + uint32_t size; + transport.readBytes(&type, 1); + transport.readBytes(&size, 4); + size = ntohl(size); + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* elemspec = Z_ARRVAL_PP(val_ptr); + + array_init(return_value); + + for (uint32_t s = 0; s < size; ++s) { + zval* key; + zval* value; + MAKE_STD_ZVAL(key); + MAKE_STD_ZVAL(value); + ZVAL_TRUE(value); + + binary_deserialize(type, transport, key, elemspec); + + if (Z_TYPE_P(key) == IS_LONG) { + zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); + } + else { + if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key); + zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); + } + zval_ptr_dtor(&key); + } + return; + } + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +void skip_element(long thrift_typeID, PHPInputTransport& transport) { + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: + while (true) { + int8_t ttype = transport.readI8(); // get field type + if (ttype == T_STOP) break; + transport.skip(2); // skip field number, I16 + skip_element(ttype, transport); // skip field payload + } + return; + case T_BOOL: + case T_BYTE: + transport.skip(1); + return; + case T_I16: + transport.skip(2); + return; + case T_I32: + transport.skip(4); + return; + case T_U64: + case T_I64: + case T_DOUBLE: + transport.skip(8); + return; + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t len = transport.readU32(); + transport.skip(len); + } return; + case T_MAP: { + int8_t keytype = transport.readI8(); + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(keytype, transport); + skip_element(valtype, transport); + } + } return; + case T_LIST: + case T_SET: { + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(valtype, transport); + } + } return; + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +void protocol_writeMessageBegin(zval* transport, const char* method_name, int32_t msgtype, int32_t seqID) { + TSRMLS_FETCH(); + zval *args[3]; + + MAKE_STD_ZVAL(args[0]); + ZVAL_STRINGL(args[0], (char*)method_name, strlen(method_name), 1); + + MAKE_STD_ZVAL(args[1]); + ZVAL_LONG(args[1], msgtype); + + MAKE_STD_ZVAL(args[2]); + ZVAL_LONG(args[2], seqID); + + zval ret; + ZVAL_NULL(&ret); + + zval writeMessagefn; + ZVAL_STRING(&writeMessagefn, "writeMessageBegin", 0); + + call_user_function(EG(function_table), &transport, &writeMessagefn, &ret, 3, args TSRMLS_CC); + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[2]); + zval_dtor(&ret); +} + +void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) { + bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16))); + + char* key; + uint key_len; + long index = 0; + + zval* z; + MAKE_STD_ZVAL(z); + + int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos); + if (keytype_is_numeric) { + if (res == HASH_KEY_IS_STRING) { + index = strtol(key, NULL, 10); + } + ZVAL_LONG(z, index); + } else { + char buf[64]; + if (res == HASH_KEY_IS_STRING) { + key_len -= 1; // skip the null terminator + } else { + sprintf(buf, "%ld", index); + key = buf; key_len = strlen(buf); + } + ZVAL_STRINGL(z, key, key_len, 1); + } + binary_serialize(keytype, transport, &z, NULL); + zval_ptr_dtor(&z); +} + +inline bool ttype_is_int(int8_t t) { + return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64))); +} + +inline bool ttypes_are_compatible(int8_t t1, int8_t t2) { + // Integer types of different widths are considered compatible; + // otherwise the typeID must match. + return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2))); +} + +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) { + // SET and LIST have 'elem' => array('type', [optional] 'class') + // MAP has 'val' => array('type', [optiona] 'class') + TSRMLS_FETCH(); + zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC); + while (true) { + zval** val_ptr = NULL; + + int8_t ttype = transport.readI8(); + if (ttype == T_STOP) return; + int16_t fieldno = transport.readI16(); + if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) { + HashTable* fieldspec = Z_ARRVAL_PP(val_ptr); + // pull the field name + // zend hash tables use the null at the end in the length... so strlen(hash key) + 1. + zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr); + char* varname = Z_STRVAL_PP(val_ptr); + + // and the type + zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + int8_t expected_ttype = Z_LVAL_PP(val_ptr); + + if (ttypes_are_compatible(ttype, expected_ttype)) { + zval* rv = NULL; + MAKE_STD_ZVAL(rv); + binary_deserialize(ttype, transport, rv, fieldspec); + zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC); + zval_ptr_dtor(&rv); + } else { + skip_element(ttype, transport); + } + } else { + skip_element(ttype, transport); + } + } +} + +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) { + // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload. + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: { + TSRMLS_FETCH(); + if (Z_TYPE_PP(value) != IS_OBJECT) { + throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA); + } + zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA); + } + binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec)); + } return; + case T_BOOL: + if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value); + transport.writeI8(Z_BVAL_PP(value) ? 1 : 0); + return; + case T_BYTE: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI8(Z_LVAL_PP(value)); + return; + case T_I16: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI16(Z_LVAL_PP(value)); + return; + case T_I32: + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + transport.writeI32(Z_LVAL_PP(value)); + return; + case T_I64: + case T_U64: { + int64_t l_data; +#if defined(_LP64) || defined(_WIN64) + if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value); + l_data = Z_LVAL_PP(value); +#else + if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value); + l_data = (int64_t)Z_DVAL_PP(value); +#endif + transport.writeI64(l_data); + } return; + case T_DOUBLE: { + union { + int64_t c; + double d; + } a; + if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value); + a.d = Z_DVAL_PP(value); + transport.writeI64(a.c); + } return; + //case T_UTF7: + case T_UTF8: + case T_UTF16: + case T_STRING: + if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value); + transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value)); + return; + case T_MAP: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t keytype = Z_LVAL_PP(val_ptr); + transport.writeI8(keytype); + zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t valtype = Z_LVAL_PP(val_ptr); + transport.writeI8(valtype); + + zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_LIST: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t valtype = Z_LVAL_PP(val_ptr); + transport.writeI8(valtype); + + zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr); + HashTable* valspec = Z_ARRVAL_PP(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_SET: { + if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value); + if (Z_TYPE_PP(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_PP(value); + zval** val_ptr; + + zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + uint8_t keytype = Z_LVAL_PP(val_ptr); + transport.writeI8(keytype); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + } + } return; + }; + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + + +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) { + HashPosition key_ptr; + zval** val_ptr; + + TSRMLS_FETCH(); + zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC); + + for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) { + ulong fieldno; + if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) { + throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA); + return; + } + HashTable* fieldspec = Z_ARRVAL_PP(val_ptr); + + // field name + zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr); + char* varname = Z_STRVAL_PP(val_ptr); + + // thrift type + zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr); + if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr); + int8_t ttype = Z_LVAL_PP(val_ptr); + + zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC); + if (Z_TYPE_P(prop) != IS_NULL) { + transport.writeI8(ttype); + transport.writeI16(fieldno); + binary_serialize(ttype, transport, &prop, fieldspec); + } + } + transport.writeI8(T_STOP); // struct end +} + +// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write +PHP_FUNCTION(thrift_protocol_write_binary) { + int argc = ZEND_NUM_ARGS(); + if (argc < 6) { + WRONG_PARAM_COUNT; + } + + zval ***args = (zval***) emalloc(argc * sizeof(zval**)); + zend_get_parameters_array_ex(argc, args); + + if (Z_TYPE_PP(args[0]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[1]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[3]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)"); + efree(args); + RETURN_NULL(); + } + + + try { + PHPOutputTransport transport(*args[0]); + zval *protocol = *args[0]; + const char* method_name = Z_STRVAL_PP(args[1]); + convert_to_long(*args[2]); + int32_t msgtype = Z_LVAL_PP(args[2]); + zval* request_struct = *args[3]; + convert_to_long(*args[4]); + int32_t seqID = Z_LVAL_PP(args[4]); + convert_to_boolean(*args[5]); + bool strictWrite = Z_BVAL_PP(args[5]); + efree(args); + args = NULL; + protocol_writeMessageBegin(protocol, method_name, msgtype, seqID); + zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA); + } + binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec)); + transport.flush(); + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex TSRMLS_CC); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex TSRMLS_CC); + RETURN_NULL(); + } +} + +// 4 params: $transport $response_Typename $strict_read $buffer_size +PHP_FUNCTION(thrift_protocol_read_binary) { + int argc = ZEND_NUM_ARGS(); + + if (argc < 3) { + WRONG_PARAM_COUNT; + } + + zval ***args = (zval***) emalloc(argc * sizeof(zval**)); + zend_get_parameters_array_ex(argc, args); + + if (Z_TYPE_PP(args[0]) != IS_OBJECT) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)"); + efree(args); + RETURN_NULL(); + } + + if (Z_TYPE_PP(args[1]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)"); + efree(args); + RETURN_NULL(); + } + + if (argc == 4 && Z_TYPE_PP(args[3]) != IS_LONG) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "4nd parameter is not an integer (typename of expected buffer size)"); + efree(args); + RETURN_NULL(); + } + + try { + size_t buffer_size = 8192; + if (argc == 4) { + buffer_size = Z_LVAL_PP(args[3]); + } + + PHPInputTransport transport(*args[0], buffer_size); + char* obj_typename = Z_STRVAL_PP(args[1]); + convert_to_boolean(*args[2]); + bool strict_read = Z_BVAL_PP(args[2]); + efree(args); + args = NULL; + + int8_t messageType = 0; + int32_t sz = transport.readI32(); + + if (sz < 0) { + // Check for correct version number + int32_t version = sz & VERSION_MASK; + if (version != VERSION_1) { + throw_tprotocolexception("Bad version identifier", BAD_VERSION); + } + messageType = (sz & 0x000000ff); + int32_t namelen = transport.readI32(); + // skip the name string and the sequence ID, we don't care about those + transport.skip(namelen + 4); + } else { + if (strict_read) { + throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION); + } else { + // Handle pre-versioned input + transport.skip(sz); // skip string body + messageType = transport.readI8(); + transport.skip(4); // skip sequence number + } + } + + if (messageType == T_EXCEPTION) { + zval* ex; + MAKE_STD_ZVAL(ex); + createObject("\\Thrift\\Exception\\TApplicationException", ex); + zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + binary_deserialize_spec(ex, transport, Z_ARRVAL_P(spec)); + throw PHPExceptionWrapper(ex); + } + + createObject(obj_typename, return_value); + zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC); + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex TSRMLS_CC); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex TSRMLS_CC); + RETURN_NULL(); + } +} + +#endif /* PHP_VERSION_ID < 70000 && PHP_VERSION_ID > 50000 */ diff --git a/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h b/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h new file mode 100644 index 00000000..44d03ccd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol.h @@ -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. + */ + +#pragma once + +PHP_FUNCTION(thrift_protocol_write_binary); +PHP_FUNCTION(thrift_protocol_read_binary); + +extern zend_module_entry thrift_protocol_module_entry; +#define phpext_thrift_protocol_ptr &thrift_protocol_module_entry + diff --git a/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp b/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp new file mode 100644 index 00000000..59fa30a7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/src/ext/thrift_protocol/php_thrift_protocol7.cpp @@ -0,0 +1,1020 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" +#include "php_thrift_protocol.h" + +#if PHP_VERSION_ID >= 70000 + +#include +#include + +#include +#include +#include + +#ifndef bswap_64 +#define bswap_64(x) (((uint64_t)(x) << 56) | \ + (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \ + (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \ + (((uint64_t)(x) << 8) & 0xff00000000ULL) | \ + (((uint64_t)(x) >> 8) & 0xff000000ULL) | \ + (((uint64_t)(x) >> 24) & 0xff0000ULL) | \ + (((uint64_t)(x) >> 40) & 0xff00ULL) | \ + ((uint64_t)(x) >> 56)) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htonll(x) bswap_64(x) +#define ntohll(x) bswap_64(x) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htonll(x) x +#define ntohll(x) x +#else +#error Unknown __BYTE_ORDER +#endif + +enum TType { + T_STOP = 0, + T_VOID = 1, + T_BOOL = 2, + T_BYTE = 3, + T_I08 = 3, + T_I16 = 6, + T_I32 = 8, + T_U64 = 9, + T_I64 = 10, + T_DOUBLE = 4, + T_STRING = 11, + T_UTF7 = 11, + T_STRUCT = 12, + T_MAP = 13, + T_SET = 14, + T_LIST = 15, + T_UTF8 = 16, + T_UTF16 = 17 +}; + +const int32_t VERSION_MASK = 0xffff0000; +const int32_t VERSION_1 = 0x80010000; +const int8_t T_CALL = 1; +const int8_t T_REPLY = 2; +const int8_t T_EXCEPTION = 3; +// tprotocolexception +const int INVALID_DATA = 1; +const int BAD_VERSION = 4; + +static zend_function_entry thrift_protocol_functions[] = { + PHP_FE(thrift_protocol_write_binary, nullptr) + PHP_FE(thrift_protocol_read_binary, nullptr) + {nullptr, nullptr, nullptr} +}; + +zend_module_entry thrift_protocol_module_entry = { + STANDARD_MODULE_HEADER, + "thrift_protocol", + thrift_protocol_functions, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "1.0", + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_THRIFT_PROTOCOL +ZEND_GET_MODULE(thrift_protocol) +#endif + +class PHPExceptionWrapper : public std::exception { +public: + PHPExceptionWrapper(zval* _ex) throw() { + ZVAL_COPY(&ex, _ex); + snprintf(_what, 40, "PHP exception zval=%p", _ex); + } + + PHPExceptionWrapper(zend_object* _exobj) throw() { + ZVAL_OBJ(&ex, _exobj); + snprintf(_what, 40, "PHP exception zval=%p", _exobj); + } + ~PHPExceptionWrapper() throw() { + zval_dtor(&ex); + } + + const char* what() const throw() { + return _what; + } + operator zval*() const throw() { + return const_cast(&ex); + } // Zend API doesn't do 'const'... +protected: + zval ex; + char _what[40]; +} ; + +class PHPTransport { +protected: + PHPTransport(zval* _p, size_t _buffer_size) { + assert(Z_TYPE_P(_p) == IS_OBJECT); + + ZVAL_UNDEF(&t); + + buffer = reinterpret_cast(emalloc(_buffer_size)); + buffer_ptr = buffer; + buffer_used = 0; + buffer_size = _buffer_size; + + // Get the transport for the passed protocol + zval gettransport; + ZVAL_STRING(&gettransport, "getTransport"); + call_user_function(nullptr, _p, &gettransport, &t, 0, nullptr); + + zval_dtor(&gettransport); + + assert(Z_TYPE(t) == IS_OBJECT); + } + + ~PHPTransport() { + efree(buffer); + zval_dtor(&t); + } + + char* buffer; + char* buffer_ptr; + size_t buffer_used; + size_t buffer_size; + + zval t; +}; + + +class PHPOutputTransport : public PHPTransport { +public: + PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { } + ~PHPOutputTransport() { } + + void write(const char* data, size_t len) { + if ((len + buffer_used) > buffer_size) { + internalFlush(); + } + if (len > buffer_size) { + directWrite(data, len); + } else { + memcpy(buffer_ptr, data, len); + buffer_used += len; + buffer_ptr += len; + } + } + + void writeI64(int64_t i) { + i = htonll(i); + write((const char*)&i, 8); + } + + void writeU32(uint32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI32(int32_t i) { + i = htonl(i); + write((const char*)&i, 4); + } + + void writeI16(int16_t i) { + i = htons(i); + write((const char*)&i, 2); + } + + void writeI8(int8_t i) { + write((const char*)&i, 1); + } + + void writeString(const char* str, size_t len) { + writeU32(len); + write(str, len); + } + + void flush() { + internalFlush(); + directFlush(); + } + +protected: + void internalFlush() { + if (buffer_used) { + directWrite(buffer, buffer_used); + buffer_ptr = buffer; + buffer_used = 0; + } + } + void directFlush() { + zval ret, flushfn; + ZVAL_NULL(&ret); + ZVAL_STRING(&flushfn, "flush"); + + call_user_function(EG(function_table), &(this->t), &flushfn, &ret, 0, nullptr); + zval_dtor(&flushfn); + zval_dtor(&ret); + } + void directWrite(const char* data, size_t len) { + zval args[1], ret, writefn; + + ZVAL_STRING(&writefn, "write"); + ZVAL_STRINGL(&args[0], data, len); + + ZVAL_NULL(&ret); + call_user_function(EG(function_table), &(this->t), &writefn, &ret, 1, args); + + zval_dtor(&writefn); + zval_dtor(&ret); + zval_dtor(&args[0]); + + if (EG(exception)) { + zend_object *ex = EG(exception); + EG(exception) = nullptr; + throw PHPExceptionWrapper(ex); + } + } +}; + +class PHPInputTransport : public PHPTransport { +public: + PHPInputTransport(zval* _p, size_t _buffer_size = 8192) : PHPTransport(_p, _buffer_size) { + } + + ~PHPInputTransport() { + put_back(); + } + + void put_back() { + if (buffer_used) { + zval args[1], ret, putbackfn; + ZVAL_STRINGL(&args[0], buffer_ptr, buffer_used); + ZVAL_STRING(&putbackfn, "putBack"); + ZVAL_NULL(&ret); + + call_user_function(EG(function_table), &(this->t), &putbackfn, &ret, 1, args); + + zval_dtor(&putbackfn); + zval_dtor(&ret); + zval_dtor(&args[0]); + } + buffer_used = 0; + buffer_ptr = buffer; + } + + void skip(size_t len) { + while (len) { + size_t chunk_size = std::min(len, buffer_used); + if (chunk_size) { + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + void readBytes(void* buf, size_t len) { + while (len) { + size_t chunk_size = std::min(len, buffer_used); + if (chunk_size) { + memcpy(buf, buffer_ptr, chunk_size); + buffer_ptr = reinterpret_cast(buffer_ptr) + chunk_size; + buffer_used -= chunk_size; + buf = reinterpret_cast(buf) + chunk_size; + len -= chunk_size; + } + if (! len) break; + refill(); + } + } + + int8_t readI8() { + int8_t c; + readBytes(&c, 1); + return c; + } + + int16_t readI16() { + int16_t c; + readBytes(&c, 2); + return (int16_t)ntohs(c); + } + + uint32_t readU32() { + uint32_t c; + readBytes(&c, 4); + return (uint32_t)ntohl(c); + } + + int32_t readI32() { + int32_t c; + readBytes(&c, 4); + return (int32_t)ntohl(c); + } + +protected: + void refill() { + assert(buffer_used == 0); + zval retval; + zval args[1]; + zval funcname; + + ZVAL_NULL(&retval); + ZVAL_LONG(&args[0], buffer_size); + + ZVAL_STRING(&funcname, "read"); + + call_user_function(EG(function_table), &(this->t), &funcname, &retval, 1, args); + zval_dtor(&args[0]); + zval_dtor(&funcname); + + if (EG(exception)) { + zval_dtor(&retval); + + zend_object *ex = EG(exception); + EG(exception) = nullptr; + throw PHPExceptionWrapper(ex); + } + + buffer_used = Z_STRLEN(retval); + memcpy(buffer, Z_STRVAL(retval), buffer_used); + + zval_dtor(&retval); + + buffer_ptr = buffer; + } + +}; + +static +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec); +static +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec); +static +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec); + +// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments +static +void createObject(const char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = nullptr, zval* arg2 = nullptr) { + /* is there a better way to do that on the stack ? */ + zend_string *obj_name = zend_string_init(obj_typename, strlen(obj_typename), 0); + zend_class_entry* ce = zend_fetch_class(obj_name, ZEND_FETCH_CLASS_DEFAULT); + zend_string_release(obj_name); + + if (! ce) { + php_error_docref(nullptr, E_ERROR, "Class %s does not exist", obj_typename); + RETURN_NULL(); + } + + object_and_properties_init(return_value, ce, nullptr); + zend_function* constructor = zend_std_get_constructor(Z_OBJ_P(return_value)); + zval ctor_rv; + zend_call_method(return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2); + zval_dtor(&ctor_rv); +} + +static +void throw_tprotocolexception(const char* what, long errorcode) { + zval zwhat, zerrorcode; + + ZVAL_STRING(&zwhat, what); + ZVAL_LONG(&zerrorcode, errorcode); + + zval ex; + createObject("\\Thrift\\Exception\\TProtocolException", &ex, 2, &zwhat, &zerrorcode); + + zval_dtor(&zwhat); + zval_dtor(&zerrorcode); + + throw PHPExceptionWrapper(&ex); +} + +// Sets EG(exception), call this and then RETURN_NULL(); +static +void throw_zend_exception_from_std_exception(const std::exception& ex) { + zend_throw_exception(zend_exception_get_default(), const_cast(ex.what()), 0); +} + +static +void skip_element(long thrift_typeID, PHPInputTransport& transport) { + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: + while (true) { + int8_t ttype = transport.readI8(); // get field type + if (ttype == T_STOP) break; + transport.skip(2); // skip field number, I16 + skip_element(ttype, transport); // skip field payload + } + return; + case T_BOOL: + case T_BYTE: + transport.skip(1); + return; + case T_I16: + transport.skip(2); + return; + case T_I32: + transport.skip(4); + return; + case T_U64: + case T_I64: + case T_DOUBLE: + transport.skip(8); + return; + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t len = transport.readU32(); + transport.skip(len); + } return; + case T_MAP: { + int8_t keytype = transport.readI8(); + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(keytype, transport); + skip_element(valtype, transport); + } + } return; + case T_LIST: + case T_SET: { + int8_t valtype = transport.readI8(); + uint32_t size = transport.readU32(); + for (uint32_t i = 0; i < size; ++i) { + skip_element(valtype, transport); + } + } return; + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +static inline +bool zval_is_bool(zval* v) { + return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE; +} + +static +void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) { + ZVAL_NULL(return_value); + + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + RETURN_NULL(); + return; + case T_STRUCT: { + zval* val_ptr = zend_hash_str_find(fieldspec, "class", sizeof("class")-1); + if (val_ptr == nullptr) { + throw_tprotocolexception("no class type in spec", INVALID_DATA); + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + + char* structType = Z_STRVAL_P(val_ptr); + // Create an object in PHP userland based on our spec + createObject(structType, return_value); + if (Z_TYPE_P(return_value) == IS_NULL) { + // unable to create class entry + skip_element(T_STRUCT, transport); + RETURN_NULL(); + } + + zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false); + if (Z_TYPE_P(spec) != IS_ARRAY) { + char errbuf[128]; + snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec)); + throw_tprotocolexception(errbuf, INVALID_DATA); + RETURN_NULL(); + } + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + return; + } break; + case T_BOOL: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_BOOL(c != 0); + } + //case T_I08: // same numeric value as T_BYTE + case T_BYTE: { + uint8_t c; + transport.readBytes(&c, 1); + RETURN_LONG((int8_t)c); + } + case T_I16: { + uint16_t c; + transport.readBytes(&c, 2); + RETURN_LONG((int16_t)ntohs(c)); + } + case T_I32: { + uint32_t c; + transport.readBytes(&c, 4); + RETURN_LONG((int32_t)ntohl(c)); + } + case T_U64: + case T_I64: { + uint64_t c; + transport.readBytes(&c, 8); + RETURN_LONG((int64_t)ntohll(c)); + } + case T_DOUBLE: { + union { + uint64_t c; + double d; + } a; + transport.readBytes(&(a.c), 8); + a.c = ntohll(a.c); + RETURN_DOUBLE(a.d); + } + //case T_UTF7: // aliases T_STRING + case T_UTF8: + case T_UTF16: + case T_STRING: { + uint32_t size = transport.readU32(); + if (size) { + char strbuf[size+1]; + transport.readBytes(strbuf, size); + strbuf[size] = '\0'; + ZVAL_STRINGL(return_value, strbuf, size); + } else { + ZVAL_EMPTY_STRING(return_value); + } + return; + } + case T_MAP: { // array of key -> value + uint8_t types[2]; + transport.readBytes(types, 2); + uint32_t size = transport.readU32(); + array_init(return_value); + + zval *val_ptr; + val_ptr = zend_hash_str_find(fieldspec, "key", sizeof("key")-1); + HashTable* keyspec = Z_ARRVAL_P(val_ptr); + val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1); + HashTable* valspec = Z_ARRVAL_P(val_ptr); + + for (uint32_t s = 0; s < size; ++s) { + zval key, value; + + binary_deserialize(types[0], transport, &key, keyspec); + binary_deserialize(types[1], transport, &value, valspec); + if (Z_TYPE(key) == IS_LONG) { + zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value); + } else { + if (Z_TYPE(key) != IS_STRING) convert_to_string(&key); + zend_hash_update(Z_ARR_P(return_value), Z_STR(key), &value); + } + } + return; // return_value already populated + } + case T_LIST: { // array with autogenerated numeric keys + int8_t type = transport.readI8(); + uint32_t size = transport.readU32(); + zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1); + HashTable* elemspec = Z_ARRVAL_P(val_ptr); + + array_init(return_value); + for (uint32_t s = 0; s < size; ++s) { + zval value; + binary_deserialize(type, transport, &value, elemspec); + zend_hash_next_index_insert(Z_ARR_P(return_value), &value); + } + return; + } + case T_SET: { // array of key -> TRUE + uint8_t type; + uint32_t size; + transport.readBytes(&type, 1); + transport.readBytes(&size, 4); + size = ntohl(size); + zval *val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1); + HashTable* elemspec = Z_ARRVAL_P(val_ptr); + + array_init(return_value); + + for (uint32_t s = 0; s < size; ++s) { + zval key, value; + ZVAL_TRUE(&value); + + binary_deserialize(type, transport, &key, elemspec); + + if (Z_TYPE(key) == IS_LONG) { + zend_hash_index_update(Z_ARR_P(return_value), Z_LVAL(key), &value); + } else { + if (Z_TYPE(key) != IS_STRING) convert_to_string(&key); + zend_hash_update(Z_ARR_P(return_value), Z_STR(key), &value); + } + } + return; + } + }; + + char errbuf[128]; + sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +static +void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) { + bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16))); + + zend_string* key; + uint key_len; + long index = 0; + + zval z; + + int res = zend_hash_get_current_key_ex(ht, &key, (zend_ulong*)&index, &ht_pos); + if (keytype_is_numeric) { + if (res == HASH_KEY_IS_STRING) { + index = strtol(ZSTR_VAL(key), nullptr, 10); + } + ZVAL_LONG(&z, index); + } else { + char buf[64]; + if (res == HASH_KEY_IS_STRING) { + ZVAL_STR(&z, key); + } else { + snprintf(buf, 64, "%ld", index); + ZVAL_STRING(&z, buf); + } + } + binary_serialize(keytype, transport, &z, nullptr); + zval_dtor(&z); +} + +static +void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval* value, HashTable* fieldspec) { + // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload. + switch (thrift_typeID) { + case T_STOP: + case T_VOID: + return; + case T_STRUCT: { + if (Z_TYPE_P(value) != IS_OBJECT) { + throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA); + } + zval* spec = zend_read_static_property(Z_OBJCE_P(value), "_TSPEC", sizeof("_TSPEC")-1, false); + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA); + } + binary_serialize_spec(value, transport, Z_ARRVAL_P(spec)); + } return; + case T_BOOL: + if (!zval_is_bool(value)) convert_to_boolean(value); + transport.writeI8(Z_TYPE_INFO_P(value) == IS_TRUE ? 1 : 0); + return; + case T_BYTE: + if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value); + transport.writeI8(Z_LVAL_P(value)); + return; + case T_I16: + if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value); + transport.writeI16(Z_LVAL_P(value)); + return; + case T_I32: + if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value); + transport.writeI32(Z_LVAL_P(value)); + return; + case T_I64: + case T_U64: { + int64_t l_data; +#if defined(_LP64) || defined(_WIN64) + if (Z_TYPE_P(value) != IS_LONG) convert_to_long(value); + l_data = Z_LVAL_P(value); +#else + if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value); + l_data = (int64_t)Z_DVAL_P(value); +#endif + transport.writeI64(l_data); + } return; + case T_DOUBLE: { + union { + int64_t c; + double d; + } a; + if (Z_TYPE_P(value) != IS_DOUBLE) convert_to_double(value); + a.d = Z_DVAL_P(value); + transport.writeI64(a.c); + } return; + case T_UTF8: + case T_UTF16: + case T_STRING: + if (Z_TYPE_P(value) != IS_STRING) convert_to_string(value); + transport.writeString(Z_STRVAL_P(value), Z_STRLEN_P(value)); + return; + case T_MAP: { + if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value); + if (Z_TYPE_P(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_P(value); + zval* val_ptr; + + val_ptr = zend_hash_str_find(fieldspec, "ktype", sizeof("ktype")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + uint8_t keytype = Z_LVAL_P(val_ptr); + transport.writeI8(keytype); + val_ptr = zend_hash_str_find(fieldspec, "vtype", sizeof("vtype")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + uint8_t valtype = Z_LVAL_P(val_ptr); + transport.writeI8(valtype); + + val_ptr = zend_hash_str_find(fieldspec, "val", sizeof("val")-1); + HashTable* valspec = Z_ARRVAL_P(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); + (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr; + zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_LIST: { + if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value); + if (Z_TYPE_P(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_P(value); + zval* val_ptr; + + val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + uint8_t valtype = Z_LVAL_P(val_ptr); + transport.writeI8(valtype); + + val_ptr = zend_hash_str_find(fieldspec, "elem", sizeof("elem")-1); + HashTable* valspec = Z_ARRVAL_P(val_ptr); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); + (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr; + zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize(valtype, transport, val_ptr, valspec); + } + } return; + case T_SET: { + if (Z_TYPE_P(value) != IS_ARRAY) convert_to_array(value); + if (Z_TYPE_P(value) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA); + } + HashTable* ht = Z_ARRVAL_P(value); + zval* val_ptr; + + val_ptr = zend_hash_str_find(fieldspec, "etype", sizeof("etype")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + uint8_t keytype = Z_LVAL_P(val_ptr); + transport.writeI8(keytype); + + transport.writeI32(zend_hash_num_elements(ht)); + HashPosition key_ptr; + for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); + (val_ptr = zend_hash_get_current_data_ex(ht, &key_ptr)) != nullptr; + zend_hash_move_forward_ex(ht, &key_ptr)) { + binary_serialize_hashtable_key(keytype, transport, ht, key_ptr); + } + } return; + }; + + char errbuf[128]; + snprintf(errbuf, 128, "Unknown thrift typeID %d", thrift_typeID); + throw_tprotocolexception(errbuf, INVALID_DATA); +} + +static +void protocol_writeMessageBegin(zval* transport, zend_string* method_name, int32_t msgtype, int32_t seqID) { + zval args[3]; + zval ret; + zval writeMessagefn; + + ZVAL_STR(&args[0], method_name); + ZVAL_LONG(&args[1], msgtype); + ZVAL_LONG(&args[2], seqID); + ZVAL_NULL(&ret); + ZVAL_STRING(&writeMessagefn, "writeMessageBegin"); + + call_user_function(EG(function_table), transport, &writeMessagefn, &ret, 3, args); + + zval_dtor(&writeMessagefn); + zval_dtor(&args[2]); zval_dtor(&args[1]); zval_dtor(&args[0]); + zval_dtor(&ret); +} + +static inline +bool ttype_is_int(int8_t t) { + return ((t == T_BYTE) || ((t >= T_I16) && (t <= T_I64))); +} + +static inline +bool ttypes_are_compatible(int8_t t1, int8_t t2) { + // Integer types of different widths are considered compatible; + // otherwise the typeID must match. + return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2))); +} + +static +void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) { + // SET and LIST have 'elem' => array('type', [optional] 'class') + // MAP has 'val' => array('type', [optiona] 'class') + zend_class_entry* ce = Z_OBJCE_P(zthis); + while (true) { + int8_t ttype = transport.readI8(); + if (ttype == T_STOP) { + return; + } + + int16_t fieldno = transport.readI16(); + zval* val_ptr = zend_hash_index_find(spec, fieldno); + if (val_ptr != nullptr) { + HashTable* fieldspec = Z_ARRVAL_P(val_ptr); + // pull the field name + val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1); + char* varname = Z_STRVAL_P(val_ptr); + + // and the type + val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + int8_t expected_ttype = Z_LVAL_P(val_ptr); + + if (ttypes_are_compatible(ttype, expected_ttype)) { + zval rv; + ZVAL_UNDEF(&rv); + + binary_deserialize(ttype, transport, &rv, fieldspec); + zend_update_property(ce, zthis, varname, strlen(varname), &rv); + + zval_ptr_dtor(&rv); + } else { + skip_element(ttype, transport); + } + } else { + skip_element(ttype, transport); + } + } +} + +static +void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) { + HashPosition key_ptr; + zval* val_ptr; + + for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); + (val_ptr = zend_hash_get_current_data_ex(spec, &key_ptr)) != nullptr; + zend_hash_move_forward_ex(spec, &key_ptr)) { + + zend_ulong fieldno; + if (zend_hash_get_current_key_ex(spec, nullptr, &fieldno, &key_ptr) != HASH_KEY_IS_LONG) { + throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA); + return; + } + HashTable* fieldspec = Z_ARRVAL_P(val_ptr); + + // field name + val_ptr = zend_hash_str_find(fieldspec, "var", sizeof("var")-1); + char* varname = Z_STRVAL_P(val_ptr); + + // thrift type + val_ptr = zend_hash_str_find(fieldspec, "type", sizeof("type")-1); + if (Z_TYPE_P(val_ptr) != IS_LONG) convert_to_long(val_ptr); + int8_t ttype = Z_LVAL_P(val_ptr); + + zval rv; + zval* prop = zend_read_property(Z_OBJCE_P(zthis), zthis, varname, strlen(varname), false, &rv); + if (Z_TYPE_P(prop) != IS_NULL) { + transport.writeI8(ttype); + transport.writeI16(fieldno); + binary_serialize(ttype, transport, prop, fieldspec); + } + } + transport.writeI8(T_STOP); // struct end +} + +// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write +PHP_FUNCTION(thrift_protocol_write_binary) { + zval *protocol; + zval *request_struct; + zend_string *method_name; + long msgtype, seqID; + zend_bool strict_write; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "oSlolb", + &protocol, &method_name, &msgtype, + &request_struct, &seqID, &strict_write) == FAILURE) { + return; + } + + try { + zval* spec = zend_read_static_property(Z_OBJCE_P(request_struct), "_TSPEC", sizeof("_TSPEC")-1, false); + + if (Z_TYPE_P(spec) != IS_ARRAY) { + throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA); + } + + PHPOutputTransport transport(protocol); + protocol_writeMessageBegin(protocol, method_name, (int32_t) msgtype, (int32_t) seqID); + binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec)); + transport.flush(); + + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex); + RETURN_NULL(); + } +} + + +// 4 params: $transport $response_Typename $strict_read $buffer_size +PHP_FUNCTION(thrift_protocol_read_binary) { + zval *protocol; + zend_string *obj_typename; + zend_bool strict_read; + size_t buffer_size = 8192; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "oSb|l", &protocol, &obj_typename, &strict_read, &buffer_size) == FAILURE) { + return; + } + + try { + PHPInputTransport transport(protocol, buffer_size); + int8_t messageType = 0; + int32_t sz = transport.readI32(); + + if (sz < 0) { + // Check for correct version number + int32_t version = sz & VERSION_MASK; + if (version != VERSION_1) { + throw_tprotocolexception("Bad version identifier", BAD_VERSION); + } + messageType = (sz & 0x000000ff); + int32_t namelen = transport.readI32(); + // skip the name string and the sequence ID, we don't care about those + transport.skip(namelen + 4); + } else { + if (strict_read) { + throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION); + } else { + // Handle pre-versioned input + transport.skip(sz); // skip string body + messageType = transport.readI8(); + transport.skip(4); // skip sequence number + } + } + + if (messageType == T_EXCEPTION) { + zval ex; + createObject("\\Thrift\\Exception\\TApplicationException", &ex); + zval* spec = zend_read_static_property(Z_OBJCE(ex), "_TSPEC", sizeof("_TPSEC")-1, false); + binary_deserialize_spec(&ex, transport, Z_ARRVAL_P(spec)); + throw PHPExceptionWrapper(&ex); + } + + createObject(ZSTR_VAL(obj_typename), return_value); + zval* spec = zend_read_static_property(Z_OBJCE_P(return_value), "_TSPEC", sizeof("_TSPEC")-1, false); + binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec)); + } catch (const PHPExceptionWrapper& ex) { + zend_throw_exception_object(ex); + RETURN_NULL(); + } catch (const std::exception& ex) { + throw_zend_exception_from_std_exception(ex); + RETURN_NULL(); + } +} + +#endif /* PHP_VERSION_ID >= 70000 */ diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Makefile.am b/vendor/src/github.com/apache/thrift/lib/php/test/Makefile.am new file mode 100644 index 00000000..d966246f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Makefile.am @@ -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. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +stubs: ../../../test/ThriftTest.thrift TestValidators.thrift + mkdir -p ./packages + $(THRIFT) --gen php -r --out ./packages ../../../test/ThriftTest.thrift + mkdir -p ./packages/phpv + mkdir -p ./packages/phpvo + mkdir -p ./packages/phpjs + $(THRIFT) --gen php:validate -r --out ./packages/phpv TestValidators.thrift + $(THRIFT) --gen php:validate,oop -r --out ./packages/phpvo TestValidators.thrift + $(THRIFT) --gen php:json -r --out ./packages/phpjs TestValidators.thrift + +check-json-serializer: stubs +if HAVE_PHPUNIT + $(PHPUNIT) --log-junit=TEST-json-serializer.xml Test/Thrift/JsonSerialize/ +endif + +check-validator: stubs + php Test/Thrift/TestValidators.php + php Test/Thrift/TestValidators.php -oop + +check-protocol: stubs +if HAVE_PHPUNIT + $(PHPUNIT) --log-junit=TEST-log-json-protocol.xml Test/Thrift/Protocol/TestTJSONProtocol.php + $(PHPUNIT) --log-junit=TEST-binary-serializer.xml Test/Thrift/Protocol/TestBinarySerializer.php + $(PHPUNIT) --log-junit=TEST-log-simple-json-protocol.xml Test/Thrift/Protocol/TestTSimpleJSONProtocol.php +endif + +check: stubs \ + check-protocol \ + check-validator \ + check-json-serializer + +clean-local: + $(RM) -r ./packages + $(RM) TEST-*.xml + +EXTRA_DIST = \ + Test \ + TestValidators.thrift + + diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Fixtures.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Fixtures.php new file mode 100644 index 00000000..2c60a08f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Fixtures.php @@ -0,0 +1,194 @@ +<><"; + + self::$testArgs['testString3'] = + "string that ends in double-backslash \\\\"; + + self::$testArgs['testUnicodeStringWithNonBMP'] = + "สวัสดี/𝒯"; + + self::$testArgs['testDouble'] = 3.1415926535898; + + // TODO: add testBinary() call + + self::$testArgs['testByte'] = 0x01; + + self::$testArgs['testI32'] = pow( 2, 30 ); + + if (PHP_INT_SIZE == 8) { + self::$testArgs['testI64'] = pow( 2, 60 ); + } else { + self::$testArgs['testI64'] = "1152921504606847000"; + } + + self::$testArgs['testStruct'] = + new Xtruct( + array( + 'string_thing' => 'worked', + 'byte_thing' => 0x01, + 'i32_thing' => pow( 2, 30 ), + 'i64_thing' => self::$testArgs['testI64'] + ) + ); + + self::$testArgs['testNestNested'] = + new Xtruct( + array( + 'string_thing' => 'worked', + 'byte_thing' => 0x01, + 'i32_thing' => pow( 2, 30 ), + 'i64_thing' => self::$testArgs['testI64'] + ) + ); + + self::$testArgs['testNest'] = + new Xtruct2( + array( + 'byte_thing' => 0x01, + 'struct_thing' => self::$testArgs['testNestNested'], + 'i32_thing' => pow( 2, 15 ) + ) + ); + + self::$testArgs['testMap'] = + array( + 7 => 77, + 8 => 88, + 9 => 99 + ); + + self::$testArgs['testStringMap'] = + array( + "a" => "123", + "a b" => "with spaces ", + "same" => "same", + "0" => "numeric key", + "longValue" => self::$testArgs['testString1'], + self::$testArgs['testString1'] => "long key" + ); + + self::$testArgs['testSet'] = array( 1 => true, 5 => true, 6 => true ); + + self::$testArgs['testList'] = array( 1, 2, 3 ); + + self::$testArgs['testEnum'] = Numberz::ONE; + + self::$testArgs['testTypedef'] = 69; + + self::$testArgs['testMapMapExpectedResult'] = + array( + 4 => array( + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + ), + -4 => array( + -4 => -4, + -3 => -3, + -2 => -2, + -1 => -1 + ) + ); + + // testInsanity ... takes a few steps to set up! + + $xtruct1 = + new Xtruct( + array( + 'string_thing' => 'Goodbye4', + 'byte_thing' => 4, + 'i32_thing' => 4, + 'i64_thing' => 4 + ) + ); + + $xtruct2 = + new Xtruct( + array( + 'string_thing' => 'Hello2', + 'byte_thing' =>2, + 'i32_thing' => 2, + 'i64_thing' => 2 + ) + ); + + $userMap = + array( + Numberz::FIVE => 5, + Numberz::EIGHT => 8 + ); + + $insanity2 = + new Insanity( + array( + 'userMap' => $userMap, + 'xtructs' => array($xtruct1,$xtruct2) + ) + ); + + $insanity3 = $insanity2; + + $insanity6 = + new Insanity( + array( + 'userMap' => null, + 'xtructs' => null + ) + ); + + self::$testArgs['testInsanityExpectedResult'] = + array( + "1" => array( + Numberz::TWO => $insanity2, + Numberz::THREE => $insanity3 + ), + "2" => array( + Numberz::SIX => $insanity6 + ) + ); + + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php new file mode 100644 index 00000000..2471b520 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/JsonSerialize/JsonSerializeTest.php @@ -0,0 +1,116 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages/phpjs'); +$loader->register(); + +class JsonSerializeTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() { + if (version_compare(phpversion(), '5.4', '<')) { + $this->markTestSkipped('Requires PHP 5.4 or newer!'); + } + } + + public function testEmptyStruct() + { + $empty = new \ThriftTest\EmptyStruct(array('non_existing_key' => 'bar')); + $this->assertEquals(new stdClass(), json_decode(json_encode($empty))); + } + + public function testStringsAndInts() + { + $input = array( + 'string_thing' => 'foo', + 'i64_thing' => 1234567890, + ); + $xtruct = new \ThriftTest\Xtruct($input); + + // Xtruct's 'i32_thing' and 'byte_thing' fields should not be present here! + $expected = new stdClass(); + $expected->string_thing = $input['string_thing']; + $expected->i64_thing = $input['i64_thing']; + $this->assertEquals($expected, json_decode(json_encode($xtruct))); + } + + public function testNestedStructs() + { + $xtruct2 = new \ThriftTest\Xtruct2(array( + 'byte_thing' => 42, + 'struct_thing' => new \ThriftTest\Xtruct(array( + 'i32_thing' => 123456, + )), + )); + + $expected = new stdClass(); + $expected->byte_thing = $xtruct2->byte_thing; + $expected->struct_thing = new stdClass(); + $expected->struct_thing->i32_thing = $xtruct2->struct_thing->i32_thing; + $this->assertEquals($expected, json_decode(json_encode($xtruct2))); + } + + public function testInsanity() + { + $xinput = array('string_thing' => 'foo'); + $xtruct = new \ThriftTest\Xtruct($xinput); + $insanity = new \ThriftTest\Insanity(array( + 'xtructs' => array($xtruct, $xtruct, $xtruct) + )); + $expected = new stdClass(); + $expected->xtructs = array((object) $xinput, (object) $xinput, (object) $xinput); + $this->assertEquals($expected, json_decode(json_encode($insanity))); + } + + public function testNestedLists() + { + $bonk = new \ThriftTest\Bonk(array('message' => 'foo')); + $nested = new \ThriftTest\NestedListsBonk(array('bonk' => array(array(array($bonk))))); + $expected = new stdClass(); + $expected->bonk = array(array(array((object) array('message' => 'foo')))); + $this->assertEquals($expected, json_decode(json_encode($nested))); + } + + public function testMaps() + { + $intmap = new \ThriftTest\ThriftTest_testMap_args(['thing' => [0 => 'zero']]); + $emptymap = new \ThriftTest\ThriftTest_testMap_args([]); + $this->assertEquals('{"thing":{"0":"zero"}}', json_encode($intmap)); + $this->assertEquals('{}', json_encode($emptymap)); + } + + public function testScalarTypes() + { + $b = new \ThriftTest\Bools(['im_true' => '1', 'im_false' => '0']); + $this->assertEquals('{"im_true":true,"im_false":false}', json_encode($b)); + $s = new \ThriftTest\StructA(['s' => 42]); + $this->assertEquals('{"s":"42"}', json_encode($s)); + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php new file mode 100644 index 00000000..a9832162 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestBinarySerializer.php @@ -0,0 +1,64 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages'); +$loader->register(); + +/*** + * This test suite depends on running the compiler against the + * standard ThriftTest.thrift file: + * + * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \ + * --out ./packages ../../../test/ThriftTest.thrift + */ + +class TestBinarySerializer extends \PHPUnit_Framework_TestCase +{ + + public function setUp() + { + } + + /** + * We try to serialize and deserialize a random object to make sure no exceptions are thrown. + * @see THRIFT-1579 + */ + public function testBinarySerializer() + { + $struct = new \ThriftTest\Xtruct(array('string_thing' => 'abc')); + $serialized = TBinarySerializer::serialize($struct, 'ThriftTest\\Xtruct'); + $deserialized = TBinarySerializer::deserialize($serialized, 'ThriftTest\\Xtruct'); + $this->assertEquals($struct, $deserialized); + } + +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php new file mode 100644 index 00000000..a4ca9d57 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTJSONProtocol.php @@ -0,0 +1,583 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages'); +$loader->register(); + +/*** + * This test suite depends on running the compiler against the + * standard ThriftTest.thrift file: + * + * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \ + * --out ./packages ../../../test/ThriftTest.thrift + */ + +class TestTJSONProtocol extends \PHPUnit_Framework_TestCase +{ + private $transport; + private $protocol; + + public static function setUpBeforeClass() + { + Fixtures::populateTestArgs(); + TestTJSONProtocol_Fixtures::populateTestArgsJSON(); + } + + public function setUp() + { + $this->transport = new TMemoryBuffer(); + $this->protocol = new TJSONProtocol($this->transport); + $this->transport->open(); + } + + /*** + * WRITE TESTS + */ + + public function testVoid_Write() + { + $args = new \ThriftTest\ThriftTest_testVoid_args(); + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testVoid']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString1_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString1']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString1']; + + #$this->assertEquals( $expected, $actual ); + } + + public function testString2_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString2']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString2']; + + $this->assertEquals( $expected, $actual ); + } + + public function testDouble_Write() + { + $args = new \ThriftTest\ThriftTest_testDouble_args(); + $args->thing = Fixtures::$testArgs['testDouble']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testDouble']; + + $this->assertEquals( $expected, $actual ); + } + + public function testByte_Write() + { + $args = new \ThriftTest\ThriftTest_testByte_args(); + $args->thing = Fixtures::$testArgs['testByte']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testByte']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI32_Write() + { + $args = new \ThriftTest\ThriftTest_testI32_args(); + $args->thing = Fixtures::$testArgs['testI32']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testI32']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI64_Write() + { + $args = new \ThriftTest\ThriftTest_testI64_args(); + $args->thing = Fixtures::$testArgs['testI64']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testI64']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStruct_Write() + { + $args = new \ThriftTest\ThriftTest_testStruct_args(); + $args->thing = Fixtures::$testArgs['testStruct']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testStruct']; + + $this->assertEquals( $expected, $actual ); + } + + public function testNest_Write() + { + $args = new \ThriftTest\ThriftTest_testNest_args(); + $args->thing = Fixtures::$testArgs['testNest']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testNest']; + + $this->assertEquals( $expected, $actual ); + } + + public function testMap_Write() + { + $args = new \ThriftTest\ThriftTest_testMap_args(); + $args->thing = Fixtures::$testArgs['testMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testMap']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStringMap_Write() + { + $args = new \ThriftTest\ThriftTest_testStringMap_args(); + $args->thing = Fixtures::$testArgs['testStringMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testStringMap']; + + /* + * The $actual returns unescaped string. + * It is required to to decode then encode it again + * to get the expected escaped unicode. + */ + $this->assertEquals( $expected, json_encode(json_decode($actual)) ); + } + + public function testSet_Write() + { + $args = new \ThriftTest\ThriftTest_testSet_args(); + $args->thing = Fixtures::$testArgs['testSet']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testSet']; + + $this->assertEquals( $expected, $actual ); + } + + public function testList_Write() + { + $args = new \ThriftTest\ThriftTest_testList_args(); + $args->thing = Fixtures::$testArgs['testList']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testList']; + + $this->assertEquals( $expected, $actual ); + } + + public function testEnum_Write() + { + $args = new \ThriftTest\ThriftTest_testEnum_args(); + $args->thing = Fixtures::$testArgs['testEnum']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testEnum']; + + $this->assertEquals( $expected, $actual ); + } + + public function testTypedef_Write() + { + $args = new \ThriftTest\ThriftTest_testTypedef_args(); + $args->thing = Fixtures::$testArgs['testTypedef']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testTypedef']; + + $this->assertEquals( $expected, $actual ); + } + + /*** + * READ TESTS + */ + + public function testVoid_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testVoid'] + ); + $args = new \ThriftTest\ThriftTest_testVoid_args(); + $args->read( $this->protocol ); + } + + public function testString1_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testString1'] + ); + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testString1']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString2_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testString2'] + ); + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testString2']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString3_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString3']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testString3']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString4_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testUnicodeStringWithNonBMP']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTJSONProtocol_Fixtures::$testArgsJSON['testUnicodeStringWithNonBMP']; + + $this->assertEquals( $expected, $actual ); + } + + public function testDouble_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testDouble'] + ); + $args = new \ThriftTest\ThriftTest_testDouble_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testDouble']; + + $this->assertEquals( $expected, $actual ); + } + + public function testByte_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testByte'] + ); + $args = new \ThriftTest\ThriftTest_testByte_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testByte']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI32_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testI32'] + ); + $args = new \ThriftTest\ThriftTest_testI32_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testI32']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI64_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testI64'] + ); + $args = new \ThriftTest\ThriftTest_testI64_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testI64']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testStruct_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testStruct'] + ); + $args = new \ThriftTest\ThriftTest_testStruct_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testStruct']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testNest_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testNest'] + ); + $args = new \ThriftTest\ThriftTest_testNest_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testNest']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testMap'] + ); + $args = new \ThriftTest\ThriftTest_testMap_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testMap']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testStringMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testStringMap'] + ); + $args = new \ThriftTest\ThriftTest_testStringMap_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testStringMap']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testSet_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testSet'] + ); + $args = new \ThriftTest\ThriftTest_testSet_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testSet']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testList_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testList'] + ); + $args = new \ThriftTest\ThriftTest_testList_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testList']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testEnum_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testEnum'] + ); + $args = new \ThriftTest\ThriftTest_testEnum_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testEnum']; + + $this->assertEquals( $expected, $actual ); + + } + + public function testTypedef_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testTypedef'] + ); + $args = new \ThriftTest\ThriftTest_testTypedef_args(); + $args->read( $this->protocol ); + + $actual = $args->thing; + $expected = Fixtures::$testArgs['testTypedef']; + + $this->assertEquals( $expected, $actual ); + } + + public function testMapMap_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testMapMap'] + ); + $result = new \ThriftTest\ThriftTest_testMapMap_result(); + $result->read( $this->protocol ); + + $actual = $result->success; + $expected = Fixtures::$testArgs['testMapMapExpectedResult']; + + $this->assertEquals( $expected, $actual ); + } + + public function testInsanity_Read() + { + $this->transport->write( + TestTJSONProtocol_Fixtures::$testArgsJSON['testInsanity'] + ); + $result = new \ThriftTest\ThriftTest_testInsanity_result(); + $result->read( $this->protocol ); + + $actual = $result->success; + $expected = Fixtures::$testArgs['testInsanityExpectedResult']; + + $this->assertEquals( $expected, $actual ); + } + +} + +class TestTJSONProtocol_Fixtures +{ + public static $testArgsJSON = array(); + + public static function populateTestArgsJSON() + { + self::$testArgsJSON['testVoid'] = '{}'; + + self::$testArgsJSON['testString1'] = '{"1":{"str":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e"}}'; + + self::$testArgsJSON['testString2'] = '{"1":{"str":"quote: \\\\\" backslash: forwardslash-escaped: \\\\\/ backspace: \\\\b formfeed: \f newline: \n return: \r tab: now-all-of-them-together: \"\\\\\\\\\/\\\\b\n\r\t now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"}}'; + + self::$testArgsJSON['testString3'] = '{"1":{"str":"string that ends in double-backslash \\\\\\\\"}}'; + + self::$testArgsJSON['testUnicodeStringWithNonBMP'] = '{"1":{"str":"สวัสดี\/𝒯"}}'; + + self::$testArgsJSON['testDouble'] = '{"1":{"dbl":3.1415926535898}}'; + + self::$testArgsJSON['testByte'] = '{"1":{"i8":1}}'; + + self::$testArgsJSON['testI32'] = '{"1":{"i32":1073741824}}'; + + if (PHP_INT_SIZE == 8) { + self::$testArgsJSON['testI64'] = '{"1":{"i64":'.pow( 2, 60 ).'}}'; + self::$testArgsJSON['testStruct'] = '{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":'.pow( 2, 60 ).'}}}}'; + self::$testArgsJSON['testNest'] = '{"1":{"rec":{"1":{"i8":1},"2":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":'.pow( 2, 60 ).'}}},"3":{"i32":32768}}}}'; + } else { + self::$testArgsJSON['testI64'] = '{"1":{"i64":1152921504606847000}}'; + self::$testArgsJSON['testStruct'] = '{"1":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}}}'; + self::$testArgsJSON['testNest'] = '{"1":{"rec":{"1":{"i8":1},"2":{"rec":{"1":{"str":"worked"},"4":{"i8":1},"9":{"i32":1073741824},"11":{"i64":1152921504606847000}}},"3":{"i32":32768}}}}'; + } + + self::$testArgsJSON['testMap'] = '{"1":{"map":["i32","i32",3,{"7":77,"8":88,"9":99}]}}'; + + self::$testArgsJSON['testStringMap'] = '{"1":{"map":["str","str",6,{"a":"123","a b":"with spaces ","same":"same","0":"numeric key","longValue":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e","Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e":"long key"}]}}'; + + self::$testArgsJSON['testSet'] = '{"1":{"set":["i32",3,1,5,6]}}'; + + self::$testArgsJSON['testList'] = '{"1":{"lst":["i32",3,1,2,3]}}'; + + self::$testArgsJSON['testEnum'] = '{"1":{"i32":1}}'; + + self::$testArgsJSON['testTypedef'] = '{"1":{"i64":69}}'; + + self::$testArgsJSON['testMapMap'] = '{"0":{"map":["i32","map",2,{"4":["i32","i32",4,{"1":1,"2":2,"3":3,"4":4}],"-4":["i32","i32",4,{"-4":-4,"-3":-3,"-2":-2,"-1":-1}]}]}}'; + + self::$testArgsJSON['testInsanity'] = '{"0":{"map":["i64","map",2,{"1":["i32","rec",2,{"2":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}},"3":{"1":{"map":["i32","i64",2,{"5":5,"8":8}]},"2":{"lst":["rec",2,{"1":{"str":"Goodbye4"},"4":{"i8":4},"9":{"i32":4},"11":{"i64":4}},{"1":{"str":"Hello2"},"4":{"i8":2},"9":{"i32":2},"11":{"i64":2}}]}}}],"2":["i32","rec",1,{"6":{}}]}]}}'; + + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTSimpleJSONProtocol.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTSimpleJSONProtocol.php new file mode 100644 index 00000000..973f55cd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/Protocol/TestTSimpleJSONProtocol.php @@ -0,0 +1,300 @@ +registerNamespace('Thrift', __DIR__ . '/../../../../lib'); +$loader->registerNamespace('Test', __DIR__ . '/../../..'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../../packages'); +$loader->register(); + +/*** + * This test suite depends on running the compiler against the + * standard ThriftTest.thrift file: + * + * lib/php/test$ ../../../compiler/cpp/thrift --gen php -r \ + * --out ./packages ../../../test/ThriftTest.thrift + */ + +class TestTSimpleJSONProtocol extends \PHPUnit_Framework_TestCase +{ + private $transport; + private $protocol; + + public static function setUpBeforeClass() + { + Fixtures::populateTestArgs(); + TestTSimpleJSONProtocol_Fixtures::populateTestArgsSimpleJSON(); + } + + public function setUp() + { + $this->transport = new TMemoryBuffer(); + $this->protocol = new TSimpleJSONProtocol($this->transport); + $this->transport->open(); + } + + /*** + * WRITE TESTS + */ + + public function testVoid_Write() + { + $args = new \ThriftTest\ThriftTest_testVoid_args(); + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testVoid']; + + $this->assertEquals( $expected, $actual ); + } + + public function testString1_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString1']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testString1']; + + #$this->assertEquals( $expected, $actual ); + } + + public function testString2_Write() + { + $args = new \ThriftTest\ThriftTest_testString_args(); + $args->thing = Fixtures::$testArgs['testString2']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testString2']; + + $this->assertEquals( $expected, $actual ); + } + + public function testDouble_Write() + { + $args = new \ThriftTest\ThriftTest_testDouble_args(); + $args->thing = Fixtures::$testArgs['testDouble']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testDouble']; + + $this->assertEquals( $expected, $actual ); + } + + public function testByte_Write() + { + $args = new \ThriftTest\ThriftTest_testByte_args(); + $args->thing = Fixtures::$testArgs['testByte']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testByte']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI32_Write() + { + $args = new \ThriftTest\ThriftTest_testI32_args(); + $args->thing = Fixtures::$testArgs['testI32']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testI32']; + + $this->assertEquals( $expected, $actual ); + } + + public function testI64_Write() + { + $args = new \ThriftTest\ThriftTest_testI64_args(); + $args->thing = Fixtures::$testArgs['testI64']; + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testI64']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStruct_Write() + { + $args = new \ThriftTest\ThriftTest_testStruct_args(); + $args->thing = Fixtures::$testArgs['testStruct']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testStruct']; + + $this->assertEquals( $expected, $actual ); + } + + public function testNest_Write() + { + $args = new \ThriftTest\ThriftTest_testNest_args(); + $args->thing = Fixtures::$testArgs['testNest']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testNest']; + + $this->assertEquals( $expected, $actual ); + } + + public function testMap_Write() + { + $args = new \ThriftTest\ThriftTest_testMap_args(); + $args->thing = Fixtures::$testArgs['testMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testMap']; + + $this->assertEquals( $expected, $actual ); + } + + public function testStringMap_Write() + { + $args = new \ThriftTest\ThriftTest_testStringMap_args(); + $args->thing = Fixtures::$testArgs['testStringMap']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testStringMap']; + + $this->assertEquals( $expected, $actual ); + } + + public function testSet_Write() + { + $args = new \ThriftTest\ThriftTest_testSet_args(); + $args->thing = Fixtures::$testArgs['testSet']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testSet']; + + $this->assertEquals( $expected, $actual ); + } + + public function testList_Write() + { + $args = new \ThriftTest\ThriftTest_testList_args(); + $args->thing = Fixtures::$testArgs['testList']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testList']; + + $this->assertEquals( $expected, $actual ); + } + + public function testEnum_Write() + { + $args = new \ThriftTest\ThriftTest_testEnum_args(); + $args->thing = Fixtures::$testArgs['testEnum']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testEnum']; + + $this->assertEquals( $expected, $actual ); + } + + public function testTypedef_Write() + { + $args = new \ThriftTest\ThriftTest_testTypedef_args(); + $args->thing = Fixtures::$testArgs['testTypedef']; + + $args->write( $this->protocol ); + + $actual = $this->transport->read( BUFSIZ ); + $expected = TestTSimpleJSONProtocol_Fixtures::$testArgsJSON['testTypedef']; + + $this->assertEquals( $expected, $actual ); + } +} + +class TestTSimpleJSONProtocol_Fixtures +{ + public static $testArgsJSON = array(); + + public static function populateTestArgsSimpleJSON() + { + self::$testArgsJSON['testVoid'] = '{}'; + + self::$testArgsJSON['testString1'] = '{"1":{"str":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e"}}'; + + self::$testArgsJSON['testString2'] = '{"thing":"quote: \\\\\" backslash: forwardslash-escaped: \\\\\/ backspace: \\\\b formfeed: \f newline: \n return: \r tab: now-all-of-them-together: \"\\\\\\\\\/\\\\b\n\r\t now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"}'; + + self::$testArgsJSON['testDouble'] = '{"thing":3.1415926535898}'; + + self::$testArgsJSON['testByte'] = '{"thing":1}'; + + self::$testArgsJSON['testI32'] = '{"thing":1073741824}'; + + if (PHP_INT_SIZE == 8) { + self::$testArgsJSON['testI64'] = '{"thing":'.pow( 2, 60 ).'}'; + self::$testArgsJSON['testStruct'] = '{"thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":'.pow( 2, 60 ).'}}'; + self::$testArgsJSON['testNest'] = '{"thing":{"byte_thing":1,"struct_thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":'.pow( 2, 60 ).'},"i32_thing":32768}}'; + } else { + self::$testArgsJSON['testI64'] = '{"thing":1152921504606847000}'; + + self::$testArgsJSON['testStruct'] = '{"thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":1152921504606847000}}'; + self::$testArgsJSON['testNest'] = '{"thing":{"byte_thing":1,"struct_thing":{"string_thing":"worked","byte_thing":1,"i32_thing":1073741824,"i64_thing":1152921504606847000},"i32_thing":32768}}'; + } + + self::$testArgsJSON['testMap'] = '{"thing":{"7":77,"8":88,"9":99}}'; + + self::$testArgsJSON['testStringMap'] = '{"thing":{"a":"123","a b":"with spaces ","same":"same","0":"numeric key","longValue":"Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e","Afrikaans, Alemannisch, Aragon\u00e9s, \u0627\u0644\u0639\u0631\u0628\u064a\u0629, \u0645\u0635\u0631\u0649, Asturianu, Aymar aru, Az\u0259rbaycan, \u0411\u0430\u0448\u04a1\u043e\u0440\u0442, Boarisch, \u017demait\u0117\u0161ka, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f, \u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430), \u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438, Bamanankan, \u09ac\u09be\u0982\u09b2\u09be, Brezhoneg, Bosanski, Catal\u00e0, M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304, \u041d\u043e\u0445\u0447\u0438\u0439\u043d, Cebuano, \u13e3\u13b3\u13a9, \u010cesky, \u0421\u043b\u043e\u0432\u0463\u0301\u043d\u044c\u0441\u043a\u044a \/ \u2c14\u2c0e\u2c11\u2c02\u2c21\u2c10\u2c20\u2c14\u2c0d\u2c1f, \u0427\u04d1\u0432\u0430\u0448\u043b\u0430, Cymraeg, Dansk, Zazaki, \u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0, \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac, Emili\u00e0n e rumagn\u00f2l, English, Esperanto, Espa\u00f1ol, Eesti, Euskara, \u0641\u0627\u0631\u0633\u06cc, Suomi, V\u00f5ro, F\u00f8royskt, Fran\u00e7ais, Arpetan, Furlan, Frysk, Gaeilge, \u8d1b\u8a9e, G\u00e0idhlig, Galego, Ava\u00f1e\'\u1ebd, \u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0, Gaelg, \u05e2\u05d1\u05e8\u05d9\u05ea, \u0939\u093f\u0928\u094d\u0926\u0940, Fiji Hindi, Hrvatski, Krey\u00f2l ayisyen, Magyar, \u0540\u0561\u0575\u0565\u0580\u0565\u0576, Interlingua, Bahasa Indonesia, Ilokano, Ido, \u00cdslenska, Italiano, \u65e5\u672c\u8a9e, Lojban, Basa Jawa, \u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8, Kongo, Kalaallisut, \u0c95\u0ca8\u0ccd\u0ca8\u0ca1, \ud55c\uad6d\uc5b4, \u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440, Ripoarisch, Kurd\u00ee, \u041a\u043e\u043c\u0438, Kernewek, \u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430, Latina, Ladino, L\u00ebtzebuergesch, Limburgs, Ling\u00e1la, \u0ea5\u0eb2\u0ea7, Lietuvi\u0173, Latvie\u0161u, Basa Banyumasan, Malagasy, \u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438, \u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02, \u092e\u0930\u093e\u0920\u0940, Bahasa Melayu, \u0645\u0627\u0632\u0650\u0631\u0648\u0646\u06cc, Nnapulitano, Nedersaksisch, \u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e, Nederlands, \u202aNorsk (nynorsk)\u202c, \u202aNorsk (bokm\u00e5l)\u202c, Nouormand, Din\u00e9 bizaad, Occitan, \u0418\u0440\u043e\u043d\u0430\u0443, Papiamentu, Deitsch, Norfuk \/ Pitkern, Polski, \u067e\u0646\u062c\u0627\u0628\u06cc, \u067e\u069a\u062a\u0648, Portugu\u00eas, Runa Simi, Rumantsch, Romani, Rom\u00e2n\u0103, \u0420\u0443\u0441\u0441\u043a\u0438\u0439, \u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430, Sardu, Sicilianu, Scots, S\u00e1megiella, Simple English, Sloven\u010dina, Sloven\u0161\u010dina, \u0421\u0440\u043f\u0441\u043a\u0438 \/ Srpski, Seeltersk, Svenska, Kiswahili, \u0ba4\u0bae\u0bbf\u0bb4\u0bcd, \u0c24\u0c46\u0c32\u0c41\u0c17\u0c41, \u0422\u043e\u04b7\u0438\u043a\u04e3, \u0e44\u0e17\u0e22, T\u00fcrkmen\u00e7e, Tagalog, T\u00fcrk\u00e7e, \u0422\u0430\u0442\u0430\u0440\u0447\u0430\/Tatar\u00e7a, \u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430, \u0627\u0631\u062f\u0648, Ti\u1ebfng Vi\u1ec7t, Volap\u00fck, Walon, Winaray, \u5434\u8bed, isiXhosa, \u05d9\u05d9\u05b4\u05d3\u05d9\u05e9, Yor\u00f9b\u00e1, Ze\u00eauws, \u4e2d\u6587, B\u00e2n-l\u00e2m-g\u00fa, \u7cb5\u8a9e":"long key"}}'; + + self::$testArgsJSON['testSet'] = '{"thing":[1,5,6]}'; + + self::$testArgsJSON['testList'] = '{"thing":[1,2,3]}'; + + self::$testArgsJSON['testEnum'] = '{"thing":1}'; + + self::$testArgsJSON['testTypedef'] = '{"thing":69}'; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/TestValidators.php b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/TestValidators.php new file mode 100644 index 00000000..36cf0009 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/Test/Thrift/TestValidators.php @@ -0,0 +1,156 @@ +registerNamespace('Thrift', __DIR__ . '/../../../lib'); +$loader->registerDefinition('ThriftTest', __DIR__ . '/../../packages/' . $GEN_DIR); +$loader->registerDefinition('TestValidators', __DIR__ . '/../../packages/' . $GEN_DIR); +$loader->register(); + +// Would be nice to have PHPUnit here, but for now just hack it. + +set_exception_handler(function ($e) { + my_assert(false, "Unexpected exception caught: " . $e->getMessage()); +}); + +set_error_handler(function ($errno, $errmsg) { + my_assert(false, "Unexpected PHP error: " . $errmsg); +}); + +// Empty structs should not have validators +assert_has_no_read_validator('ThriftTest\EmptyStruct'); +assert_has_no_write_validator('ThriftTest\EmptyStruct'); + +// Bonk has only opt_in_req_out fields +{ + assert_has_no_read_validator('ThriftTest\Bonk'); + assert_has_a_write_validator('ThriftTest\Bonk'); + { + // Check that we can read an empty object + $bonk = new \ThriftTest\Bonk(); + $transport = new TMemoryBuffer("\000"); + $protocol = new TBinaryProtocol($transport); + $bonk->read($protocol); + } + { + // ...but not write an empty object + $bonk = new \ThriftTest\Bonk(); + $transport = new TMemoryBuffer(); + $protocol = new TBinaryProtocol($transport); + assert_protocol_exception_thrown(function () use ($bonk, $protocol) { $bonk->write($protocol); }, + 'Bonk was able to write an empty object'); + } +} + +// StructA has a single required field +{ + assert_has_a_read_validator('ThriftTest\StructA'); + assert_has_a_write_validator('ThriftTest\StructA'); + { + // Check that we are not able to write StructA with a missing required field + $structa = new \ThriftTest\StructA(); + $transport = new TMemoryBuffer(); + $protocol = new TBinaryProtocol($transport); + assert_protocol_exception_thrown(function () use ($structa, $protocol) { $structa->write($protocol); }, + 'StructA was able to write an empty object'); + } + { + // Check that we are able to read and write a message with a good StructA + $transport = new TMemoryBuffer(base64_decode('CwABAAAAA2FiYwA=')); + $protocol = new TBinaryProtocol($transport); + $structa = new \ThriftTest\StructA(); + $structa->read($protocol); + $structa->write($protocol); + } +} + +// Unions should not get write validators +assert_has_no_write_validator('TestValidators\UnionOfStrings'); + +// Service _result classes should not get any validators +assert_has_no_read_validator('TestValidators\TestService_test_result'); +assert_has_no_write_validator('TestValidators\TestService_test_result'); + +function assert_has_a_read_validator($class) +{ + my_assert(has_read_validator_method($class), + $class . ' class should have a read validator'); +} + +function assert_has_no_read_validator($class) +{ + my_assert(!has_read_validator_method($class), + $class . ' class should not have a read validator'); +} + +function assert_has_a_write_validator($class) +{ + my_assert(has_write_validator_method($class), + $class . ' class should have a write validator'); +} + +function assert_has_no_write_validator($class) +{ + my_assert(!has_write_validator_method($class), + $class . ' class should not have a write validator'); +} + +function assert_protocol_exception_thrown($callable, $message) +{ + try { + call_user_func($callable); + my_assert(false, $message); + } catch (TProtocolException $e) { + } +} + +function has_write_validator_method($class) +{ + $rc = new \ReflectionClass($class); + + return $rc->hasMethod('_validateForWrite'); +} + +function has_read_validator_method($class) +{ + $rc = new \ReflectionClass($class); + + return $rc->hasMethod('_validateForRead'); +} + +function my_assert($something, $message) +{ + if (!$something) { + fwrite(STDERR, basename(__FILE__) . " FAILED: $message\n"); + exit(1); + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/test/TestValidators.thrift b/vendor/src/github.com/apache/thrift/lib/php/test/TestValidators.thrift new file mode 100644 index 00000000..9c38d92a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/test/TestValidators.thrift @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace php TestValidators + +include "../../../test/ThriftTest.thrift" + +union UnionOfStrings { + 1: string aa; + 2: string bb; +} + +service TestService { + void test() throws(1: ThriftTest.Xception xception); +} diff --git a/vendor/src/github.com/apache/thrift/lib/php/thrift_protocol.ini b/vendor/src/github.com/apache/thrift/lib/php/thrift_protocol.ini new file mode 100644 index 00000000..a260d83b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/php/thrift_protocol.ini @@ -0,0 +1 @@ +extension=thrift_protocol.so diff --git a/vendor/src/github.com/apache/thrift/lib/py/CMakeLists.txt b/vendor/src/github.com/apache/thrift/lib/py/CMakeLists.txt new file mode 100644 index 00000000..7bb91fe6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +include_directories(${PYTHON_INCLUDE_DIRS}) + +add_custom_target(python_build ALL + COMMAND ${PYTHON_EXECUTABLE} setup.py build + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building Python library" +) + +if(BUILD_TESTING) + add_test(PythonTestSSLSocket ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_sslsocket.py) + add_test(PythonThriftJson ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/thrift_json.py) +endif() diff --git a/vendor/src/github.com/apache/thrift/lib/py/Makefile.am b/vendor/src/github.com/apache/thrift/lib/py/Makefile.am new file mode 100644 index 00000000..fd9ce257 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/Makefile.am @@ -0,0 +1,58 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +AUTOMAKE_OPTIONS = serial-tests +DESTDIR ?= / + +if WITH_PY3 +py3-build: + $(PYTHON3) setup.py build +py3-test: py3-build + $(PYTHON3) test/thrift_json.py + $(PYTHON3) test/test_sslsocket.py +else +py3-build: +py3-test: +endif + +all-local: py3-build + $(PYTHON) setup.py build + +# We're ignoring prefix here because site-packages seems to be +# the equivalent of /usr/local/lib in Python land. +# Old version (can't put inline because it's not portable). +#$(PYTHON) setup.py install --prefix=$(prefix) --root=$(DESTDIR) $(PYTHON_SETUPUTIL_ARGS) +install-exec-hook: + $(PYTHON) setup.py install --root=$(DESTDIR) --prefix=$(PY_PREFIX) $(PYTHON_SETUPUTIL_ARGS) + +clean-local: + $(RM) -r build + +check-local: all py3-test + $(PYTHON) test/thrift_json.py + $(PYTHON) test/test_sslsocket.py + +EXTRA_DIST = \ + CMakeLists.txt \ + coding_standards.md \ + compat \ + setup.py \ + setup.cfg \ + src \ + test \ + README.md diff --git a/vendor/src/github.com/apache/thrift/lib/py/README.md b/vendor/src/github.com/apache/thrift/lib/py/README.md new file mode 100644 index 00000000..29b8c73c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/README.md @@ -0,0 +1,35 @@ +Thrift Python Software Library + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Using Thrift with Python +======================== + +Thrift is provided as a set of Python packages. The top level package is +thrift, and there are subpackages for the protocol, transport, and server +code. Each package contains modules using standard Thrift naming conventions +(i.e. TProtocol, TTransport) and implementations in corresponding modules +(i.e. TSocket). There is also a subpackage reflection, which contains +the generated code for the reflection structures. + +The Python libraries can be installed manually using the provided setup.py +file, or automatically using the install hook provided via autoconf/automake. +To use the latter, become superuser and do make install. diff --git a/vendor/src/github.com/apache/thrift/lib/py/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/py/coding_standards.md new file mode 100644 index 00000000..4c560b52 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/coding_standards.md @@ -0,0 +1,7 @@ +## Python Coding Standards + +Please follow: + * [Thrift General Coding Standards](/doc/coding_standards.md) + * Code Style for Python Code [PEP8](http://legacy.python.org/dev/peps/pep-0008/) + +When in doubt - check with or online with . diff --git a/vendor/src/github.com/apache/thrift/lib/py/compat/win32/stdint.h b/vendor/src/github.com/apache/thrift/lib/py/compat/win32/stdint.h new file mode 100644 index 00000000..d02608a5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/compat/win32/stdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/vendor/src/github.com/apache/thrift/lib/py/setup.cfg b/vendor/src/github.com/apache/thrift/lib/py/setup.cfg new file mode 100644 index 00000000..c9ed0aec --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/setup.cfg @@ -0,0 +1,6 @@ +[install] +optimize = 1 +[metadata] +description-file = README.md +[flake8] +max-line-length = 100 diff --git a/vendor/src/github.com/apache/thrift/lib/py/setup.py b/vendor/src/github.com/apache/thrift/lib/py/setup.py new file mode 100644 index 00000000..1fdfe72a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/setup.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +try: + from setuptools import setup, Extension +except: + from distutils.core import setup, Extension + +from distutils.command.build_ext import build_ext +from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError + +# Fix to build sdist under vagrant +import os +if 'vagrant' in str(os.environ): + del os.link + +include_dirs = ['src'] +if sys.platform == 'win32': + include_dirs.append('compat/win32') + ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError) +else: + ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError) + + +class BuildFailed(Exception): + pass + + +class ve_build_ext(build_ext): + def run(self): + try: + build_ext.run(self) + except DistutilsPlatformError: + raise BuildFailed() + + def build_extension(self, ext): + try: + build_ext.build_extension(self, ext) + except ext_errors: + raise BuildFailed() + + +def run_setup(with_binary): + if with_binary: + extensions = dict( + ext_modules=[ + Extension('thrift.protocol.fastbinary', + sources=[ + 'src/ext/module.cpp', + 'src/ext/types.cpp', + 'src/ext/binary.cpp', + 'src/ext/compact.cpp', + ], + include_dirs=include_dirs, + ) + ], + cmdclass=dict(build_ext=ve_build_ext) + ) + else: + extensions = dict() + + ssl_deps = [] + if sys.version_info[0] == 2: + ssl_deps.append('ipaddress') + if sys.hexversion < 0x03050000: + ssl_deps.append('backports.ssl_match_hostname>=3.5') + tornado_deps = ['tornado>=4.0'] + twisted_deps = ['twisted'] + + setup(name='thrift', + version='0.10.0', + description='Python bindings for the Apache Thrift RPC system', + author='Thrift Developers', + author_email='dev@thrift.apache.org', + url='http://thrift.apache.org', + license='Apache License 2.0', + install_requires=['six>=1.7.2'], + extras_require={ + 'ssl': ssl_deps, + 'tornado': tornado_deps, + 'twisted': twisted_deps, + 'all': ssl_deps + tornado_deps + twisted_deps, + }, + packages=[ + 'thrift', + 'thrift.protocol', + 'thrift.transport', + 'thrift.server', + ], + package_dir={'thrift': 'src'}, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Networking' + ], + **extensions + ) + +try: + with_binary = True + run_setup(with_binary) +except BuildFailed: + print() + print('*' * 80) + print("An error occurred while trying to compile with the C extension enabled") + print("Attempting to build without the extension now") + print('*' * 80) + print() + + run_setup(False) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/TMultiplexedProcessor.py b/vendor/src/github.com/apache/thrift/lib/py/src/TMultiplexedProcessor.py new file mode 100644 index 00000000..605aa1f2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/TMultiplexedProcessor.py @@ -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. +# + +from thrift.Thrift import TProcessor, TMessageType, TException +from thrift.protocol import TProtocolDecorator, TMultiplexedProtocol + + +class TMultiplexedProcessor(TProcessor): + def __init__(self): + self.services = {} + + def registerProcessor(self, serviceName, processor): + self.services[serviceName] = processor + + def process(self, iprot, oprot): + (name, type, seqid) = iprot.readMessageBegin() + if type != TMessageType.CALL and type != TMessageType.ONEWAY: + raise TException("TMultiplex protocol 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 TMultiplexProtocol 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()?") + + standardMessage = (call, type, seqid) + return self.services[serviceName].process(StoredMessageProtocol(iprot, standardMessage), oprot) + + +class StoredMessageProtocol(TProtocolDecorator.TProtocolDecorator): + def __init__(self, protocol, messageBegin): + TProtocolDecorator.TProtocolDecorator.__init__(self, protocol) + self.messageBegin = messageBegin + + def readMessageBegin(self): + return self.messageBegin diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/TSCons.py b/vendor/src/github.com/apache/thrift/lib/py/src/TSCons.py new file mode 100644 index 00000000..bc67d706 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/TSCons.py @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from os import path +from SCons.Builder import Builder +from six.moves import map + + +def scons_env(env, add=''): + opath = path.dirname(path.abspath('$TARGET')) + lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE' + cppbuild = Builder(action=lstr) + env.Append(BUILDERS={'ThriftCpp': cppbuild}) + + +def gen_cpp(env, dir, file): + scons_env(env) + suffixes = ['_types.h', '_types.cpp'] + targets = map(lambda s: 'gen-cpp/' + file + s, suffixes) + return env.ThriftCpp(targets, dir + file + '.thrift') diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/TSerialization.py b/vendor/src/github.com/apache/thrift/lib/py/src/TSerialization.py new file mode 100644 index 00000000..fbbe7680 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/TSerialization.py @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from .protocol import TBinaryProtocol +from .transport import TTransport + + +def serialize(thrift_object, + protocol_factory=TBinaryProtocol.TBinaryProtocolFactory()): + transport = TTransport.TMemoryBuffer() + protocol = protocol_factory.getProtocol(transport) + thrift_object.write(protocol) + return transport.getvalue() + + +def deserialize(base, + buf, + protocol_factory=TBinaryProtocol.TBinaryProtocolFactory()): + transport = TTransport.TMemoryBuffer(buf) + protocol = protocol_factory.getProtocol(transport) + base.read(protocol) + return base diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/TTornado.py b/vendor/src/github.com/apache/thrift/lib/py/src/TTornado.py new file mode 100644 index 00000000..5eff11d2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/TTornado.py @@ -0,0 +1,188 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from __future__ import absolute_import +import logging +import socket +import struct + +from .transport.TTransport import TTransportException, TTransportBase, TMemoryBuffer + +from io import BytesIO +from collections import deque +from contextlib import contextmanager +from tornado import gen, iostream, ioloop, tcpserver, concurrent + +__all__ = ['TTornadoServer', 'TTornadoStreamTransport'] + +logger = logging.getLogger(__name__) + + +class _Lock(object): + def __init__(self): + self._waiters = deque() + + def acquired(self): + return len(self._waiters) > 0 + + @gen.coroutine + def acquire(self): + blocker = self._waiters[-1] if self.acquired() else None + future = concurrent.Future() + self._waiters.append(future) + if blocker: + yield blocker + + raise gen.Return(self._lock_context()) + + def release(self): + assert self.acquired(), 'Lock not aquired' + future = self._waiters.popleft() + future.set_result(None) + + @contextmanager + def _lock_context(self): + try: + yield + finally: + self.release() + + +class TTornadoStreamTransport(TTransportBase): + """a framed, buffered transport over a Tornado stream""" + def __init__(self, host, port, stream=None, io_loop=None): + self.host = host + self.port = port + self.io_loop = io_loop or ioloop.IOLoop.current() + self.__wbuf = BytesIO() + self._read_lock = _Lock() + + # servers provide a ready-to-go stream + self.stream = stream + + def with_timeout(self, timeout, future): + return gen.with_timeout(timeout, future, self.io_loop) + + @gen.coroutine + def open(self, timeout=None): + logger.debug('socket connecting') + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + self.stream = iostream.IOStream(sock) + + try: + connect = self.stream.connect((self.host, self.port)) + if timeout is not None: + yield self.with_timeout(timeout, connect) + else: + yield connect + except (socket.error, IOError, ioloop.TimeoutError) as e: + message = 'could not connect to {}:{} ({})'.format(self.host, self.port, e) + raise TTransportException( + type=TTransportException.NOT_OPEN, + message=message) + + raise gen.Return(self) + + def set_close_callback(self, callback): + """ + Should be called only after open() returns + """ + self.stream.set_close_callback(callback) + + def close(self): + # don't raise if we intend to close + self.stream.set_close_callback(None) + self.stream.close() + + def read(self, _): + # The generated code for Tornado shouldn't do individual reads -- only + # frames at a time + assert False, "you're doing it wrong" + + @contextmanager + def io_exception_context(self): + try: + yield + except (socket.error, IOError) as e: + raise TTransportException( + type=TTransportException.END_OF_FILE, + message=str(e)) + except iostream.StreamBufferFullError as e: + raise TTransportException( + type=TTransportException.UNKNOWN, + message=str(e)) + + @gen.coroutine + def readFrame(self): + # IOStream processes reads one at a time + with (yield self._read_lock.acquire()): + with self.io_exception_context(): + frame_header = yield self.stream.read_bytes(4) + if len(frame_header) == 0: + raise iostream.StreamClosedError('Read zero bytes from stream') + frame_length, = struct.unpack('!i', frame_header) + frame = yield self.stream.read_bytes(frame_length) + raise gen.Return(frame) + + def write(self, buf): + self.__wbuf.write(buf) + + def flush(self): + frame = self.__wbuf.getvalue() + # reset wbuf before write/flush to preserve state on underlying failure + frame_length = struct.pack('!i', len(frame)) + self.__wbuf = BytesIO() + with self.io_exception_context(): + return self.stream.write(frame_length + frame) + + +class TTornadoServer(tcpserver.TCPServer): + def __init__(self, processor, iprot_factory, oprot_factory=None, + *args, **kwargs): + super(TTornadoServer, self).__init__(*args, **kwargs) + + self._processor = processor + self._iprot_factory = iprot_factory + self._oprot_factory = (oprot_factory if oprot_factory is not None + else iprot_factory) + + @gen.coroutine + def handle_stream(self, stream, address): + host, port = address[:2] + trans = TTornadoStreamTransport(host=host, port=port, stream=stream, + io_loop=self.io_loop) + oprot = self._oprot_factory.getProtocol(trans) + + try: + while not trans.stream.closed(): + try: + frame = yield trans.readFrame() + except TTransportException as e: + if e.type == TTransportException.END_OF_FILE: + break + else: + raise + tr = TMemoryBuffer(frame) + iprot = self._iprot_factory.getProtocol(tr) + yield self._processor.process(iprot, oprot) + except Exception: + logger.exception('thrift exception in handle_stream') + trans.close() + + logger.info('client disconnected %s:%d', host, port) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/Thrift.py b/vendor/src/github.com/apache/thrift/lib/py/src/Thrift.py new file mode 100644 index 00000000..c4dabdca --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/Thrift.py @@ -0,0 +1,192 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys + + +class TType(object): + STOP = 0 + VOID = 1 + BOOL = 2 + BYTE = 3 + I08 = 3 + DOUBLE = 4 + I16 = 6 + I32 = 8 + I64 = 10 + STRING = 11 + UTF7 = 11 + STRUCT = 12 + MAP = 13 + SET = 14 + LIST = 15 + UTF8 = 16 + UTF16 = 17 + + _VALUES_TO_NAMES = ( + 'STOP', + 'VOID', + 'BOOL', + 'BYTE', + 'DOUBLE', + None, + 'I16', + None, + 'I32', + None, + 'I64', + 'STRING', + 'STRUCT', + 'MAP', + 'SET', + 'LIST', + 'UTF8', + 'UTF16', + ) + + +class TMessageType(object): + CALL = 1 + REPLY = 2 + EXCEPTION = 3 + ONEWAY = 4 + + +class TProcessor(object): + """Base class for procsessor, which works on two streams.""" + + def process(iprot, oprot): + pass + + +class TException(Exception): + """Base class for all thrift exceptions.""" + + # BaseException.message is deprecated in Python v[2.6,3.0) + if (2, 6, 0) <= sys.version_info < (3, 0): + def _get_message(self): + return self._message + + def _set_message(self, message): + self._message = message + message = property(_get_message, _set_message) + + def __init__(self, message=None): + Exception.__init__(self, message) + self.message = message + + +class TApplicationException(TException): + """Application level thrift exceptions.""" + + UNKNOWN = 0 + UNKNOWN_METHOD = 1 + INVALID_MESSAGE_TYPE = 2 + WRONG_METHOD_NAME = 3 + BAD_SEQUENCE_ID = 4 + MISSING_RESULT = 5 + INTERNAL_ERROR = 6 + PROTOCOL_ERROR = 7 + INVALID_TRANSFORM = 8 + INVALID_PROTOCOL = 9 + UNSUPPORTED_CLIENT_TYPE = 10 + + def __init__(self, type=UNKNOWN, message=None): + TException.__init__(self, message) + self.type = type + + def __str__(self): + if self.message: + return self.message + elif self.type == self.UNKNOWN_METHOD: + return 'Unknown method' + elif self.type == self.INVALID_MESSAGE_TYPE: + return 'Invalid message type' + elif self.type == self.WRONG_METHOD_NAME: + return 'Wrong method name' + elif self.type == self.BAD_SEQUENCE_ID: + return 'Bad sequence ID' + elif self.type == self.MISSING_RESULT: + return 'Missing result' + elif self.type == self.INTERNAL_ERROR: + return 'Internal error' + elif self.type == self.PROTOCOL_ERROR: + return 'Protocol error' + elif self.type == self.INVALID_TRANSFORM: + return 'Invalid transform' + elif self.type == self.INVALID_PROTOCOL: + return 'Invalid protocol' + elif self.type == self.UNSUPPORTED_CLIENT_TYPE: + return 'Unsupported client type' + else: + return 'Default (unknown) TApplicationException' + + def read(self, iprot): + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.message = iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + oprot.writeStructBegin('TApplicationException') + if self.message is not None: + oprot.writeFieldBegin('message', TType.STRING, 1) + oprot.writeString(self.message) + oprot.writeFieldEnd() + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 2) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + +class TFrozenDict(dict): + """A dictionary that is "frozen" like a frozenset""" + + def __init__(self, *args, **kwargs): + super(TFrozenDict, self).__init__(*args, **kwargs) + # Sort the items so they will be in a consistent order. + # XOR in the hash of the class so we don't collide with + # the hash of a list of tuples. + self.__hashval = hash(TFrozenDict) ^ hash(tuple(sorted(self.items()))) + + def __setitem__(self, *args): + raise TypeError("Can't modify frozen TFreezableDict") + + def __delitem__(self, *args): + raise TypeError("Can't modify frozen TFreezableDict") + + def __hash__(self): + return self.__hashval diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/__init__.py b/vendor/src/github.com/apache/thrift/lib/py/src/__init__.py new file mode 100644 index 00000000..48d659c4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/__init__.py @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +__all__ = ['Thrift', 'TSCons'] diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/compat.py b/vendor/src/github.com/apache/thrift/lib/py/src/compat.py new file mode 100644 index 00000000..41bcf353 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/compat.py @@ -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. +# + +import sys + +if sys.version_info[0] == 2: + + from cStringIO import StringIO as BufferIO + + def binary_to_str(bin_val): + return bin_val + + def str_to_binary(str_val): + return str_val + +else: + + from io import BytesIO as BufferIO # noqa + + def binary_to_str(bin_val): + return bin_val.decode('utf8') + + def str_to_binary(str_val): + return bytes(str_val, 'utf8') diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.cpp b/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.cpp new file mode 100644 index 00000000..85d8d922 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.cpp @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ext/binary.h" +namespace apache { +namespace thrift { +namespace py { + +bool BinaryProtocol::readFieldBegin(TType& type, int16_t& tag) { + uint8_t b = 0; + if (!readByte(b)) { + return false; + } + type = static_cast(b); + if (type == T_STOP) { + return true; + } + return readI16(tag); +} +} +} +} diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.h b/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.h new file mode 100644 index 00000000..dedeec35 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/binary.h @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef THRIFT_PY_BINARY_H +#define THRIFT_PY_BINARY_H + +#include +#include "ext/protocol.h" +#include "ext/endian.h" +#include + +namespace apache { +namespace thrift { +namespace py { + +class BinaryProtocol : public ProtocolBase { +public: + virtual ~BinaryProtocol() {} + + void writeI8(int8_t val) { writeBuffer(reinterpret_cast(&val), sizeof(int8_t)); } + + void writeI16(int16_t val) { + int16_t net = static_cast(htons(val)); + writeBuffer(reinterpret_cast(&net), sizeof(int16_t)); + } + + void writeI32(int32_t val) { + int32_t net = static_cast(htonl(val)); + writeBuffer(reinterpret_cast(&net), sizeof(int32_t)); + } + + void writeI64(int64_t val) { + int64_t net = static_cast(htonll(val)); + writeBuffer(reinterpret_cast(&net), sizeof(int64_t)); + } + + void writeDouble(double dub) { + // Unfortunately, bitwise_cast doesn't work in C. Bad C! + union { + double f; + int64_t t; + } transfer; + transfer.f = dub; + writeI64(transfer.t); + } + + void writeBool(int v) { writeByte(static_cast(v)); } + + void writeString(PyObject* value, int32_t len) { + writeI32(len); + writeBuffer(PyBytes_AS_STRING(value), len); + } + + bool writeListBegin(PyObject* value, const SetListTypeArgs& parsedargs, int32_t len) { + writeByte(parsedargs.element_type); + writeI32(len); + return true; + } + + bool writeMapBegin(PyObject* value, const MapTypeArgs& parsedargs, int32_t len) { + writeByte(parsedargs.ktag); + writeByte(parsedargs.vtag); + writeI32(len); + return true; + } + + bool writeStructBegin() { return true; } + bool writeStructEnd() { return true; } + bool writeField(PyObject* value, const StructItemSpec& parsedspec) { + writeByte(static_cast(parsedspec.type)); + writeI16(parsedspec.tag); + return encodeValue(value, parsedspec.type, parsedspec.typeargs); + } + + void writeFieldStop() { writeByte(static_cast(T_STOP)); } + + bool readBool(bool& val) { + char* buf; + if (!readBytes(&buf, 1)) { + return false; + } + val = buf[0] == 1; + return true; + } + + bool readI8(int8_t& val) { + char* buf; + if (!readBytes(&buf, 1)) { + return false; + } + val = buf[0]; + return true; + } + + bool readI16(int16_t& val) { + char* buf; + if (!readBytes(&buf, sizeof(int16_t))) { + return false; + } + val = static_cast(ntohs(*reinterpret_cast(buf))); + return true; + } + + bool readI32(int32_t& val) { + char* buf; + if (!readBytes(&buf, sizeof(int32_t))) { + return false; + } + val = static_cast(ntohl(*reinterpret_cast(buf))); + return true; + } + + bool readI64(int64_t& val) { + char* buf; + if (!readBytes(&buf, sizeof(int64_t))) { + return false; + } + val = static_cast(ntohll(*reinterpret_cast(buf))); + return true; + } + + bool readDouble(double& val) { + union { + int64_t f; + double t; + } transfer; + + if (!readI64(transfer.f)) { + return false; + } + val = transfer.t; + return true; + } + + int32_t readString(char** buf) { + int32_t len = 0; + if (!readI32(len) || !checkLengthLimit(len, stringLimit()) || !readBytes(buf, len)) { + return -1; + } + return len; + } + + int32_t readListBegin(TType& etype) { + int32_t len; + uint8_t b = 0; + if (!readByte(b) || !readI32(len) || !checkLengthLimit(len, containerLimit())) { + return -1; + } + etype = static_cast(b); + return len; + } + + int32_t readMapBegin(TType& ktype, TType& vtype) { + int32_t len; + uint8_t k, v; + if (!readByte(k) || !readByte(v) || !readI32(len) || !checkLengthLimit(len, containerLimit())) { + return -1; + } + ktype = static_cast(k); + vtype = static_cast(v); + return len; + } + + bool readStructBegin() { return true; } + bool readStructEnd() { return true; } + + bool readFieldBegin(TType& type, int16_t& tag); + +#define SKIPBYTES(n) \ + do { \ + if (!readBytes(&dummy_buf_, (n))) { \ + return false; \ + } \ + return true; \ + } while (0) + + bool skipBool() { SKIPBYTES(1); } + bool skipByte() { SKIPBYTES(1); } + bool skipI16() { SKIPBYTES(2); } + bool skipI32() { SKIPBYTES(4); } + bool skipI64() { SKIPBYTES(8); } + bool skipDouble() { SKIPBYTES(8); } + bool skipString() { + int32_t len; + if (!readI32(len)) { + return false; + } + SKIPBYTES(len); + } +#undef SKIPBYTES + +private: + char* dummy_buf_; +}; +} +} +} +#endif // THRIFT_PY_BINARY_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.cpp b/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.cpp new file mode 100644 index 00000000..15a99a07 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.cpp @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ext/compact.h" + +namespace apache { +namespace thrift { +namespace py { + +const uint8_t CompactProtocol::TTypeToCType[] = { + CT_STOP, // T_STOP + 0, // unused + CT_BOOLEAN_TRUE, // T_BOOL + CT_BYTE, // T_BYTE + CT_DOUBLE, // T_DOUBLE + 0, // unused + CT_I16, // T_I16 + 0, // unused + CT_I32, // T_I32 + 0, // unused + CT_I64, // T_I64 + CT_BINARY, // T_STRING + CT_STRUCT, // T_STRUCT + CT_MAP, // T_MAP + CT_SET, // T_SET + CT_LIST, // T_LIST +}; + +bool CompactProtocol::readFieldBegin(TType& type, int16_t& tag) { + uint8_t b; + if (!readByte(b)) { + return false; + } + uint8_t ctype = b & 0xf; + type = getTType(ctype); + if (type == -1) { + return false; + } else if (type == T_STOP) { + tag = 0; + return true; + } + uint8_t diff = (b & 0xf0) >> 4; + if (diff) { + tag = readTags_.top() + diff; + } else if (!readI16(tag)) { + readTags_.top() = -1; + return false; + } + if (ctype == CT_BOOLEAN_FALSE || ctype == CT_BOOLEAN_TRUE) { + readBool_.exists = true; + readBool_.value = ctype == CT_BOOLEAN_TRUE; + } + readTags_.top() = tag; + return true; +} + +TType CompactProtocol::getTType(uint8_t type) { + switch (type) { + case T_STOP: + return T_STOP; + case CT_BOOLEAN_FALSE: + case CT_BOOLEAN_TRUE: + return T_BOOL; + case CT_BYTE: + return T_BYTE; + case CT_I16: + return T_I16; + case CT_I32: + return T_I32; + case CT_I64: + return T_I64; + case CT_DOUBLE: + return T_DOUBLE; + case CT_BINARY: + return T_STRING; + case CT_LIST: + return T_LIST; + case CT_SET: + return T_SET; + case CT_MAP: + return T_MAP; + case CT_STRUCT: + return T_STRUCT; + default: + PyErr_Format(PyExc_TypeError, "don't know what type: %d", type); + return static_cast(-1); + } +} +} +} +} diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.h b/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.h new file mode 100644 index 00000000..5bba2376 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/compact.h @@ -0,0 +1,367 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef THRIFT_PY_COMPACT_H +#define THRIFT_PY_COMPACT_H + +#include +#include "ext/protocol.h" +#include "ext/endian.h" +#include +#include + +namespace apache { +namespace thrift { +namespace py { + +class CompactProtocol : public ProtocolBase { +public: + CompactProtocol() { readBool_.exists = false; } + + virtual ~CompactProtocol() {} + + void writeI8(int8_t val) { writeBuffer(reinterpret_cast(&val), 1); } + + void writeI16(int16_t val) { writeVarint(toZigZag(val)); } + + int writeI32(int32_t val) { return writeVarint(toZigZag(val)); } + + void writeI64(int64_t val) { writeVarint64(toZigZag64(val)); } + + void writeDouble(double dub) { + union { + double f; + int64_t t; + } transfer; + transfer.f = htolell(dub); + writeBuffer(reinterpret_cast(&transfer.t), sizeof(int64_t)); + } + + void writeBool(int v) { writeByte(static_cast(v ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE)); } + + void writeString(PyObject* value, int32_t len) { + writeVarint(len); + writeBuffer(PyBytes_AS_STRING(value), len); + } + + bool writeListBegin(PyObject* value, const SetListTypeArgs& args, int32_t len) { + int ctype = toCompactType(args.element_type); + if (len <= 14) { + writeByte(static_cast(len << 4 | ctype)); + } else { + writeByte(0xf0 | ctype); + writeVarint(len); + } + return true; + } + + bool writeMapBegin(PyObject* value, const MapTypeArgs& args, int32_t len) { + if (len == 0) { + writeByte(0); + return true; + } + int ctype = toCompactType(args.ktag) << 4 | toCompactType(args.vtag); + writeVarint(len); + writeByte(ctype); + return true; + } + + bool writeStructBegin() { + writeTags_.push(0); + return true; + } + bool writeStructEnd() { + writeTags_.pop(); + return true; + } + + bool writeField(PyObject* value, const StructItemSpec& spec) { + if (spec.type == T_BOOL) { + doWriteFieldBegin(spec, PyObject_IsTrue(value) ? CT_BOOLEAN_TRUE : CT_BOOLEAN_FALSE); + return true; + } else { + doWriteFieldBegin(spec, toCompactType(spec.type)); + return encodeValue(value, spec.type, spec.typeargs); + } + } + + void writeFieldStop() { writeByte(0); } + + bool readBool(bool& val) { + if (readBool_.exists) { + readBool_.exists = false; + val = readBool_.value; + return true; + } + char* buf; + if (!readBytes(&buf, 1)) { + return false; + } + val = buf[0] == CT_BOOLEAN_TRUE; + return true; + } + bool readI8(int8_t& val) { + char* buf; + if (!readBytes(&buf, 1)) { + return false; + } + val = buf[0]; + return true; + } + + bool readI16(int16_t& val) { + uint16_t uval; + if (readVarint(uval)) { + val = fromZigZag(uval); + return true; + } + return false; + } + + bool readI32(int32_t& val) { + uint32_t uval; + if (readVarint(uval)) { + val = fromZigZag(uval); + return true; + } + return false; + } + + bool readI64(int64_t& val) { + uint64_t uval; + if (readVarint(uval)) { + val = fromZigZag(uval); + return true; + } + return false; + } + + bool readDouble(double& val) { + union { + int64_t f; + double t; + } transfer; + + char* buf; + if (!readBytes(&buf, 8)) { + return false; + } + transfer.f = letohll(*reinterpret_cast(buf)); + val = transfer.t; + return true; + } + + int32_t readString(char** buf) { + uint32_t len; + if (!readVarint(len) || !checkLengthLimit(len, stringLimit())) { + return -1; + } + if (len == 0) { + return 0; + } + if (!readBytes(buf, len)) { + return -1; + } + return len; + } + + int32_t readListBegin(TType& etype) { + uint8_t b; + if (!readByte(b)) { + return -1; + } + etype = getTType(b & 0xf); + if (etype == -1) { + return -1; + } + uint32_t len = (b >> 4) & 0xf; + if (len == 15 && !readVarint(len)) { + return -1; + } + if (!checkLengthLimit(len, containerLimit())) { + return -1; + } + return len; + } + + int32_t readMapBegin(TType& ktype, TType& vtype) { + uint32_t len; + if (!readVarint(len) || !checkLengthLimit(len, containerLimit())) { + return -1; + } + if (len != 0) { + uint8_t kvType; + if (!readByte(kvType)) { + return -1; + } + ktype = getTType(kvType >> 4); + vtype = getTType(kvType & 0xf); + if (ktype == -1 || vtype == -1) { + return -1; + } + } + return len; + } + + bool readStructBegin() { + readTags_.push(0); + return true; + } + bool readStructEnd() { + readTags_.pop(); + return true; + } + bool readFieldBegin(TType& type, int16_t& tag); + + bool skipBool() { + bool val; + return readBool(val); + } +#define SKIPBYTES(n) \ + do { \ + if (!readBytes(&dummy_buf_, (n))) { \ + return false; \ + } \ + return true; \ + } while (0) + bool skipByte() { SKIPBYTES(1); } + bool skipDouble() { SKIPBYTES(8); } + bool skipI16() { + int16_t val; + return readI16(val); + } + bool skipI32() { + int32_t val; + return readI32(val); + } + bool skipI64() { + int64_t val; + return readI64(val); + } + bool skipString() { + uint32_t len; + if (!readVarint(len)) { + return false; + } + SKIPBYTES(len); + } +#undef SKIPBYTES + +private: + enum Types { + CT_STOP = 0x00, + CT_BOOLEAN_TRUE = 0x01, + CT_BOOLEAN_FALSE = 0x02, + CT_BYTE = 0x03, + CT_I16 = 0x04, + CT_I32 = 0x05, + CT_I64 = 0x06, + CT_DOUBLE = 0x07, + CT_BINARY = 0x08, + CT_LIST = 0x09, + CT_SET = 0x0A, + CT_MAP = 0x0B, + CT_STRUCT = 0x0C + }; + + static const uint8_t TTypeToCType[]; + + TType getTType(uint8_t type); + + int toCompactType(TType type) { + int i = static_cast(type); + return i < 16 ? TTypeToCType[i] : -1; + } + + uint32_t toZigZag(int32_t val) { return (val >> 31) ^ (val << 1); } + + uint64_t toZigZag64(int64_t val) { return (val >> 63) ^ (val << 1); } + + int writeVarint(uint32_t val) { + int cnt = 1; + while (val & ~0x7fU) { + writeByte(static_cast((val & 0x7fU) | 0x80U)); + val >>= 7; + ++cnt; + } + writeByte(static_cast(val)); + return cnt; + } + + int writeVarint64(uint64_t val) { + int cnt = 1; + while (val & ~0x7fULL) { + writeByte(static_cast((val & 0x7fULL) | 0x80ULL)); + val >>= 7; + ++cnt; + } + writeByte(static_cast(val)); + return cnt; + } + + template + bool readVarint(T& result) { + uint8_t b; + T val = 0; + int shift = 0; + for (int i = 0; i < Max; ++i) { + if (!readByte(b)) { + return false; + } + if (b & 0x80) { + val |= static_cast(b & 0x7f) << shift; + } else { + val |= static_cast(b) << shift; + result = val; + return true; + } + shift += 7; + } + PyErr_Format(PyExc_OverflowError, "varint exceeded %d bytes", Max); + return false; + } + + template + S fromZigZag(U val) { + return (val >> 1) ^ static_cast(-static_cast(val & 1)); + } + + void doWriteFieldBegin(const StructItemSpec& spec, int ctype) { + int diff = spec.tag - writeTags_.top(); + if (diff > 0 && diff <= 15) { + writeByte(static_cast(diff << 4 | ctype)); + } else { + writeByte(static_cast(ctype)); + writeI16(spec.tag); + } + writeTags_.top() = spec.tag; + } + + std::stack writeTags_; + std::stack readTags_; + struct { + bool exists; + bool value; + } readBool_; + char* dummy_buf_; +}; +} +} +} +#endif // THRIFT_PY_COMPACT_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/endian.h b/vendor/src/github.com/apache/thrift/lib/py/src/ext/endian.h new file mode 100644 index 00000000..91372a7b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/endian.h @@ -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. + */ + +#ifndef THRIFT_PY_ENDIAN_H +#define THRIFT_PY_ENDIAN_H + +#include + +#ifndef _WIN32 +#include +#else +#include +#pragma comment(lib, "ws2_32.lib") +#define BIG_ENDIAN (4321) +#define LITTLE_ENDIAN (1234) +#define BYTE_ORDER LITTLE_ENDIAN +#define inline __inline +#endif + +/* Fix endianness issues on Solaris */ +#if defined(__SVR4) && defined(__sun) +#if defined(__i386) && !defined(__i386__) +#define __i386__ +#endif + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN (4321) +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN (1234) +#endif + +/* I386 is LE, even on Solaris */ +#if !defined(BYTE_ORDER) && defined(__i386__) +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#endif + +#ifndef __BYTE_ORDER +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#else +#error "Cannot determine endianness" +#endif +#endif + +// Same comment as the enum. Sorry. +#if __BYTE_ORDER == __BIG_ENDIAN +#define ntohll(n) (n) +#define htonll(n) (n) +#if defined(__GNUC__) && defined(__GLIBC__) +#include +#define letohll(n) bswap_64(n) +#define htolell(n) bswap_64(n) +#else /* GNUC & GLIBC */ +#define letohll(n) ((((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32)) +#define htolell(n) ((((unsigned long long)htonl(n)) << 32) + htonl(n >> 32)) +#endif +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#if defined(__GNUC__) && defined(__GLIBC__) +#include +#define ntohll(n) bswap_64(n) +#define htonll(n) bswap_64(n) +#else /* GNUC & GLIBC */ +#define ntohll(n) ((((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32)) +#define htonll(n) ((((unsigned long long)htonl(n)) << 32) + htonl(n >> 32)) +#endif /* GNUC & GLIBC */ +#define letohll(n) (n) +#define htolell(n) (n) +#else /* __BYTE_ORDER */ +#error "Can't define htonll or ntohll!" +#endif + +#endif // THRIFT_PY_ENDIAN_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/module.cpp b/vendor/src/github.com/apache/thrift/lib/py/src/ext/module.cpp new file mode 100644 index 00000000..34ec7f62 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/module.cpp @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include "types.h" +#include "binary.h" +#include "compact.h" +#include +#include + +// TODO(dreiss): defval appears to be unused. Look into removing it. +// TODO(dreiss): Make parse_spec_args recursive, and cache the output +// permanently in the object. (Malloc and orphan.) +// TODO(dreiss): Why do we need cStringIO for reading, why not just char*? +// Can cStringIO let us work with a BufferedTransport? +// TODO(dreiss): Don't ignore the rv from cwrite (maybe). + +// Doing a benchmark shows that interning actually makes a difference, amazingly. + +/** Pointer to interned string to speed up attribute lookup. */ +PyObject* INTERN_STRING(TFrozenDict); +PyObject* INTERN_STRING(cstringio_buf); +PyObject* INTERN_STRING(cstringio_refill); +static PyObject* INTERN_STRING(string_length_limit); +static PyObject* INTERN_STRING(container_length_limit); +static PyObject* INTERN_STRING(trans); + +namespace apache { +namespace thrift { +namespace py { + +template +static PyObject* encode_impl(PyObject* args) { + if (!args) + return NULL; + + PyObject* enc_obj = NULL; + PyObject* type_args = NULL; + if (!PyArg_ParseTuple(args, "OO", &enc_obj, &type_args)) { + return NULL; + } + if (!enc_obj || !type_args) { + return NULL; + } + + T protocol; + if (!protocol.prepareEncodeBuffer() || !protocol.encodeValue(enc_obj, T_STRUCT, type_args)) { + return NULL; + } + + return protocol.getEncodedValue(); +} + +static inline long as_long_then_delete(PyObject* value, long default_value) { + ScopedPyObject scope(value); + long v = PyInt_AsLong(value); + if (INT_CONV_ERROR_OCCURRED(v)) { + PyErr_Clear(); + return default_value; + } + return v; +} + +template +static PyObject* decode_impl(PyObject* args) { + PyObject* output_obj = NULL; + PyObject* oprot = NULL; + PyObject* typeargs = NULL; + if (!PyArg_ParseTuple(args, "OOO", &output_obj, &oprot, &typeargs)) { + return NULL; + } + + T protocol; +#ifdef _MSC_VER + // workaround strange VC++ 2015 bug where #else path does not compile + int32_t default_limit = INT32_MAX; +#else + int32_t default_limit = std::numeric_limits::max(); +#endif + protocol.setStringLengthLimit( + as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(string_length_limit)), + default_limit)); + protocol.setContainerLengthLimit( + as_long_then_delete(PyObject_GetAttr(oprot, INTERN_STRING(container_length_limit)), + default_limit)); + ScopedPyObject transport(PyObject_GetAttr(oprot, INTERN_STRING(trans))); + if (!transport) { + return NULL; + } + + StructTypeArgs parsedargs; + if (!parse_struct_args(&parsedargs, typeargs)) { + return NULL; + } + + if (!protocol.prepareDecodeBufferFromTransport(transport.get())) { + return NULL; + } + + return protocol.readStruct(output_obj, parsedargs.klass, parsedargs.spec); +} +} +} +} + +using namespace apache::thrift::py; + +/* -- PYTHON MODULE SETUP STUFF --- */ + +extern "C" { + +static PyObject* encode_binary(PyObject*, PyObject* args) { + return encode_impl(args); +} + +static PyObject* decode_binary(PyObject*, PyObject* args) { + return decode_impl(args); +} + +static PyObject* encode_compact(PyObject*, PyObject* args) { + return encode_impl(args); +} + +static PyObject* decode_compact(PyObject*, PyObject* args) { + return decode_impl(args); +} + +static PyMethodDef ThriftFastBinaryMethods[] = { + {"encode_binary", encode_binary, METH_VARARGS, ""}, + {"decode_binary", decode_binary, METH_VARARGS, ""}, + {"encode_compact", encode_compact, METH_VARARGS, ""}, + {"decode_compact", decode_compact, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef ThriftFastBinaryDef = {PyModuleDef_HEAD_INIT, + "thrift.protocol.fastbinary", + NULL, + 0, + ThriftFastBinaryMethods, + NULL, + NULL, + NULL, + NULL}; + +#define INITERROR return NULL; + +PyObject* PyInit_fastbinary() { + +#else + +#define INITERROR return; + +void initfastbinary() { + + PycString_IMPORT; + if (PycStringIO == NULL) + INITERROR + +#endif + +#define INIT_INTERN_STRING(value) \ + do { \ + INTERN_STRING(value) = PyString_InternFromString(#value); \ + if (!INTERN_STRING(value)) \ + INITERROR \ + } while (0) + + INIT_INTERN_STRING(TFrozenDict); + INIT_INTERN_STRING(cstringio_buf); + INIT_INTERN_STRING(cstringio_refill); + INIT_INTERN_STRING(string_length_limit); + INIT_INTERN_STRING(container_length_limit); + INIT_INTERN_STRING(trans); +#undef INIT_INTERN_STRING + + PyObject* module = +#if PY_MAJOR_VERSION >= 3 + PyModule_Create(&ThriftFastBinaryDef); +#else + Py_InitModule("thrift.protocol.fastbinary", ThriftFastBinaryMethods); +#endif + if (module == NULL) + INITERROR; + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} +} diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.h b/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.h new file mode 100644 index 00000000..126dbc37 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.h @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef THRIFT_PY_PROTOCOL_H +#define THRIFT_PY_PROTOCOL_H + +#include "ext/types.h" +#include +#include + +namespace apache { +namespace thrift { +namespace py { + +template +class ProtocolBase { + +public: + ProtocolBase() + : stringLimit_(std::numeric_limits::max()), + containerLimit_(std::numeric_limits::max()), + output_(NULL) {} + inline virtual ~ProtocolBase(); + + bool prepareDecodeBufferFromTransport(PyObject* trans); + + PyObject* readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq); + + bool prepareEncodeBuffer(); + + bool encodeValue(PyObject* value, TType type, PyObject* typeargs); + + PyObject* getEncodedValue(); + + long stringLimit() const { return stringLimit_; } + void setStringLengthLimit(long limit) { stringLimit_ = limit; } + + long containerLimit() const { return containerLimit_; } + void setContainerLengthLimit(long limit) { containerLimit_ = limit; } + +protected: + bool readBytes(char** output, int len); + + bool readByte(uint8_t& val) { + char* buf; + if (!readBytes(&buf, 1)) { + return false; + } + val = static_cast(buf[0]); + return true; + } + + bool writeBuffer(char* data, size_t len); + + void writeByte(uint8_t val) { writeBuffer(reinterpret_cast(&val), 1); } + + PyObject* decodeValue(TType type, PyObject* typeargs); + + bool skip(TType type); + + inline bool checkType(TType got, TType expected); + inline bool checkLengthLimit(int32_t len, long limit); + + inline bool isUtf8(PyObject* typeargs); + +private: + Impl* impl() { return static_cast(this); } + + long stringLimit_; + long containerLimit_; + EncodeBuffer* output_; + DecodeBuffer input_; +}; +} +} +} + +#include "ext/protocol.tcc" + +#endif // THRIFT_PY_PROTOCOL_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.tcc b/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.tcc new file mode 100644 index 00000000..6e978d7c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/protocol.tcc @@ -0,0 +1,913 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef THRIFT_PY_PROTOCOL_TCC +#define THRIFT_PY_PROTOCOL_TCC + +#include + +#define CHECK_RANGE(v, min, max) (((v) <= (max)) && ((v) >= (min))) +#define INIT_OUTBUF_SIZE 128 + +#if PY_MAJOR_VERSION < 3 +#include +#else +#include +#endif + +namespace apache { +namespace thrift { +namespace py { + +#if PY_MAJOR_VERSION < 3 + +namespace detail { + +inline bool input_check(PyObject* input) { + return PycStringIO_InputCheck(input); +} + +inline EncodeBuffer* new_encode_buffer(size_t size) { + if (!PycStringIO) { + PycString_IMPORT; + } + if (!PycStringIO) { + return NULL; + } + return PycStringIO->NewOutput(size); +} + +inline int read_buffer(PyObject* buf, char** output, int len) { + if (!PycStringIO) { + PycString_IMPORT; + } + if (!PycStringIO) { + PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO"); + return -1; + } + return PycStringIO->cread(buf, output, len); +} +} + +template +inline ProtocolBase::~ProtocolBase() { + if (output_) { + Py_CLEAR(output_); + } +} + +template +inline bool ProtocolBase::isUtf8(PyObject* typeargs) { + return PyString_Check(typeargs) && !strncmp(PyString_AS_STRING(typeargs), "UTF8", 4); +} + +template +PyObject* ProtocolBase::getEncodedValue() { + if (!PycStringIO) { + PycString_IMPORT; + } + if (!PycStringIO) { + return NULL; + } + return PycStringIO->cgetvalue(output_); +} + +template +inline bool ProtocolBase::writeBuffer(char* data, size_t size) { + if (!PycStringIO) { + PycString_IMPORT; + } + if (!PycStringIO) { + PyErr_SetString(PyExc_ImportError, "failed to import native cStringIO"); + return false; + } + int len = PycStringIO->cwrite(output_, data, size); + if (len < 0) { + PyErr_SetString(PyExc_IOError, "failed to write to cStringIO object"); + return false; + } + if (len != size) { + PyErr_Format(PyExc_EOFError, "write length mismatch: expected %lu got %d", size, len); + return false; + } + return true; +} + +#else + +namespace detail { + +inline bool input_check(PyObject* input) { + // TODO: Check for BytesIO type + return true; +} + +inline EncodeBuffer* new_encode_buffer(size_t size) { + EncodeBuffer* buffer = new EncodeBuffer; + buffer->buf.reserve(size); + buffer->pos = 0; + return buffer; +} + +struct bytesio { + PyObject_HEAD +#if PY_MINOR_VERSION < 5 + char* buf; +#else + PyObject* buf; +#endif + Py_ssize_t pos; + Py_ssize_t string_size; +}; + +inline int read_buffer(PyObject* buf, char** output, int len) { + bytesio* buf2 = reinterpret_cast(buf); +#if PY_MINOR_VERSION < 5 + *output = buf2->buf + buf2->pos; +#else + *output = PyBytes_AS_STRING(buf2->buf) + buf2->pos; +#endif + Py_ssize_t pos0 = buf2->pos; + buf2->pos = std::min(buf2->pos + static_cast(len), buf2->string_size); + return static_cast(buf2->pos - pos0); +} +} + +template +inline ProtocolBase::~ProtocolBase() { + if (output_) { + delete output_; + } +} + +template +inline bool ProtocolBase::isUtf8(PyObject* typeargs) { + // while condition for py2 is "arg == 'UTF8'", it should be "arg != 'BINARY'" for py3. + // HACK: check the length and don't bother reading the value + return !PyUnicode_Check(typeargs) || PyUnicode_GET_LENGTH(typeargs) != 6; +} + +template +PyObject* ProtocolBase::getEncodedValue() { + return PyBytes_FromStringAndSize(output_->buf.data(), output_->buf.size()); +} + +template +inline bool ProtocolBase::writeBuffer(char* data, size_t size) { + size_t need = size + output_->pos; + if (output_->buf.capacity() < need) { + try { + output_->buf.reserve(need); + } catch (std::bad_alloc& ex) { + PyErr_SetString(PyExc_MemoryError, "Failed to allocate write buffer"); + return false; + } + } + std::copy(data, data + size, std::back_inserter(output_->buf)); + return true; +} + +#endif + +namespace detail { + +#define DECLARE_OP_SCOPE(name, op) \ + template \ + struct name##Scope { \ + Impl* impl; \ + bool valid; \ + name##Scope(Impl* thiz) : impl(thiz), valid(impl->op##Begin()) {} \ + ~name##Scope() { \ + if (valid) \ + impl->op##End(); \ + } \ + operator bool() { return valid; } \ + }; \ + template class T> \ + name##Scope op##Scope(T* thiz) { \ + return name##Scope(static_cast(thiz)); \ + } +DECLARE_OP_SCOPE(WriteStruct, writeStruct) +DECLARE_OP_SCOPE(ReadStruct, readStruct) +#undef DECLARE_OP_SCOPE + +inline bool check_ssize_t_32(Py_ssize_t len) { + // error from getting the int + if (INT_CONV_ERROR_OCCURRED(len)) { + return false; + } + if (!CHECK_RANGE(len, 0, std::numeric_limits::max())) { + PyErr_SetString(PyExc_OverflowError, "size out of range: exceeded INT32_MAX"); + return false; + } + return true; +} +} + +template +bool parse_pyint(PyObject* o, T* ret, int32_t min, int32_t max) { + long val = PyInt_AsLong(o); + + if (INT_CONV_ERROR_OCCURRED(val)) { + return false; + } + if (!CHECK_RANGE(val, min, max)) { + PyErr_SetString(PyExc_OverflowError, "int out of range"); + return false; + } + + *ret = static_cast(val); + return true; +} + +template +inline bool ProtocolBase::checkType(TType got, TType expected) { + if (expected != got) { + PyErr_SetString(PyExc_TypeError, "got wrong ttype while reading field"); + return false; + } + return true; +} + +template +bool ProtocolBase::checkLengthLimit(int32_t len, long limit) { + if (len < 0) { + PyErr_Format(PyExc_OverflowError, "negative length: %ld", limit); + return false; + } + if (len > limit) { + PyErr_Format(PyExc_OverflowError, "size exceeded specified limit: %ld", limit); + return false; + } + return true; +} + +template +bool ProtocolBase::readBytes(char** output, int len) { + if (len < 0) { + PyErr_Format(PyExc_ValueError, "attempted to read negative length: %d", len); + return false; + } + // TODO(dreiss): Don't fear the malloc. Think about taking a copy of + // the partial read instead of forcing the transport + // to prepend it to its buffer. + + int rlen = detail::read_buffer(input_.stringiobuf.get(), output, len); + + if (rlen == len) { + return true; + } else if (rlen == -1) { + return false; + } else { + // using building functions as this is a rare codepath + ScopedPyObject newiobuf(PyObject_CallFunction(input_.refill_callable.get(), refill_signature, + *output, rlen, len, NULL)); + if (!newiobuf) { + return false; + } + + // must do this *AFTER* the call so that we don't deref the io buffer + input_.stringiobuf.reset(newiobuf.release()); + + rlen = detail::read_buffer(input_.stringiobuf.get(), output, len); + + if (rlen == len) { + return true; + } else if (rlen == -1) { + return false; + } else { + // TODO(dreiss): This could be a valid code path for big binary blobs. + PyErr_SetString(PyExc_TypeError, "refill claimed to have refilled the buffer, but didn't!!"); + return false; + } + } +} + +template +bool ProtocolBase::prepareDecodeBufferFromTransport(PyObject* trans) { + if (input_.stringiobuf) { + PyErr_SetString(PyExc_ValueError, "decode buffer is already initialized"); + return false; + } + + ScopedPyObject stringiobuf(PyObject_GetAttr(trans, INTERN_STRING(cstringio_buf))); + if (!stringiobuf) { + return false; + } + if (!detail::input_check(stringiobuf.get())) { + PyErr_SetString(PyExc_TypeError, "expecting stringio input_"); + return false; + } + + ScopedPyObject refill_callable(PyObject_GetAttr(trans, INTERN_STRING(cstringio_refill))); + if (!refill_callable) { + return false; + } + if (!PyCallable_Check(refill_callable.get())) { + PyErr_SetString(PyExc_TypeError, "expecting callable"); + return false; + } + + input_.stringiobuf.swap(stringiobuf); + input_.refill_callable.swap(refill_callable); + return true; +} + +template +bool ProtocolBase::prepareEncodeBuffer() { + output_ = detail::new_encode_buffer(INIT_OUTBUF_SIZE); + return output_ != NULL; +} + +template +bool ProtocolBase::encodeValue(PyObject* value, TType type, PyObject* typeargs) { + /* + * Refcounting Strategy: + * + * We assume that elements of the thrift_spec tuple are not going to be + * mutated, so we don't ref count those at all. Other than that, we try to + * keep a reference to all the user-created objects while we work with them. + * encodeValue assumes that a reference is already held. The *caller* is + * responsible for handling references + */ + + switch (type) { + + case T_BOOL: { + int v = PyObject_IsTrue(value); + if (v == -1) { + return false; + } + impl()->writeBool(v); + return true; + } + case T_I08: { + int8_t val; + + if (!parse_pyint(value, &val, std::numeric_limits::min(), + std::numeric_limits::max())) { + return false; + } + + impl()->writeI8(val); + return true; + } + case T_I16: { + int16_t val; + + if (!parse_pyint(value, &val, std::numeric_limits::min(), + std::numeric_limits::max())) { + return false; + } + + impl()->writeI16(val); + return true; + } + case T_I32: { + int32_t val; + + if (!parse_pyint(value, &val, std::numeric_limits::min(), + std::numeric_limits::max())) { + return false; + } + + impl()->writeI32(val); + return true; + } + case T_I64: { + int64_t nval = PyLong_AsLongLong(value); + + if (INT_CONV_ERROR_OCCURRED(nval)) { + return false; + } + + if (!CHECK_RANGE(nval, std::numeric_limits::min(), + std::numeric_limits::max())) { + PyErr_SetString(PyExc_OverflowError, "int out of range"); + return false; + } + + impl()->writeI64(nval); + return true; + } + + case T_DOUBLE: { + double nval = PyFloat_AsDouble(value); + if (nval == -1.0 && PyErr_Occurred()) { + return false; + } + + impl()->writeDouble(nval); + return true; + } + + case T_STRING: { + ScopedPyObject nval; + + if (PyUnicode_Check(value)) { + nval.reset(PyUnicode_AsUTF8String(value)); + if (!nval) { + return false; + } + } else { + Py_INCREF(value); + nval.reset(value); + } + + Py_ssize_t len = PyBytes_Size(nval.get()); + if (!detail::check_ssize_t_32(len)) { + return false; + } + + impl()->writeString(nval.get(), static_cast(len)); + return true; + } + + case T_LIST: + case T_SET: { + SetListTypeArgs parsedargs; + if (!parse_set_list_args(&parsedargs, typeargs)) { + return false; + } + + Py_ssize_t len = PyObject_Length(value); + if (!detail::check_ssize_t_32(len)) { + return false; + } + + if (!impl()->writeListBegin(value, parsedargs, static_cast(len)) || PyErr_Occurred()) { + return false; + } + ScopedPyObject iterator(PyObject_GetIter(value)); + if (!iterator) { + return false; + } + + while (PyObject* rawItem = PyIter_Next(iterator.get())) { + ScopedPyObject item(rawItem); + if (!encodeValue(item.get(), parsedargs.element_type, parsedargs.typeargs)) { + return false; + } + } + + return true; + } + + case T_MAP: { + Py_ssize_t len = PyDict_Size(value); + if (!detail::check_ssize_t_32(len)) { + return false; + } + + MapTypeArgs parsedargs; + if (!parse_map_args(&parsedargs, typeargs)) { + return false; + } + + if (!impl()->writeMapBegin(value, parsedargs, static_cast(len)) || PyErr_Occurred()) { + return false; + } + Py_ssize_t pos = 0; + PyObject* k = NULL; + PyObject* v = NULL; + // TODO(bmaurer): should support any mapping, not just dicts + while (PyDict_Next(value, &pos, &k, &v)) { + if (!encodeValue(k, parsedargs.ktag, parsedargs.ktypeargs) + || !encodeValue(v, parsedargs.vtag, parsedargs.vtypeargs)) { + return false; + } + } + return true; + } + + case T_STRUCT: { + StructTypeArgs parsedargs; + if (!parse_struct_args(&parsedargs, typeargs)) { + return false; + } + + Py_ssize_t nspec = PyTuple_Size(parsedargs.spec); + if (nspec == -1) { + PyErr_SetString(PyExc_TypeError, "spec is not a tuple"); + return false; + } + + detail::WriteStructScope scope = detail::writeStructScope(this); + if (!scope) { + return false; + } + for (Py_ssize_t i = 0; i < nspec; i++) { + PyObject* spec_tuple = PyTuple_GET_ITEM(parsedargs.spec, i); + if (spec_tuple == Py_None) { + continue; + } + + StructItemSpec parsedspec; + if (!parse_struct_item_spec(&parsedspec, spec_tuple)) { + return false; + } + + ScopedPyObject instval(PyObject_GetAttr(value, parsedspec.attrname)); + + if (!instval) { + return false; + } + + if (instval.get() == Py_None) { + continue; + } + + bool res = impl()->writeField(instval.get(), parsedspec); + if (!res) { + return false; + } + } + impl()->writeFieldStop(); + return true; + } + + case T_STOP: + case T_VOID: + case T_UTF16: + case T_UTF8: + case T_U64: + default: + PyErr_Format(PyExc_TypeError, "Unexpected TType for encodeValue: %d", type); + return false; + } + + return true; +} + +template +bool ProtocolBase::skip(TType type) { + switch (type) { + case T_BOOL: + return impl()->skipBool(); + case T_I08: + return impl()->skipByte(); + case T_I16: + return impl()->skipI16(); + case T_I32: + return impl()->skipI32(); + case T_I64: + return impl()->skipI64(); + case T_DOUBLE: + return impl()->skipDouble(); + + case T_STRING: { + return impl()->skipString(); + } + + case T_LIST: + case T_SET: { + TType etype = T_STOP; + int32_t len = impl()->readListBegin(etype); + if (len < 0) { + return false; + } + for (int32_t i = 0; i < len; i++) { + if (!skip(etype)) { + return false; + } + } + return true; + } + + case T_MAP: { + TType ktype = T_STOP; + TType vtype = T_STOP; + int32_t len = impl()->readMapBegin(ktype, vtype); + if (len < 0) { + return false; + } + for (int32_t i = 0; i < len; i++) { + if (!skip(ktype) || !skip(vtype)) { + return false; + } + } + return true; + } + + case T_STRUCT: { + detail::ReadStructScope scope = detail::readStructScope(this); + if (!scope) { + return false; + } + while (true) { + TType type = T_STOP; + int16_t tag; + if (!impl()->readFieldBegin(type, tag)) { + return false; + } + if (type == T_STOP) { + return true; + } + if (!skip(type)) { + return false; + } + } + return true; + } + + case T_STOP: + case T_VOID: + case T_UTF16: + case T_UTF8: + case T_U64: + default: + PyErr_Format(PyExc_TypeError, "Unexpected TType for skip: %d", type); + return false; + } + + return true; +} + +// Returns a new reference. +template +PyObject* ProtocolBase::decodeValue(TType type, PyObject* typeargs) { + switch (type) { + + case T_BOOL: { + bool v = 0; + if (!impl()->readBool(v)) { + return NULL; + } + if (v) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } + } + case T_I08: { + int8_t v = 0; + if (!impl()->readI8(v)) { + return NULL; + } + return PyInt_FromLong(v); + } + case T_I16: { + int16_t v = 0; + if (!impl()->readI16(v)) { + return NULL; + } + return PyInt_FromLong(v); + } + case T_I32: { + int32_t v = 0; + if (!impl()->readI32(v)) { + return NULL; + } + return PyInt_FromLong(v); + } + + case T_I64: { + int64_t v = 0; + if (!impl()->readI64(v)) { + return NULL; + } + // TODO(dreiss): Find out if we can take this fastpath always when + // sizeof(long) == sizeof(long long). + if (CHECK_RANGE(v, LONG_MIN, LONG_MAX)) { + return PyInt_FromLong((long)v); + } + return PyLong_FromLongLong(v); + } + + case T_DOUBLE: { + double v = 0.0; + if (!impl()->readDouble(v)) { + return NULL; + } + return PyFloat_FromDouble(v); + } + + case T_STRING: { + char* buf = NULL; + int len = impl()->readString(&buf); + if (len < 0) { + return NULL; + } + if (isUtf8(typeargs)) { + return PyUnicode_DecodeUTF8(buf, len, 0); + } else { + return PyBytes_FromStringAndSize(buf, len); + } + } + + case T_LIST: + case T_SET: { + SetListTypeArgs parsedargs; + if (!parse_set_list_args(&parsedargs, typeargs)) { + return NULL; + } + + TType etype = T_STOP; + int32_t len = impl()->readListBegin(etype); + if (len < 0) { + return NULL; + } + if (len > 0 && !checkType(etype, parsedargs.element_type)) { + return NULL; + } + + bool use_tuple = type == T_LIST && parsedargs.immutable; + ScopedPyObject ret(use_tuple ? PyTuple_New(len) : PyList_New(len)); + if (!ret) { + return NULL; + } + + for (int i = 0; i < len; i++) { + PyObject* item = decodeValue(etype, parsedargs.typeargs); + if (!item) { + return NULL; + } + if (use_tuple) { + PyTuple_SET_ITEM(ret.get(), i, item); + } else { + PyList_SET_ITEM(ret.get(), i, item); + } + } + + // TODO(dreiss): Consider biting the bullet and making two separate cases + // for list and set, avoiding this post facto conversion. + if (type == T_SET) { + PyObject* setret; + setret = parsedargs.immutable ? PyFrozenSet_New(ret.get()) : PySet_New(ret.get()); + return setret; + } + return ret.release(); + } + + case T_MAP: { + MapTypeArgs parsedargs; + if (!parse_map_args(&parsedargs, typeargs)) { + return NULL; + } + + TType ktype = T_STOP; + TType vtype = T_STOP; + uint32_t len = impl()->readMapBegin(ktype, vtype); + if (len > 0 && (!checkType(ktype, parsedargs.ktag) || !checkType(vtype, parsedargs.vtag))) { + return NULL; + } + + ScopedPyObject ret(PyDict_New()); + if (!ret) { + return NULL; + } + + for (uint32_t i = 0; i < len; i++) { + ScopedPyObject k(decodeValue(ktype, parsedargs.ktypeargs)); + if (!k) { + return NULL; + } + ScopedPyObject v(decodeValue(vtype, parsedargs.vtypeargs)); + if (!v) { + return NULL; + } + if (PyDict_SetItem(ret.get(), k.get(), v.get()) == -1) { + return NULL; + } + } + + if (parsedargs.immutable) { + if (!ThriftModule) { + ThriftModule = PyImport_ImportModule("thrift.Thrift"); + } + if (!ThriftModule) { + return NULL; + } + + ScopedPyObject cls(PyObject_GetAttr(ThriftModule, INTERN_STRING(TFrozenDict))); + if (!cls) { + return NULL; + } + + ScopedPyObject arg(PyTuple_New(1)); + PyTuple_SET_ITEM(arg.get(), 0, ret.release()); + ret.reset(PyObject_CallObject(cls.get(), arg.get())); + } + + return ret.release(); + } + + case T_STRUCT: { + StructTypeArgs parsedargs; + if (!parse_struct_args(&parsedargs, typeargs)) { + return NULL; + } + return readStruct(Py_None, parsedargs.klass, parsedargs.spec); + } + + case T_STOP: + case T_VOID: + case T_UTF16: + case T_UTF8: + case T_U64: + default: + PyErr_Format(PyExc_TypeError, "Unexpected TType for decodeValue: %d", type); + return NULL; + } +} + +template +PyObject* ProtocolBase::readStruct(PyObject* output, PyObject* klass, PyObject* spec_seq) { + int spec_seq_len = PyTuple_Size(spec_seq); + bool immutable = output == Py_None; + ScopedPyObject kwargs; + if (spec_seq_len == -1) { + return NULL; + } + + if (immutable) { + kwargs.reset(PyDict_New()); + if (!kwargs) { + PyErr_SetString(PyExc_TypeError, "failed to prepare kwargument storage"); + return NULL; + } + } + + detail::ReadStructScope scope = detail::readStructScope(this); + if (!scope) { + return NULL; + } + while (true) { + TType type = T_STOP; + int16_t tag; + if (!impl()->readFieldBegin(type, tag)) { + return NULL; + } + if (type == T_STOP) { + break; + } + if (tag < 0 || tag >= spec_seq_len) { + if (!skip(type)) { + PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field"); + return NULL; + } + continue; + } + + PyObject* item_spec = PyTuple_GET_ITEM(spec_seq, tag); + if (item_spec == Py_None) { + if (!skip(type)) { + PyErr_SetString(PyExc_TypeError, "Error while skipping unknown field"); + return NULL; + } + continue; + } + StructItemSpec parsedspec; + if (!parse_struct_item_spec(&parsedspec, item_spec)) { + return NULL; + } + if (parsedspec.type != type) { + if (!skip(type)) { + PyErr_Format(PyExc_TypeError, "struct field had wrong type: expected %d but got %d", + parsedspec.type, type); + return NULL; + } + continue; + } + + ScopedPyObject fieldval(decodeValue(parsedspec.type, parsedspec.typeargs)); + if (!fieldval) { + return NULL; + } + + if ((immutable && PyDict_SetItem(kwargs.get(), parsedspec.attrname, fieldval.get()) == -1) + || (!immutable && PyObject_SetAttr(output, parsedspec.attrname, fieldval.get()) == -1)) { + return NULL; + } + } + if (immutable) { + ScopedPyObject args(PyTuple_New(0)); + if (!args) { + PyErr_SetString(PyExc_TypeError, "failed to prepare argument storage"); + return NULL; + } + return PyObject_Call(klass, args.get(), kwargs.get()); + } + Py_INCREF(output); + return output; +} +} +} +} +#endif // THRIFT_PY_PROTOCOL_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.cpp b/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.cpp new file mode 100644 index 00000000..849ab2f4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.cpp @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "ext/types.h" +#include "ext/protocol.h" + +namespace apache { +namespace thrift { +namespace py { + +PyObject* ThriftModule = NULL; + +#if PY_MAJOR_VERSION < 3 +char refill_signature[] = {'s', '#', 'i'}; +#else +const char* refill_signature = "y#i"; +#endif + +bool parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple) { + // i'd like to use ParseArgs here, but it seems to be a bottleneck. + if (PyTuple_Size(spec_tuple) != 5) { + PyErr_Format(PyExc_TypeError, "expecting 5 arguments for spec tuple but got %d", + static_cast(PyTuple_Size(spec_tuple))); + return false; + } + + dest->tag = static_cast(PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 0))); + if (INT_CONV_ERROR_OCCURRED(dest->tag)) { + return false; + } + + dest->type = static_cast(PyInt_AsLong(PyTuple_GET_ITEM(spec_tuple, 1))); + if (INT_CONV_ERROR_OCCURRED(dest->type)) { + return false; + } + + dest->attrname = PyTuple_GET_ITEM(spec_tuple, 2); + dest->typeargs = PyTuple_GET_ITEM(spec_tuple, 3); + dest->defval = PyTuple_GET_ITEM(spec_tuple, 4); + return true; +} + +bool parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs) { + if (PyTuple_Size(typeargs) != 3) { + PyErr_SetString(PyExc_TypeError, "expecting tuple of size 3 for list/set type args"); + return false; + } + + dest->element_type = static_cast(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0))); + if (INT_CONV_ERROR_OCCURRED(dest->element_type)) { + return false; + } + + dest->typeargs = PyTuple_GET_ITEM(typeargs, 1); + + dest->immutable = Py_True == PyTuple_GET_ITEM(typeargs, 2); + + return true; +} + +bool parse_map_args(MapTypeArgs* dest, PyObject* typeargs) { + if (PyTuple_Size(typeargs) != 5) { + PyErr_SetString(PyExc_TypeError, "expecting 5 arguments for typeargs to map"); + return false; + } + + dest->ktag = static_cast(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 0))); + if (INT_CONV_ERROR_OCCURRED(dest->ktag)) { + return false; + } + + dest->vtag = static_cast(PyInt_AsLong(PyTuple_GET_ITEM(typeargs, 2))); + if (INT_CONV_ERROR_OCCURRED(dest->vtag)) { + return false; + } + + dest->ktypeargs = PyTuple_GET_ITEM(typeargs, 1); + dest->vtypeargs = PyTuple_GET_ITEM(typeargs, 3); + dest->immutable = Py_True == PyTuple_GET_ITEM(typeargs, 4); + + return true; +} + +bool parse_struct_args(StructTypeArgs* dest, PyObject* typeargs) { + if (PyTuple_Size(typeargs) != 2) { + PyErr_SetString(PyExc_TypeError, "expecting tuple of size 2 for struct args"); + return false; + } + + dest->klass = PyTuple_GET_ITEM(typeargs, 0); + dest->spec = PyTuple_GET_ITEM(typeargs, 1); + + return true; +} +} +} +} diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.h b/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.h new file mode 100644 index 00000000..2fc9d9cc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/ext/types.h @@ -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. + */ + +#ifndef THRIFT_PY_TYPES_H +#define THRIFT_PY_TYPES_H + +#include + +#ifdef _MSC_VER +#define __STDC_LIMIT_MACROS +#endif +#include + +#if PY_MAJOR_VERSION >= 3 + +#include + +// TODO: better macros +#define PyInt_AsLong(v) PyLong_AsLong(v) +#define PyInt_FromLong(v) PyLong_FromLong(v) + +#define PyString_InternFromString(v) PyUnicode_InternFromString(v) + +#endif + +#define INTERN_STRING(value) _intern_##value + +#define INT_CONV_ERROR_OCCURRED(v) (((v) == -1) && PyErr_Occurred()) + +extern "C" { +extern PyObject* INTERN_STRING(TFrozenDict); +extern PyObject* INTERN_STRING(cstringio_buf); +extern PyObject* INTERN_STRING(cstringio_refill); +} + +namespace apache { +namespace thrift { +namespace py { + +extern PyObject* ThriftModule; + +// Stolen out of TProtocol.h. +// It would be a huge pain to have both get this from one place. +enum TType { + T_INVALID = -1, + T_STOP = 0, + T_VOID = 1, + T_BOOL = 2, + T_BYTE = 3, + T_I08 = 3, + T_I16 = 6, + T_I32 = 8, + T_U64 = 9, + T_I64 = 10, + T_DOUBLE = 4, + T_STRING = 11, + T_UTF7 = 11, + T_STRUCT = 12, + T_MAP = 13, + T_SET = 14, + T_LIST = 15, + T_UTF8 = 16, + T_UTF16 = 17 +}; + +// replace with unique_ptr when we're OK with C++11 +class ScopedPyObject { +public: + ScopedPyObject() : obj_(NULL) {} + explicit ScopedPyObject(PyObject* py_object) : obj_(py_object) {} + ~ScopedPyObject() { + if (obj_) + Py_DECREF(obj_); + } + PyObject* get() throw() { return obj_; } + operator bool() { return obj_; } + void reset(PyObject* py_object) throw() { + if (obj_) + Py_DECREF(obj_); + obj_ = py_object; + } + PyObject* release() throw() { + PyObject* tmp = obj_; + obj_ = NULL; + return tmp; + } + void swap(ScopedPyObject& other) throw() { + ScopedPyObject tmp(other.release()); + other.reset(release()); + reset(tmp.release()); + } + +private: + ScopedPyObject(const ScopedPyObject&) {} + ScopedPyObject& operator=(const ScopedPyObject&) { return *this; } + + PyObject* obj_; +}; + +/** + * A cache of the two key attributes of a CReadableTransport, + * so we don't have to keep calling PyObject_GetAttr. + */ +struct DecodeBuffer { + ScopedPyObject stringiobuf; + ScopedPyObject refill_callable; +}; + +#if PY_MAJOR_VERSION < 3 +extern char refill_signature[3]; +typedef PyObject EncodeBuffer; +#else +extern const char* refill_signature; +struct EncodeBuffer { + std::vector buf; + size_t pos; +}; +#endif + +/** + * A cache of the spec_args for a set or list, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +struct SetListTypeArgs { + TType element_type; + PyObject* typeargs; + bool immutable; +}; + +/** + * A cache of the spec_args for a map, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +struct MapTypeArgs { + TType ktag; + TType vtag; + PyObject* ktypeargs; + PyObject* vtypeargs; + bool immutable; +}; + +/** + * A cache of the spec_args for a struct, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +struct StructTypeArgs { + PyObject* klass; + PyObject* spec; + bool immutable; +}; + +/** + * A cache of the item spec from a struct specification, + * so we don't have to keep calling PyTuple_GET_ITEM. + */ +struct StructItemSpec { + int tag; + TType type; + PyObject* attrname; + PyObject* typeargs; + PyObject* defval; +}; + +bool parse_set_list_args(SetListTypeArgs* dest, PyObject* typeargs); + +bool parse_map_args(MapTypeArgs* dest, PyObject* typeargs); + +bool parse_struct_args(StructTypeArgs* dest, PyObject* typeargs); + +bool parse_struct_item_spec(StructItemSpec* dest, PyObject* spec_tuple); +} +} +} + +#endif // THRIFT_PY_TYPES_H diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBase.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBase.py new file mode 100644 index 00000000..55da19ed --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBase.py @@ -0,0 +1,82 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from thrift.transport import TTransport + + +class TBase(object): + __slots__ = () + + def __repr__(self): + L = ['%s=%r' % (key, getattr(self, key)) for key in self.__slots__] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + for attr in self.__slots__: + my_val = getattr(self, attr) + other_val = getattr(other, attr) + if my_val != other_val: + return False + return True + + def __ne__(self, other): + return not (self == other) + + def read(self, iprot): + if (iprot._fast_decode is not None and + isinstance(iprot.trans, TTransport.CReadableTransport) and + self.thrift_spec is not None): + iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec)) + else: + iprot.readStruct(self, self.thrift_spec) + + def write(self, oprot): + if (oprot._fast_encode is not None and self.thrift_spec is not None): + oprot.trans.write( + oprot._fast_encode(self, (self.__class__, self.thrift_spec))) + else: + oprot.writeStruct(self, self.thrift_spec) + + +class TExceptionBase(TBase, Exception): + pass + + +class TFrozenBase(TBase): + def __setitem__(self, *args): + raise TypeError("Can't modify frozen struct") + + def __delitem__(self, *args): + raise TypeError("Can't modify frozen struct") + + def __hash__(self, *args): + return hash(self.__class__) ^ hash(self.__slots__) + + @classmethod + def read(cls, iprot): + if (iprot._fast_decode is not None and + isinstance(iprot.trans, TTransport.CReadableTransport) and + cls.thrift_spec is not None): + self = cls() + return iprot._fast_decode(None, iprot, + (self.__class__, self.thrift_spec)) + else: + return iprot.readStruct(cls, cls.thrift_spec, True) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBinaryProtocol.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBinaryProtocol.py new file mode 100644 index 00000000..f6be7721 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TBinaryProtocol.py @@ -0,0 +1,301 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from .TProtocol import TType, TProtocolBase, TProtocolException +from struct import pack, unpack + + +class TBinaryProtocol(TProtocolBase): + """Binary implementation of the Thrift protocol driver.""" + + # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be + # positive, converting this into a long. If we hardcode the int value + # instead it'll stay in 32 bit-land. + + # VERSION_MASK = 0xffff0000 + VERSION_MASK = -65536 + + # VERSION_1 = 0x80010000 + VERSION_1 = -2147418112 + + TYPE_MASK = 0x000000ff + + def __init__(self, trans, strictRead=False, strictWrite=True, **kwargs): + TProtocolBase.__init__(self, trans) + self.strictRead = strictRead + self.strictWrite = strictWrite + self.string_length_limit = kwargs.get('string_length_limit', None) + self.container_length_limit = kwargs.get('container_length_limit', None) + + def _check_string_length(self, length): + self._check_length(self.string_length_limit, length) + + def _check_container_length(self, length): + self._check_length(self.container_length_limit, length) + + def writeMessageBegin(self, name, type, seqid): + if self.strictWrite: + self.writeI32(TBinaryProtocol.VERSION_1 | type) + self.writeString(name) + self.writeI32(seqid) + else: + self.writeString(name) + self.writeByte(type) + self.writeI32(seqid) + + def writeMessageEnd(self): + pass + + def writeStructBegin(self, name): + pass + + def writeStructEnd(self): + pass + + def writeFieldBegin(self, name, type, id): + self.writeByte(type) + self.writeI16(id) + + def writeFieldEnd(self): + pass + + def writeFieldStop(self): + self.writeByte(TType.STOP) + + def writeMapBegin(self, ktype, vtype, size): + self.writeByte(ktype) + self.writeByte(vtype) + self.writeI32(size) + + def writeMapEnd(self): + pass + + def writeListBegin(self, etype, size): + self.writeByte(etype) + self.writeI32(size) + + def writeListEnd(self): + pass + + def writeSetBegin(self, etype, size): + self.writeByte(etype) + self.writeI32(size) + + def writeSetEnd(self): + pass + + def writeBool(self, bool): + if bool: + self.writeByte(1) + else: + self.writeByte(0) + + def writeByte(self, byte): + buff = pack("!b", byte) + self.trans.write(buff) + + def writeI16(self, i16): + buff = pack("!h", i16) + self.trans.write(buff) + + def writeI32(self, i32): + buff = pack("!i", i32) + self.trans.write(buff) + + def writeI64(self, i64): + buff = pack("!q", i64) + self.trans.write(buff) + + def writeDouble(self, dub): + buff = pack("!d", dub) + self.trans.write(buff) + + def writeBinary(self, str): + self.writeI32(len(str)) + self.trans.write(str) + + def readMessageBegin(self): + sz = self.readI32() + if sz < 0: + version = sz & TBinaryProtocol.VERSION_MASK + if version != TBinaryProtocol.VERSION_1: + raise TProtocolException( + type=TProtocolException.BAD_VERSION, + message='Bad version in readMessageBegin: %d' % (sz)) + type = sz & TBinaryProtocol.TYPE_MASK + name = self.readString() + seqid = self.readI32() + else: + if self.strictRead: + raise TProtocolException(type=TProtocolException.BAD_VERSION, + message='No protocol version header') + name = self.trans.readAll(sz) + type = self.readByte() + seqid = self.readI32() + return (name, type, seqid) + + def readMessageEnd(self): + pass + + def readStructBegin(self): + pass + + def readStructEnd(self): + pass + + def readFieldBegin(self): + type = self.readByte() + if type == TType.STOP: + return (None, type, 0) + id = self.readI16() + return (None, type, id) + + def readFieldEnd(self): + pass + + def readMapBegin(self): + ktype = self.readByte() + vtype = self.readByte() + size = self.readI32() + self._check_container_length(size) + return (ktype, vtype, size) + + def readMapEnd(self): + pass + + def readListBegin(self): + etype = self.readByte() + size = self.readI32() + self._check_container_length(size) + return (etype, size) + + def readListEnd(self): + pass + + def readSetBegin(self): + etype = self.readByte() + size = self.readI32() + self._check_container_length(size) + return (etype, size) + + def readSetEnd(self): + pass + + def readBool(self): + byte = self.readByte() + if byte == 0: + return False + return True + + def readByte(self): + buff = self.trans.readAll(1) + val, = unpack('!b', buff) + return val + + def readI16(self): + buff = self.trans.readAll(2) + val, = unpack('!h', buff) + return val + + def readI32(self): + buff = self.trans.readAll(4) + val, = unpack('!i', buff) + return val + + def readI64(self): + buff = self.trans.readAll(8) + val, = unpack('!q', buff) + return val + + def readDouble(self): + buff = self.trans.readAll(8) + val, = unpack('!d', buff) + return val + + def readBinary(self): + size = self.readI32() + self._check_string_length(size) + s = self.trans.readAll(size) + return s + + +class TBinaryProtocolFactory(object): + def __init__(self, strictRead=False, strictWrite=True, **kwargs): + self.strictRead = strictRead + self.strictWrite = strictWrite + self.string_length_limit = kwargs.get('string_length_limit', None) + self.container_length_limit = kwargs.get('container_length_limit', None) + + def getProtocol(self, trans): + prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite, + string_length_limit=self.string_length_limit, + container_length_limit=self.container_length_limit) + return prot + + +class TBinaryProtocolAccelerated(TBinaryProtocol): + """C-Accelerated version of TBinaryProtocol. + + This class does not override any of TBinaryProtocol's methods, + but the generated code recognizes it directly and will call into + our C module to do the encoding, bypassing this object entirely. + We inherit from TBinaryProtocol so that the normal TBinaryProtocol + encoding can happen if the fastbinary module doesn't work for some + reason. (TODO(dreiss): Make this happen sanely in more cases.) + To disable this behavior, pass fallback=False constructor argument. + + In order to take advantage of the C module, just use + TBinaryProtocolAccelerated instead of TBinaryProtocol. + + NOTE: This code was contributed by an external developer. + The internal Thrift team has reviewed and tested it, + but we cannot guarantee that it is production-ready. + Please feel free to report bugs and/or success stories + to the public mailing list. + """ + pass + + def __init__(self, *args, **kwargs): + fallback = kwargs.pop('fallback', True) + super(TBinaryProtocolAccelerated, self).__init__(*args, **kwargs) + try: + from thrift.protocol import fastbinary + except ImportError: + if not fallback: + raise + else: + self._fast_decode = fastbinary.decode_binary + self._fast_encode = fastbinary.encode_binary + + +class TBinaryProtocolAcceleratedFactory(object): + def __init__(self, + string_length_limit=None, + container_length_limit=None, + fallback=True): + self.string_length_limit = string_length_limit + self.container_length_limit = container_length_limit + self._fallback = fallback + + def getProtocol(self, trans): + return TBinaryProtocolAccelerated( + trans, + string_length_limit=self.string_length_limit, + container_length_limit=self.container_length_limit, + fallback=self._fallback) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TCompactProtocol.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TCompactProtocol.py new file mode 100644 index 00000000..16fd9be2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TCompactProtocol.py @@ -0,0 +1,472 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from .TProtocol import TType, TProtocolBase, TProtocolException, checkIntegerLimits +from struct import pack, unpack + +from ..compat import binary_to_str, str_to_binary + +__all__ = ['TCompactProtocol', 'TCompactProtocolFactory'] + +CLEAR = 0 +FIELD_WRITE = 1 +VALUE_WRITE = 2 +CONTAINER_WRITE = 3 +BOOL_WRITE = 4 +FIELD_READ = 5 +CONTAINER_READ = 6 +VALUE_READ = 7 +BOOL_READ = 8 + + +def make_helper(v_from, container): + def helper(func): + def nested(self, *args, **kwargs): + assert self.state in (v_from, container), (self.state, v_from, container) + return func(self, *args, **kwargs) + return nested + return helper +writer = make_helper(VALUE_WRITE, CONTAINER_WRITE) +reader = make_helper(VALUE_READ, CONTAINER_READ) + + +def makeZigZag(n, bits): + checkIntegerLimits(n, bits) + return (n << 1) ^ (n >> (bits - 1)) + + +def fromZigZag(n): + return (n >> 1) ^ -(n & 1) + + +def writeVarint(trans, n): + out = bytearray() + while True: + if n & ~0x7f == 0: + out.append(n) + break + else: + out.append((n & 0xff) | 0x80) + n = n >> 7 + trans.write(bytes(out)) + + +def readVarint(trans): + result = 0 + shift = 0 + while True: + x = trans.readAll(1) + byte = ord(x) + result |= (byte & 0x7f) << shift + if byte >> 7 == 0: + return result + shift += 7 + + +class CompactType(object): + STOP = 0x00 + TRUE = 0x01 + FALSE = 0x02 + BYTE = 0x03 + I16 = 0x04 + I32 = 0x05 + I64 = 0x06 + DOUBLE = 0x07 + BINARY = 0x08 + LIST = 0x09 + SET = 0x0A + MAP = 0x0B + STRUCT = 0x0C + +CTYPES = { + TType.STOP: CompactType.STOP, + TType.BOOL: CompactType.TRUE, # used for collection + TType.BYTE: CompactType.BYTE, + TType.I16: CompactType.I16, + TType.I32: CompactType.I32, + TType.I64: CompactType.I64, + TType.DOUBLE: CompactType.DOUBLE, + TType.STRING: CompactType.BINARY, + TType.STRUCT: CompactType.STRUCT, + TType.LIST: CompactType.LIST, + TType.SET: CompactType.SET, + TType.MAP: CompactType.MAP, +} + +TTYPES = {} +for k, v in CTYPES.items(): + TTYPES[v] = k +TTYPES[CompactType.FALSE] = TType.BOOL +del k +del v + + +class TCompactProtocol(TProtocolBase): + """Compact implementation of the Thrift protocol driver.""" + + PROTOCOL_ID = 0x82 + VERSION = 1 + VERSION_MASK = 0x1f + TYPE_MASK = 0xe0 + TYPE_BITS = 0x07 + TYPE_SHIFT_AMOUNT = 5 + + def __init__(self, trans, + string_length_limit=None, + container_length_limit=None): + TProtocolBase.__init__(self, trans) + self.state = CLEAR + self.__last_fid = 0 + self.__bool_fid = None + self.__bool_value = None + self.__structs = [] + self.__containers = [] + self.string_length_limit = string_length_limit + self.container_length_limit = container_length_limit + + def _check_string_length(self, length): + self._check_length(self.string_length_limit, length) + + def _check_container_length(self, length): + self._check_length(self.container_length_limit, length) + + def __writeVarint(self, n): + writeVarint(self.trans, n) + + def writeMessageBegin(self, name, type, seqid): + assert self.state == CLEAR + self.__writeUByte(self.PROTOCOL_ID) + self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT)) + self.__writeVarint(seqid) + self.__writeBinary(str_to_binary(name)) + self.state = VALUE_WRITE + + def writeMessageEnd(self): + assert self.state == VALUE_WRITE + self.state = CLEAR + + def writeStructBegin(self, name): + assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state + self.__structs.append((self.state, self.__last_fid)) + self.state = FIELD_WRITE + self.__last_fid = 0 + + def writeStructEnd(self): + assert self.state == FIELD_WRITE + self.state, self.__last_fid = self.__structs.pop() + + def writeFieldStop(self): + self.__writeByte(0) + + def __writeFieldHeader(self, type, fid): + delta = fid - self.__last_fid + if 0 < delta <= 15: + self.__writeUByte(delta << 4 | type) + else: + self.__writeByte(type) + self.__writeI16(fid) + self.__last_fid = fid + + def writeFieldBegin(self, name, type, fid): + assert self.state == FIELD_WRITE, self.state + if type == TType.BOOL: + self.state = BOOL_WRITE + self.__bool_fid = fid + else: + self.state = VALUE_WRITE + self.__writeFieldHeader(CTYPES[type], fid) + + def writeFieldEnd(self): + assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state + self.state = FIELD_WRITE + + def __writeUByte(self, byte): + self.trans.write(pack('!B', byte)) + + def __writeByte(self, byte): + self.trans.write(pack('!b', byte)) + + def __writeI16(self, i16): + self.__writeVarint(makeZigZag(i16, 16)) + + def __writeSize(self, i32): + self.__writeVarint(i32) + + def writeCollectionBegin(self, etype, size): + assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state + if size <= 14: + self.__writeUByte(size << 4 | CTYPES[etype]) + else: + self.__writeUByte(0xf0 | CTYPES[etype]) + self.__writeSize(size) + self.__containers.append(self.state) + self.state = CONTAINER_WRITE + writeSetBegin = writeCollectionBegin + writeListBegin = writeCollectionBegin + + def writeMapBegin(self, ktype, vtype, size): + assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state + if size == 0: + self.__writeByte(0) + else: + self.__writeSize(size) + self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype]) + self.__containers.append(self.state) + self.state = CONTAINER_WRITE + + def writeCollectionEnd(self): + assert self.state == CONTAINER_WRITE, self.state + self.state = self.__containers.pop() + writeMapEnd = writeCollectionEnd + writeSetEnd = writeCollectionEnd + writeListEnd = writeCollectionEnd + + def writeBool(self, bool): + if self.state == BOOL_WRITE: + if bool: + ctype = CompactType.TRUE + else: + ctype = CompactType.FALSE + self.__writeFieldHeader(ctype, self.__bool_fid) + elif self.state == CONTAINER_WRITE: + if bool: + self.__writeByte(CompactType.TRUE) + else: + self.__writeByte(CompactType.FALSE) + else: + raise AssertionError("Invalid state in compact protocol") + + writeByte = writer(__writeByte) + writeI16 = writer(__writeI16) + + @writer + def writeI32(self, i32): + self.__writeVarint(makeZigZag(i32, 32)) + + @writer + def writeI64(self, i64): + self.__writeVarint(makeZigZag(i64, 64)) + + @writer + def writeDouble(self, dub): + self.trans.write(pack('> 4 + if delta == 0: + fid = self.__readI16() + else: + fid = self.__last_fid + delta + self.__last_fid = fid + type = type & 0x0f + if type == CompactType.TRUE: + self.state = BOOL_READ + self.__bool_value = True + elif type == CompactType.FALSE: + self.state = BOOL_READ + self.__bool_value = False + else: + self.state = VALUE_READ + return (None, self.__getTType(type), fid) + + def readFieldEnd(self): + assert self.state in (VALUE_READ, BOOL_READ), self.state + self.state = FIELD_READ + + def __readUByte(self): + result, = unpack('!B', self.trans.readAll(1)) + return result + + def __readByte(self): + result, = unpack('!b', self.trans.readAll(1)) + return result + + def __readVarint(self): + return readVarint(self.trans) + + def __readZigZag(self): + return fromZigZag(self.__readVarint()) + + def __readSize(self): + result = self.__readVarint() + if result < 0: + raise TProtocolException("Length < 0") + return result + + def readMessageBegin(self): + assert self.state == CLEAR + proto_id = self.__readUByte() + if proto_id != self.PROTOCOL_ID: + raise TProtocolException(TProtocolException.BAD_VERSION, + 'Bad protocol id in the message: %d' % proto_id) + ver_type = self.__readUByte() + type = (ver_type >> self.TYPE_SHIFT_AMOUNT) & self.TYPE_BITS + version = ver_type & self.VERSION_MASK + if version != self.VERSION: + raise TProtocolException(TProtocolException.BAD_VERSION, + 'Bad version: %d (expect %d)' % (version, self.VERSION)) + seqid = self.__readVarint() + name = binary_to_str(self.__readBinary()) + return (name, type, seqid) + + def readMessageEnd(self): + assert self.state == CLEAR + assert len(self.__structs) == 0 + + def readStructBegin(self): + assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state + self.__structs.append((self.state, self.__last_fid)) + self.state = FIELD_READ + self.__last_fid = 0 + + def readStructEnd(self): + assert self.state == FIELD_READ + self.state, self.__last_fid = self.__structs.pop() + + def readCollectionBegin(self): + assert self.state in (VALUE_READ, CONTAINER_READ), self.state + size_type = self.__readUByte() + size = size_type >> 4 + type = self.__getTType(size_type) + if size == 15: + size = self.__readSize() + self._check_container_length(size) + self.__containers.append(self.state) + self.state = CONTAINER_READ + return type, size + readSetBegin = readCollectionBegin + readListBegin = readCollectionBegin + + def readMapBegin(self): + assert self.state in (VALUE_READ, CONTAINER_READ), self.state + size = self.__readSize() + self._check_container_length(size) + types = 0 + if size > 0: + types = self.__readUByte() + vtype = self.__getTType(types) + ktype = self.__getTType(types >> 4) + self.__containers.append(self.state) + self.state = CONTAINER_READ + return (ktype, vtype, size) + + def readCollectionEnd(self): + assert self.state == CONTAINER_READ, self.state + self.state = self.__containers.pop() + readSetEnd = readCollectionEnd + readListEnd = readCollectionEnd + readMapEnd = readCollectionEnd + + def readBool(self): + if self.state == BOOL_READ: + return self.__bool_value == CompactType.TRUE + elif self.state == CONTAINER_READ: + return self.__readByte() == CompactType.TRUE + else: + raise AssertionError("Invalid state in compact protocol: %d" % + self.state) + + readByte = reader(__readByte) + __readI16 = __readZigZag + readI16 = reader(__readZigZag) + readI32 = reader(__readZigZag) + readI64 = reader(__readZigZag) + + @reader + def readDouble(self): + buff = self.trans.readAll(8) + val, = unpack('= 0xd800 and codeunit <= 0xdbff + + def _isLowSurrogate(self, codeunit): + return codeunit >= 0xdc00 and codeunit <= 0xdfff + + def _toChar(self, high, low=None): + if not low: + if sys.version_info[0] == 2: + return ("\\u%04x" % high).decode('unicode-escape') \ + .encode('utf-8') + else: + return chr(high) + else: + codepoint = (1 << 16) + ((high & 0x3ff) << 10) + codepoint += low & 0x3ff + if sys.version_info[0] == 2: + s = "\\U%08x" % codepoint + return s.decode('unicode-escape').encode('utf-8') + else: + return chr(codepoint) + + def readJSONString(self, skipContext): + highSurrogate = None + string = [] + if skipContext is False: + self.context.read() + self.readJSONSyntaxChar(QUOTE) + while True: + character = self.reader.read() + if character == QUOTE: + break + if ord(character) == ESCSEQ0: + character = self.reader.read() + if ord(character) == ESCSEQ1: + character = self.trans.read(4).decode('ascii') + codeunit = int(character, 16) + if self._isHighSurrogate(codeunit): + if highSurrogate: + raise TProtocolException( + TProtocolException.INVALID_DATA, + "Expected low surrogate char") + highSurrogate = codeunit + continue + elif self._isLowSurrogate(codeunit): + if not highSurrogate: + raise TProtocolException( + TProtocolException.INVALID_DATA, + "Expected high surrogate char") + character = self._toChar(highSurrogate, codeunit) + highSurrogate = None + else: + character = self._toChar(codeunit) + else: + if character not in ESCAPE_CHARS: + raise TProtocolException( + TProtocolException.INVALID_DATA, + "Expected control char") + character = ESCAPE_CHARS[character] + elif character in ESCAPE_CHAR_VALS: + raise TProtocolException(TProtocolException.INVALID_DATA, + "Unescaped control char") + elif sys.version_info[0] > 2: + utf8_bytes = bytearray([ord(character)]) + while ord(self.reader.peek()) >= 0x80: + utf8_bytes.append(ord(self.reader.read())) + character = utf8_bytes.decode('utf8') + string.append(character) + + if highSurrogate: + raise TProtocolException(TProtocolException.INVALID_DATA, + "Expected low surrogate char") + return ''.join(string) + + def isJSONNumeric(self, character): + return (True if NUMERIC_CHAR.find(character) != - 1 else False) + + def readJSONQuotes(self): + if (self.context.escapeNum()): + self.readJSONSyntaxChar(QUOTE) + + def readJSONNumericChars(self): + numeric = [] + while True: + character = self.reader.peek() + if self.isJSONNumeric(character) is False: + break + numeric.append(self.reader.read()) + return b''.join(numeric).decode('ascii') + + def readJSONInteger(self): + self.context.read() + self.readJSONQuotes() + numeric = self.readJSONNumericChars() + self.readJSONQuotes() + try: + return int(numeric) + except ValueError: + raise TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data") + + def readJSONDouble(self): + self.context.read() + if self.reader.peek() == QUOTE: + string = self.readJSONString(True) + try: + double = float(string) + if (self.context.escapeNum is False and + not math.isinf(double) and + not math.isnan(double)): + raise TProtocolException( + TProtocolException.INVALID_DATA, + "Numeric data unexpectedly quoted") + return double + except ValueError: + raise TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data") + else: + if self.context.escapeNum() is True: + self.readJSONSyntaxChar(QUOTE) + try: + return float(self.readJSONNumericChars()) + except ValueError: + raise TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data") + + def readJSONBase64(self): + string = self.readJSONString(False) + size = len(string) + m = size % 4 + # Force padding since b64encode method does not allow it + if m != 0: + for i in range(4 - m): + string += '=' + return base64.b64decode(string) + + def readJSONObjectStart(self): + self.context.read() + self.readJSONSyntaxChar(LBRACE) + self.pushContext(JSONPairContext(self)) + + def readJSONObjectEnd(self): + self.readJSONSyntaxChar(RBRACE) + self.popContext() + + def readJSONArrayStart(self): + self.context.read() + self.readJSONSyntaxChar(LBRACKET) + self.pushContext(JSONListContext(self)) + + def readJSONArrayEnd(self): + self.readJSONSyntaxChar(RBRACKET) + self.popContext() + + +class TJSONProtocol(TJSONProtocolBase): + + def readMessageBegin(self): + self.resetReadContext() + self.readJSONArrayStart() + if self.readJSONInteger() != VERSION: + raise TProtocolException(TProtocolException.BAD_VERSION, + "Message contained bad version.") + name = self.readJSONString(False) + typen = self.readJSONInteger() + seqid = self.readJSONInteger() + return (name, typen, seqid) + + def readMessageEnd(self): + self.readJSONArrayEnd() + + def readStructBegin(self): + self.readJSONObjectStart() + + def readStructEnd(self): + self.readJSONObjectEnd() + + def readFieldBegin(self): + character = self.reader.peek() + ttype = 0 + id = 0 + if character == RBRACE: + ttype = TType.STOP + else: + id = self.readJSONInteger() + self.readJSONObjectStart() + ttype = JTYPES[self.readJSONString(False)] + return (None, ttype, id) + + def readFieldEnd(self): + self.readJSONObjectEnd() + + def readMapBegin(self): + self.readJSONArrayStart() + keyType = JTYPES[self.readJSONString(False)] + valueType = JTYPES[self.readJSONString(False)] + size = self.readJSONInteger() + self.readJSONObjectStart() + return (keyType, valueType, size) + + def readMapEnd(self): + self.readJSONObjectEnd() + self.readJSONArrayEnd() + + def readCollectionBegin(self): + self.readJSONArrayStart() + elemType = JTYPES[self.readJSONString(False)] + size = self.readJSONInteger() + return (elemType, size) + readListBegin = readCollectionBegin + readSetBegin = readCollectionBegin + + def readCollectionEnd(self): + self.readJSONArrayEnd() + readSetEnd = readCollectionEnd + readListEnd = readCollectionEnd + + def readBool(self): + return (False if self.readJSONInteger() == 0 else True) + + def readNumber(self): + return self.readJSONInteger() + readByte = readNumber + readI16 = readNumber + readI32 = readNumber + readI64 = readNumber + + def readDouble(self): + return self.readJSONDouble() + + def readString(self): + return self.readJSONString(False) + + def readBinary(self): + return self.readJSONBase64() + + def writeMessageBegin(self, name, request_type, seqid): + self.resetWriteContext() + self.writeJSONArrayStart() + self.writeJSONNumber(VERSION) + self.writeJSONString(name) + self.writeJSONNumber(request_type) + self.writeJSONNumber(seqid) + + def writeMessageEnd(self): + self.writeJSONArrayEnd() + + def writeStructBegin(self, name): + self.writeJSONObjectStart() + + def writeStructEnd(self): + self.writeJSONObjectEnd() + + def writeFieldBegin(self, name, ttype, id): + self.writeJSONNumber(id) + self.writeJSONObjectStart() + self.writeJSONString(CTYPES[ttype]) + + def writeFieldEnd(self): + self.writeJSONObjectEnd() + + def writeFieldStop(self): + pass + + def writeMapBegin(self, ktype, vtype, size): + self.writeJSONArrayStart() + self.writeJSONString(CTYPES[ktype]) + self.writeJSONString(CTYPES[vtype]) + self.writeJSONNumber(size) + self.writeJSONObjectStart() + + def writeMapEnd(self): + self.writeJSONObjectEnd() + self.writeJSONArrayEnd() + + def writeListBegin(self, etype, size): + self.writeJSONArrayStart() + self.writeJSONString(CTYPES[etype]) + self.writeJSONNumber(size) + + def writeListEnd(self): + self.writeJSONArrayEnd() + + def writeSetBegin(self, etype, size): + self.writeJSONArrayStart() + self.writeJSONString(CTYPES[etype]) + self.writeJSONNumber(size) + + def writeSetEnd(self): + self.writeJSONArrayEnd() + + def writeBool(self, boolean): + self.writeJSONNumber(1 if boolean is True else 0) + + def writeByte(self, byte): + checkIntegerLimits(byte, 8) + self.writeJSONNumber(byte) + + def writeI16(self, i16): + checkIntegerLimits(i16, 16) + self.writeJSONNumber(i16) + + def writeI32(self, i32): + checkIntegerLimits(i32, 32) + self.writeJSONNumber(i32) + + def writeI64(self, i64): + checkIntegerLimits(i64, 64) + self.writeJSONNumber(i64) + + def writeDouble(self, dbl): + # 17 significant digits should be just enough for any double precision + # value. + self.writeJSONNumber(dbl, '{0:.17g}') + + def writeString(self, string): + self.writeJSONString(string) + + def writeBinary(self, binary): + self.writeJSONBase64(binary) + + +class TJSONProtocolFactory(object): + def getProtocol(self, trans): + return TJSONProtocol(trans) + + @property + def string_length_limit(senf): + return None + + @property + def container_length_limit(senf): + return None + + +class TSimpleJSONProtocol(TJSONProtocolBase): + """Simple, readable, write-only JSON protocol. + + Useful for interacting with scripting languages. + """ + + def readMessageBegin(self): + raise NotImplementedError() + + def readMessageEnd(self): + raise NotImplementedError() + + def readStructBegin(self): + raise NotImplementedError() + + def readStructEnd(self): + raise NotImplementedError() + + def writeMessageBegin(self, name, request_type, seqid): + self.resetWriteContext() + + def writeMessageEnd(self): + pass + + def writeStructBegin(self, name): + self.writeJSONObjectStart() + + def writeStructEnd(self): + self.writeJSONObjectEnd() + + def writeFieldBegin(self, name, ttype, fid): + self.writeJSONString(name) + + def writeFieldEnd(self): + pass + + def writeMapBegin(self, ktype, vtype, size): + self.writeJSONObjectStart() + + def writeMapEnd(self): + self.writeJSONObjectEnd() + + def _writeCollectionBegin(self, etype, size): + self.writeJSONArrayStart() + + def _writeCollectionEnd(self): + self.writeJSONArrayEnd() + writeListBegin = _writeCollectionBegin + writeListEnd = _writeCollectionEnd + writeSetBegin = _writeCollectionBegin + writeSetEnd = _writeCollectionEnd + + def writeByte(self, byte): + checkIntegerLimits(byte, 8) + self.writeJSONNumber(byte) + + def writeI16(self, i16): + checkIntegerLimits(i16, 16) + self.writeJSONNumber(i16) + + def writeI32(self, i32): + checkIntegerLimits(i32, 32) + self.writeJSONNumber(i32) + + def writeI64(self, i64): + checkIntegerLimits(i64, 64) + self.writeJSONNumber(i64) + + def writeBool(self, boolean): + self.writeJSONNumber(1 if boolean is True else 0) + + def writeDouble(self, dbl): + self.writeJSONNumber(dbl) + + def writeString(self, string): + self.writeJSONString(string) + + def writeBinary(self, binary): + self.writeJSONBase64(binary) + + +class TSimpleJSONProtocolFactory(object): + + def getProtocol(self, trans): + return TSimpleJSONProtocol(trans) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TMultiplexedProtocol.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TMultiplexedProtocol.py new file mode 100644 index 00000000..309f896d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TMultiplexedProtocol.py @@ -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. +# + +from thrift.Thrift import TMessageType +from thrift.protocol import TProtocolDecorator + +SEPARATOR = ":" + + +class TMultiplexedProtocol(TProtocolDecorator.TProtocolDecorator): + def __init__(self, protocol, serviceName): + TProtocolDecorator.TProtocolDecorator.__init__(self, protocol) + self.serviceName = serviceName + + def writeMessageBegin(self, name, type, seqid): + if (type == TMessageType.CALL or + type == TMessageType.ONEWAY): + self.protocol.writeMessageBegin( + self.serviceName + SEPARATOR + name, + type, + seqid + ) + else: + self.protocol.writeMessageBegin(name, type, seqid) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocol.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocol.py new file mode 100644 index 00000000..f29c58db --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocol.py @@ -0,0 +1,419 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from thrift.Thrift import TException, TType, TFrozenDict +from thrift.transport.TTransport import TTransportException +from ..compat import binary_to_str, str_to_binary + +import six +import sys +from itertools import islice +from six.moves import zip + + +class TProtocolException(TException): + """Custom Protocol Exception class""" + + UNKNOWN = 0 + INVALID_DATA = 1 + NEGATIVE_SIZE = 2 + SIZE_LIMIT = 3 + BAD_VERSION = 4 + NOT_IMPLEMENTED = 5 + DEPTH_LIMIT = 6 + + def __init__(self, type=UNKNOWN, message=None): + TException.__init__(self, message) + self.type = type + + +class TProtocolBase(object): + """Base class for Thrift protocol driver.""" + + def __init__(self, trans): + self.trans = trans + self._fast_decode = None + self._fast_encode = None + + @staticmethod + def _check_length(limit, length): + if length < 0: + raise TTransportException(TTransportException.NEGATIVE_SIZE, + 'Negative length: %d' % length) + if limit is not None and length > limit: + raise TTransportException(TTransportException.SIZE_LIMIT, + 'Length exceeded max allowed: %d' % limit) + + def writeMessageBegin(self, name, ttype, seqid): + pass + + def writeMessageEnd(self): + pass + + def writeStructBegin(self, name): + pass + + def writeStructEnd(self): + pass + + def writeFieldBegin(self, name, ttype, fid): + pass + + def writeFieldEnd(self): + pass + + def writeFieldStop(self): + pass + + def writeMapBegin(self, ktype, vtype, size): + pass + + def writeMapEnd(self): + pass + + def writeListBegin(self, etype, size): + pass + + def writeListEnd(self): + pass + + def writeSetBegin(self, etype, size): + pass + + def writeSetEnd(self): + pass + + def writeBool(self, bool_val): + pass + + def writeByte(self, byte): + pass + + def writeI16(self, i16): + pass + + def writeI32(self, i32): + pass + + def writeI64(self, i64): + pass + + def writeDouble(self, dub): + pass + + def writeString(self, str_val): + self.writeBinary(str_to_binary(str_val)) + + def writeBinary(self, str_val): + pass + + def writeUtf8(self, str_val): + self.writeString(str_val.encode('utf8')) + + def readMessageBegin(self): + pass + + def readMessageEnd(self): + pass + + def readStructBegin(self): + pass + + def readStructEnd(self): + pass + + def readFieldBegin(self): + pass + + def readFieldEnd(self): + pass + + def readMapBegin(self): + pass + + def readMapEnd(self): + pass + + def readListBegin(self): + pass + + def readListEnd(self): + pass + + def readSetBegin(self): + pass + + def readSetEnd(self): + pass + + def readBool(self): + pass + + def readByte(self): + pass + + def readI16(self): + pass + + def readI32(self): + pass + + def readI64(self): + pass + + def readDouble(self): + pass + + def readString(self): + return binary_to_str(self.readBinary()) + + def readBinary(self): + pass + + def readUtf8(self): + return self.readString().decode('utf8') + + def skip(self, ttype): + if ttype == TType.STOP: + return + elif ttype == TType.BOOL: + self.readBool() + elif ttype == TType.BYTE: + self.readByte() + elif ttype == TType.I16: + self.readI16() + elif ttype == TType.I32: + self.readI32() + elif ttype == TType.I64: + self.readI64() + elif ttype == TType.DOUBLE: + self.readDouble() + elif ttype == TType.STRING: + self.readString() + elif ttype == TType.STRUCT: + name = self.readStructBegin() + while True: + (name, ttype, id) = self.readFieldBegin() + if ttype == TType.STOP: + break + self.skip(ttype) + self.readFieldEnd() + self.readStructEnd() + elif ttype == TType.MAP: + (ktype, vtype, size) = self.readMapBegin() + for i in range(size): + self.skip(ktype) + self.skip(vtype) + self.readMapEnd() + elif ttype == TType.SET: + (etype, size) = self.readSetBegin() + for i in range(size): + self.skip(etype) + self.readSetEnd() + elif ttype == TType.LIST: + (etype, size) = self.readListBegin() + for i in range(size): + self.skip(etype) + self.readListEnd() + + # tuple of: ( 'reader method' name, is_container bool, 'writer_method' name ) + _TTYPE_HANDLERS = ( + (None, None, False), # 0 TType.STOP + (None, None, False), # 1 TType.VOID # TODO: handle void? + ('readBool', 'writeBool', False), # 2 TType.BOOL + ('readByte', 'writeByte', False), # 3 TType.BYTE and I08 + ('readDouble', 'writeDouble', False), # 4 TType.DOUBLE + (None, None, False), # 5 undefined + ('readI16', 'writeI16', False), # 6 TType.I16 + (None, None, False), # 7 undefined + ('readI32', 'writeI32', False), # 8 TType.I32 + (None, None, False), # 9 undefined + ('readI64', 'writeI64', False), # 10 TType.I64 + ('readString', 'writeString', False), # 11 TType.STRING and UTF7 + ('readContainerStruct', 'writeContainerStruct', True), # 12 *.STRUCT + ('readContainerMap', 'writeContainerMap', True), # 13 TType.MAP + ('readContainerSet', 'writeContainerSet', True), # 14 TType.SET + ('readContainerList', 'writeContainerList', True), # 15 TType.LIST + (None, None, False), # 16 TType.UTF8 # TODO: handle utf8 types? + (None, None, False) # 17 TType.UTF16 # TODO: handle utf16 types? + ) + + def _ttype_handlers(self, ttype, spec): + if spec == 'BINARY': + if ttype != TType.STRING: + raise TProtocolException(type=TProtocolException.INVALID_DATA, + message='Invalid binary field type %d' % ttype) + return ('readBinary', 'writeBinary', False) + if sys.version_info[0] == 2 and spec == 'UTF8': + if ttype != TType.STRING: + raise TProtocolException(type=TProtocolException.INVALID_DATA, + message='Invalid string field type %d' % ttype) + return ('readUtf8', 'writeUtf8', False) + return self._TTYPE_HANDLERS[ttype] if ttype < len(self._TTYPE_HANDLERS) else (None, None, False) + + def _read_by_ttype(self, ttype, spec, espec): + reader_name, _, is_container = self._ttype_handlers(ttype, spec) + if reader_name is None: + raise TProtocolException(type=TProtocolException.INVALID_DATA, + message='Invalid type %d' % (ttype)) + reader_func = getattr(self, reader_name) + read = (lambda: reader_func(espec)) if is_container else reader_func + while True: + yield read() + + def readFieldByTType(self, ttype, spec): + return next(self._read_by_ttype(ttype, spec, spec)) + + def readContainerList(self, spec): + ttype, tspec, is_immutable = spec + (list_type, list_len) = self.readListBegin() + # TODO: compare types we just decoded with thrift_spec + elems = islice(self._read_by_ttype(ttype, spec, tspec), list_len) + results = (tuple if is_immutable else list)(elems) + self.readListEnd() + return results + + def readContainerSet(self, spec): + ttype, tspec, is_immutable = spec + (set_type, set_len) = self.readSetBegin() + # TODO: compare types we just decoded with thrift_spec + elems = islice(self._read_by_ttype(ttype, spec, tspec), set_len) + results = (frozenset if is_immutable else set)(elems) + self.readSetEnd() + return results + + def readContainerStruct(self, spec): + (obj_class, obj_spec) = spec + obj = obj_class() + obj.read(self) + return obj + + def readContainerMap(self, spec): + ktype, kspec, vtype, vspec, is_immutable = spec + (map_ktype, map_vtype, map_len) = self.readMapBegin() + # TODO: compare types we just decoded with thrift_spec and + # abort/skip if types disagree + keys = self._read_by_ttype(ktype, spec, kspec) + vals = self._read_by_ttype(vtype, spec, vspec) + keyvals = islice(zip(keys, vals), map_len) + results = (TFrozenDict if is_immutable else dict)(keyvals) + self.readMapEnd() + return results + + def readStruct(self, obj, thrift_spec, is_immutable=False): + if is_immutable: + fields = {} + self.readStructBegin() + while True: + (fname, ftype, fid) = self.readFieldBegin() + if ftype == TType.STOP: + break + try: + field = thrift_spec[fid] + except IndexError: + self.skip(ftype) + else: + if field is not None and ftype == field[1]: + fname = field[2] + fspec = field[3] + val = self.readFieldByTType(ftype, fspec) + if is_immutable: + fields[fname] = val + else: + setattr(obj, fname, val) + else: + self.skip(ftype) + self.readFieldEnd() + self.readStructEnd() + if is_immutable: + return obj(**fields) + + def writeContainerStruct(self, val, spec): + val.write(self) + + def writeContainerList(self, val, spec): + ttype, tspec, _ = spec + self.writeListBegin(ttype, len(val)) + for _ in self._write_by_ttype(ttype, val, spec, tspec): + pass + self.writeListEnd() + + def writeContainerSet(self, val, spec): + ttype, tspec, _ = spec + self.writeSetBegin(ttype, len(val)) + for _ in self._write_by_ttype(ttype, val, spec, tspec): + pass + self.writeSetEnd() + + def writeContainerMap(self, val, spec): + ktype, kspec, vtype, vspec, _ = spec + self.writeMapBegin(ktype, vtype, len(val)) + for _ in zip(self._write_by_ttype(ktype, six.iterkeys(val), spec, kspec), + self._write_by_ttype(vtype, six.itervalues(val), spec, vspec)): + pass + self.writeMapEnd() + + def writeStruct(self, obj, thrift_spec): + self.writeStructBegin(obj.__class__.__name__) + for field in thrift_spec: + if field is None: + continue + fname = field[2] + val = getattr(obj, fname) + if val is None: + # skip writing out unset fields + continue + fid = field[0] + ftype = field[1] + fspec = field[3] + self.writeFieldBegin(fname, ftype, fid) + self.writeFieldByTType(ftype, val, fspec) + self.writeFieldEnd() + self.writeFieldStop() + self.writeStructEnd() + + def _write_by_ttype(self, ttype, vals, spec, espec): + _, writer_name, is_container = self._ttype_handlers(ttype, spec) + writer_func = getattr(self, writer_name) + write = (lambda v: writer_func(v, espec)) if is_container else writer_func + for v in vals: + yield write(v) + + def writeFieldByTType(self, ttype, val, spec): + next(self._write_by_ttype(ttype, [val], spec, spec)) + + +def checkIntegerLimits(i, bits): + if bits == 8 and (i < -128 or i > 127): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i8 requires -128 <= number <= 127") + elif bits == 16 and (i < -32768 or i > 32767): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i16 requires -32768 <= number <= 32767") + elif bits == 32 and (i < -2147483648 or i > 2147483647): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i32 requires -2147483648 <= number <= 2147483647") + elif bits == 64 and (i < -9223372036854775808 or i > 9223372036854775807): + raise TProtocolException(TProtocolException.INVALID_DATA, + "i64 requires -9223372036854775808 <= number <= 9223372036854775807") + + +class TProtocolFactory(object): + def getProtocol(self, trans): + pass diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocolDecorator.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocolDecorator.py new file mode 100644 index 00000000..8b270a46 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/TProtocolDecorator.py @@ -0,0 +1,50 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import types + +from thrift.protocol.TProtocol import TProtocolBase + + +class TProtocolDecorator(): + def __init__(self, protocol): + TProtocolBase(protocol) + self.protocol = protocol + + def __getattr__(self, name): + if hasattr(self.protocol, name): + member = getattr(self.protocol, name) + if type(member) in [ + types.MethodType, + types.FunctionType, + types.LambdaType, + types.BuiltinFunctionType, + types.BuiltinMethodType, + ]: + return lambda *args, **kwargs: self._wrap(member, args, kwargs) + else: + return member + raise AttributeError(name) + + def _wrap(self, func, args, kwargs): + if isinstance(func, types.MethodType): + result = func(*args, **kwargs) + else: + result = func(self.protocol, *args, **kwargs) + return result diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/protocol/__init__.py b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/__init__.py new file mode 100644 index 00000000..7148f66b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/protocol/__init__.py @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +__all__ = ['fastbinary', 'TBase', 'TBinaryProtocol', 'TCompactProtocol', + 'TJSONProtocol', 'TProtocol'] diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/server/THttpServer.py b/vendor/src/github.com/apache/thrift/lib/py/src/server/THttpServer.py new file mode 100644 index 00000000..1b501a7a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/server/THttpServer.py @@ -0,0 +1,87 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from six.moves import BaseHTTPServer + +from thrift.server import TServer +from thrift.transport import TTransport + + +class ResponseException(Exception): + """Allows handlers to override the HTTP response + + Normally, THttpServer always sends a 200 response. If a handler wants + to override this behavior (e.g., to simulate a misconfigured or + overloaded web server during testing), it can raise a ResponseException. + The function passed to the constructor will be called with the + RequestHandler as its only argument. + """ + def __init__(self, handler): + self.handler = handler + + +class THttpServer(TServer.TServer): + """A simple HTTP-based Thrift server + + This class is not very performant, but it is useful (for example) for + acting as a mock version of an Apache-based PHP Thrift endpoint. + """ + def __init__(self, + processor, + server_address, + inputProtocolFactory, + outputProtocolFactory=None, + server_class=BaseHTTPServer.HTTPServer): + """Set up protocol factories and HTTP server. + + See BaseHTTPServer for server_address. + See TServer for protocol factories. + """ + if outputProtocolFactory is None: + outputProtocolFactory = inputProtocolFactory + + TServer.TServer.__init__(self, processor, None, None, None, + inputProtocolFactory, outputProtocolFactory) + + thttpserver = self + + 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) + itrans = TTransport.TBufferedTransport( + itrans, int(self.headers['Content-Length'])) + otrans = TTransport.TMemoryBuffer() + iprot = thttpserver.inputProtocolFactory.getProtocol(itrans) + oprot = thttpserver.outputProtocolFactory.getProtocol(otrans) + try: + thttpserver.processor.process(iprot, oprot) + except ResponseException as exn: + exn.handler(self) + else: + self.send_response(200) + self.send_header("content-type", "application/x-thrift") + self.end_headers() + self.wfile.write(otrans.getvalue()) + + self.httpd = server_class(server_address, RequestHander) + + def serve(self): + self.httpd.serve_forever() diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/server/TNonblockingServer.py b/vendor/src/github.com/apache/thrift/lib/py/src/server/TNonblockingServer.py new file mode 100644 index 00000000..87031c13 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/server/TNonblockingServer.py @@ -0,0 +1,350 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +"""Implementation of non-blocking server. + +The main idea of the server is to receive and send requests +only from the main thread. + +The thread poool should be sized for concurrent tasks, not +maximum connections +""" + +import logging +import select +import socket +import struct +import threading + +from six.moves import queue + +from thrift.transport import TTransport +from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory + +__all__ = ['TNonblockingServer'] + +logger = logging.getLogger(__name__) + + +class Worker(threading.Thread): + """Worker is a small helper to process incoming connection.""" + + def __init__(self, queue): + threading.Thread.__init__(self) + self.queue = queue + + def run(self): + """Process queries from task queue, stop if processor is None.""" + while True: + try: + processor, iprot, oprot, otrans, callback = self.queue.get() + if processor is None: + break + processor.process(iprot, oprot) + callback(True, otrans.getvalue()) + except Exception: + logger.exception("Exception while processing request") + callback(False, b'') + +WAIT_LEN = 0 +WAIT_MESSAGE = 1 +WAIT_PROCESS = 2 +SEND_ANSWER = 3 +CLOSED = 4 + + +def locked(func): + """Decorator which locks self.lock.""" + def nested(self, *args, **kwargs): + self.lock.acquire() + try: + return func(self, *args, **kwargs) + finally: + self.lock.release() + return nested + + +def socket_exception(func): + """Decorator close object on socket.error.""" + def read(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except socket.error: + self.close() + return read + + +class Connection(object): + """Basic class is represented connection. + + It can be in state: + WAIT_LEN --- connection is reading request len. + WAIT_MESSAGE --- connection is reading request. + WAIT_PROCESS --- connection has just read whole request and + waits for call ready routine. + SEND_ANSWER --- connection is sending answer string (including length + of answer). + CLOSED --- socket was closed and connection should be deleted. + """ + def __init__(self, new_socket, wake_up): + self.socket = new_socket + self.socket.setblocking(False) + self.status = WAIT_LEN + self.len = 0 + self.message = b'' + self.lock = threading.Lock() + self.wake_up = wake_up + + def _read_len(self): + """Reads length of request. + + It's a safer alternative to self.socket.recv(4) + """ + read = self.socket.recv(4 - len(self.message)) + if len(read) == 0: + # if we read 0 bytes and self.message is empty, then + # the client closed the connection + if len(self.message) != 0: + logger.error("can't read frame size from socket") + self.close() + return + self.message += read + if len(self.message) == 4: + self.len, = struct.unpack('!i', self.message) + if self.len < 0: + logger.error("negative frame size, it seems client " + "doesn't use FramedTransport") + self.close() + elif self.len == 0: + logger.error("empty frame, it's really strange") + self.close() + else: + self.message = b'' + self.status = WAIT_MESSAGE + + @socket_exception + def read(self): + """Reads data from stream and switch state.""" + assert self.status in (WAIT_LEN, WAIT_MESSAGE) + if self.status == WAIT_LEN: + self._read_len() + # go back to the main loop here for simplicity instead of + # falling through, even though there is a good chance that + # the message is already available + elif self.status == WAIT_MESSAGE: + read = self.socket.recv(self.len - len(self.message)) + if len(read) == 0: + logger.error("can't read frame from socket (get %d of " + "%d bytes)" % (len(self.message), self.len)) + self.close() + return + self.message += read + if len(self.message) == self.len: + self.status = WAIT_PROCESS + + @socket_exception + def write(self): + """Writes data from socket and switch state.""" + assert self.status == SEND_ANSWER + sent = self.socket.send(self.message) + if sent == len(self.message): + self.status = WAIT_LEN + self.message = b'' + self.len = 0 + else: + self.message = self.message[sent:] + + @locked + def ready(self, all_ok, message): + """Callback function for switching state and waking up main thread. + + This function is the only function witch can be called asynchronous. + + The ready can switch Connection to three states: + WAIT_LEN if request was oneway. + SEND_ANSWER if request was processed in normal way. + CLOSED if request throws unexpected exception. + + The one wakes up main thread. + """ + assert self.status == WAIT_PROCESS + if not all_ok: + self.close() + self.wake_up() + return + self.len = 0 + if len(message) == 0: + # it was a oneway request, do not write answer + self.message = b'' + self.status = WAIT_LEN + else: + self.message = struct.pack('!i', len(message)) + message + self.status = SEND_ANSWER + self.wake_up() + + @locked + def is_writeable(self): + """Return True if connection should be added to write list of select""" + return self.status == SEND_ANSWER + + # it's not necessary, but... + @locked + def is_readable(self): + """Return True if connection should be added to read list of select""" + return self.status in (WAIT_LEN, WAIT_MESSAGE) + + @locked + def is_closed(self): + """Returns True if connection is closed.""" + return self.status == CLOSED + + def fileno(self): + """Returns the file descriptor of the associated socket.""" + return self.socket.fileno() + + def close(self): + """Closes connection""" + self.status = CLOSED + self.socket.close() + + +class TNonblockingServer(object): + """Non-blocking server.""" + + def __init__(self, + processor, + lsocket, + inputProtocolFactory=None, + outputProtocolFactory=None, + threads=10): + self.processor = processor + self.socket = lsocket + self.in_protocol = inputProtocolFactory or TBinaryProtocolFactory() + self.out_protocol = outputProtocolFactory or self.in_protocol + self.threads = int(threads) + self.clients = {} + self.tasks = queue.Queue() + self._read, self._write = socket.socketpair() + self.prepared = False + self._stop = False + + def setNumThreads(self, num): + """Set the number of worker threads that should be created.""" + # implement ThreadPool interface + assert not self.prepared, "Can't change number of threads after start" + self.threads = num + + def prepare(self): + """Prepares server for serve requests.""" + if self.prepared: + return + self.socket.listen() + for _ in range(self.threads): + thread = Worker(self.tasks) + thread.setDaemon(True) + thread.start() + self.prepared = True + + def wake_up(self): + """Wake up main thread. + + The server usually waits in select call in we should terminate one. + The simplest way is using socketpair. + + Select always wait to read from the first socket of socketpair. + + In this case, we can just write anything to the second socket from + socketpair. + """ + self._write.send(b'1') + + def stop(self): + """Stop the server. + + This method causes the serve() method to return. stop() may be invoked + from within your handler, or from another thread. + + After stop() is called, serve() will return but the server will still + be listening on the socket. serve() may then be called again to resume + processing requests. Alternatively, close() may be called after + serve() returns to close the server socket and shutdown all worker + threads. + """ + self._stop = True + self.wake_up() + + def _select(self): + """Does select on open connections.""" + readable = [self.socket.handle.fileno(), self._read.fileno()] + writable = [] + for i, connection in list(self.clients.items()): + if connection.is_readable(): + readable.append(connection.fileno()) + if connection.is_writeable(): + writable.append(connection.fileno()) + if connection.is_closed(): + del self.clients[i] + return select.select(readable, writable, readable) + + def handle(self): + """Handle requests. + + WARNING! You must call prepare() BEFORE calling handle() + """ + assert self.prepared, "You have to call prepare before handle" + rset, wset, xset = self._select() + for readable in rset: + if readable == self._read.fileno(): + # don't care i just need to clean readable flag + self._read.recv(1024) + elif readable == self.socket.handle.fileno(): + client = self.socket.accept().handle + self.clients[client.fileno()] = Connection(client, + self.wake_up) + else: + connection = self.clients[readable] + connection.read() + if connection.status == WAIT_PROCESS: + itransport = TTransport.TMemoryBuffer(connection.message) + otransport = TTransport.TMemoryBuffer() + iprot = self.in_protocol.getProtocol(itransport) + oprot = self.out_protocol.getProtocol(otransport) + self.tasks.put([self.processor, iprot, oprot, + otransport, connection.ready]) + for writeable in wset: + self.clients[writeable].write() + for oob in xset: + self.clients[oob].close() + del self.clients[oob] + + def close(self): + """Closes the server.""" + for _ in range(self.threads): + self.tasks.put([None, None, None, None, None]) + self.socket.close() + self.prepared = False + + def serve(self): + """Serve requests. + + Serve requests forever, or until stop() is called. + """ + self._stop = False + self.prepare() + while not self._stop: + self.handle() diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/server/TProcessPoolServer.py b/vendor/src/github.com/apache/thrift/lib/py/src/server/TProcessPoolServer.py new file mode 100644 index 00000000..fe6dc816 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/server/TProcessPoolServer.py @@ -0,0 +1,123 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +import logging + +from multiprocessing import Process, Value, Condition + +from .TServer import TServer +from thrift.transport.TTransport import TTransportException + +logger = logging.getLogger(__name__) + + +class TProcessPoolServer(TServer): + """Server with a fixed size pool of worker subprocesses to service requests + + Note that if you need shared state between the handlers - it's up to you! + Written by Dvir Volk, doat.com + """ + def __init__(self, *args): + TServer.__init__(self, *args) + self.numWorkers = 10 + self.workers = [] + self.isRunning = Value('b', False) + self.stopCondition = Condition() + self.postForkCallback = None + + def setPostForkCallback(self, callback): + if not callable(callback): + raise TypeError("This is not a callback!") + self.postForkCallback = callback + + def setNumWorkers(self, num): + """Set the number of worker threads that should be created""" + self.numWorkers = num + + def workerProcess(self): + """Loop getting clients from the shared queue and process them""" + if self.postForkCallback: + self.postForkCallback() + + while self.isRunning.value: + try: + client = self.serverTransport.accept() + if not client: + continue + self.serveClient(client) + except (KeyboardInterrupt, SystemExit): + return 0 + except Exception as x: + logger.exception(x) + + def serveClient(self, client): + """Process input/output from a client for as long as possible""" + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + iprot = self.inputProtocolFactory.getProtocol(itrans) + oprot = self.outputProtocolFactory.getProtocol(otrans) + + try: + while True: + self.processor.process(iprot, oprot) + except TTransportException: + pass + except Exception as x: + logger.exception(x) + + itrans.close() + otrans.close() + + def serve(self): + """Start workers and put into queue""" + # this is a shared state that can tell the workers to exit when False + self.isRunning.value = True + + # first bind and listen to the port + self.serverTransport.listen() + + # fork the children + for i in range(self.numWorkers): + try: + w = Process(target=self.workerProcess) + w.daemon = True + w.start() + self.workers.append(w) + except Exception as x: + logger.exception(x) + + # wait until the condition is set by stop() + while True: + self.stopCondition.acquire() + try: + self.stopCondition.wait() + break + except (SystemExit, KeyboardInterrupt): + break + except Exception as x: + logger.exception(x) + + self.isRunning.value = False + + def stop(self): + self.isRunning.value = False + self.stopCondition.acquire() + self.stopCondition.notify() + self.stopCondition.release() diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/server/TServer.py b/vendor/src/github.com/apache/thrift/lib/py/src/server/TServer.py new file mode 100644 index 00000000..d5d9c98a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/server/TServer.py @@ -0,0 +1,276 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from six.moves import queue +import logging +import os +import threading + +from thrift.protocol import TBinaryProtocol +from thrift.transport import TTransport + +logger = logging.getLogger(__name__) + + +class TServer(object): + """Base interface for a server, which must have a serve() method. + + Three constructors for all servers: + 1) (processor, serverTransport) + 2) (processor, serverTransport, transportFactory, protocolFactory) + 3) (processor, serverTransport, + inputTransportFactory, outputTransportFactory, + inputProtocolFactory, outputProtocolFactory) + """ + def __init__(self, *args): + if (len(args) == 2): + self.__initArgs__(args[0], args[1], + TTransport.TTransportFactoryBase(), + TTransport.TTransportFactoryBase(), + TBinaryProtocol.TBinaryProtocolFactory(), + TBinaryProtocol.TBinaryProtocolFactory()) + elif (len(args) == 4): + self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3]) + elif (len(args) == 6): + self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5]) + + def __initArgs__(self, processor, serverTransport, + inputTransportFactory, outputTransportFactory, + inputProtocolFactory, outputProtocolFactory): + self.processor = processor + self.serverTransport = serverTransport + self.inputTransportFactory = inputTransportFactory + self.outputTransportFactory = outputTransportFactory + self.inputProtocolFactory = inputProtocolFactory + self.outputProtocolFactory = outputProtocolFactory + + def serve(self): + pass + + +class TSimpleServer(TServer): + """Simple single-threaded server that just pumps around one transport.""" + + def __init__(self, *args): + TServer.__init__(self, *args) + + def serve(self): + self.serverTransport.listen() + while True: + client = self.serverTransport.accept() + if not client: + continue + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + iprot = self.inputProtocolFactory.getProtocol(itrans) + oprot = self.outputProtocolFactory.getProtocol(otrans) + try: + while True: + self.processor.process(iprot, oprot) + except TTransport.TTransportException: + pass + except Exception as x: + logger.exception(x) + + itrans.close() + otrans.close() + + +class TThreadedServer(TServer): + """Threaded server that spawns a new thread per each connection.""" + + def __init__(self, *args, **kwargs): + TServer.__init__(self, *args) + self.daemon = kwargs.get("daemon", False) + + def serve(self): + self.serverTransport.listen() + while True: + try: + client = self.serverTransport.accept() + if not client: + continue + t = threading.Thread(target=self.handle, args=(client,)) + t.setDaemon(self.daemon) + t.start() + except KeyboardInterrupt: + raise + except Exception as x: + logger.exception(x) + + def handle(self, client): + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + iprot = self.inputProtocolFactory.getProtocol(itrans) + oprot = self.outputProtocolFactory.getProtocol(otrans) + try: + while True: + self.processor.process(iprot, oprot) + except TTransport.TTransportException: + pass + except Exception as x: + logger.exception(x) + + itrans.close() + otrans.close() + + +class TThreadPoolServer(TServer): + """Server with a fixed size pool of threads which service requests.""" + + def __init__(self, *args, **kwargs): + TServer.__init__(self, *args) + self.clients = queue.Queue() + self.threads = 10 + self.daemon = kwargs.get("daemon", False) + + def setNumThreads(self, num): + """Set the number of worker threads that should be created""" + self.threads = num + + def serveThread(self): + """Loop around getting clients from the shared queue and process them.""" + while True: + try: + client = self.clients.get() + self.serveClient(client) + except Exception as x: + logger.exception(x) + + def serveClient(self, client): + """Process input/output from a client for as long as possible""" + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + iprot = self.inputProtocolFactory.getProtocol(itrans) + oprot = self.outputProtocolFactory.getProtocol(otrans) + try: + while True: + self.processor.process(iprot, oprot) + except TTransport.TTransportException: + pass + except Exception as x: + logger.exception(x) + + itrans.close() + otrans.close() + + def serve(self): + """Start a fixed number of worker threads and put client into a queue""" + for i in range(self.threads): + try: + t = threading.Thread(target=self.serveThread) + t.setDaemon(self.daemon) + t.start() + except Exception as x: + logger.exception(x) + + # Pump the socket for clients + self.serverTransport.listen() + while True: + try: + client = self.serverTransport.accept() + if not client: + continue + self.clients.put(client) + except Exception as x: + logger.exception(x) + + +class TForkingServer(TServer): + """A Thrift server that forks a new process for each request + + This is more scalable than the threaded server as it does not cause + GIL contention. + + Note that this has different semantics from the threading server. + Specifically, updates to shared variables will no longer be shared. + It will also not work on windows. + + This code is heavily inspired by SocketServer.ForkingMixIn in the + Python stdlib. + """ + def __init__(self, *args): + TServer.__init__(self, *args) + self.children = [] + + def serve(self): + def try_close(file): + try: + file.close() + except IOError as e: + logger.warning(e, exc_info=True) + + self.serverTransport.listen() + while True: + client = self.serverTransport.accept() + if not client: + continue + try: + pid = os.fork() + + if pid: # parent + # add before collect, otherwise you race w/ waitpid + self.children.append(pid) + self.collect_children() + + # Parent must close socket or the connection may not get + # closed promptly + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + try_close(itrans) + try_close(otrans) + else: + itrans = self.inputTransportFactory.getTransport(client) + otrans = self.outputTransportFactory.getTransport(client) + + iprot = self.inputProtocolFactory.getProtocol(itrans) + oprot = self.outputProtocolFactory.getProtocol(otrans) + + ecode = 0 + try: + try: + while True: + self.processor.process(iprot, oprot) + except TTransport.TTransportException: + pass + except Exception as e: + logger.exception(e) + ecode = 1 + finally: + try_close(itrans) + try_close(otrans) + + os._exit(ecode) + + except TTransport.TTransportException: + pass + except Exception as x: + logger.exception(x) + + def collect_children(self): + while self.children: + try: + pid, status = os.waitpid(0, os.WNOHANG) + except os.error: + pid = None + + if pid: + self.children.remove(pid) + else: + break diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/server/__init__.py b/vendor/src/github.com/apache/thrift/lib/py/src/server/__init__.py new file mode 100644 index 00000000..1bf6e254 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/server/__init__.py @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +__all__ = ['TServer', 'TNonblockingServer'] diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/THttpClient.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/THttpClient.py new file mode 100644 index 00000000..fb33421d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/THttpClient.py @@ -0,0 +1,194 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from io import BytesIO +import os +import socket +import sys +import warnings +import base64 + +from six.moves import urllib +from six.moves import http_client + +from .TTransport import TTransportBase +import six + + +class THttpClient(TTransportBase): + """Http implementation of TTransport base.""" + + def __init__(self, uri_or_host, port=None, path=None): + """THttpClient supports two different types constructor parameters. + + THttpClient(host, port, path) - deprecated + THttpClient(uri) + + Only the second supports https. + """ + if port is not None: + warnings.warn( + "Please use the THttpClient('http://host:port/path') syntax", + DeprecationWarning, + stacklevel=2) + self.host = uri_or_host + self.port = port + assert path + self.path = path + self.scheme = 'http' + else: + parsed = urllib.parse.urlparse(uri_or_host) + self.scheme = parsed.scheme + assert self.scheme in ('http', 'https') + if self.scheme == 'http': + self.port = parsed.port or http_client.HTTP_PORT + elif self.scheme == 'https': + self.port = parsed.port or http_client.HTTPS_PORT + self.host = parsed.hostname + self.path = parsed.path + if parsed.query: + self.path += '?%s' % parsed.query + try: + proxy = urllib.request.getproxies()[self.scheme] + except KeyError: + proxy = None + else: + if urllib.request.proxy_bypass(self.host): + proxy = None + if proxy: + parsed = urllib.parse.urlparse(proxy) + self.realhost = self.host + self.realport = self.port + self.host = parsed.hostname + self.port = parsed.port + self.proxy_auth = self.basic_proxy_auth_header(parsed) + else: + self.realhost = self.realport = self.proxy_auth = None + self.__wbuf = BytesIO() + self.__http = None + self.__http_response = None + self.__timeout = None + self.__custom_headers = None + + @staticmethod + def basic_proxy_auth_header(proxy): + if proxy is None or not proxy.username: + return None + ap = "%s:%s" % (urllib.parse.unquote(proxy.username), + urllib.parse.unquote(proxy.password)) + cr = base64.b64encode(ap).strip() + return "Basic " + cr + + def using_proxy(self): + return self.realhost is not None + + def open(self): + if self.scheme == 'http': + self.__http = http_client.HTTPConnection(self.host, self.port) + elif self.scheme == 'https': + self.__http = http_client.HTTPSConnection(self.host, self.port) + if self.using_proxy(): + self.__http.set_tunnel(self.realhost, self.realport, + {"Proxy-Authorization": self.proxy_auth}) + + def close(self): + self.__http.close() + self.__http = None + self.__http_response = None + + def isOpen(self): + return self.__http is not None + + def setTimeout(self, ms): + if not hasattr(socket, 'getdefaulttimeout'): + raise NotImplementedError + + if ms is None: + self.__timeout = None + else: + self.__timeout = ms / 1000.0 + + def setCustomHeaders(self, headers): + self.__custom_headers = headers + + def read(self, sz): + return self.__http_response.read(sz) + + def write(self, buf): + self.__wbuf.write(buf) + + def __withTimeout(f): + def _f(*args, **kwargs): + orig_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(args[0].__timeout) + try: + result = f(*args, **kwargs) + finally: + socket.setdefaulttimeout(orig_timeout) + return result + return _f + + def flush(self): + if self.isOpen(): + self.close() + self.open() + + # Pull data out of buffer + data = self.__wbuf.getvalue() + self.__wbuf = BytesIO() + + # HTTP request + if self.using_proxy() and self.scheme == "http": + # need full URL of real host for HTTP proxy here (HTTPS uses CONNECT tunnel) + self.__http.putrequest('POST', "http://%s:%s%s" % + (self.realhost, self.realport, self.path)) + else: + self.__http.putrequest('POST', self.path) + + # Write headers + self.__http.putheader('Content-Type', 'application/x-thrift') + self.__http.putheader('Content-Length', str(len(data))) + if self.using_proxy() and self.scheme == "http" and self.proxy_auth is not None: + self.__http.putheader("Proxy-Authorization", self.proxy_auth) + + if not self.__custom_headers or 'User-Agent' not in self.__custom_headers: + user_agent = 'Python/THttpClient' + script = os.path.basename(sys.argv[0]) + if script: + user_agent = '%s (%s)' % (user_agent, urllib.parse.quote(script)) + self.__http.putheader('User-Agent', user_agent) + + if self.__custom_headers: + for key, val in six.iteritems(self.__custom_headers): + self.__http.putheader(key, val) + + self.__http.endheaders() + + # Write payload + self.__http.send(data) + + # Get reply to flush the request + self.__http_response = self.__http.getresponse() + self.code = self.__http_response.status + self.message = self.__http_response.reason + self.headers = self.__http_response.msg + + # Decorate if we know how to timeout + if hasattr(socket, 'getdefaulttimeout'): + flush = __withTimeout(flush) diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSSLSocket.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSSLSocket.py new file mode 100644 index 00000000..0f7d45e9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSSLSocket.py @@ -0,0 +1,396 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import logging +import os +import socket +import ssl +import sys +import warnings + +from .sslcompat import _match_hostname, _match_has_ipaddress +from thrift.transport import TSocket +from thrift.transport.TTransport import TTransportException + +logger = logging.getLogger(__name__) +warnings.filterwarnings( + 'default', category=DeprecationWarning, module=__name__) + + +class TSSLBase(object): + # SSLContext is not available for Python < 2.7.9 + _has_ssl_context = sys.hexversion >= 0x020709F0 + + # ciphers argument is not available for Python < 2.7.0 + _has_ciphers = sys.hexversion >= 0x020700F0 + + # For pythoon >= 2.7.9, use latest TLS that both client and server + # supports. + # SSL 2.0 and 3.0 are disabled via ssl.OP_NO_SSLv2 and ssl.OP_NO_SSLv3. + # For pythoon < 2.7.9, use TLS 1.0 since TLSv1_X nor OP_NO_SSLvX is + # unavailable. + _default_protocol = ssl.PROTOCOL_SSLv23 if _has_ssl_context else \ + ssl.PROTOCOL_TLSv1 + + def _init_context(self, ssl_version): + if self._has_ssl_context: + self._context = ssl.SSLContext(ssl_version) + if self._context.protocol == ssl.PROTOCOL_SSLv23: + self._context.options |= ssl.OP_NO_SSLv2 + self._context.options |= ssl.OP_NO_SSLv3 + else: + self._context = None + self._ssl_version = ssl_version + + @property + def _should_verify(self): + if self._has_ssl_context: + return self._context.verify_mode != ssl.CERT_NONE + else: + return self.cert_reqs != ssl.CERT_NONE + + @property + def ssl_version(self): + if self._has_ssl_context: + return self.ssl_context.protocol + else: + return self._ssl_version + + @property + def ssl_context(self): + return self._context + + SSL_VERSION = _default_protocol + """ + Default SSL version. + For backword compatibility, it can be modified. + Use __init__ keywoard argument "ssl_version" instead. + """ + + def _deprecated_arg(self, args, kwargs, pos, key): + if len(args) <= pos: + return + real_pos = pos + 3 + warnings.warn( + '%dth positional argument is deprecated.' + 'please use keyward argument insteand.' + % real_pos, DeprecationWarning, stacklevel=3) + + if key in kwargs: + raise TypeError( + 'Duplicate argument: %dth argument and %s keyward argument.' + % (real_pos, key)) + kwargs[key] = args[pos] + + def _unix_socket_arg(self, host, port, args, kwargs): + key = 'unix_socket' + if host is None and port is None and len(args) == 1 and key not in kwargs: + kwargs[key] = args[0] + return True + return False + + def __getattr__(self, key): + if key == 'SSL_VERSION': + warnings.warn( + 'SSL_VERSION is deprecated.' + 'please use ssl_version attribute instead.', + DeprecationWarning, stacklevel=2) + return self.ssl_version + + def __init__(self, server_side, host, ssl_opts): + self._server_side = server_side + if TSSLBase.SSL_VERSION != self._default_protocol: + warnings.warn( + 'SSL_VERSION is deprecated.' + 'please use ssl_version keyward argument instead.', + DeprecationWarning, stacklevel=2) + self._context = ssl_opts.pop('ssl_context', None) + self._server_hostname = None + if not self._server_side: + self._server_hostname = ssl_opts.pop('server_hostname', host) + if self._context: + self._custom_context = True + if ssl_opts: + raise ValueError( + 'Incompatible arguments: ssl_context and %s' + % ' '.join(ssl_opts.keys())) + if not self._has_ssl_context: + raise ValueError( + 'ssl_context is not available for this version of Python') + else: + self._custom_context = False + ssl_version = ssl_opts.pop('ssl_version', TSSLBase.SSL_VERSION) + self._init_context(ssl_version) + self.cert_reqs = ssl_opts.pop('cert_reqs', ssl.CERT_REQUIRED) + self.ca_certs = ssl_opts.pop('ca_certs', None) + self.keyfile = ssl_opts.pop('keyfile', None) + self.certfile = ssl_opts.pop('certfile', None) + self.ciphers = ssl_opts.pop('ciphers', None) + + if ssl_opts: + raise ValueError( + 'Unknown keyword arguments: ', ' '.join(ssl_opts.keys())) + + if self._should_verify: + if not self.ca_certs: + raise ValueError( + 'ca_certs is needed when cert_reqs is not ssl.CERT_NONE') + if not os.access(self.ca_certs, os.R_OK): + raise IOError('Certificate Authority ca_certs file "%s" ' + 'is not readable, cannot validate SSL ' + 'certificates.' % (self.ca_certs)) + + @property + def certfile(self): + return self._certfile + + @certfile.setter + def certfile(self, certfile): + if self._server_side and not certfile: + raise ValueError('certfile is needed for server-side') + if certfile and not os.access(certfile, os.R_OK): + raise IOError('No such certfile found: %s' % (certfile)) + self._certfile = certfile + + def _wrap_socket(self, sock): + if self._has_ssl_context: + if not self._custom_context: + self.ssl_context.verify_mode = self.cert_reqs + if self.certfile: + self.ssl_context.load_cert_chain(self.certfile, + self.keyfile) + if self.ciphers: + self.ssl_context.set_ciphers(self.ciphers) + if self.ca_certs: + self.ssl_context.load_verify_locations(self.ca_certs) + return self.ssl_context.wrap_socket( + sock, server_side=self._server_side, + server_hostname=self._server_hostname) + else: + ssl_opts = { + 'ssl_version': self._ssl_version, + 'server_side': self._server_side, + 'ca_certs': self.ca_certs, + 'keyfile': self.keyfile, + 'certfile': self.certfile, + 'cert_reqs': self.cert_reqs, + } + if self.ciphers: + if self._has_ciphers: + ssl_opts['ciphers'] = self.ciphers + else: + logger.warning( + 'ciphers is specified but ignored due to old Python version') + return ssl.wrap_socket(sock, **ssl_opts) + + +class TSSLSocket(TSocket.TSocket, TSSLBase): + """ + SSL implementation of TSocket + + This class creates outbound sockets wrapped using the + python standard ssl module for encrypted connections. + """ + + # New signature + # def __init__(self, host='localhost', port=9090, unix_socket=None, + # **ssl_args): + # Deprecated signature + # def __init__(self, host='localhost', port=9090, validate=True, + # ca_certs=None, keyfile=None, certfile=None, + # unix_socket=None, ciphers=None): + def __init__(self, host='localhost', port=9090, *args, **kwargs): + """Positional arguments: ``host``, ``port``, ``unix_socket`` + + Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``, + ``ssl_version``, ``ca_certs``, + ``ciphers`` (Python 2.7.0 or later), + ``server_hostname`` (Python 2.7.9 or later) + Passed to ssl.wrap_socket. See ssl.wrap_socket documentation. + + Alternative keyword arguments: (Python 2.7.9 or later) + ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket + ``server_hostname``: Passed to SSLContext.wrap_socket + + Common keyword argument: + ``validate_callback`` (cert, hostname) -> None: + Called after SSL handshake. Can raise when hostname does not + match the cert. + """ + self.is_valid = False + self.peercert = None + + if args: + if len(args) > 6: + raise TypeError('Too many positional argument') + if not self._unix_socket_arg(host, port, args, kwargs): + self._deprecated_arg(args, kwargs, 0, 'validate') + self._deprecated_arg(args, kwargs, 1, 'ca_certs') + self._deprecated_arg(args, kwargs, 2, 'keyfile') + self._deprecated_arg(args, kwargs, 3, 'certfile') + self._deprecated_arg(args, kwargs, 4, 'unix_socket') + self._deprecated_arg(args, kwargs, 5, 'ciphers') + + validate = kwargs.pop('validate', None) + if validate is not None: + cert_reqs_name = 'CERT_REQUIRED' if validate else 'CERT_NONE' + warnings.warn( + 'validate is deprecated. please use cert_reqs=ssl.%s instead' + % cert_reqs_name, + DeprecationWarning, stacklevel=2) + if 'cert_reqs' in kwargs: + raise TypeError('Cannot specify both validate and cert_reqs') + kwargs['cert_reqs'] = ssl.CERT_REQUIRED if validate else ssl.CERT_NONE + + unix_socket = kwargs.pop('unix_socket', None) + self._validate_callback = kwargs.pop('validate_callback', _match_hostname) + TSSLBase.__init__(self, False, host, kwargs) + TSocket.TSocket.__init__(self, host, port, unix_socket) + + @property + def validate(self): + warnings.warn('validate is deprecated. please use cert_reqs instead', + DeprecationWarning, stacklevel=2) + return self.cert_reqs != ssl.CERT_NONE + + @validate.setter + def validate(self, value): + warnings.warn('validate is deprecated. please use cert_reqs instead', + DeprecationWarning, stacklevel=2) + self.cert_reqs = ssl.CERT_REQUIRED if value else ssl.CERT_NONE + + def _do_open(self, family, socktype): + plain_sock = socket.socket(family, socktype) + try: + return self._wrap_socket(plain_sock) + except Exception: + plain_sock.close() + msg = 'failed to initialize SSL' + logger.exception(msg) + raise TTransportException(TTransportException.NOT_OPEN, msg) + + def open(self): + super(TSSLSocket, self).open() + if self._should_verify: + self.peercert = self.handle.getpeercert() + try: + self._validate_callback(self.peercert, self._server_hostname) + self.is_valid = True + except TTransportException: + raise + except Exception as ex: + raise TTransportException(TTransportException.UNKNOWN, str(ex)) + + +class TSSLServerSocket(TSocket.TServerSocket, TSSLBase): + """SSL implementation of TServerSocket + + This uses the ssl module's wrap_socket() method to provide SSL + negotiated encryption. + """ + + # New signature + # def __init__(self, host='localhost', port=9090, unix_socket=None, **ssl_args): + # Deprecated signature + # def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None, ciphers=None): + def __init__(self, host=None, port=9090, *args, **kwargs): + """Positional arguments: ``host``, ``port``, ``unix_socket`` + + Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``, ``ssl_version``, + ``ca_certs``, ``ciphers`` (Python 2.7.0 or later) + See ssl.wrap_socket documentation. + + Alternative keyword arguments: (Python 2.7.9 or later) + ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket + ``server_hostname``: Passed to SSLContext.wrap_socket + + Common keyword argument: + ``validate_callback`` (cert, hostname) -> None: + Called after SSL handshake. Can raise when hostname does not + match the cert. + """ + if args: + if len(args) > 3: + raise TypeError('Too many positional argument') + if not self._unix_socket_arg(host, port, args, kwargs): + self._deprecated_arg(args, kwargs, 0, 'certfile') + self._deprecated_arg(args, kwargs, 1, 'unix_socket') + self._deprecated_arg(args, kwargs, 2, 'ciphers') + + if 'ssl_context' not in kwargs: + # Preserve existing behaviors for default values + if 'cert_reqs' not in kwargs: + kwargs['cert_reqs'] = ssl.CERT_NONE + if'certfile' not in kwargs: + kwargs['certfile'] = 'cert.pem' + + unix_socket = kwargs.pop('unix_socket', None) + self._validate_callback = \ + kwargs.pop('validate_callback', _match_hostname) + TSSLBase.__init__(self, True, None, kwargs) + TSocket.TServerSocket.__init__(self, host, port, unix_socket) + if self._should_verify and not _match_has_ipaddress: + raise ValueError('Need ipaddress and backports.ssl_match_hostname ' + 'module to verify client certificate') + + def setCertfile(self, certfile): + """Set or change the server certificate file used to wrap new + connections. + + @param certfile: The filename of the server certificate, + i.e. '/etc/certs/server.pem' + @type certfile: str + + Raises an IOError exception if the certfile is not present or unreadable. + """ + warnings.warn( + 'setCertfile is deprecated. please use certfile property instead.', + DeprecationWarning, stacklevel=2) + self.certfile = certfile + + def accept(self): + plain_client, addr = self.handle.accept() + try: + client = self._wrap_socket(plain_client) + except ssl.SSLError: + logger.exception('Error while accepting from %s', addr) + # failed handshake/ssl wrap, close socket to client + plain_client.close() + # raise + # We can't raise the exception, because it kills most TServer derived + # serve() methods. + # Instead, return None, and let the TServer instance deal with it in + # other exception handling. (but TSimpleServer dies anyway) + return None + + if self._should_verify: + client.peercert = client.getpeercert() + try: + self._validate_callback(client.peercert, addr[0]) + client.is_valid = True + except Exception: + logger.warn('Failed to validate client certificate address: %s', + addr[0], exc_info=True) + client.close() + plain_client.close() + return None + + result = TSocket.TSocket() + result.handle = client + return result diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSocket.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSocket.py new file mode 100644 index 00000000..c91de9d7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TSocket.py @@ -0,0 +1,192 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import errno +import logging +import os +import socket +import sys + +from .TTransport import TTransportBase, TTransportException, TServerTransportBase + +logger = logging.getLogger(__name__) + + +class TSocketBase(TTransportBase): + def _resolveAddr(self): + if self._unix_socket is not None: + return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None, + self._unix_socket)] + else: + return socket.getaddrinfo(self.host, + self.port, + self._socket_family, + socket.SOCK_STREAM, + 0, + socket.AI_PASSIVE | socket.AI_ADDRCONFIG) + + def close(self): + if self.handle: + self.handle.close() + self.handle = None + + +class TSocket(TSocketBase): + """Socket implementation of TTransport base.""" + + def __init__(self, host='localhost', port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC): + """Initialize a TSocket + + @param host(str) The host to connect to. + @param port(int) The (TCP) port to connect to. + @param unix_socket(str) The filename of a unix socket to connect to. + (host and port will be ignored.) + @param socket_family(int) The socket family to use with this socket. + """ + self.host = host + self.port = port + self.handle = None + self._unix_socket = unix_socket + self._timeout = None + self._socket_family = socket_family + + def setHandle(self, h): + self.handle = h + + def isOpen(self): + return self.handle is not None + + def setTimeout(self, ms): + if ms is None: + self._timeout = None + else: + self._timeout = ms / 1000.0 + + if self.handle is not None: + self.handle.settimeout(self._timeout) + + def _do_open(self, family, socktype): + return socket.socket(family, socktype) + + @property + def _address(self): + return self._unix_socket if self._unix_socket else '%s:%d' % (self.host, self.port) + + def open(self): + if self.handle: + raise TTransportException(TTransportException.ALREADY_OPEN) + try: + addrs = self._resolveAddr() + except socket.gaierror: + msg = 'failed to resolve sockaddr for ' + str(self._address) + logger.exception(msg) + raise TTransportException(TTransportException.NOT_OPEN, msg) + for family, socktype, _, _, sockaddr in addrs: + handle = self._do_open(family, socktype) + handle.settimeout(self._timeout) + try: + handle.connect(sockaddr) + self.handle = handle + return + except socket.error: + handle.close() + logger.info('Could not connect to %s', sockaddr, exc_info=True) + msg = 'Could not connect to any of %s' % list(map(lambda a: a[4], + addrs)) + logger.error(msg) + raise TTransportException(TTransportException.NOT_OPEN, msg) + + def read(self, sz): + try: + buff = self.handle.recv(sz) + except socket.error as e: + if (e.args[0] == errno.ECONNRESET and + (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))): + # freebsd and Mach don't follow POSIX semantic of recv + # and fail with ECONNRESET if peer performed shutdown. + # See corresponding comment and code in TSocket::read() + # in lib/cpp/src/transport/TSocket.cpp. + self.close() + # Trigger the check to raise the END_OF_FILE exception below. + buff = '' + else: + raise + if len(buff) == 0: + raise TTransportException(type=TTransportException.END_OF_FILE, + message='TSocket read 0 bytes') + return buff + + def write(self, buff): + if not self.handle: + raise TTransportException(type=TTransportException.NOT_OPEN, + message='Transport not open') + sent = 0 + have = len(buff) + while sent < have: + 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:] + + def flush(self): + pass + + +class TServerSocket(TSocketBase, TServerTransportBase): + """Socket implementation of TServerTransport base.""" + + def __init__(self, host=None, port=9090, unix_socket=None, socket_family=socket.AF_UNSPEC): + self.host = host + self.port = port + self._unix_socket = unix_socket + self._socket_family = socket_family + self.handle = None + + def listen(self): + res0 = self._resolveAddr() + socket_family = self._socket_family == socket.AF_UNSPEC and socket.AF_INET6 or self._socket_family + for res in res0: + if res[0] is socket_family or res is res0[-1]: + break + + # We need remove the old unix socket if the file exists and + # nobody is listening on it. + if self._unix_socket: + tmp = socket.socket(res[0], res[1]) + try: + tmp.connect(res[4]) + except socket.error as err: + eno, message = err.args + if eno == errno.ECONNREFUSED: + os.unlink(res[4]) + + self.handle = socket.socket(res[0], res[1]) + self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if hasattr(self.handle, 'settimeout'): + self.handle.settimeout(None) + self.handle.bind(res[4]) + self.handle.listen(128) + + def accept(self): + client, addr = self.handle.accept() + result = TSocket() + result.setHandle(client) + return result diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTransport.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTransport.py new file mode 100644 index 00000000..5283fce4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTransport.py @@ -0,0 +1,452 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from struct import pack, unpack +from thrift.Thrift import TException +from ..compat import BufferIO + + +class TTransportException(TException): + """Custom Transport Exception class""" + + UNKNOWN = 0 + NOT_OPEN = 1 + ALREADY_OPEN = 2 + TIMED_OUT = 3 + END_OF_FILE = 4 + NEGATIVE_SIZE = 5 + SIZE_LIMIT = 6 + + def __init__(self, type=UNKNOWN, message=None): + TException.__init__(self, message) + self.type = type + + +class TTransportBase(object): + """Base class for Thrift transport layer.""" + + def isOpen(self): + pass + + def open(self): + pass + + def close(self): + pass + + def read(self, sz): + pass + + def readAll(self, sz): + buff = b'' + have = 0 + while (have < sz): + chunk = self.read(sz - have) + have += len(chunk) + buff += chunk + + if len(chunk) == 0: + raise EOFError() + + return buff + + def write(self, buf): + pass + + def flush(self): + pass + + +# This class should be thought of as an interface. +class CReadableTransport(object): + """base class for transports that are readable from C""" + + # TODO(dreiss): Think about changing this interface to allow us to use + # a (Python, not c) StringIO instead, because it allows + # you to write after reading. + + # NOTE: This is a classic class, so properties will NOT work + # correctly for setting. + @property + def cstringio_buf(self): + """A cStringIO buffer that contains the current chunk we are reading.""" + pass + + def cstringio_refill(self, partialread, reqlen): + """Refills cstringio_buf. + + Returns the currently used buffer (which can but need not be the same as + the old cstringio_buf). partialread is what the C code has read from the + buffer, and should be inserted into the buffer before any more reads. The + return value must be a new, not borrowed reference. Something along the + lines of self._buf should be fine. + + If reqlen bytes can't be read, throw EOFError. + """ + pass + + +class TServerTransportBase(object): + """Base class for Thrift server transports.""" + + def listen(self): + pass + + def accept(self): + pass + + def close(self): + pass + + +class TTransportFactoryBase(object): + """Base class for a Transport Factory""" + + def getTransport(self, trans): + return trans + + +class TBufferedTransportFactory(object): + """Factory transport that builds buffered transports""" + + def getTransport(self, trans): + buffered = TBufferedTransport(trans) + return buffered + + +class TBufferedTransport(TTransportBase, CReadableTransport): + """Class that wraps another transport and buffers its I/O. + + The implementation uses a (configurable) fixed-size read buffer + but buffers all writes until a flush is performed. + """ + DEFAULT_BUFFER = 4096 + + def __init__(self, trans, rbuf_size=DEFAULT_BUFFER): + self.__trans = trans + self.__wbuf = BufferIO() + # Pass string argument to initialize read buffer as cStringIO.InputType + self.__rbuf = BufferIO(b'') + self.__rbuf_size = rbuf_size + + def isOpen(self): + return self.__trans.isOpen() + + def open(self): + return self.__trans.open() + + def close(self): + return self.__trans.close() + + def read(self, sz): + ret = self.__rbuf.read(sz) + if len(ret) != 0: + return ret + self.__rbuf = BufferIO(self.__trans.read(max(sz, self.__rbuf_size))) + return self.__rbuf.read(sz) + + def write(self, buf): + try: + self.__wbuf.write(buf) + except Exception as e: + # on exception reset wbuf so it doesn't contain a partial function call + self.__wbuf = BufferIO() + raise e + self.__wbuf.getvalue() + + def flush(self): + out = self.__wbuf.getvalue() + # reset wbuf before write/flush to preserve state on underlying failure + self.__wbuf = BufferIO() + self.__trans.write(out) + self.__trans.flush() + + # Implement the CReadableTransport interface. + @property + def cstringio_buf(self): + return self.__rbuf + + def cstringio_refill(self, partialread, reqlen): + retstring = partialread + if reqlen < self.__rbuf_size: + # try to make a read of as much as we can. + retstring += self.__trans.read(self.__rbuf_size) + + # but make sure we do read reqlen bytes. + if len(retstring) < reqlen: + retstring += self.__trans.readAll(reqlen - len(retstring)) + + self.__rbuf = BufferIO(retstring) + return self.__rbuf + + +class TMemoryBuffer(TTransportBase, CReadableTransport): + """Wraps a cBytesIO object as a TTransport. + + NOTE: Unlike the C++ version of this class, you cannot write to it + then immediately read from it. If you want to read from a + TMemoryBuffer, you must either pass a string to the constructor. + TODO(dreiss): Make this work like the C++ version. + """ + + def __init__(self, value=None): + """value -- a value to read from for stringio + + If value is set, this will be a transport for reading, + otherwise, it is for writing""" + if value is not None: + self._buffer = BufferIO(value) + else: + self._buffer = BufferIO() + + def isOpen(self): + return not self._buffer.closed + + def open(self): + pass + + def close(self): + self._buffer.close() + + def read(self, sz): + return self._buffer.read(sz) + + def write(self, buf): + self._buffer.write(buf) + + def flush(self): + pass + + def getvalue(self): + return self._buffer.getvalue() + + # Implement the CReadableTransport interface. + @property + def cstringio_buf(self): + return self._buffer + + def cstringio_refill(self, partialread, reqlen): + # only one shot at reading... + raise EOFError() + + +class TFramedTransportFactory(object): + """Factory transport that builds framed transports""" + + def getTransport(self, trans): + framed = TFramedTransport(trans) + return framed + + +class TFramedTransport(TTransportBase, CReadableTransport): + """Class that wraps another transport and frames its I/O when writing.""" + + def __init__(self, trans,): + self.__trans = trans + self.__rbuf = BufferIO(b'') + self.__wbuf = BufferIO() + + def isOpen(self): + return self.__trans.isOpen() + + def open(self): + return self.__trans.open() + + def close(self): + return self.__trans.close() + + def read(self, sz): + ret = self.__rbuf.read(sz) + if len(ret) != 0: + return ret + + self.readFrame() + return self.__rbuf.read(sz) + + def readFrame(self): + buff = self.__trans.readAll(4) + sz, = unpack('!i', buff) + self.__rbuf = BufferIO(self.__trans.readAll(sz)) + + def write(self, buf): + self.__wbuf.write(buf) + + def flush(self): + wout = self.__wbuf.getvalue() + wsz = len(wout) + # reset wbuf before write/flush to preserve state on underlying failure + self.__wbuf = BufferIO() + # N.B.: Doing this string concatenation is WAY cheaper than making + # two separate calls to the underlying socket object. Socket writes in + # Python turn out to be REALLY expensive, but it seems to do a pretty + # good job of managing string buffer operations without excessive copies + buf = pack("!i", wsz) + wout + self.__trans.write(buf) + self.__trans.flush() + + # Implement the CReadableTransport interface. + @property + def cstringio_buf(self): + return self.__rbuf + + def cstringio_refill(self, prefix, reqlen): + # self.__rbuf will already be empty here because fastbinary doesn't + # ask for a refill until the previous buffer is empty. Therefore, + # we can start reading new frames immediately. + while len(prefix) < reqlen: + self.readFrame() + prefix += self.__rbuf.getvalue() + self.__rbuf = BufferIO(prefix) + return self.__rbuf + + +class TFileObjectTransport(TTransportBase): + """Wraps a file-like object to make it work as a Thrift transport.""" + + def __init__(self, fileobj): + self.fileobj = fileobj + + def isOpen(self): + return True + + def close(self): + self.fileobj.close() + + def read(self, sz): + return self.fileobj.read(sz) + + def write(self, buf): + self.fileobj.write(buf) + + def flush(self): + self.fileobj.flush() + + +class TSaslClientTransport(TTransportBase, CReadableTransport): + """ + SASL transport + """ + + START = 1 + OK = 2 + BAD = 3 + ERROR = 4 + COMPLETE = 5 + + def __init__(self, transport, host, service, mechanism='GSSAPI', + **sasl_kwargs): + """ + transport: an underlying transport to use, typically just a TSocket + host: the name of the server, from a SASL perspective + service: the name of the server's service, from a SASL perspective + mechanism: the name of the preferred mechanism to use + + All other kwargs will be passed to the puresasl.client.SASLClient + constructor. + """ + + from puresasl.client import SASLClient + + self.transport = transport + self.sasl = SASLClient(host, service, mechanism, **sasl_kwargs) + + self.__wbuf = BufferIO() + self.__rbuf = BufferIO(b'') + + def open(self): + if not self.transport.isOpen(): + self.transport.open() + + self.send_sasl_msg(self.START, self.sasl.mechanism) + self.send_sasl_msg(self.OK, self.sasl.process()) + + while True: + status, challenge = self.recv_sasl_msg() + if status == self.OK: + self.send_sasl_msg(self.OK, self.sasl.process(challenge)) + elif status == self.COMPLETE: + if not self.sasl.complete: + raise TTransportException( + TTransportException.NOT_OPEN, + "The server erroneously indicated " + "that SASL negotiation was complete") + else: + break + else: + raise TTransportException( + TTransportException.NOT_OPEN, + "Bad SASL negotiation status: %d (%s)" + % (status, challenge)) + + def send_sasl_msg(self, status, body): + header = pack(">BI", status, len(body)) + self.transport.write(header + body) + self.transport.flush() + + def recv_sasl_msg(self): + header = self.transport.readAll(5) + status, length = unpack(">BI", header) + if length > 0: + payload = self.transport.readAll(length) + else: + payload = "" + return status, payload + + def write(self, data): + self.__wbuf.write(data) + + def flush(self): + data = self.__wbuf.getvalue() + encoded = self.sasl.wrap(data) + self.transport.write(''.join((pack("!i", len(encoded)), encoded))) + self.transport.flush() + self.__wbuf = BufferIO() + + def read(self, sz): + ret = self.__rbuf.read(sz) + if len(ret) != 0: + return ret + + self._read_frame() + return self.__rbuf.read(sz) + + def _read_frame(self): + header = self.transport.readAll(4) + length, = unpack('!i', header) + encoded = self.transport.readAll(length) + self.__rbuf = BufferIO(self.sasl.unwrap(encoded)) + + def close(self): + self.sasl.dispose() + self.transport.close() + + # based on TFramedTransport + @property + def cstringio_buf(self): + return self.__rbuf + + def cstringio_refill(self, prefix, reqlen): + # self.__rbuf will already be empty here because fastbinary doesn't + # ask for a refill until the previous buffer is empty. Therefore, + # we can start reading new frames immediately. + while len(prefix) < reqlen: + self._read_frame() + prefix += self.__rbuf.getvalue() + self.__rbuf = BufferIO(prefix) + return self.__rbuf diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTwisted.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTwisted.py new file mode 100644 index 00000000..5710b573 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TTwisted.py @@ -0,0 +1,331 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from io import BytesIO +import struct + +from zope.interface import implements, Interface, Attribute +from twisted.internet.protocol import ServerFactory, ClientFactory, \ + connectionDone +from twisted.internet import defer +from twisted.internet.threads import deferToThread +from twisted.protocols import basic +from twisted.web import server, resource, http + +from thrift.transport import TTransport + + +class TMessageSenderTransport(TTransport.TTransportBase): + + def __init__(self): + self.__wbuf = BytesIO() + + def write(self, buf): + self.__wbuf.write(buf) + + def flush(self): + msg = self.__wbuf.getvalue() + self.__wbuf = BytesIO() + return self.sendMessage(msg) + + def sendMessage(self, message): + raise NotImplementedError + + +class TCallbackTransport(TMessageSenderTransport): + + def __init__(self, func): + TMessageSenderTransport.__init__(self) + self.func = func + + def sendMessage(self, message): + return self.func(message) + + +class ThriftClientProtocol(basic.Int32StringReceiver): + + MAX_LENGTH = 2 ** 31 - 1 + + def __init__(self, client_class, iprot_factory, oprot_factory=None): + self._client_class = client_class + self._iprot_factory = iprot_factory + if oprot_factory is None: + self._oprot_factory = iprot_factory + else: + self._oprot_factory = oprot_factory + + self.recv_map = {} + self.started = defer.Deferred() + + def dispatch(self, msg): + self.sendString(msg) + + def connectionMade(self): + tmo = TCallbackTransport(self.dispatch) + self.client = self._client_class(tmo, self._oprot_factory) + self.started.callback(self.client) + + def connectionLost(self, reason=connectionDone): + # the called errbacks can add items to our client's _reqs, + # so we need to use a tmp, and iterate until no more requests + # are added during errbacks + if self.client: + tex = TTransport.TTransportException( + type=TTransport.TTransportException.END_OF_FILE, + message='Connection closed (%s)' % reason) + while self.client._reqs: + _, v = self.client._reqs.popitem() + v.errback(tex) + del self.client._reqs + self.client = None + + def stringReceived(self, frame): + tr = TTransport.TMemoryBuffer(frame) + iprot = self._iprot_factory.getProtocol(tr) + (fname, mtype, rseqid) = iprot.readMessageBegin() + + try: + method = self.recv_map[fname] + except KeyError: + method = getattr(self.client, 'recv_' + fname) + self.recv_map[fname] = method + + method(iprot, mtype, rseqid) + + +class ThriftSASLClientProtocol(ThriftClientProtocol): + + START = 1 + OK = 2 + BAD = 3 + ERROR = 4 + COMPLETE = 5 + + MAX_LENGTH = 2 ** 31 - 1 + + def __init__(self, client_class, iprot_factory, oprot_factory=None, + host=None, service=None, mechanism='GSSAPI', **sasl_kwargs): + """ + host: the name of the server, from a SASL perspective + service: the name of the server's service, from a SASL perspective + mechanism: the name of the preferred mechanism to use + + All other kwargs will be passed to the puresasl.client.SASLClient + constructor. + """ + + from puresasl.client import SASLClient + self.SASLCLient = SASLClient + + ThriftClientProtocol.__init__(self, client_class, iprot_factory, oprot_factory) + + self._sasl_negotiation_deferred = None + self._sasl_negotiation_status = None + self.client = None + + if host is not None: + self.createSASLClient(host, service, mechanism, **sasl_kwargs) + + def createSASLClient(self, host, service, mechanism, **kwargs): + self.sasl = self.SASLClient(host, service, mechanism, **kwargs) + + def dispatch(self, msg): + encoded = self.sasl.wrap(msg) + len_and_encoded = ''.join((struct.pack('!i', len(encoded)), encoded)) + ThriftClientProtocol.dispatch(self, len_and_encoded) + + @defer.inlineCallbacks + def connectionMade(self): + self._sendSASLMessage(self.START, self.sasl.mechanism) + initial_message = yield deferToThread(self.sasl.process) + self._sendSASLMessage(self.OK, initial_message) + + while True: + status, challenge = yield self._receiveSASLMessage() + if status == self.OK: + response = yield deferToThread(self.sasl.process, challenge) + self._sendSASLMessage(self.OK, response) + elif status == self.COMPLETE: + if not self.sasl.complete: + msg = "The server erroneously indicated that SASL " \ + "negotiation was complete" + raise TTransport.TTransportException(msg, message=msg) + else: + break + else: + msg = "Bad SASL negotiation status: %d (%s)" % (status, challenge) + raise TTransport.TTransportException(msg, message=msg) + + self._sasl_negotiation_deferred = None + ThriftClientProtocol.connectionMade(self) + + def _sendSASLMessage(self, status, body): + if body is None: + body = "" + header = struct.pack(">BI", status, len(body)) + self.transport.write(header + body) + + def _receiveSASLMessage(self): + self._sasl_negotiation_deferred = defer.Deferred() + self._sasl_negotiation_status = None + return self._sasl_negotiation_deferred + + def connectionLost(self, reason=connectionDone): + if self.client: + ThriftClientProtocol.connectionLost(self, reason) + + def dataReceived(self, data): + if self._sasl_negotiation_deferred: + # we got a sasl challenge in the format (status, length, challenge) + # save the status, let IntNStringReceiver piece the challenge data together + self._sasl_negotiation_status, = struct.unpack("B", data[0]) + ThriftClientProtocol.dataReceived(self, data[1:]) + else: + # normal frame, let IntNStringReceiver piece it together + ThriftClientProtocol.dataReceived(self, data) + + def stringReceived(self, frame): + if self._sasl_negotiation_deferred: + # the frame is just a SASL challenge + response = (self._sasl_negotiation_status, frame) + self._sasl_negotiation_deferred.callback(response) + else: + # there's a second 4 byte length prefix inside the frame + decoded_frame = self.sasl.unwrap(frame[4:]) + ThriftClientProtocol.stringReceived(self, decoded_frame) + + +class ThriftServerProtocol(basic.Int32StringReceiver): + + MAX_LENGTH = 2 ** 31 - 1 + + def dispatch(self, msg): + self.sendString(msg) + + def processError(self, error): + self.transport.loseConnection() + + def processOk(self, _, tmo): + msg = tmo.getvalue() + + if len(msg) > 0: + self.dispatch(msg) + + def stringReceived(self, frame): + tmi = TTransport.TMemoryBuffer(frame) + tmo = TTransport.TMemoryBuffer() + + iprot = self.factory.iprot_factory.getProtocol(tmi) + oprot = self.factory.oprot_factory.getProtocol(tmo) + + d = self.factory.processor.process(iprot, oprot) + d.addCallbacks(self.processOk, self.processError, + callbackArgs=(tmo,)) + + +class IThriftServerFactory(Interface): + + processor = Attribute("Thrift processor") + + iprot_factory = Attribute("Input protocol factory") + + oprot_factory = Attribute("Output protocol factory") + + +class IThriftClientFactory(Interface): + + client_class = Attribute("Thrift client class") + + iprot_factory = Attribute("Input protocol factory") + + oprot_factory = Attribute("Output protocol factory") + + +class ThriftServerFactory(ServerFactory): + + implements(IThriftServerFactory) + + protocol = ThriftServerProtocol + + def __init__(self, processor, iprot_factory, oprot_factory=None): + self.processor = processor + self.iprot_factory = iprot_factory + if oprot_factory is None: + self.oprot_factory = iprot_factory + else: + self.oprot_factory = oprot_factory + + +class ThriftClientFactory(ClientFactory): + + implements(IThriftClientFactory) + + protocol = ThriftClientProtocol + + def __init__(self, client_class, iprot_factory, oprot_factory=None): + self.client_class = client_class + self.iprot_factory = iprot_factory + if oprot_factory is None: + self.oprot_factory = iprot_factory + else: + self.oprot_factory = oprot_factory + + def buildProtocol(self, addr): + p = self.protocol(self.client_class, self.iprot_factory, + self.oprot_factory) + p.factory = self + return p + + +class ThriftResource(resource.Resource): + + allowedMethods = ('POST',) + + def __init__(self, processor, inputProtocolFactory, + outputProtocolFactory=None): + resource.Resource.__init__(self) + self.inputProtocolFactory = inputProtocolFactory + if outputProtocolFactory is None: + self.outputProtocolFactory = inputProtocolFactory + else: + self.outputProtocolFactory = outputProtocolFactory + self.processor = processor + + def getChild(self, path, request): + return self + + def _cbProcess(self, _, request, tmo): + msg = tmo.getvalue() + request.setResponseCode(http.OK) + request.setHeader("content-type", "application/x-thrift") + request.write(msg) + request.finish() + + def render_POST(self, request): + request.content.seek(0, 0) + data = request.content.read() + tmi = TTransport.TMemoryBuffer(data) + tmo = TTransport.TMemoryBuffer() + + iprot = self.inputProtocolFactory.getProtocol(tmi) + oprot = self.outputProtocolFactory.getProtocol(tmo) + + d = self.processor.process(iprot, oprot) + d.addCallback(self._cbProcess, request, tmo) + return server.NOT_DONE_YET diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/TZlibTransport.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TZlibTransport.py new file mode 100644 index 00000000..e8485792 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/TZlibTransport.py @@ -0,0 +1,248 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +"""TZlibTransport provides a compressed transport and transport factory +class, using the python standard library zlib module to implement +data compression. +""" + +from __future__ import division +import zlib +from .TTransport import TTransportBase, CReadableTransport +from ..compat import BufferIO + + +class TZlibTransportFactory(object): + """Factory transport that builds zlib compressed transports. + + This factory caches the last single client/transport that it was passed + and returns the same TZlibTransport object that was created. + + This caching means the TServer class will get the _same_ transport + object for both input and output transports from this factory. + (For non-threaded scenarios only, since the cache only holds one object) + + The purpose of this caching is to allocate only one TZlibTransport where + only one is really needed (since it must have separate read/write buffers), + and makes the statistics from getCompSavings() and getCompRatio() + easier to understand. + """ + # class scoped cache of last transport given and zlibtransport returned + _last_trans = None + _last_z = None + + def getTransport(self, trans, compresslevel=9): + """Wrap a transport, trans, with the TZlibTransport + compressed transport class, returning a new + transport to the caller. + + @param compresslevel: The zlib compression level, ranging + from 0 (no compression) to 9 (best compression). Defaults to 9. + @type compresslevel: int + + This method returns a TZlibTransport which wraps the + passed C{trans} TTransport derived instance. + """ + if trans == self._last_trans: + return self._last_z + ztrans = TZlibTransport(trans, compresslevel) + self._last_trans = trans + self._last_z = ztrans + return ztrans + + +class TZlibTransport(TTransportBase, CReadableTransport): + """Class that wraps a transport with zlib, compressing writes + and decompresses reads, using the python standard + library zlib module. + """ + # Read buffer size for the python fastbinary C extension, + # the TBinaryProtocolAccelerated class. + DEFAULT_BUFFSIZE = 4096 + + def __init__(self, trans, compresslevel=9): + """Create a new TZlibTransport, wrapping C{trans}, another + TTransport derived object. + + @param trans: A thrift transport object, i.e. a TSocket() object. + @type trans: TTransport + @param compresslevel: The zlib compression level, ranging + from 0 (no compression) to 9 (best compression). Default is 9. + @type compresslevel: int + """ + self.__trans = trans + self.compresslevel = compresslevel + self.__rbuf = BufferIO() + self.__wbuf = BufferIO() + self._init_zlib() + self._init_stats() + + def _reinit_buffers(self): + """Internal method to initialize/reset the internal StringIO objects + for read and write buffers. + """ + self.__rbuf = BufferIO() + self.__wbuf = BufferIO() + + def _init_stats(self): + """Internal method to reset the internal statistics counters + for compression ratios and bandwidth savings. + """ + self.bytes_in = 0 + self.bytes_out = 0 + self.bytes_in_comp = 0 + self.bytes_out_comp = 0 + + def _init_zlib(self): + """Internal method for setting up the zlib compression and + decompression objects. + """ + self._zcomp_read = zlib.decompressobj() + self._zcomp_write = zlib.compressobj(self.compresslevel) + + def getCompRatio(self): + """Get the current measured compression ratios (in,out) from + this transport. + + Returns a tuple of: + (inbound_compression_ratio, outbound_compression_ratio) + + The compression ratios are computed as: + compressed / uncompressed + + E.g., data that compresses by 10x will have a ratio of: 0.10 + and data that compresses to half of ts original size will + have a ratio of 0.5 + + None is returned if no bytes have yet been processed in + a particular direction. + """ + r_percent, w_percent = (None, None) + if self.bytes_in > 0: + r_percent = self.bytes_in_comp / self.bytes_in + if self.bytes_out > 0: + w_percent = self.bytes_out_comp / self.bytes_out + return (r_percent, w_percent) + + def getCompSavings(self): + """Get the current count of saved bytes due to data + compression. + + Returns a tuple of: + (inbound_saved_bytes, outbound_saved_bytes) + + Note: if compression is actually expanding your + data (only likely with very tiny thrift objects), then + the values returned will be negative. + """ + r_saved = self.bytes_in - self.bytes_in_comp + w_saved = self.bytes_out - self.bytes_out_comp + return (r_saved, w_saved) + + def isOpen(self): + """Return the underlying transport's open status""" + return self.__trans.isOpen() + + def open(self): + """Open the underlying transport""" + self._init_stats() + return self.__trans.open() + + def listen(self): + """Invoke the underlying transport's listen() method""" + self.__trans.listen() + + def accept(self): + """Accept connections on the underlying transport""" + return self.__trans.accept() + + def close(self): + """Close the underlying transport,""" + self._reinit_buffers() + self._init_zlib() + return self.__trans.close() + + def read(self, sz): + """Read up to sz bytes from the decompressed bytes buffer, and + read from the underlying transport if the decompression + buffer is empty. + """ + ret = self.__rbuf.read(sz) + if len(ret) > 0: + return ret + # keep reading from transport until something comes back + while True: + if self.readComp(sz): + break + ret = self.__rbuf.read(sz) + return ret + + def readComp(self, sz): + """Read compressed data from the underlying transport, then + decompress it and append it to the internal StringIO read buffer + """ + zbuf = self.__trans.read(sz) + zbuf = self._zcomp_read.unconsumed_tail + zbuf + buf = self._zcomp_read.decompress(zbuf) + self.bytes_in += len(zbuf) + self.bytes_in_comp += len(buf) + old = self.__rbuf.read() + self.__rbuf = BufferIO(old + buf) + if len(old) + len(buf) == 0: + return False + return True + + def write(self, buf): + """Write some bytes, putting them into the internal write + buffer for eventual compression. + """ + self.__wbuf.write(buf) + + def flush(self): + """Flush any queued up data in the write buffer and ensure the + compression buffer is flushed out to the underlying transport + """ + wout = self.__wbuf.getvalue() + if len(wout) > 0: + zbuf = self._zcomp_write.compress(wout) + self.bytes_out += len(wout) + self.bytes_out_comp += len(zbuf) + else: + zbuf = '' + ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH) + self.bytes_out_comp += len(ztail) + if (len(zbuf) + len(ztail)) > 0: + self.__wbuf = BufferIO() + self.__trans.write(zbuf + ztail) + self.__trans.flush() + + @property + def cstringio_buf(self): + """Implement the CReadableTransport interface""" + return self.__rbuf + + def cstringio_refill(self, partialread, reqlen): + """Implement the CReadableTransport interface for refill""" + retstring = partialread + if reqlen < self.DEFAULT_BUFFSIZE: + retstring += self.read(self.DEFAULT_BUFFSIZE) + while len(retstring) < reqlen: + retstring += self.read(reqlen - len(retstring)) + self.__rbuf = BufferIO(retstring) + return self.__rbuf diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/__init__.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/__init__.py new file mode 100644 index 00000000..c9596d9a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/__init__.py @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +__all__ = ['TTransport', 'TSocket', 'THttpClient', 'TZlibTransport'] diff --git a/vendor/src/github.com/apache/thrift/lib/py/src/transport/sslcompat.py b/vendor/src/github.com/apache/thrift/lib/py/src/transport/sslcompat.py new file mode 100644 index 00000000..7bf5e066 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/src/transport/sslcompat.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 logging +import sys + +from thrift.transport.TTransport import TTransportException + +logger = logging.getLogger(__name__) + + +def legacy_validate_callback(self, cert, hostname): + """legacy method to validate the peer's SSL certificate, and to check + the commonName of the certificate to ensure it matches the hostname we + used to make this connection. Does not support subjectAltName records + in certificates. + + raises TTransportException if the certificate fails validation. + """ + if 'subject' not in cert: + raise TTransportException( + TTransportException.NOT_OPEN, + 'No SSL certificate found from %s:%s' % (self.host, self.port)) + fields = cert['subject'] + for field in fields: + # ensure structure we get back is what we expect + if not isinstance(field, tuple): + continue + cert_pair = field[0] + if len(cert_pair) < 2: + continue + cert_key, cert_value = cert_pair[0:2] + if cert_key != 'commonName': + continue + certhost = cert_value + # this check should be performed by some sort of Access Manager + if certhost == hostname: + # success, cert commonName matches desired hostname + return + else: + raise TTransportException( + TTransportException.UNKNOWN, + 'Hostname we connected to "%s" doesn\'t match certificate ' + 'provided commonName "%s"' % (self.host, certhost)) + raise TTransportException( + TTransportException.UNKNOWN, + 'Could not validate SSL certificate from host "%s". Cert=%s' + % (hostname, cert)) + + +def _optional_dependencies(): + try: + import ipaddress # noqa + logger.debug('ipaddress module is available') + ipaddr = True + except ImportError: + logger.warn('ipaddress module is unavailable') + ipaddr = False + + if sys.hexversion < 0x030500F0: + try: + from backports.ssl_match_hostname import match_hostname, __version__ as ver + ver = list(map(int, ver.split('.'))) + logger.debug('backports.ssl_match_hostname module is available') + match = match_hostname + if ver[0] * 10 + ver[1] >= 35: + return ipaddr, match + else: + logger.warn('backports.ssl_match_hostname module is too old') + ipaddr = False + except ImportError: + logger.warn('backports.ssl_match_hostname is unavailable') + ipaddr = False + try: + from ssl import match_hostname + logger.debug('ssl.match_hostname is available') + match = match_hostname + except ImportError: + logger.warn('using legacy validation callback') + match = legacy_validate_callback + return ipaddr, match + +_match_has_ipaddress, _match_hostname = _optional_dependencies() diff --git a/vendor/src/github.com/apache/thrift/lib/py/test/_import_local_thrift.py b/vendor/src/github.com/apache/thrift/lib/py/test/_import_local_thrift.py new file mode 100644 index 00000000..d2231229 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/test/_import_local_thrift.py @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import os +import sys + +SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__)) +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR))) + +for libpath in glob.glob(os.path.join(ROOT_DIR, 'lib', 'py', 'build', 'lib.*')): + if libpath.endswith('-%d.%d' % (sys.version_info[0], sys.version_info[1])): + sys.path.insert(0, libpath) + break diff --git a/vendor/src/github.com/apache/thrift/lib/py/test/test_sslsocket.py b/vendor/src/github.com/apache/thrift/lib/py/test/test_sslsocket.py new file mode 100644 index 00000000..3e4b266c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/test/test_sslsocket.py @@ -0,0 +1,339 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import inspect +import logging +import os +import platform +import ssl +import sys +import tempfile +import threading +import unittest +import warnings +from contextlib import contextmanager + +import _import_local_thrift # noqa + +SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__)) +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR))) +SERVER_PEM = os.path.join(ROOT_DIR, 'test', 'keys', 'server.pem') +SERVER_CERT = os.path.join(ROOT_DIR, 'test', 'keys', 'server.crt') +SERVER_KEY = os.path.join(ROOT_DIR, 'test', 'keys', 'server.key') +CLIENT_CERT_NO_IP = os.path.join(ROOT_DIR, 'test', 'keys', 'client.crt') +CLIENT_KEY_NO_IP = os.path.join(ROOT_DIR, 'test', 'keys', 'client.key') +CLIENT_CERT = os.path.join(ROOT_DIR, 'test', 'keys', 'client_v3.crt') +CLIENT_KEY = os.path.join(ROOT_DIR, 'test', 'keys', 'client_v3.key') +CLIENT_CA = os.path.join(ROOT_DIR, 'test', 'keys', 'CA.pem') + +TEST_CIPHERS = 'DES-CBC3-SHA' + + +class ServerAcceptor(threading.Thread): + def __init__(self, server, expect_failure=False): + super(ServerAcceptor, self).__init__() + self.daemon = True + self._server = server + self._listening = threading.Event() + self._port = None + self._port_bound = threading.Event() + self._client = None + self._client_accepted = threading.Event() + self._expect_failure = expect_failure + frame = inspect.stack(3)[2] + self.name = frame[3] + del frame + + def run(self): + self._server.listen() + self._listening.set() + + try: + address = self._server.handle.getsockname() + if len(address) > 1: + # AF_INET addresses are 2-tuples (host, port) and AF_INET6 are + # 4-tuples (host, port, ...), but in each case port is in the second slot. + self._port = address[1] + finally: + self._port_bound.set() + + try: + self._client = self._server.accept() + except Exception: + logging.exception('error on server side (%s):' % self.name) + if not self._expect_failure: + raise + finally: + self._client_accepted.set() + + def await_listening(self): + self._listening.wait() + + @property + def port(self): + self._port_bound.wait() + return self._port + + @property + def client(self): + self._client_accepted.wait() + return self._client + + +# Python 2.6 compat +class AssertRaises(object): + def __init__(self, expected): + self._expected = expected + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + if not exc_type or not issubclass(exc_type, self._expected): + raise Exception('fail') + return True + + +class TSSLSocketTest(unittest.TestCase): + def _server_socket(self, **kwargs): + return TSSLServerSocket(port=0, **kwargs) + + @contextmanager + def _connectable_client(self, server, expect_failure=False, path=None, **client_kwargs): + acc = ServerAcceptor(server, expect_failure) + try: + acc.start() + acc.await_listening() + + host, port = ('localhost', acc.port) if path is None else (None, None) + client = TSSLSocket(host, port, unix_socket=path, **client_kwargs) + yield acc, client + finally: + if acc.client: + acc.client.close() + server.close() + + def _assert_connection_failure(self, server, path=None, **client_args): + logging.disable(logging.CRITICAL) + try: + with self._connectable_client(server, True, path=path, **client_args) as (acc, client): + # We need to wait for a connection failure, but not too long. 20ms is a tunable + # compromise between test speed and stability + client.setTimeout(20) + with self._assert_raises(TTransportException): + client.open() + self.assertTrue(acc.client is None) + finally: + logging.disable(logging.NOTSET) + + def _assert_raises(self, exc): + if sys.hexversion >= 0x020700F0: + return self.assertRaises(exc) + else: + return AssertRaises(exc) + + def _assert_connection_success(self, server, path=None, **client_args): + with self._connectable_client(server, path=path, **client_args) as (acc, client): + client.open() + try: + self.assertTrue(acc.client is not None) + finally: + client.close() + + # deprecated feature + def test_deprecation(self): + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__) + TSSLSocket('localhost', 0, validate=True, ca_certs=SERVER_CERT) + self.assertEqual(len(w), 1) + + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__) + # Deprecated signature + # def __init__(self, host='localhost', port=9090, validate=True, ca_certs=None, keyfile=None, certfile=None, unix_socket=None, ciphers=None): + TSSLSocket('localhost', 0, True, SERVER_CERT, CLIENT_KEY, CLIENT_CERT, None, TEST_CIPHERS) + self.assertEqual(len(w), 7) + + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__) + # Deprecated signature + # def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None, ciphers=None): + TSSLServerSocket(None, 0, SERVER_PEM, None, TEST_CIPHERS) + self.assertEqual(len(w), 3) + + # deprecated feature + def test_set_cert_reqs_by_validate(self): + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__) + c1 = TSSLSocket('localhost', 0, validate=True, ca_certs=SERVER_CERT) + self.assertEqual(c1.cert_reqs, ssl.CERT_REQUIRED) + + c1 = TSSLSocket('localhost', 0, validate=False) + self.assertEqual(c1.cert_reqs, ssl.CERT_NONE) + + self.assertEqual(len(w), 2) + + # deprecated feature + def test_set_validate_by_cert_reqs(self): + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always', category=DeprecationWarning, module=self.__module__) + c1 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_NONE) + self.assertFalse(c1.validate) + + c2 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT) + self.assertTrue(c2.validate) + + c3 = TSSLSocket('localhost', 0, cert_reqs=ssl.CERT_OPTIONAL, ca_certs=SERVER_CERT) + self.assertTrue(c3.validate) + + self.assertEqual(len(w), 3) + + def test_unix_domain_socket(self): + if platform.system() == 'Windows': + print('skipping test_unix_domain_socket') + return + fd, path = tempfile.mkstemp() + os.close(fd) + try: + server = self._server_socket(unix_socket=path, keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_success(server, path=path, cert_reqs=ssl.CERT_NONE) + finally: + os.unlink(path) + + def test_server_cert(self): + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_success(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT) + + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + # server cert not in ca_certs + self._assert_connection_failure(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=CLIENT_CERT) + + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE) + + def test_set_server_cert(self): + server = self._server_socket(keyfile=SERVER_KEY, certfile=CLIENT_CERT) + with self._assert_raises(Exception): + server.certfile = 'foo' + with self._assert_raises(Exception): + server.certfile = None + server.certfile = SERVER_CERT + self._assert_connection_success(server, cert_reqs=ssl.CERT_REQUIRED, ca_certs=SERVER_CERT) + + def test_client_cert(self): + server = self._server_socket( + cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY, + certfile=SERVER_CERT, ca_certs=CLIENT_CERT) + self._assert_connection_failure(server, cert_reqs=ssl.CERT_NONE, certfile=SERVER_CERT, keyfile=SERVER_KEY) + + server = self._server_socket( + cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY, + certfile=SERVER_CERT, ca_certs=CLIENT_CA) + self._assert_connection_failure(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT_NO_IP, keyfile=CLIENT_KEY_NO_IP) + + server = self._server_socket( + cert_reqs=ssl.CERT_REQUIRED, keyfile=SERVER_KEY, + certfile=SERVER_CERT, ca_certs=CLIENT_CA) + self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT, keyfile=CLIENT_KEY) + + server = self._server_socket( + cert_reqs=ssl.CERT_OPTIONAL, keyfile=SERVER_KEY, + certfile=SERVER_CERT, ca_certs=CLIENT_CA) + self._assert_connection_success(server, cert_reqs=ssl.CERT_NONE, certfile=CLIENT_CERT, keyfile=CLIENT_KEY) + + def test_ciphers(self): + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ciphers=TEST_CIPHERS) + self._assert_connection_success(server, ca_certs=SERVER_CERT, ciphers=TEST_CIPHERS) + + if not TSSLSocket._has_ciphers: + # unittest.skip is not available for Python 2.6 + print('skipping test_ciphers') + return + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_failure(server, ca_certs=SERVER_CERT, ciphers='NULL') + + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ciphers=TEST_CIPHERS) + self._assert_connection_failure(server, ca_certs=SERVER_CERT, ciphers='NULL') + + def test_ssl2_and_ssl3_disabled(self): + if not hasattr(ssl, 'PROTOCOL_SSLv3'): + print('PROTOCOL_SSLv3 is not available') + else: + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv3) + + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv3) + self._assert_connection_failure(server, ca_certs=SERVER_CERT) + + if not hasattr(ssl, 'PROTOCOL_SSLv2'): + print('PROTOCOL_SSLv2 is not available') + else: + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT) + self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv2) + + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_SSLv2) + self._assert_connection_failure(server, ca_certs=SERVER_CERT) + + def test_newer_tls(self): + if not TSSLSocket._has_ssl_context: + # unittest.skip is not available for Python 2.6 + print('skipping test_newer_tls') + return + if not hasattr(ssl, 'PROTOCOL_TLSv1_2'): + print('PROTOCOL_TLSv1_2 is not available') + else: + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2) + self._assert_connection_success(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2) + + if not hasattr(ssl, 'PROTOCOL_TLSv1_1'): + print('PROTOCOL_TLSv1_1 is not available') + else: + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1) + self._assert_connection_success(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1) + + if not hasattr(ssl, 'PROTOCOL_TLSv1_1') or not hasattr(ssl, 'PROTOCOL_TLSv1_2'): + print('PROTOCOL_TLSv1_1 and/or PROTOCOL_TLSv1_2 is not available') + else: + server = self._server_socket(keyfile=SERVER_KEY, certfile=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_2) + self._assert_connection_failure(server, ca_certs=SERVER_CERT, ssl_version=ssl.PROTOCOL_TLSv1_1) + + def test_ssl_context(self): + if not TSSLSocket._has_ssl_context: + # unittest.skip is not available for Python 2.6 + print('skipping test_ssl_context') + return + server_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + server_context.load_cert_chain(SERVER_CERT, SERVER_KEY) + server_context.load_verify_locations(CLIENT_CA) + server_context.verify_mode = ssl.CERT_REQUIRED + server = self._server_socket(ssl_context=server_context) + + client_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) + client_context.load_cert_chain(CLIENT_CERT, CLIENT_KEY) + client_context.load_verify_locations(SERVER_CERT) + client_context.verify_mode = ssl.CERT_REQUIRED + + self._assert_connection_success(server, ssl_context=client_context) + +if __name__ == '__main__': + logging.basicConfig(level=logging.WARN) + from thrift.transport.TSSLSocket import TSSLSocket, TSSLServerSocket + from thrift.transport.TTransport import TTransportException + + unittest.main() diff --git a/vendor/src/github.com/apache/thrift/lib/py/test/thrift_json.py b/vendor/src/github.com/apache/thrift/lib/py/test/thrift_json.py new file mode 100644 index 00000000..fc1e79cc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/py/test/thrift_json.py @@ -0,0 +1,50 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +import unittest + +import _import_local_thrift # noqa +from thrift.protocol.TJSONProtocol import TJSONProtocol +from thrift.transport import TTransport + +# +# In order to run the test under Windows. We need to create symbolic link +# name 'thrift' to '../src' folder by using: +# +# mklink /D thrift ..\src +# + + +class TestJSONString(unittest.TestCase): + + def test_escaped_unicode_string(self): + unicode_json = b'"hello \\u0e01\\u0e02\\u0e03\\ud835\\udcab\\udb40\\udc70 unicode"' + unicode_text = u'hello \u0e01\u0e02\u0e03\U0001D4AB\U000E0070 unicode' + + buf = TTransport.TMemoryBuffer(unicode_json) + transport = TTransport.TBufferedTransportFactory().getTransport(buf) + protocol = TJSONProtocol(transport) + + if sys.version_info[0] == 2: + unicode_text = unicode_text.encode('utf8') + self.assertEqual(protocol.readString(), unicode_text) + +if __name__ == '__main__': + unittest.main() diff --git a/vendor/src/github.com/apache/thrift/lib/rb/Gemfile b/vendor/src/github.com/apache/thrift/lib/rb/Gemfile new file mode 100644 index 00000000..1c86af95 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/Gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gemspec + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/Makefile.am b/vendor/src/github.com/apache/thrift/lib/rb/Makefile.am new file mode 100644 index 00000000..137edb4d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/Makefile.am @@ -0,0 +1,51 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +DESTDIR ?= / + +if HAVE_BUNDLER + +all-local: + $(BUNDLER) install + $(BUNDLER) exec rake build_ext + +install-exec-hook: + $(BUNDLER) exec rake install + +clean-local: + $(BUNDLER) install + $(BUNDLER) exec rake clean + +check-local: all + $(BUNDLER) install + $(BUNDLER) exec rake + +endif + +EXTRA_DIST = \ + coding_standards.md \ + Rakefile \ + Gemfile \ + thrift.gemspec \ + lib \ + ext \ + benchmark \ + script \ + spec \ + README.md diff --git a/vendor/src/github.com/apache/thrift/lib/rb/README.md b/vendor/src/github.com/apache/thrift/lib/rb/README.md new file mode 100644 index 00000000..b6e9a079 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/README.md @@ -0,0 +1,43 @@ +Thrift Ruby Software Library + http://thrift.apache.org + +== LICENSE: + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +== DESCRIPTION: + +Thrift is a strongly-typed language-agnostic RPC system. +This library is the ruby implementation for both clients and servers. + +== INSTALL: + + $ gem install thrift + +== CAVEATS: + +This library provides the client and server implementations of thrift. +It does not provide the compiler for the .thrift files. To compile +.thrift files into language-specific implementations, please download the full +thrift software package. + +== USAGE: + +This section should get written by someone with the time and inclination. +In the meantime, look at existing code, such as the benchmark or the tutorial +in the full thrift distribution. diff --git a/vendor/src/github.com/apache/thrift/lib/rb/Rakefile b/vendor/src/github.com/apache/thrift/lib/rb/Rakefile new file mode 100644 index 00000000..cdecaa68 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/Rakefile @@ -0,0 +1,119 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'rubygems' +require 'rake' +require 'rake/clean' +require 'rspec/core/rake_task' + +THRIFT = '../../compiler/cpp/thrift' + +task :default => [:gem] +task :spec => [:'gen-rb', :build_ext, :realspec] + +RSpec::Core::RakeTask.new(:realspec) do |t| + t.rspec_opts = ['--color', '--format d'] +end + +RSpec::Core::RakeTask.new(:'spec:rcov') do |t| + t.rspec_opts = ['--color', '--format d'] + t.rcov = true + t.rcov_opts = ['--exclude', '^spec,/gems/'] +end + +desc 'Compile the .thrift files for the specs' +task :'gen-rb' => [:'gen-rb:spec', :'gen-rb:namespaced_spec', :'gen-rb:flat_spec', :'gen-rb:benchmark', :'gen-rb:debug_proto'] +namespace :'gen-rb' do + task :'spec' do + dir = File.dirname(__FILE__) + '/spec' + sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/ThriftSpec.thrift" + end + + task :'namespaced_spec' do + dir = File.dirname(__FILE__) + '/spec' + sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ThriftNamespacedSpec.thrift" + sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/BaseService.thrift" + sh THRIFT, '--gen', 'rb:namespaced', '--recurse', '-o', dir, "#{dir}/ExtendedService.thrift" + end + + task :'flat_spec' do + dir = File.dirname(__FILE__) + '/spec' + mkdir_p("#{dir}/gen-rb/flat") + sh THRIFT, '--gen', 'rb', '--recurse', '-out', "#{dir}/gen-rb/flat", "#{dir}/ThriftNamespacedSpec.thrift" + end + + task :'benchmark' do + dir = File.dirname(__FILE__) + '/benchmark' + sh THRIFT, '--gen', 'rb', '-o', dir, "#{dir}/Benchmark.thrift" + end + + task :'debug_proto' do + sh "mkdir", "-p", "test/debug_proto" + sh THRIFT, '--gen', 'rb', "-o", "test/debug_proto", "../../test/DebugProtoTest.thrift" + end +end + +desc "Build the native library" +task :build_ext => :'gen-rb' do + Dir::chdir(File::dirname('ext/extconf.rb')) do + unless sh "ruby #{File::basename('ext/extconf.rb')}" + $stderr.puts "Failed to run extconf" + break + end + unless sh "make" + $stderr.puts "make failed" + break + end + end +end + +desc 'Run the compiler tests (requires full thrift checkout)' +task :test do + # ensure this is a full thrift checkout and not a tarball of the ruby libs + cmd = 'head -1 ../../README.md 2>/dev/null | grep Thrift >/dev/null 2>/dev/null' + system(cmd) or fail "rake test requires a full thrift checkout" + sh 'make', '-C', File.dirname(__FILE__) + "/../../test/rb", "check" +end + +desc 'Run benchmarking of NonblockingServer' +task :benchmark do + ruby 'benchmark/benchmark.rb' +end + +desc 'Builds the thrift gem' +task :gem => [:spec, :build_ext] do + unless sh 'gem', 'build', 'thrift.gemspec' + $stderr.puts "Failed to build thrift gem" + break + end +end + +desc 'Install the thrift gem' +task :install => [:gem] do + unless sh 'gem', 'install', Dir.glob('thrift-*.gem').last + $stderr.puts "Failed to install thrift gem" + break + end +end + +CLEAN.include [ + '.bundle', 'benchmark/gen-rb', 'coverage', 'ext/*.{o,bundle,so,dll}', 'ext/mkmf.log', + 'ext/Makefile', 'ext/conftest.dSYM', 'Gemfile.lock', 'mkmf.log', 'pkg', 'spec/gen-rb', + 'test', 'thrift-*.gem' +] diff --git a/vendor/src/github.com/apache/thrift/lib/rb/benchmark/Benchmark.thrift b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/Benchmark.thrift new file mode 100644 index 00000000..eb5ae38e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/Benchmark.thrift @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb ThriftBenchmark + +service BenchmarkService { + i32 fibonacci(1:byte n) +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/benchmark/benchmark.rb b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/benchmark.rb new file mode 100644 index 00000000..3dc67dd8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/benchmark.rb @@ -0,0 +1,271 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'rubygems' +$:.unshift File.dirname(__FILE__) + '/../lib' +require 'thrift' +require 'stringio' + +HOST = '127.0.0.1' +PORT = 42587 + +############### +## Server +############### + +class Server + attr_accessor :serverclass + attr_accessor :interpreter + attr_accessor :host + attr_accessor :port + + def initialize(opts) + @serverclass = opts.fetch(:class, Thrift::NonblockingServer) + @interpreter = opts.fetch(:interpreter, "ruby") + @host = opts.fetch(:host, ::HOST) + @port = opts.fetch(:port, ::PORT) + end + + def start + return if @serverclass == Object + args = (File.basename(@interpreter) == "jruby" ? "-J-server" : "") + @pipe = IO.popen("#{@interpreter} #{args} #{File.dirname(__FILE__)}/server.rb #{@host} #{@port} #{@serverclass.name}", "r+") + Marshal.load(@pipe) # wait until the server has started + sleep 0.4 # give the server time to actually start spawning sockets + end + + def shutdown + return unless @pipe + Marshal.dump(:shutdown, @pipe) + begin + @pipe.read(10) # block until the server shuts down + rescue EOFError + end + @pipe.close + @pipe = nil + end +end + +class BenchmarkManager + def initialize(opts, server) + @socket = opts.fetch(:socket) do + @host = opts.fetch(:host, 'localhost') + @port = opts.fetch(:port) + nil + end + @num_processes = opts.fetch(:num_processes, 40) + @clients_per_process = opts.fetch(:clients_per_process, 10) + @calls_per_client = opts.fetch(:calls_per_client, 50) + @interpreter = opts.fetch(:interpreter, "ruby") + @server = server + @log_exceptions = opts.fetch(:log_exceptions, false) + end + + def run + @pool = [] + @benchmark_start = Time.now + puts "Spawning benchmark processes..." + @num_processes.times do + spawn + sleep 0.02 # space out spawns + end + collect_output + @benchmark_end = Time.now # we know the procs are done here + translate_output + analyze_output + report_output + end + + def spawn + pipe = IO.popen("#{@interpreter} #{File.dirname(__FILE__)}/client.rb #{"-log-exceptions" if @log_exceptions} #{@host} #{@port} #{@clients_per_process} #{@calls_per_client}") + @pool << pipe + end + + def socket_class + if @socket + Thrift::UNIXSocket + else + Thrift::Socket + end + end + + def collect_output + puts "Collecting output..." + # read from @pool until all sockets are closed + @buffers = Hash.new { |h,k| h[k] = '' } + until @pool.empty? + rd, = select(@pool) + next if rd.nil? + rd.each do |fd| + begin + @buffers[fd] << fd.readpartial(4096) + rescue EOFError + @pool.delete fd + end + end + end + end + + def translate_output + puts "Translating output..." + @output = [] + @buffers.each do |fd, buffer| + strio = StringIO.new(buffer) + logs = [] + begin + loop do + logs << Marshal.load(strio) + end + rescue EOFError + @output << logs + end + end + end + + def analyze_output + puts "Analyzing output..." + call_times = [] + client_times = [] + connection_failures = [] + connection_errors = [] + shortest_call = 0 + shortest_client = 0 + longest_call = 0 + longest_client = 0 + @output.each do |logs| + cur_call, cur_client = nil + logs.each do |tok, time| + case tok + when :start + cur_client = time + when :call_start + cur_call = time + when :call_end + delta = time - cur_call + call_times << delta + longest_call = delta unless longest_call > delta + shortest_call = delta if shortest_call == 0 or delta < shortest_call + cur_call = nil + when :end + delta = time - cur_client + client_times << delta + longest_client = delta unless longest_client > delta + shortest_client = delta if shortest_client == 0 or delta < shortest_client + cur_client = nil + when :connection_failure + connection_failures << time + when :connection_error + connection_errors << time + end + end + end + @report = {} + @report[:total_calls] = call_times.inject(0.0) { |a,t| a += t } + @report[:avg_calls] = @report[:total_calls] / call_times.size + @report[:total_clients] = client_times.inject(0.0) { |a,t| a += t } + @report[:avg_clients] = @report[:total_clients] / client_times.size + @report[:connection_failures] = connection_failures.size + @report[:connection_errors] = connection_errors.size + @report[:shortest_call] = shortest_call + @report[:shortest_client] = shortest_client + @report[:longest_call] = longest_call + @report[:longest_client] = longest_client + @report[:total_benchmark_time] = @benchmark_end - @benchmark_start + @report[:fastthread] = $".include?('fastthread.bundle') + end + + def report_output + fmt = "%.4f seconds" + puts + tabulate "%d", + [["Server class", "%s"], @server.serverclass == Object ? "" : @server.serverclass], + [["Server interpreter", "%s"], @server.interpreter], + [["Client interpreter", "%s"], @interpreter], + [["Socket class", "%s"], socket_class], + ["Number of processes", @num_processes], + ["Clients per process", @clients_per_process], + ["Calls per client", @calls_per_client], + [["Using fastthread", "%s"], @report[:fastthread] ? "yes" : "no"] + puts + failures = (@report[:connection_failures] > 0) + tabulate fmt, + [["Connection failures", "%d", [:red, :bold]], @report[:connection_failures]], + [["Connection errors", "%d", [:red, :bold]], @report[:connection_errors]], + ["Average time per call", @report[:avg_calls]], + ["Average time per client (%d calls)" % @calls_per_client, @report[:avg_clients]], + ["Total time for all calls", @report[:total_calls]], + ["Real time for benchmarking", @report[:total_benchmark_time]], + ["Shortest call time", @report[:shortest_call]], + ["Longest call time", @report[:longest_call]], + ["Shortest client time (%d calls)" % @calls_per_client, @report[:shortest_client]], + ["Longest client time (%d calls)" % @calls_per_client, @report[:longest_client]] + end + + ANSI = { + :reset => 0, + :bold => 1, + :black => 30, + :red => 31, + :green => 32, + :yellow => 33, + :blue => 34, + :magenta => 35, + :cyan => 36, + :white => 37 + } + + def tabulate(fmt, *labels_and_values) + labels = labels_and_values.map { |l| Array === l ? l.first : l } + label_width = labels.inject(0) { |w,l| l.size > w ? l.size : w } + labels_and_values.each do |(l,v)| + f = fmt + l, f, c = l if Array === l + fmtstr = "%-#{label_width+1}s #{f}" + if STDOUT.tty? and c and v.to_i > 0 + fmtstr = "\e[#{[*c].map { |x| ANSI[x] } * ";"}m" + fmtstr + "\e[#{ANSI[:reset]}m" + end + puts fmtstr % [l+":", v] + end + end +end + +def resolve_const(const) + const and const.split('::').inject(Object) { |k,c| k.const_get(c) } +end + +puts "Starting server..." +args = {} +args[:interpreter] = ENV['THRIFT_SERVER_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby" +args[:class] = resolve_const(ENV['THRIFT_SERVER']) || Thrift::NonblockingServer +args[:host] = ENV['THRIFT_HOST'] || HOST +args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i +server = Server.new(args) +server.start + +args = {} +args[:host] = ENV['THRIFT_HOST'] || HOST +args[:port] = (ENV['THRIFT_PORT'] || PORT).to_i +args[:num_processes] = (ENV['THRIFT_NUM_PROCESSES'] || 40).to_i +args[:clients_per_process] = (ENV['THRIFT_NUM_CLIENTS'] || 5).to_i +args[:calls_per_client] = (ENV['THRIFT_NUM_CALLS'] || 50).to_i +args[:interpreter] = ENV['THRIFT_CLIENT_INTERPRETER'] || ENV['THRIFT_INTERPRETER'] || "ruby" +args[:log_exceptions] = !!ENV['THRIFT_LOG_EXCEPTIONS'] +BenchmarkManager.new(args, server).run + +server.shutdown diff --git a/vendor/src/github.com/apache/thrift/lib/rb/benchmark/client.rb b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/client.rb new file mode 100644 index 00000000..703dc8f5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/client.rb @@ -0,0 +1,74 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$:.unshift File.dirname(__FILE__) + '/../lib' +require 'thrift' +$:.unshift File.dirname(__FILE__) + "/gen-rb" +require 'benchmark_service' + +class Client + def initialize(host, port, clients_per_process, calls_per_client, log_exceptions) + @host = host + @port = port + @clients_per_process = clients_per_process + @calls_per_client = calls_per_client + @log_exceptions = log_exceptions + end + + def run + @clients_per_process.times do + socket = Thrift::Socket.new(@host, @port) + transport = Thrift::FramedTransport.new(socket) + protocol = Thrift::BinaryProtocol.new(transport) + client = ThriftBenchmark::BenchmarkService::Client.new(protocol) + begin + start = Time.now + transport.open + Marshal.dump [:start, start], STDOUT + rescue => e + Marshal.dump [:connection_failure, Time.now], STDOUT + print_exception e if @log_exceptions + else + begin + @calls_per_client.times do + Marshal.dump [:call_start, Time.now], STDOUT + client.fibonacci(15) + Marshal.dump [:call_end, Time.now], STDOUT + end + transport.close + Marshal.dump [:end, Time.now], STDOUT + rescue Thrift::TransportException => e + Marshal.dump [:connection_error, Time.now], STDOUT + print_exception e if @log_exceptions + end + end + end + end + + def print_exception(e) + STDERR.puts "ERROR: #{e.message}" + STDERR.puts "\t#{e.backtrace * "\n\t"}" + end +end + +log_exceptions = true if ARGV[0] == '-log-exceptions' and ARGV.shift + +host, port, clients_per_process, calls_per_client = ARGV + +Client.new(host, port.to_i, clients_per_process.to_i, calls_per_client.to_i, log_exceptions).run diff --git a/vendor/src/github.com/apache/thrift/lib/rb/benchmark/server.rb b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/server.rb new file mode 100644 index 00000000..74e13f41 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/server.rb @@ -0,0 +1,82 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$:.unshift File.dirname(__FILE__) + '/../lib' +require 'thrift' +$:.unshift File.dirname(__FILE__) + "/gen-rb" +require 'benchmark_service' + +module Server + include Thrift + + class BenchmarkHandler + # 1-based index into the fibonacci sequence + def fibonacci(n) + seq = [1, 1] + 3.upto(n) do + seq << seq[-1] + seq[-2] + end + seq[n-1] # n is 1-based + end + end + + def self.start_server(host, port, serverClass) + handler = BenchmarkHandler.new + processor = ThriftBenchmark::BenchmarkService::Processor.new(handler) + transport = ServerSocket.new(host, port) + transport_factory = FramedTransportFactory.new + args = [processor, transport, transport_factory, nil, 20] + if serverClass == NonblockingServer + logger = Logger.new(STDERR) + logger.level = Logger::WARN + args << logger + end + server = serverClass.new(*args) + @server_thread = Thread.new do + server.serve + end + @server = server + end + + def self.shutdown + return if @server.nil? + if @server.respond_to? :shutdown + @server.shutdown + else + @server_thread.kill + end + end +end + +def resolve_const(const) + const and const.split('::').inject(Object) { |k,c| k.const_get(c) } +end + +host, port, serverklass = ARGV + +Server.start_server(host, port.to_i, resolve_const(serverklass)) + +# let our host know that the interpreter has started +# ideally we'd wait until the server was serving, but we don't have a hook for that +Marshal.dump(:started, STDOUT) +STDOUT.flush + +Marshal.load(STDIN) # wait until we're instructed to shut down + +Server.shutdown diff --git a/vendor/src/github.com/apache/thrift/lib/rb/benchmark/thin_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/thin_server.rb new file mode 100644 index 00000000..4de2eef3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/benchmark/thin_server.rb @@ -0,0 +1,44 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$:.unshift File.dirname(__FILE__) + '/../lib' +require 'thrift' +$:.unshift File.dirname(__FILE__) + "/gen-rb" +require 'benchmark_service' +HOST = 'localhost' +PORT = 42587 + +class BenchmarkHandler + # 1-based index into the fibonacci sequence + def fibonacci(n) + seq = [1, 1] + 3.upto(n) do + seq << seq[-1] + seq[-2] + end + seq[n-1] # n is 1-based + end +end + +handler = BenchmarkHandler.new +processor = ThriftBenchmark::BenchmarkService::Processor.new(handler) +transport = Thrift::ServerSocket.new(HOST, PORT) +transport_factory = Thrift::FramedTransportFactory.new +logger = Logger.new(STDERR) +logger.level = Logger::WARN +Thrift::NonblockingServer.new(processor, transport, transport_factory, nil, 20, logger).serve diff --git a/vendor/src/github.com/apache/thrift/lib/rb/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/rb/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.c new file mode 100644 index 00000000..65cbe5ff --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.c @@ -0,0 +1,460 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +VALUE rb_thrift_binary_proto_native_qmark(VALUE self) { + return Qtrue; +} + + + +static int VERSION_1; +static int VERSION_MASK; +static int TYPE_MASK; +static int BAD_VERSION; +static ID rbuf_ivar_id; + +static void write_byte_direct(VALUE trans, int8_t b) { + WRITE(trans, (char*)&b, 1); +} + +static void write_i16_direct(VALUE trans, int16_t value) { + char data[2]; + + data[1] = value; + data[0] = (value >> 8); + + WRITE(trans, data, 2); +} + +static void write_i32_direct(VALUE trans, int32_t value) { + char data[4]; + + data[3] = value; + data[2] = (value >> 8); + data[1] = (value >> 16); + data[0] = (value >> 24); + + WRITE(trans, data, 4); +} + + +static void write_i64_direct(VALUE trans, int64_t value) { + char data[8]; + + data[7] = value; + data[6] = (value >> 8); + data[5] = (value >> 16); + data[4] = (value >> 24); + data[3] = (value >> 32); + data[2] = (value >> 40); + data[1] = (value >> 48); + data[0] = (value >> 56); + + WRITE(trans, data, 8); +} + +static void write_string_direct(VALUE trans, VALUE str) { + if (TYPE(str) != T_STRING) { + rb_raise(rb_eStandardError, "Value should be a string"); + } + str = convert_to_utf8_byte_buffer(str); + write_i32_direct(trans, RSTRING_LEN(str)); + rb_funcall(trans, write_method_id, 1, str); +} + +//-------------------------------- +// interface writing methods +//-------------------------------- + +VALUE rb_thrift_binary_proto_write_message_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_struct_begin(VALUE self, VALUE name) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_struct_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_field_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_map_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_list_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_set_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) { + VALUE trans = GET_TRANSPORT(self); + VALUE strict_write = GET_STRICT_WRITE(self); + + if (strict_write == Qtrue) { + write_i32_direct(trans, VERSION_1 | FIX2INT(type)); + write_string_direct(trans, name); + write_i32_direct(trans, FIX2INT(seqid)); + } else { + write_string_direct(trans, name); + write_byte_direct(trans, FIX2INT(type)); + write_i32_direct(trans, FIX2INT(seqid)); + } + + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) { + VALUE trans = GET_TRANSPORT(self); + write_byte_direct(trans, FIX2INT(type)); + write_i16_direct(trans, FIX2INT(id)); + + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_field_stop(VALUE self) { + write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size) { + VALUE trans = GET_TRANSPORT(self); + write_byte_direct(trans, FIX2INT(ktype)); + write_byte_direct(trans, FIX2INT(vtype)); + write_i32_direct(trans, FIX2INT(size)); + + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) { + VALUE trans = GET_TRANSPORT(self); + write_byte_direct(trans, FIX2INT(etype)); + write_i32_direct(trans, FIX2INT(size)); + + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) { + rb_thrift_binary_proto_write_list_begin(self, etype, size); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_bool(VALUE self, VALUE b) { + write_byte_direct(GET_TRANSPORT(self), RTEST(b) ? 1 : 0); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_byte(VALUE self, VALUE byte) { + CHECK_NIL(byte); + write_byte_direct(GET_TRANSPORT(self), NUM2INT(byte)); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_i16(VALUE self, VALUE i16) { + CHECK_NIL(i16); + write_i16_direct(GET_TRANSPORT(self), FIX2INT(i16)); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_i32(VALUE self, VALUE i32) { + CHECK_NIL(i32); + write_i32_direct(GET_TRANSPORT(self), NUM2INT(i32)); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_i64(VALUE self, VALUE i64) { + CHECK_NIL(i64); + write_i64_direct(GET_TRANSPORT(self), NUM2LL(i64)); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_double(VALUE self, VALUE dub) { + CHECK_NIL(dub); + // Unfortunately, bitwise_cast doesn't work in C. Bad C! + union { + double f; + int64_t t; + } transfer; + transfer.f = RFLOAT_VALUE(rb_Float(dub)); + write_i64_direct(GET_TRANSPORT(self), transfer.t); + + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_string(VALUE self, VALUE str) { + CHECK_NIL(str); + VALUE trans = GET_TRANSPORT(self); + write_string_direct(trans, str); + return Qnil; +} + +VALUE rb_thrift_binary_proto_write_binary(VALUE self, VALUE buf) { + CHECK_NIL(buf); + VALUE trans = GET_TRANSPORT(self); + buf = force_binary_encoding(buf); + write_i32_direct(trans, RSTRING_LEN(buf)); + rb_funcall(trans, write_method_id, 1, buf); + return Qnil; +} + +//--------------------------------------- +// interface reading methods +//--------------------------------------- + +VALUE rb_thrift_binary_proto_read_string(VALUE self); +VALUE rb_thrift_binary_proto_read_binary(VALUE self); +VALUE rb_thrift_binary_proto_read_byte(VALUE self); +VALUE rb_thrift_binary_proto_read_i32(VALUE self); +VALUE rb_thrift_binary_proto_read_i16(VALUE self); + +static char read_byte_direct(VALUE self) { + VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0); + return (char)(FIX2INT(byte)); +} + +static int16_t read_i16_direct(VALUE self) { + VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id); + rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(2)); + return (int16_t)(((uint8_t)(RSTRING_PTR(rbuf)[1])) | ((uint16_t)((RSTRING_PTR(rbuf)[0]) << 8))); +} + +static int32_t read_i32_direct(VALUE self) { + VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id); + rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(4)); + return ((uint8_t)(RSTRING_PTR(rbuf)[3])) | + (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) | + (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) | + (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24); +} + +static int64_t read_i64_direct(VALUE self) { + VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id); + rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8)); + uint64_t hi = ((uint8_t)(RSTRING_PTR(rbuf)[3])) | + (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 8) | + (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 16) | + (((uint8_t)(RSTRING_PTR(rbuf)[0])) << 24); + uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[7])) | + (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 8) | + (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 16) | + (((uint8_t)(RSTRING_PTR(rbuf)[4])) << 24); + return (hi << 32) | lo; +} + +static VALUE get_protocol_exception(VALUE code, VALUE message) { + VALUE args[2]; + args[0] = code; + args[1] = message; + return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class); +} + +VALUE rb_thrift_binary_proto_read_message_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_struct_begin(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_struct_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_field_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_map_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_list_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_set_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_binary_proto_read_message_begin(VALUE self) { + VALUE strict_read = GET_STRICT_READ(self); + VALUE name, seqid; + int type; + + int version = read_i32_direct(self); + + if (version < 0) { + if ((version & VERSION_MASK) != VERSION_1) { + rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("Missing version identifier"))); + } + type = version & TYPE_MASK; + name = rb_thrift_binary_proto_read_string(self); + seqid = rb_thrift_binary_proto_read_i32(self); + } else { + if (strict_read == Qtrue) { + rb_exc_raise(get_protocol_exception(INT2FIX(BAD_VERSION), rb_str_new2("No version identifier, old protocol client?"))); + } + name = READ(self, version); + type = read_byte_direct(self); + seqid = rb_thrift_binary_proto_read_i32(self); + } + + return rb_ary_new3(3, name, INT2FIX(type), seqid); +} + +VALUE rb_thrift_binary_proto_read_field_begin(VALUE self) { + int type = read_byte_direct(self); + if (type == TTYPE_STOP) { + return rb_ary_new3(3, Qnil, INT2FIX(type), INT2FIX(0)); + } else { + VALUE id = rb_thrift_binary_proto_read_i16(self); + return rb_ary_new3(3, Qnil, INT2FIX(type), id); + } +} + +VALUE rb_thrift_binary_proto_read_map_begin(VALUE self) { + VALUE ktype = rb_thrift_binary_proto_read_byte(self); + VALUE vtype = rb_thrift_binary_proto_read_byte(self); + VALUE size = rb_thrift_binary_proto_read_i32(self); + return rb_ary_new3(3, ktype, vtype, size); +} + +VALUE rb_thrift_binary_proto_read_list_begin(VALUE self) { + VALUE etype = rb_thrift_binary_proto_read_byte(self); + VALUE size = rb_thrift_binary_proto_read_i32(self); + return rb_ary_new3(2, etype, size); +} + +VALUE rb_thrift_binary_proto_read_set_begin(VALUE self) { + return rb_thrift_binary_proto_read_list_begin(self); +} + +VALUE rb_thrift_binary_proto_read_bool(VALUE self) { + char byte = read_byte_direct(self); + return byte != 0 ? Qtrue : Qfalse; +} + +VALUE rb_thrift_binary_proto_read_byte(VALUE self) { + return INT2FIX(read_byte_direct(self)); +} + +VALUE rb_thrift_binary_proto_read_i16(VALUE self) { + return INT2FIX(read_i16_direct(self)); +} + +VALUE rb_thrift_binary_proto_read_i32(VALUE self) { + return INT2NUM(read_i32_direct(self)); +} + +VALUE rb_thrift_binary_proto_read_i64(VALUE self) { + return LL2NUM(read_i64_direct(self)); +} + +VALUE rb_thrift_binary_proto_read_double(VALUE self) { + union { + double f; + int64_t t; + } transfer; + transfer.t = read_i64_direct(self); + return rb_float_new(transfer.f); +} + +VALUE rb_thrift_binary_proto_read_string(VALUE self) { + VALUE buffer = rb_thrift_binary_proto_read_binary(self); + return convert_to_string(buffer); +} + +VALUE rb_thrift_binary_proto_read_binary(VALUE self) { + int size = read_i32_direct(self); + return READ(self, size); +} + +void Init_binary_protocol_accelerated() { + VALUE thrift_binary_protocol_class = rb_const_get(thrift_module, rb_intern("BinaryProtocol")); + + VERSION_1 = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_1"))); + VERSION_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("VERSION_MASK"))); + TYPE_MASK = rb_num2ll(rb_const_get(thrift_binary_protocol_class, rb_intern("TYPE_MASK"))); + + VALUE bpa_class = rb_define_class_under(thrift_module, "BinaryProtocolAccelerated", thrift_binary_protocol_class); + + rb_define_method(bpa_class, "native?", rb_thrift_binary_proto_native_qmark, 0); + + rb_define_method(bpa_class, "write_message_begin", rb_thrift_binary_proto_write_message_begin, 3); + rb_define_method(bpa_class, "write_field_begin", rb_thrift_binary_proto_write_field_begin, 3); + rb_define_method(bpa_class, "write_field_stop", rb_thrift_binary_proto_write_field_stop, 0); + rb_define_method(bpa_class, "write_map_begin", rb_thrift_binary_proto_write_map_begin, 3); + rb_define_method(bpa_class, "write_list_begin", rb_thrift_binary_proto_write_list_begin, 2); + rb_define_method(bpa_class, "write_set_begin", rb_thrift_binary_proto_write_set_begin, 2); + rb_define_method(bpa_class, "write_byte", rb_thrift_binary_proto_write_byte, 1); + rb_define_method(bpa_class, "write_bool", rb_thrift_binary_proto_write_bool, 1); + rb_define_method(bpa_class, "write_i16", rb_thrift_binary_proto_write_i16, 1); + rb_define_method(bpa_class, "write_i32", rb_thrift_binary_proto_write_i32, 1); + rb_define_method(bpa_class, "write_i64", rb_thrift_binary_proto_write_i64, 1); + rb_define_method(bpa_class, "write_double", rb_thrift_binary_proto_write_double, 1); + rb_define_method(bpa_class, "write_string", rb_thrift_binary_proto_write_string, 1); + rb_define_method(bpa_class, "write_binary", rb_thrift_binary_proto_write_binary, 1); + // unused methods + rb_define_method(bpa_class, "write_message_end", rb_thrift_binary_proto_write_message_end, 0); + rb_define_method(bpa_class, "write_struct_begin", rb_thrift_binary_proto_write_struct_begin, 1); + rb_define_method(bpa_class, "write_struct_end", rb_thrift_binary_proto_write_struct_end, 0); + rb_define_method(bpa_class, "write_field_end", rb_thrift_binary_proto_write_field_end, 0); + rb_define_method(bpa_class, "write_map_end", rb_thrift_binary_proto_write_map_end, 0); + rb_define_method(bpa_class, "write_list_end", rb_thrift_binary_proto_write_list_end, 0); + rb_define_method(bpa_class, "write_set_end", rb_thrift_binary_proto_write_set_end, 0); + + rb_define_method(bpa_class, "read_message_begin", rb_thrift_binary_proto_read_message_begin, 0); + rb_define_method(bpa_class, "read_field_begin", rb_thrift_binary_proto_read_field_begin, 0); + rb_define_method(bpa_class, "read_map_begin", rb_thrift_binary_proto_read_map_begin, 0); + rb_define_method(bpa_class, "read_list_begin", rb_thrift_binary_proto_read_list_begin, 0); + rb_define_method(bpa_class, "read_set_begin", rb_thrift_binary_proto_read_set_begin, 0); + rb_define_method(bpa_class, "read_byte", rb_thrift_binary_proto_read_byte, 0); + rb_define_method(bpa_class, "read_bool", rb_thrift_binary_proto_read_bool, 0); + rb_define_method(bpa_class, "read_i16", rb_thrift_binary_proto_read_i16, 0); + rb_define_method(bpa_class, "read_i32", rb_thrift_binary_proto_read_i32, 0); + rb_define_method(bpa_class, "read_i64", rb_thrift_binary_proto_read_i64, 0); + rb_define_method(bpa_class, "read_double", rb_thrift_binary_proto_read_double, 0); + rb_define_method(bpa_class, "read_string", rb_thrift_binary_proto_read_string, 0); + rb_define_method(bpa_class, "read_binary", rb_thrift_binary_proto_read_binary, 0); + // unused methods + rb_define_method(bpa_class, "read_message_end", rb_thrift_binary_proto_read_message_end, 0); + rb_define_method(bpa_class, "read_struct_begin", rb_thrift_binary_proto_read_struct_begin, 0); + rb_define_method(bpa_class, "read_struct_end", rb_thrift_binary_proto_read_struct_end, 0); + rb_define_method(bpa_class, "read_field_end", rb_thrift_binary_proto_read_field_end, 0); + rb_define_method(bpa_class, "read_map_end", rb_thrift_binary_proto_read_map_end, 0); + rb_define_method(bpa_class, "read_list_end", rb_thrift_binary_proto_read_list_end, 0); + rb_define_method(bpa_class, "read_set_end", rb_thrift_binary_proto_read_set_end, 0); + + rbuf_ivar_id = rb_intern("@rbuf"); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.h new file mode 100644 index 00000000..37baf414 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/binary_protocol_accelerated.h @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +void Init_binary_protocol_accelerated(); diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.c new file mode 100644 index 00000000..8a6fac4a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.c @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#ifdef HAVE_RUBY_ENCODING_H +#include +#endif +#include + +VALUE force_binary_encoding(VALUE buffer) { + return rb_funcall(thrift_bytes_module, force_binary_encoding_id, 1, buffer); +} + +VALUE convert_to_utf8_byte_buffer(VALUE string) { + return rb_funcall(thrift_bytes_module, convert_to_utf8_byte_buffer_id, 1, string); +} + +VALUE convert_to_string(VALUE utf8_buffer) { + return rb_funcall(thrift_bytes_module, convert_to_string_id, 1, utf8_buffer); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.h new file mode 100644 index 00000000..7108d83f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/bytes.h @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include + +/* + * A collection of utilities for working with bytes and byte buffers. + * + * These methods are the native analogies to some of the methods in + * Thrift::Bytes (thrift/bytes.rb). + */ + +VALUE force_binary_encoding(VALUE buffer); +VALUE convert_to_utf8_byte_buffer(VALUE string); +VALUE convert_to_string(VALUE utf8_buffer); diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.c new file mode 100644 index 00000000..c0f46b95 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.c @@ -0,0 +1,637 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LAST_ID(obj) FIX2INT(rb_ary_pop(rb_ivar_get(obj, last_field_id))) +#define SET_LAST_ID(obj, val) rb_ary_push(rb_ivar_get(obj, last_field_id), val) + +VALUE rb_thrift_compact_proto_native_qmark(VALUE self) { + return Qtrue; +} + +static ID last_field_id; +static ID boolean_field_id; +static ID bool_value_id; +static ID rbuf_ivar_id; + +static int VERSION; +static int VERSION_MASK; +static int TYPE_MASK; +static int TYPE_BITS; +static int TYPE_SHIFT_AMOUNT; +static int PROTOCOL_ID; + +static VALUE thrift_compact_protocol_class; + +static int CTYPE_BOOLEAN_TRUE = 0x01; +static int CTYPE_BOOLEAN_FALSE = 0x02; +static int CTYPE_BYTE = 0x03; +static int CTYPE_I16 = 0x04; +static int CTYPE_I32 = 0x05; +static int CTYPE_I64 = 0x06; +static int CTYPE_DOUBLE = 0x07; +static int CTYPE_BINARY = 0x08; +static int CTYPE_LIST = 0x09; +static int CTYPE_SET = 0x0A; +static int CTYPE_MAP = 0x0B; +static int CTYPE_STRUCT = 0x0C; + +VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16); + +// TODO: implement this +static int get_compact_type(VALUE type_value) { + int type = FIX2INT(type_value); + if (type == TTYPE_BOOL) { + return CTYPE_BOOLEAN_TRUE; + } else if (type == TTYPE_BYTE) { + return CTYPE_BYTE; + } else if (type == TTYPE_I16) { + return CTYPE_I16; + } else if (type == TTYPE_I32) { + return CTYPE_I32; + } else if (type == TTYPE_I64) { + return CTYPE_I64; + } else if (type == TTYPE_DOUBLE) { + return CTYPE_DOUBLE; + } else if (type == TTYPE_STRING) { + return CTYPE_BINARY; + } else if (type == TTYPE_LIST) { + return CTYPE_LIST; + } else if (type == TTYPE_SET) { + return CTYPE_SET; + } else if (type == TTYPE_MAP) { + return CTYPE_MAP; + } else if (type == TTYPE_STRUCT) { + return CTYPE_STRUCT; + } else { + char str[50]; + sprintf(str, "don't know what type: %d", type); + rb_raise(rb_eStandardError, "%s", str); + return 0; + } +} + +static void write_byte_direct(VALUE transport, int8_t b) { + WRITE(transport, (char*)&b, 1); +} + +static void write_field_begin_internal(VALUE self, VALUE type, VALUE id_value, VALUE type_override) { + int id = FIX2INT(id_value); + int last_id = LAST_ID(self); + VALUE transport = GET_TRANSPORT(self); + + // if there's a type override, use that. + int8_t type_to_write = RTEST(type_override) ? FIX2INT(type_override) : get_compact_type(type); + // check if we can use delta encoding for the field id + int diff = id - last_id; + if (diff > 0 && diff <= 15) { + // write them together + write_byte_direct(transport, diff << 4 | (type_to_write & 0x0f)); + } else { + // write them separate + write_byte_direct(transport, type_to_write & 0x0f); + rb_thrift_compact_proto_write_i16(self, id_value); + } + + SET_LAST_ID(self, id_value); +} + +static int32_t int_to_zig_zag(int32_t n) { + return (n << 1) ^ (n >> 31); +} + +static uint64_t ll_to_zig_zag(int64_t n) { + return (n << 1) ^ (n >> 63); +} + +static void write_varint32(VALUE transport, uint32_t n) { + while (true) { + if ((n & ~0x7F) == 0) { + write_byte_direct(transport, n & 0x7f); + break; + } else { + write_byte_direct(transport, (n & 0x7F) | 0x80); + n = n >> 7; + } + } +} + +static void write_varint64(VALUE transport, uint64_t n) { + while (true) { + if ((n & ~0x7F) == 0) { + write_byte_direct(transport, n & 0x7f); + break; + } else { + write_byte_direct(transport, (n & 0x7F) | 0x80); + n = n >> 7; + } + } +} + +static void write_collection_begin(VALUE transport, VALUE elem_type, VALUE size_value) { + int size = FIX2INT(size_value); + if (size <= 14) { + write_byte_direct(transport, size << 4 | get_compact_type(elem_type)); + } else { + write_byte_direct(transport, 0xf0 | get_compact_type(elem_type)); + write_varint32(transport, size); + } +} + + +//-------------------------------- +// interface writing methods +//-------------------------------- + +VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32); +VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str); +VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf); + +VALUE rb_thrift_compact_proto_write_message_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_struct_begin(VALUE self, VALUE name) { + rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0)); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_struct_end(VALUE self) { + rb_ary_pop(rb_ivar_get(self, last_field_id)); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_field_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_map_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_list_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_set_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_message_begin(VALUE self, VALUE name, VALUE type, VALUE seqid) { + VALUE transport = GET_TRANSPORT(self); + write_byte_direct(transport, PROTOCOL_ID); + write_byte_direct(transport, (VERSION & VERSION_MASK) | ((FIX2INT(type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK)); + write_varint32(transport, FIX2INT(seqid)); + rb_thrift_compact_proto_write_string(self, name); + + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_field_begin(VALUE self, VALUE name, VALUE type, VALUE id) { + if (FIX2INT(type) == TTYPE_BOOL) { + // we want to possibly include the value, so we'll wait. + rb_ivar_set(self, boolean_field_id, rb_ary_new3(2, type, id)); + } else { + write_field_begin_internal(self, type, id, Qnil); + } + + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_field_stop(VALUE self) { + write_byte_direct(GET_TRANSPORT(self), TTYPE_STOP); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_map_begin(VALUE self, VALUE ktype, VALUE vtype, VALUE size_value) { + int size = FIX2INT(size_value); + VALUE transport = GET_TRANSPORT(self); + if (size == 0) { + write_byte_direct(transport, 0); + } else { + write_varint32(transport, size); + write_byte_direct(transport, get_compact_type(ktype) << 4 | get_compact_type(vtype)); + } + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_list_begin(VALUE self, VALUE etype, VALUE size) { + write_collection_begin(GET_TRANSPORT(self), etype, size); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_set_begin(VALUE self, VALUE etype, VALUE size) { + write_collection_begin(GET_TRANSPORT(self), etype, size); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_bool(VALUE self, VALUE b) { + int8_t type = b == Qtrue ? CTYPE_BOOLEAN_TRUE : CTYPE_BOOLEAN_FALSE; + VALUE boolean_field = rb_ivar_get(self, boolean_field_id); + if (NIL_P(boolean_field)) { + // we're not part of a field, so just write the value. + write_byte_direct(GET_TRANSPORT(self), type); + } else { + // we haven't written the field header yet + write_field_begin_internal(self, rb_ary_entry(boolean_field, 0), rb_ary_entry(boolean_field, 1), INT2FIX(type)); + rb_ivar_set(self, boolean_field_id, Qnil); + } + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_byte(VALUE self, VALUE byte) { + CHECK_NIL(byte); + write_byte_direct(GET_TRANSPORT(self), FIX2INT(byte)); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_i16(VALUE self, VALUE i16) { + rb_thrift_compact_proto_write_i32(self, i16); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_i32(VALUE self, VALUE i32) { + CHECK_NIL(i32); + write_varint32(GET_TRANSPORT(self), int_to_zig_zag(NUM2INT(i32))); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_i64(VALUE self, VALUE i64) { + CHECK_NIL(i64); + write_varint64(GET_TRANSPORT(self), ll_to_zig_zag(NUM2LL(i64))); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_double(VALUE self, VALUE dub) { + CHECK_NIL(dub); + // Unfortunately, bitwise_cast doesn't work in C. Bad C! + union { + double f; + int64_t l; + } transfer; + transfer.f = RFLOAT_VALUE(rb_Float(dub)); + char buf[8]; + buf[0] = transfer.l & 0xff; + buf[1] = (transfer.l >> 8) & 0xff; + buf[2] = (transfer.l >> 16) & 0xff; + buf[3] = (transfer.l >> 24) & 0xff; + buf[4] = (transfer.l >> 32) & 0xff; + buf[5] = (transfer.l >> 40) & 0xff; + buf[6] = (transfer.l >> 48) & 0xff; + buf[7] = (transfer.l >> 56) & 0xff; + WRITE(GET_TRANSPORT(self), buf, 8); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_string(VALUE self, VALUE str) { + str = convert_to_utf8_byte_buffer(str); + rb_thrift_compact_proto_write_binary(self, str); + return Qnil; +} + +VALUE rb_thrift_compact_proto_write_binary(VALUE self, VALUE buf) { + buf = force_binary_encoding(buf); + VALUE transport = GET_TRANSPORT(self); + write_varint32(transport, RSTRING_LEN(buf)); + WRITE(transport, StringValuePtr(buf), RSTRING_LEN(buf)); + return Qnil; +} + +//--------------------------------------- +// interface reading methods +//--------------------------------------- + +#define is_bool_type(ctype) (((ctype) & 0x0F) == CTYPE_BOOLEAN_TRUE || ((ctype) & 0x0F) == CTYPE_BOOLEAN_FALSE) + +VALUE rb_thrift_compact_proto_read_string(VALUE self); +VALUE rb_thrift_compact_proto_read_binary(VALUE self); +VALUE rb_thrift_compact_proto_read_byte(VALUE self); +VALUE rb_thrift_compact_proto_read_i32(VALUE self); +VALUE rb_thrift_compact_proto_read_i16(VALUE self); + +static int8_t get_ttype(int8_t ctype) { + if (ctype == TTYPE_STOP) { + return TTYPE_STOP; + } else if (ctype == CTYPE_BOOLEAN_TRUE || ctype == CTYPE_BOOLEAN_FALSE) { + return TTYPE_BOOL; + } else if (ctype == CTYPE_BYTE) { + return TTYPE_BYTE; + } else if (ctype == CTYPE_I16) { + return TTYPE_I16; + } else if (ctype == CTYPE_I32) { + return TTYPE_I32; + } else if (ctype == CTYPE_I64) { + return TTYPE_I64; + } else if (ctype == CTYPE_DOUBLE) { + return TTYPE_DOUBLE; + } else if (ctype == CTYPE_BINARY) { + return TTYPE_STRING; + } else if (ctype == CTYPE_LIST) { + return TTYPE_LIST; + } else if (ctype == CTYPE_SET) { + return TTYPE_SET; + } else if (ctype == CTYPE_MAP) { + return TTYPE_MAP; + } else if (ctype == CTYPE_STRUCT) { + return TTYPE_STRUCT; + } else { + char str[50]; + sprintf(str, "don't know what type: %d", ctype); + rb_raise(rb_eStandardError, "%s", str); + return 0; + } +} + +static char read_byte_direct(VALUE self) { + VALUE byte = rb_funcall(GET_TRANSPORT(self), read_byte_method_id, 0); + return (char)(FIX2INT(byte)); +} + +static int64_t zig_zag_to_ll(int64_t n) { + return (((uint64_t)n) >> 1) ^ -(n & 1); +} + +static int32_t zig_zag_to_int(int32_t n) { + return (((uint32_t)n) >> 1) ^ -(n & 1); +} + +static int64_t read_varint64(VALUE self) { + int shift = 0; + int64_t result = 0; + while (true) { + int8_t b = read_byte_direct(self); + result = result | ((uint64_t)(b & 0x7f) << shift); + if ((b & 0x80) != 0x80) { + break; + } + shift += 7; + } + return result; +} + +static int16_t read_i16(VALUE self) { + return zig_zag_to_int((int32_t)read_varint64(self)); +} + +static VALUE get_protocol_exception(VALUE code, VALUE message) { + VALUE args[2]; + args[0] = code; + args[1] = message; + return rb_class_new_instance(2, (VALUE*)&args, protocol_exception_class); +} + +VALUE rb_thrift_compact_proto_read_message_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_struct_begin(VALUE self) { + rb_ary_push(rb_ivar_get(self, last_field_id), INT2FIX(0)); + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_struct_end(VALUE self) { + rb_ary_pop(rb_ivar_get(self, last_field_id)); + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_field_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_map_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_list_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_set_end(VALUE self) { + return Qnil; +} + +VALUE rb_thrift_compact_proto_read_message_begin(VALUE self) { + int8_t protocol_id = read_byte_direct(self); + if (protocol_id != PROTOCOL_ID) { + char buf[100]; + int len = sprintf(buf, "Expected protocol id %d but got %d", PROTOCOL_ID, protocol_id); + buf[len] = 0; + rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf))); + } + + int8_t version_and_type = read_byte_direct(self); + int8_t version = version_and_type & VERSION_MASK; + if (version != VERSION) { + char buf[100]; + int len = sprintf(buf, "Expected version id %d but got %d", version, VERSION); + buf[len] = 0; + rb_exc_raise(get_protocol_exception(INT2FIX(-1), rb_str_new2(buf))); + } + + int8_t type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS; + int32_t seqid = read_varint64(self); + VALUE messageName = rb_thrift_compact_proto_read_string(self); + return rb_ary_new3(3, messageName, INT2FIX(type), INT2NUM(seqid)); +} + +VALUE rb_thrift_compact_proto_read_field_begin(VALUE self) { + int8_t type = read_byte_direct(self); + // if it's a stop, then we can return immediately, as the struct is over. + if ((type & 0x0f) == TTYPE_STOP) { + return rb_ary_new3(3, Qnil, INT2FIX(0), INT2FIX(0)); + } else { + int field_id = 0; + + // mask off the 4 MSB of the type header. it could contain a field id delta. + uint8_t modifier = ((type & 0xf0) >> 4); + + if (modifier == 0) { + // not a delta. look ahead for the zigzag varint field id. + (void) LAST_ID(self); + field_id = read_i16(self); + } else { + // has a delta. add the delta to the last read field id. + field_id = LAST_ID(self) + modifier; + } + + // if this happens to be a boolean field, the value is encoded in the type + if (is_bool_type(type)) { + // save the boolean value in a special instance variable. + rb_ivar_set(self, bool_value_id, (type & 0x0f) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse); + } + + // push the new field onto the field stack so we can keep the deltas going. + SET_LAST_ID(self, INT2FIX(field_id)); + return rb_ary_new3(3, Qnil, INT2FIX(get_ttype(type & 0x0f)), INT2FIX(field_id)); + } +} + +VALUE rb_thrift_compact_proto_read_map_begin(VALUE self) { + int32_t size = read_varint64(self); + uint8_t key_and_value_type = size == 0 ? 0 : read_byte_direct(self); + return rb_ary_new3(3, INT2FIX(get_ttype(key_and_value_type >> 4)), INT2FIX(get_ttype(key_and_value_type & 0xf)), INT2FIX(size)); +} + +VALUE rb_thrift_compact_proto_read_list_begin(VALUE self) { + uint8_t size_and_type = read_byte_direct(self); + int32_t size = (size_and_type >> 4) & 0x0f; + if (size == 15) { + size = read_varint64(self); + } + uint8_t type = get_ttype(size_and_type & 0x0f); + return rb_ary_new3(2, INT2FIX(type), INT2FIX(size)); +} + +VALUE rb_thrift_compact_proto_read_set_begin(VALUE self) { + return rb_thrift_compact_proto_read_list_begin(self); +} + +VALUE rb_thrift_compact_proto_read_bool(VALUE self) { + VALUE bool_value = rb_ivar_get(self, bool_value_id); + if (NIL_P(bool_value)) { + return read_byte_direct(self) == CTYPE_BOOLEAN_TRUE ? Qtrue : Qfalse; + } else { + rb_ivar_set(self, bool_value_id, Qnil); + return bool_value; + } +} + +VALUE rb_thrift_compact_proto_read_byte(VALUE self) { + return INT2FIX(read_byte_direct(self)); +} + +VALUE rb_thrift_compact_proto_read_i16(VALUE self) { + return INT2FIX(read_i16(self)); +} + +VALUE rb_thrift_compact_proto_read_i32(VALUE self) { + return INT2NUM(zig_zag_to_int(read_varint64(self))); +} + +VALUE rb_thrift_compact_proto_read_i64(VALUE self) { + return LL2NUM(zig_zag_to_ll(read_varint64(self))); +} + +VALUE rb_thrift_compact_proto_read_double(VALUE self) { + union { + double f; + int64_t l; + } transfer; + VALUE rbuf = rb_ivar_get(self, rbuf_ivar_id); + rb_funcall(GET_TRANSPORT(self), read_into_buffer_method_id, 2, rbuf, INT2FIX(8)); + uint32_t lo = ((uint8_t)(RSTRING_PTR(rbuf)[0])) + | (((uint8_t)(RSTRING_PTR(rbuf)[1])) << 8) + | (((uint8_t)(RSTRING_PTR(rbuf)[2])) << 16) + | (((uint8_t)(RSTRING_PTR(rbuf)[3])) << 24); + uint64_t hi = (((uint8_t)(RSTRING_PTR(rbuf)[4]))) + | (((uint8_t)(RSTRING_PTR(rbuf)[5])) << 8) + | (((uint8_t)(RSTRING_PTR(rbuf)[6])) << 16) + | (((uint8_t)(RSTRING_PTR(rbuf)[7])) << 24); + transfer.l = (hi << 32) | lo; + + return rb_float_new(transfer.f); +} + +VALUE rb_thrift_compact_proto_read_string(VALUE self) { + VALUE buffer = rb_thrift_compact_proto_read_binary(self); + return convert_to_string(buffer); +} + +VALUE rb_thrift_compact_proto_read_binary(VALUE self) { + int64_t size = read_varint64(self); + return READ(self, size); +} + +static void Init_constants() { + thrift_compact_protocol_class = rb_const_get(thrift_module, rb_intern("CompactProtocol")); + + VERSION = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION"))); + VERSION_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("VERSION_MASK"))); + TYPE_MASK = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_MASK"))); + TYPE_BITS = rb_num2ll(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_BITS"))); + TYPE_SHIFT_AMOUNT = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("TYPE_SHIFT_AMOUNT"))); + PROTOCOL_ID = FIX2INT(rb_const_get(thrift_compact_protocol_class, rb_intern("PROTOCOL_ID"))); + + last_field_id = rb_intern("@last_field"); + boolean_field_id = rb_intern("@boolean_field"); + bool_value_id = rb_intern("@bool_value"); + rbuf_ivar_id = rb_intern("@rbuf"); +} + +static void Init_rb_methods() { + rb_define_method(thrift_compact_protocol_class, "native?", rb_thrift_compact_proto_native_qmark, 0); + + rb_define_method(thrift_compact_protocol_class, "write_message_begin", rb_thrift_compact_proto_write_message_begin, 3); + rb_define_method(thrift_compact_protocol_class, "write_field_begin", rb_thrift_compact_proto_write_field_begin, 3); + rb_define_method(thrift_compact_protocol_class, "write_field_stop", rb_thrift_compact_proto_write_field_stop, 0); + rb_define_method(thrift_compact_protocol_class, "write_map_begin", rb_thrift_compact_proto_write_map_begin, 3); + rb_define_method(thrift_compact_protocol_class, "write_list_begin", rb_thrift_compact_proto_write_list_begin, 2); + rb_define_method(thrift_compact_protocol_class, "write_set_begin", rb_thrift_compact_proto_write_set_begin, 2); + rb_define_method(thrift_compact_protocol_class, "write_byte", rb_thrift_compact_proto_write_byte, 1); + rb_define_method(thrift_compact_protocol_class, "write_bool", rb_thrift_compact_proto_write_bool, 1); + rb_define_method(thrift_compact_protocol_class, "write_i16", rb_thrift_compact_proto_write_i16, 1); + rb_define_method(thrift_compact_protocol_class, "write_i32", rb_thrift_compact_proto_write_i32, 1); + rb_define_method(thrift_compact_protocol_class, "write_i64", rb_thrift_compact_proto_write_i64, 1); + rb_define_method(thrift_compact_protocol_class, "write_double", rb_thrift_compact_proto_write_double, 1); + rb_define_method(thrift_compact_protocol_class, "write_string", rb_thrift_compact_proto_write_string, 1); + rb_define_method(thrift_compact_protocol_class, "write_binary", rb_thrift_compact_proto_write_binary, 1); + + rb_define_method(thrift_compact_protocol_class, "write_message_end", rb_thrift_compact_proto_write_message_end, 0); + rb_define_method(thrift_compact_protocol_class, "write_struct_begin", rb_thrift_compact_proto_write_struct_begin, 1); + rb_define_method(thrift_compact_protocol_class, "write_struct_end", rb_thrift_compact_proto_write_struct_end, 0); + rb_define_method(thrift_compact_protocol_class, "write_field_end", rb_thrift_compact_proto_write_field_end, 0); + rb_define_method(thrift_compact_protocol_class, "write_map_end", rb_thrift_compact_proto_write_map_end, 0); + rb_define_method(thrift_compact_protocol_class, "write_list_end", rb_thrift_compact_proto_write_list_end, 0); + rb_define_method(thrift_compact_protocol_class, "write_set_end", rb_thrift_compact_proto_write_set_end, 0); + + + rb_define_method(thrift_compact_protocol_class, "read_message_begin", rb_thrift_compact_proto_read_message_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_field_begin", rb_thrift_compact_proto_read_field_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_map_begin", rb_thrift_compact_proto_read_map_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_list_begin", rb_thrift_compact_proto_read_list_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_set_begin", rb_thrift_compact_proto_read_set_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_byte", rb_thrift_compact_proto_read_byte, 0); + rb_define_method(thrift_compact_protocol_class, "read_bool", rb_thrift_compact_proto_read_bool, 0); + rb_define_method(thrift_compact_protocol_class, "read_i16", rb_thrift_compact_proto_read_i16, 0); + rb_define_method(thrift_compact_protocol_class, "read_i32", rb_thrift_compact_proto_read_i32, 0); + rb_define_method(thrift_compact_protocol_class, "read_i64", rb_thrift_compact_proto_read_i64, 0); + rb_define_method(thrift_compact_protocol_class, "read_double", rb_thrift_compact_proto_read_double, 0); + rb_define_method(thrift_compact_protocol_class, "read_string", rb_thrift_compact_proto_read_string, 0); + rb_define_method(thrift_compact_protocol_class, "read_binary", rb_thrift_compact_proto_read_binary, 0); + + rb_define_method(thrift_compact_protocol_class, "read_message_end", rb_thrift_compact_proto_read_message_end, 0); + rb_define_method(thrift_compact_protocol_class, "read_struct_begin", rb_thrift_compact_proto_read_struct_begin, 0); + rb_define_method(thrift_compact_protocol_class, "read_struct_end", rb_thrift_compact_proto_read_struct_end, 0); + rb_define_method(thrift_compact_protocol_class, "read_field_end", rb_thrift_compact_proto_read_field_end, 0); + rb_define_method(thrift_compact_protocol_class, "read_map_end", rb_thrift_compact_proto_read_map_end, 0); + rb_define_method(thrift_compact_protocol_class, "read_list_end", rb_thrift_compact_proto_read_list_end, 0); + rb_define_method(thrift_compact_protocol_class, "read_set_end", rb_thrift_compact_proto_read_set_end, 0); +} + +void Init_compact_protocol() { + Init_constants(); + Init_rb_methods(); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.h new file mode 100644 index 00000000..163915e9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/compact_protocol.h @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +void Init_compact_protocol(); diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/constants.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/constants.h new file mode 100644 index 00000000..e7aec447 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/constants.h @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +extern int TTYPE_STOP; +extern int TTYPE_BOOL; +extern int TTYPE_BYTE; +extern int TTYPE_I16; +extern int TTYPE_I32; +extern int TTYPE_I64; +extern int TTYPE_DOUBLE; +extern int TTYPE_STRING; +extern int TTYPE_MAP; +extern int TTYPE_SET; +extern int TTYPE_LIST; +extern int TTYPE_STRUCT; + +extern ID validate_method_id; +extern ID write_struct_begin_method_id; +extern ID write_struct_end_method_id; +extern ID write_field_begin_method_id; +extern ID write_field_end_method_id; +extern ID write_boolean_method_id; +extern ID write_byte_method_id; +extern ID write_i16_method_id; +extern ID write_i32_method_id; +extern ID write_i64_method_id; +extern ID write_double_method_id; +extern ID write_string_method_id; +extern ID write_binary_method_id; +extern ID write_map_begin_method_id; +extern ID write_map_end_method_id; +extern ID write_list_begin_method_id; +extern ID write_list_end_method_id; +extern ID write_set_begin_method_id; +extern ID write_set_end_method_id; +extern ID read_bool_method_id; +extern ID read_byte_method_id; +extern ID read_i16_method_id; +extern ID read_i32_method_id; +extern ID read_i64_method_id; +extern ID read_string_method_id; +extern ID read_binary_method_id; +extern ID read_double_method_id; +extern ID read_map_begin_method_id; +extern ID read_map_end_method_id; +extern ID read_list_begin_method_id; +extern ID read_list_end_method_id; +extern ID read_set_begin_method_id; +extern ID read_set_end_method_id; +extern ID read_struct_begin_method_id; +extern ID read_struct_end_method_id; +extern ID read_field_begin_method_id; +extern ID read_field_end_method_id; +extern ID keys_method_id; +extern ID entries_method_id; +extern ID write_field_stop_method_id; +extern ID skip_method_id; +extern ID write_method_id; +extern ID read_all_method_id; +extern ID read_into_buffer_method_id; +extern ID force_binary_encoding_id; +extern ID convert_to_utf8_byte_buffer_id; +extern ID convert_to_string_id; + +extern ID fields_const_id; +extern ID transport_ivar_id; +extern ID strict_read_ivar_id; +extern ID strict_write_ivar_id; + +extern VALUE type_sym; +extern VALUE name_sym; +extern VALUE key_sym; +extern VALUE value_sym; +extern VALUE element_sym; +extern VALUE class_sym; +extern VALUE binary_sym; + +extern VALUE rb_cSet; +extern VALUE thrift_module; +extern VALUE thrift_types_module; +extern VALUE thrift_bytes_module; +extern VALUE class_thrift_protocol; +extern VALUE protocol_exception_class; diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/extconf.rb b/vendor/src/github.com/apache/thrift/lib/rb/ext/extconf.rb new file mode 100644 index 00000000..b35f60bf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/extconf.rb @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/ + File.open('Makefile', 'w'){|f| f.puts "all:\n\ninstall:\n" } +else + require 'mkmf' + require 'rbconfig' + + $ARCH_FLAGS = RbConfig::CONFIG['CFLAGS'].scan( /(-arch )(\S+)/ ).map{|x,y| x + y + ' ' }.join('') + + + $CFLAGS = "-fsigned-char -g -O2 -Wall -Werror " + $ARCH_FLAGS + + have_func("strlcpy", "string.h") + + create_makefile 'thrift_native' +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/macros.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/macros.h new file mode 100644 index 00000000..265f6930 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/macros.h @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define GET_TRANSPORT(obj) rb_ivar_get(obj, transport_ivar_id) +#define GET_STRICT_READ(obj) rb_ivar_get(obj, strict_read_ivar_id) +#define GET_STRICT_WRITE(obj) rb_ivar_get(obj, strict_write_ivar_id) +#define WRITE(obj, data, length) rb_funcall(obj, write_method_id, 1, rb_str_new(data, length)) +#define CHECK_NIL(obj) if (NIL_P(obj)) { rb_raise(rb_eStandardError, "nil argument not allowed!");} +#define READ(obj, length) rb_funcall(GET_TRANSPORT(obj), read_all_method_id, 1, INT2FIX(length)) + +#ifndef RFLOAT_VALUE +# define RFLOAT_VALUE(v) RFLOAT(rb_Float(v))->value +#endif + +#ifndef RSTRING_LEN +# define RSTRING_LEN(v) RSTRING(rb_String(v))->len +#endif + +#ifndef RSTRING_PTR +# define RSTRING_PTR(v) RSTRING(rb_String(v))->ptr +#endif + +#ifndef RARRAY_LEN +# define RARRAY_LEN(v) RARRAY(rb_Array(v))->len +#endif diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.c new file mode 100644 index 00000000..8b52c4a3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.c @@ -0,0 +1,134 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +ID buf_ivar_id; +ID index_ivar_id; + +ID slice_method_id; + +int GARBAGE_BUFFER_SIZE; + +#define GET_BUF(self) rb_ivar_get(self, buf_ivar_id) + +VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str); +VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value); +VALUE rb_thrift_memory_buffer_read_byte(VALUE self); +VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value); + +VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str) { + VALUE buf = GET_BUF(self); + str = force_binary_encoding(str); + rb_str_buf_cat(buf, StringValuePtr(str), RSTRING_LEN(str)); + return Qnil; +} + +VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) { + int length = FIX2INT(length_value); + + VALUE index_value = rb_ivar_get(self, index_ivar_id); + int index = FIX2INT(index_value); + + VALUE buf = GET_BUF(self); + VALUE data = rb_funcall(buf, slice_method_id, 2, index_value, length_value); + + index += length; + if (index > RSTRING_LEN(buf)) { + index = RSTRING_LEN(buf); + } + if (index >= GARBAGE_BUFFER_SIZE) { + rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1))); + index = 0; + } + rb_ivar_set(self, index_ivar_id, INT2FIX(index)); + + if (RSTRING_LEN(data) < length) { + rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer"); + } + + return data; +} + +VALUE rb_thrift_memory_buffer_read_byte(VALUE self) { + VALUE index_value = rb_ivar_get(self, index_ivar_id); + int index = FIX2INT(index_value); + + VALUE buf = GET_BUF(self); + if (index >= RSTRING_LEN(buf)) { + rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer"); + } + char byte = RSTRING_PTR(buf)[index++]; + + if (index >= GARBAGE_BUFFER_SIZE) { + rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1))); + index = 0; + } + rb_ivar_set(self, index_ivar_id, INT2FIX(index)); + + int result = (int) byte; + return INT2FIX(result); +} + +VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value) { + int i = 0; + int size = FIX2INT(size_value); + int index; + VALUE buf = GET_BUF(self); + + index = FIX2INT(rb_ivar_get(self, index_ivar_id)); + while (i < size) { + if (index >= RSTRING_LEN(buf)) { + rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer"); + } + char byte = RSTRING_PTR(buf)[index++]; + + if (i >= RSTRING_LEN(buffer_value)) { + rb_raise(rb_eIndexError, "index %d out of string", i); + } + ((char*)RSTRING_PTR(buffer_value))[i] = byte; + i++; + } + + if (index >= GARBAGE_BUFFER_SIZE) { + rb_ivar_set(self, buf_ivar_id, rb_funcall(buf, slice_method_id, 2, INT2FIX(index), INT2FIX(RSTRING_LEN(buf) - 1))); + index = 0; + } + rb_ivar_set(self, index_ivar_id, INT2FIX(index)); + + return INT2FIX(i); +} + +void Init_memory_buffer() { + VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport")); + rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1); + rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1); + rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0); + rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2); + + buf_ivar_id = rb_intern("@buf"); + index_ivar_id = rb_intern("@index"); + + slice_method_id = rb_intern("slice"); + + GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE"))); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.h new file mode 100644 index 00000000..b277fa6f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/memory_buffer.h @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +void Init_memory_buffer(); diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/protocol.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/protocol.c new file mode 100644 index 00000000..e69de29b diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/protocol.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/protocol.h new file mode 100644 index 00000000..e69de29b diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.c new file mode 100644 index 00000000..6700ff2d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.c @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "strlcpy.h" + +#ifndef HAVE_STRLCPY +#define HAVE_STRLCPY +size_t +strlcpy (char *dst, const char *src, size_t dst_sz) +{ + size_t n; + + for (n = 0; n < dst_sz; n++) { + if ((*dst++ = *src++) == '\0') + break; + } + + if (n < dst_sz) + return n; + if (n > 0) + *(dst - 1) = '\0'; + return n + strlen (src); +} +#endif + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.h new file mode 100644 index 00000000..f6fe0fe6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/strlcpy.h @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy (char *dst, const char *src, size_t dst_sz); +#else +#if !__has_builtin(strlcpy) +extern size_t strlcpy(char *, const char *, size_t); +#endif +#endif + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.c new file mode 100644 index 00000000..e3aa855e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.c @@ -0,0 +1,711 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "struct.h" +#include "constants.h" +#include "macros.h" +#include "strlcpy.h" + +VALUE thrift_union_class; + +ID setfield_id; +ID setvalue_id; + +ID to_s_method_id; +ID name_to_id_method_id; +static ID sorted_field_ids_method_id; + +#define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET) +#define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id) + +//------------------------------------------- +// Writing section +//------------------------------------------- + +// default fn pointers for protocol stuff here + +VALUE default_write_bool(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_boolean_method_id, 1, value); + return Qnil; +} + +VALUE default_write_byte(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_byte_method_id, 1, value); + return Qnil; +} + +VALUE default_write_i16(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_i16_method_id, 1, value); + return Qnil; +} + +VALUE default_write_i32(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_i32_method_id, 1, value); + return Qnil; +} + +VALUE default_write_i64(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_i64_method_id, 1, value); + return Qnil; +} + +VALUE default_write_double(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_double_method_id, 1, value); + return Qnil; +} + +VALUE default_write_string(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_string_method_id, 1, value); + return Qnil; +} + +VALUE default_write_binary(VALUE protocol, VALUE value) { + rb_funcall(protocol, write_binary_method_id, 1, value); + return Qnil; +} + +VALUE default_write_list_begin(VALUE protocol, VALUE etype, VALUE length) { + rb_funcall(protocol, write_list_begin_method_id, 2, etype, length); + return Qnil; +} + +VALUE default_write_list_end(VALUE protocol) { + rb_funcall(protocol, write_list_end_method_id, 0); + return Qnil; +} + +VALUE default_write_set_begin(VALUE protocol, VALUE etype, VALUE length) { + rb_funcall(protocol, write_set_begin_method_id, 2, etype, length); + return Qnil; +} + +VALUE default_write_set_end(VALUE protocol) { + rb_funcall(protocol, write_set_end_method_id, 0); + return Qnil; +} + +VALUE default_write_map_begin(VALUE protocol, VALUE ktype, VALUE vtype, VALUE length) { + rb_funcall(protocol, write_map_begin_method_id, 3, ktype, vtype, length); + return Qnil; +} + +VALUE default_write_map_end(VALUE protocol) { + rb_funcall(protocol, write_map_end_method_id, 0); + return Qnil; +} + +VALUE default_write_struct_begin(VALUE protocol, VALUE struct_name) { + rb_funcall(protocol, write_struct_begin_method_id, 1, struct_name); + return Qnil; +} + +VALUE default_write_struct_end(VALUE protocol) { + rb_funcall(protocol, write_struct_end_method_id, 0); + return Qnil; +} + +VALUE default_write_field_begin(VALUE protocol, VALUE name, VALUE type, VALUE id) { + rb_funcall(protocol, write_field_begin_method_id, 3, name, type, id); + return Qnil; +} + +VALUE default_write_field_end(VALUE protocol) { + rb_funcall(protocol, write_field_end_method_id, 0); + return Qnil; +} + +VALUE default_write_field_stop(VALUE protocol) { + rb_funcall(protocol, write_field_stop_method_id, 0); + return Qnil; +} + +VALUE default_read_field_begin(VALUE protocol) { + return rb_funcall(protocol, read_field_begin_method_id, 0); +} + +VALUE default_read_field_end(VALUE protocol) { + return rb_funcall(protocol, read_field_end_method_id, 0); +} + +VALUE default_read_map_begin(VALUE protocol) { + return rb_funcall(protocol, read_map_begin_method_id, 0); +} + +VALUE default_read_map_end(VALUE protocol) { + return rb_funcall(protocol, read_map_end_method_id, 0); +} + +VALUE default_read_list_begin(VALUE protocol) { + return rb_funcall(protocol, read_list_begin_method_id, 0); +} + +VALUE default_read_list_end(VALUE protocol) { + return rb_funcall(protocol, read_list_end_method_id, 0); +} + +VALUE default_read_set_begin(VALUE protocol) { + return rb_funcall(protocol, read_set_begin_method_id, 0); +} + +VALUE default_read_set_end(VALUE protocol) { + return rb_funcall(protocol, read_set_end_method_id, 0); +} + +VALUE default_read_byte(VALUE protocol) { + return rb_funcall(protocol, read_byte_method_id, 0); +} + +VALUE default_read_bool(VALUE protocol) { + return rb_funcall(protocol, read_bool_method_id, 0); +} + +VALUE default_read_i16(VALUE protocol) { + return rb_funcall(protocol, read_i16_method_id, 0); +} + +VALUE default_read_i32(VALUE protocol) { + return rb_funcall(protocol, read_i32_method_id, 0); +} + +VALUE default_read_i64(VALUE protocol) { + return rb_funcall(protocol, read_i64_method_id, 0); +} + +VALUE default_read_double(VALUE protocol) { + return rb_funcall(protocol, read_double_method_id, 0); +} + +VALUE default_read_string(VALUE protocol) { + return rb_funcall(protocol, read_string_method_id, 0); +} + +VALUE default_read_binary(VALUE protocol) { + return rb_funcall(protocol, read_binary_method_id, 0); +} + +VALUE default_read_struct_begin(VALUE protocol) { + return rb_funcall(protocol, read_struct_begin_method_id, 0); +} + +VALUE default_read_struct_end(VALUE protocol) { + return rb_funcall(protocol, read_struct_end_method_id, 0); +} + +// end default protocol methods + +static VALUE rb_thrift_union_write (VALUE self, VALUE protocol); +static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol); +static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info); + +VALUE get_field_value(VALUE obj, VALUE field_name) { + char name_buf[RSTRING_LEN(field_name) + 2]; + + name_buf[0] = '@'; + strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name) + 1); + + VALUE value = rb_ivar_get(obj, rb_intern(name_buf)); + + return value; +} + +static void write_container(int ttype, VALUE field_info, VALUE value, VALUE protocol) { + int sz, i; + + if (ttype == TTYPE_MAP) { + VALUE keys; + VALUE key; + VALUE val; + + Check_Type(value, T_HASH); + + VALUE key_info = rb_hash_aref(field_info, key_sym); + VALUE keytype_value = rb_hash_aref(key_info, type_sym); + int keytype = FIX2INT(keytype_value); + + VALUE value_info = rb_hash_aref(field_info, value_sym); + VALUE valuetype_value = rb_hash_aref(value_info, type_sym); + int valuetype = FIX2INT(valuetype_value); + + keys = rb_funcall(value, keys_method_id, 0); + + sz = RARRAY_LEN(keys); + + default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz)); + + for (i = 0; i < sz; i++) { + key = rb_ary_entry(keys, i); + val = rb_hash_aref(value, key); + + if (IS_CONTAINER(keytype)) { + write_container(keytype, key_info, key, protocol); + } else { + write_anything(keytype, key, protocol, key_info); + } + + if (IS_CONTAINER(valuetype)) { + write_container(valuetype, value_info, val, protocol); + } else { + write_anything(valuetype, val, protocol, value_info); + } + } + + default_write_map_end(protocol); + } else if (ttype == TTYPE_LIST) { + Check_Type(value, T_ARRAY); + + sz = RARRAY_LEN(value); + + VALUE element_type_info = rb_hash_aref(field_info, element_sym); + VALUE element_type_value = rb_hash_aref(element_type_info, type_sym); + int element_type = FIX2INT(element_type_value); + + default_write_list_begin(protocol, element_type_value, INT2FIX(sz)); + for (i = 0; i < sz; ++i) { + VALUE val = rb_ary_entry(value, i); + if (IS_CONTAINER(element_type)) { + write_container(element_type, element_type_info, val, protocol); + } else { + write_anything(element_type, val, protocol, element_type_info); + } + } + default_write_list_end(protocol); + } else if (ttype == TTYPE_SET) { + VALUE items; + + if (TYPE(value) == T_ARRAY) { + items = value; + } else { + if (rb_cSet == CLASS_OF(value)) { + items = rb_funcall(value, entries_method_id, 0); + } else { + Check_Type(value, T_HASH); + items = rb_funcall(value, keys_method_id, 0); + } + } + + sz = RARRAY_LEN(items); + + VALUE element_type_info = rb_hash_aref(field_info, element_sym); + VALUE element_type_value = rb_hash_aref(element_type_info, type_sym); + int element_type = FIX2INT(element_type_value); + + default_write_set_begin(protocol, element_type_value, INT2FIX(sz)); + + for (i = 0; i < sz; i++) { + VALUE val = rb_ary_entry(items, i); + if (IS_CONTAINER(element_type)) { + write_container(element_type, element_type_info, val, protocol); + } else { + write_anything(element_type, val, protocol, element_type_info); + } + } + + default_write_set_end(protocol); + } else { + rb_raise(rb_eNotImpError, "can't write container of type: %d", ttype); + } +} + +static void write_anything(int ttype, VALUE value, VALUE protocol, VALUE field_info) { + if (ttype == TTYPE_BOOL) { + default_write_bool(protocol, value); + } else if (ttype == TTYPE_BYTE) { + default_write_byte(protocol, value); + } else if (ttype == TTYPE_I16) { + default_write_i16(protocol, value); + } else if (ttype == TTYPE_I32) { + default_write_i32(protocol, value); + } else if (ttype == TTYPE_I64) { + default_write_i64(protocol, value); + } else if (ttype == TTYPE_DOUBLE) { + default_write_double(protocol, value); + } else if (ttype == TTYPE_STRING) { + VALUE is_binary = rb_hash_aref(field_info, binary_sym); + if (is_binary != Qtrue) { + default_write_string(protocol, value); + } else { + default_write_binary(protocol, value); + } + } else if (IS_CONTAINER(ttype)) { + write_container(ttype, field_info, value, protocol); + } else if (ttype == TTYPE_STRUCT) { + if (rb_obj_is_kind_of(value, thrift_union_class)) { + rb_thrift_union_write(value, protocol); + } else { + rb_thrift_struct_write(value, protocol); + } + } else { + rb_raise(rb_eNotImpError, "Unknown type for binary_encoding: %d", ttype); + } +} + +static VALUE rb_thrift_struct_write(VALUE self, VALUE protocol) { + // call validate + rb_funcall(self, validate_method_id, 0); + + // write struct begin + default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self))); + + // iterate through all the fields here + VALUE struct_fields = STRUCT_FIELDS(self); + VALUE sorted_field_ids = rb_funcall(self, sorted_field_ids_method_id, 0); + + int i = 0; + for (i=0; i < RARRAY_LEN(sorted_field_ids); i++) { + VALUE field_id = rb_ary_entry(sorted_field_ids, i); + + VALUE field_info = rb_hash_aref(struct_fields, field_id); + + VALUE ttype_value = rb_hash_aref(field_info, type_sym); + int ttype = FIX2INT(ttype_value); + VALUE field_name = rb_hash_aref(field_info, name_sym); + + VALUE field_value = get_field_value(self, field_name); + + if (!NIL_P(field_value)) { + default_write_field_begin(protocol, field_name, ttype_value, field_id); + + write_anything(ttype, field_value, protocol, field_info); + + default_write_field_end(protocol); + } + } + + default_write_field_stop(protocol); + + // write struct end + default_write_struct_end(protocol); + + return Qnil; +} + +//------------------------------------------- +// Reading section +//------------------------------------------- + +static VALUE rb_thrift_union_read(VALUE self, VALUE protocol); +static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol); +static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size); +static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size); + +static void set_field_value(VALUE obj, VALUE field_name, VALUE value) { + char name_buf[RSTRING_LEN(field_name) + 2]; + + name_buf[0] = '@'; + strlcpy(&name_buf[1], RSTRING_PTR(field_name), RSTRING_LEN(field_name)+1); + + rb_ivar_set(obj, rb_intern(name_buf), value); +} + +// Helper method to skip the contents of a map (assumes the map header has been read). +static void skip_map_contents(VALUE protocol, VALUE key_type_value, VALUE value_type_value, int size) { + int i; + for (i = 0; i < size; i++) { + rb_funcall(protocol, skip_method_id, 1, key_type_value); + rb_funcall(protocol, skip_method_id, 1, value_type_value); + } +} + +// Helper method to skip the contents of a list or set (assumes the list/set header has been read). +static void skip_list_or_set_contents(VALUE protocol, VALUE element_type_value, int size) { + int i; + for (i = 0; i < size; i++) { + rb_funcall(protocol, skip_method_id, 1, element_type_value); + } +} + +static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) { + VALUE result = Qnil; + + if (ttype == TTYPE_BOOL) { + result = default_read_bool(protocol); + } else if (ttype == TTYPE_BYTE) { + result = default_read_byte(protocol); + } else if (ttype == TTYPE_I16) { + result = default_read_i16(protocol); + } else if (ttype == TTYPE_I32) { + result = default_read_i32(protocol); + } else if (ttype == TTYPE_I64) { + result = default_read_i64(protocol); + } else if (ttype == TTYPE_STRING) { + VALUE is_binary = rb_hash_aref(field_info, binary_sym); + if (is_binary != Qtrue) { + result = default_read_string(protocol); + } else { + result = default_read_binary(protocol); + } + } else if (ttype == TTYPE_DOUBLE) { + result = default_read_double(protocol); + } else if (ttype == TTYPE_STRUCT) { + VALUE klass = rb_hash_aref(field_info, class_sym); + result = rb_class_new_instance(0, NULL, klass); + + if (rb_obj_is_kind_of(result, thrift_union_class)) { + rb_thrift_union_read(result, protocol); + } else { + rb_thrift_struct_read(result, protocol); + } + } else if (ttype == TTYPE_MAP) { + int i; + + VALUE map_header = default_read_map_begin(protocol); + int key_ttype = FIX2INT(rb_ary_entry(map_header, 0)); + int value_ttype = FIX2INT(rb_ary_entry(map_header, 1)); + int num_entries = FIX2INT(rb_ary_entry(map_header, 2)); + + // Check the declared key and value types against the expected ones and skip the map contents + // if the types don't match. + VALUE key_info = rb_hash_aref(field_info, key_sym); + VALUE value_info = rb_hash_aref(field_info, value_sym); + + if (!NIL_P(key_info) && !NIL_P(value_info)) { + int specified_key_type = FIX2INT(rb_hash_aref(key_info, type_sym)); + int specified_value_type = FIX2INT(rb_hash_aref(value_info, type_sym)); + if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) { + result = rb_hash_new(); + + for (i = 0; i < num_entries; ++i) { + VALUE key, val; + + key = read_anything(protocol, key_ttype, key_info); + val = read_anything(protocol, value_ttype, value_info); + + rb_hash_aset(result, key, val); + } + } else { + skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries); + } + } else { + skip_map_contents(protocol, INT2FIX(key_ttype), INT2FIX(value_ttype), num_entries); + } + + default_read_map_end(protocol); + } else if (ttype == TTYPE_LIST) { + int i; + + VALUE list_header = default_read_list_begin(protocol); + int element_ttype = FIX2INT(rb_ary_entry(list_header, 0)); + int num_elements = FIX2INT(rb_ary_entry(list_header, 1)); + + // Check the declared element type against the expected one and skip the list contents + // if the types don't match. + VALUE element_info = rb_hash_aref(field_info, element_sym); + if (!NIL_P(element_info)) { + int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym)); + if (specified_element_type == element_ttype) { + result = rb_ary_new2(num_elements); + + for (i = 0; i < num_elements; ++i) { + rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym))); + } + } else { + skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements); + } + } else { + skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements); + } + + default_read_list_end(protocol); + } else if (ttype == TTYPE_SET) { + VALUE items; + int i; + + VALUE set_header = default_read_set_begin(protocol); + int element_ttype = FIX2INT(rb_ary_entry(set_header, 0)); + int num_elements = FIX2INT(rb_ary_entry(set_header, 1)); + + // Check the declared element type against the expected one and skip the set contents + // if the types don't match. + VALUE element_info = rb_hash_aref(field_info, element_sym); + if (!NIL_P(element_info)) { + int specified_element_type = FIX2INT(rb_hash_aref(element_info, type_sym)); + if (specified_element_type == element_ttype) { + items = rb_ary_new2(num_elements); + + for (i = 0; i < num_elements; ++i) { + rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym))); + } + + result = rb_class_new_instance(1, &items, rb_cSet); + } else { + skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements); + } + } else { + skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements); + } + + default_read_set_end(protocol); + } else { + rb_raise(rb_eNotImpError, "read_anything not implemented for type %d!", ttype); + } + + return result; +} + +static VALUE rb_thrift_struct_read(VALUE self, VALUE protocol) { + // read struct begin + default_read_struct_begin(protocol); + + VALUE struct_fields = STRUCT_FIELDS(self); + + // read each field + while (true) { + VALUE field_header = default_read_field_begin(protocol); + VALUE field_type_value = rb_ary_entry(field_header, 1); + int field_type = FIX2INT(field_type_value); + + if (field_type == TTYPE_STOP) { + break; + } + + // make sure we got a type we expected + VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2)); + + if (!NIL_P(field_info)) { + int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym)); + if (field_type == specified_type) { + // read the value + VALUE name = rb_hash_aref(field_info, name_sym); + set_field_value(self, name, read_anything(protocol, field_type, field_info)); + } else { + rb_funcall(protocol, skip_method_id, 1, field_type_value); + } + } else { + rb_funcall(protocol, skip_method_id, 1, field_type_value); + } + + // read field end + default_read_field_end(protocol); + } + + // read struct end + default_read_struct_end(protocol); + + // call validate + rb_funcall(self, validate_method_id, 0); + + return Qnil; +} + + +// -------------------------------- +// Union section +// -------------------------------- + +static VALUE rb_thrift_union_read(VALUE self, VALUE protocol) { + // read struct begin + default_read_struct_begin(protocol); + + VALUE struct_fields = STRUCT_FIELDS(self); + + VALUE field_header = default_read_field_begin(protocol); + VALUE field_type_value = rb_ary_entry(field_header, 1); + int field_type = FIX2INT(field_type_value); + + // make sure we got a type we expected + VALUE field_info = rb_hash_aref(struct_fields, rb_ary_entry(field_header, 2)); + + if (!NIL_P(field_info)) { + int specified_type = FIX2INT(rb_hash_aref(field_info, type_sym)); + if (field_type == specified_type) { + // read the value + VALUE name = rb_hash_aref(field_info, name_sym); + rb_iv_set(self, "@setfield", rb_str_intern(name)); + rb_iv_set(self, "@value", read_anything(protocol, field_type, field_info)); + } else { + rb_funcall(protocol, skip_method_id, 1, field_type_value); + } + } else { + rb_funcall(protocol, skip_method_id, 1, field_type_value); + } + + // read field end + default_read_field_end(protocol); + + field_header = default_read_field_begin(protocol); + field_type_value = rb_ary_entry(field_header, 1); + field_type = FIX2INT(field_type_value); + + if (field_type != TTYPE_STOP) { + rb_raise(rb_eRuntimeError, "too many fields in union!"); + } + + // read struct end + default_read_struct_end(protocol); + + // call validate + rb_funcall(self, validate_method_id, 0); + + return Qnil; +} + +static VALUE rb_thrift_union_write(VALUE self, VALUE protocol) { + // call validate + rb_funcall(self, validate_method_id, 0); + + // write struct begin + default_write_struct_begin(protocol, rb_class_name(CLASS_OF(self))); + + VALUE struct_fields = STRUCT_FIELDS(self); + + VALUE setfield = rb_ivar_get(self, setfield_id); + VALUE setvalue = rb_ivar_get(self, setvalue_id); + VALUE field_id = rb_funcall(self, name_to_id_method_id, 1, rb_funcall(setfield, to_s_method_id, 0)); + + VALUE field_info = rb_hash_aref(struct_fields, field_id); + + if(NIL_P(field_info)) { + rb_raise(rb_eRuntimeError, "set_field is not valid for this union!"); + } + + VALUE ttype_value = rb_hash_aref(field_info, type_sym); + int ttype = FIX2INT(ttype_value); + + default_write_field_begin(protocol, setfield, ttype_value, field_id); + + write_anything(ttype, setvalue, protocol, field_info); + + default_write_field_end(protocol); + + default_write_field_stop(protocol); + + // write struct end + default_write_struct_end(protocol); + + return Qnil; +} + +void Init_struct() { + VALUE struct_module = rb_const_get(thrift_module, rb_intern("Struct")); + + rb_define_method(struct_module, "write", rb_thrift_struct_write, 1); + rb_define_method(struct_module, "read", rb_thrift_struct_read, 1); + + thrift_union_class = rb_const_get(thrift_module, rb_intern("Union")); + + rb_define_method(thrift_union_class, "write", rb_thrift_union_write, 1); + rb_define_method(thrift_union_class, "read", rb_thrift_union_read, 1); + + setfield_id = rb_intern("@setfield"); + setvalue_id = rb_intern("@value"); + + to_s_method_id = rb_intern("to_s"); + name_to_id_method_id = rb_intern("name_to_id"); + sorted_field_ids_method_id = rb_intern("sorted_field_ids"); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.h b/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.h new file mode 100644 index 00000000..4748be5c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/struct.h @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#include +#include + +void Init_struct(); +void Init_union(); diff --git a/vendor/src/github.com/apache/thrift/lib/rb/ext/thrift_native.c b/vendor/src/github.com/apache/thrift/lib/rb/ext/thrift_native.c new file mode 100644 index 00000000..3430b7c2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/ext/thrift_native.c @@ -0,0 +1,201 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include + +// cached classes/modules +VALUE rb_cSet; +VALUE thrift_module; +VALUE thrift_bytes_module; +VALUE thrift_types_module; + +// TType constants +int TTYPE_STOP; +int TTYPE_BOOL; +int TTYPE_BYTE; +int TTYPE_I16; +int TTYPE_I32; +int TTYPE_I64; +int TTYPE_DOUBLE; +int TTYPE_STRING; +int TTYPE_MAP; +int TTYPE_SET; +int TTYPE_LIST; +int TTYPE_STRUCT; + +// method ids +ID validate_method_id; +ID write_struct_begin_method_id; +ID write_struct_end_method_id; +ID write_field_begin_method_id; +ID write_field_end_method_id; +ID write_boolean_method_id; +ID write_byte_method_id; +ID write_i16_method_id; +ID write_i32_method_id; +ID write_i64_method_id; +ID write_double_method_id; +ID write_string_method_id; +ID write_binary_method_id; +ID write_map_begin_method_id; +ID write_map_end_method_id; +ID write_list_begin_method_id; +ID write_list_end_method_id; +ID write_set_begin_method_id; +ID write_set_end_method_id; +ID read_bool_method_id; +ID read_byte_method_id; +ID read_i16_method_id; +ID read_i32_method_id; +ID read_i64_method_id; +ID read_string_method_id; +ID read_binary_method_id; +ID read_double_method_id; +ID read_map_begin_method_id; +ID read_map_end_method_id; +ID read_list_begin_method_id; +ID read_list_end_method_id; +ID read_set_begin_method_id; +ID read_set_end_method_id; +ID read_struct_begin_method_id; +ID read_struct_end_method_id; +ID read_field_begin_method_id; +ID read_field_end_method_id; +ID keys_method_id; +ID entries_method_id; +ID write_field_stop_method_id; +ID skip_method_id; +ID write_method_id; +ID read_all_method_id; +ID read_into_buffer_method_id; +ID force_binary_encoding_id; +ID convert_to_utf8_byte_buffer_id; +ID convert_to_string_id; + +// constant ids +ID fields_const_id; +ID transport_ivar_id; +ID strict_read_ivar_id; +ID strict_write_ivar_id; + +// cached symbols +VALUE type_sym; +VALUE name_sym; +VALUE key_sym; +VALUE value_sym; +VALUE element_sym; +VALUE class_sym; +VALUE binary_sym; +VALUE protocol_exception_class; + +void Init_thrift_native() { + // cached classes + thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift")); + thrift_bytes_module = rb_const_get(thrift_module, rb_intern("Bytes")); + thrift_types_module = rb_const_get(thrift_module, rb_intern("Types")); + rb_cSet = rb_const_get(rb_cObject, rb_intern("Set")); + protocol_exception_class = rb_const_get(thrift_module, rb_intern("ProtocolException")); + + // Init ttype constants + TTYPE_BOOL = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BOOL"))); + TTYPE_BYTE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("BYTE"))); + TTYPE_I16 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I16"))); + TTYPE_I32 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I32"))); + TTYPE_I64 = FIX2INT(rb_const_get(thrift_types_module, rb_intern("I64"))); + TTYPE_DOUBLE = FIX2INT(rb_const_get(thrift_types_module, rb_intern("DOUBLE"))); + TTYPE_STRING = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRING"))); + TTYPE_MAP = FIX2INT(rb_const_get(thrift_types_module, rb_intern("MAP"))); + TTYPE_SET = FIX2INT(rb_const_get(thrift_types_module, rb_intern("SET"))); + TTYPE_LIST = FIX2INT(rb_const_get(thrift_types_module, rb_intern("LIST"))); + TTYPE_STRUCT = FIX2INT(rb_const_get(thrift_types_module, rb_intern("STRUCT"))); + + // method ids + validate_method_id = rb_intern("validate"); + write_struct_begin_method_id = rb_intern("write_struct_begin"); + write_struct_end_method_id = rb_intern("write_struct_end"); + write_field_begin_method_id = rb_intern("write_field_begin"); + write_field_end_method_id = rb_intern("write_field_end"); + write_boolean_method_id = rb_intern("write_bool"); + write_byte_method_id = rb_intern("write_byte"); + write_i16_method_id = rb_intern("write_i16"); + write_i32_method_id = rb_intern("write_i32"); + write_i64_method_id = rb_intern("write_i64"); + write_double_method_id = rb_intern("write_double"); + write_string_method_id = rb_intern("write_string"); + write_binary_method_id = rb_intern("write_binary"); + write_map_begin_method_id = rb_intern("write_map_begin"); + write_map_end_method_id = rb_intern("write_map_end"); + write_list_begin_method_id = rb_intern("write_list_begin"); + write_list_end_method_id = rb_intern("write_list_end"); + write_set_begin_method_id = rb_intern("write_set_begin"); + write_set_end_method_id = rb_intern("write_set_end"); + read_bool_method_id = rb_intern("read_bool"); + read_byte_method_id = rb_intern("read_byte"); + read_i16_method_id = rb_intern("read_i16"); + read_i32_method_id = rb_intern("read_i32"); + read_i64_method_id = rb_intern("read_i64"); + read_string_method_id = rb_intern("read_string"); + read_binary_method_id = rb_intern("read_binary"); + read_double_method_id = rb_intern("read_double"); + read_map_begin_method_id = rb_intern("read_map_begin"); + read_map_end_method_id = rb_intern("read_map_end"); + read_list_begin_method_id = rb_intern("read_list_begin"); + read_list_end_method_id = rb_intern("read_list_end"); + read_set_begin_method_id = rb_intern("read_set_begin"); + read_set_end_method_id = rb_intern("read_set_end"); + read_struct_begin_method_id = rb_intern("read_struct_begin"); + read_struct_end_method_id = rb_intern("read_struct_end"); + read_field_begin_method_id = rb_intern("read_field_begin"); + read_field_end_method_id = rb_intern("read_field_end"); + keys_method_id = rb_intern("keys"); + entries_method_id = rb_intern("entries"); + write_field_stop_method_id = rb_intern("write_field_stop"); + skip_method_id = rb_intern("skip"); + write_method_id = rb_intern("write"); + read_all_method_id = rb_intern("read_all"); + read_into_buffer_method_id = rb_intern("read_into_buffer"); + force_binary_encoding_id = rb_intern("force_binary_encoding"); + convert_to_utf8_byte_buffer_id = rb_intern("convert_to_utf8_byte_buffer"); + convert_to_string_id = rb_intern("convert_to_string"); + + // constant ids + fields_const_id = rb_intern("FIELDS"); + transport_ivar_id = rb_intern("@trans"); + strict_read_ivar_id = rb_intern("@strict_read"); + strict_write_ivar_id = rb_intern("@strict_write"); + + // cached symbols + type_sym = ID2SYM(rb_intern("type")); + name_sym = ID2SYM(rb_intern("name")); + key_sym = ID2SYM(rb_intern("key")); + value_sym = ID2SYM(rb_intern("value")); + element_sym = ID2SYM(rb_intern("element")); + class_sym = ID2SYM(rb_intern("class")); + binary_sym = ID2SYM(rb_intern("binary")); + + Init_struct(); + Init_binary_protocol_accelerated(); + Init_compact_protocol(); + Init_memory_buffer(); +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift.rb new file mode 100644 index 00000000..0f581229 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift.rb @@ -0,0 +1,70 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Contains some contributions under the Thrift Software License. +# Please see doc/old-thrift-license.txt in the Thrift distribution for +# details. + +$:.unshift File.dirname(__FILE__) + +require 'thrift/bytes' +require 'thrift/core_ext' +require 'thrift/exceptions' +require 'thrift/types' +require 'thrift/processor' +require 'thrift/multiplexed_processor' +require 'thrift/client' +require 'thrift/struct' +require 'thrift/union' +require 'thrift/struct_union' + +# serializer +require 'thrift/serializer/serializer' +require 'thrift/serializer/deserializer' + +# protocol +require 'thrift/protocol/base_protocol' +require 'thrift/protocol/binary_protocol' +require 'thrift/protocol/binary_protocol_accelerated' +require 'thrift/protocol/compact_protocol' +require 'thrift/protocol/json_protocol' +require 'thrift/protocol/multiplexed_protocol' + +# transport +require 'thrift/transport/base_transport' +require 'thrift/transport/base_server_transport' +require 'thrift/transport/socket' +require 'thrift/transport/ssl_socket' +require 'thrift/transport/server_socket' +require 'thrift/transport/ssl_server_socket' +require 'thrift/transport/unix_socket' +require 'thrift/transport/unix_server_socket' +require 'thrift/transport/buffered_transport' +require 'thrift/transport/framed_transport' +require 'thrift/transport/http_client_transport' +require 'thrift/transport/io_stream_transport' +require 'thrift/transport/memory_buffer_transport' + +# server +require 'thrift/server/base_server' +require 'thrift/server/nonblocking_server' +require 'thrift/server/simple_server' +require 'thrift/server/threaded_server' +require 'thrift/server/thread_pool_server' + +require 'thrift/thrift_native' diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/bytes.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/bytes.rb new file mode 100644 index 00000000..efd4f644 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/bytes.rb @@ -0,0 +1,131 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + # A collection of utilities for working with bytes and byte buffers. + module Bytes + if RUBY_VERSION >= '1.9' + # Creates and empty byte buffer (String with BINARY encoding) + # + # size - The Integer size of the buffer (default: nil) to create + # + # Returns a String with BINARY encoding, filled with null characters + # if size is greater than zero + def self.empty_byte_buffer(size = nil) + if (size && size > 0) + "\0".force_encoding(Encoding::BINARY) * size + else + ''.force_encoding(Encoding::BINARY) + end + end + + # Forces the encoding of the buffer to BINARY. If the buffer + # passed is frozen, then it will be duplicated. + # + # buffer - The String to force the encoding of. + # + # Returns the String passed with an encoding of BINARY; returned + # String may be a duplicate. + def self.force_binary_encoding(buffer) + buffer = buffer.dup if buffer.frozen? + buffer.force_encoding(Encoding::BINARY) + end + + # Gets the byte value of a given position in a String. + # + # string - The String to retrive the byte value from. + # index - The Integer location of the byte value to retrieve. + # + # Returns an Integer value between 0 and 255. + def self.get_string_byte(string, index) + string.getbyte(index) + end + + # Sets the byte value given to a given index in a String. + # + # string - The String to set the byte value in. + # index - The Integer location to set the byte value at. + # byte - The Integer value (0 to 255) to set in the string. + # + # Returns an Integer value of the byte value to set. + def self.set_string_byte(string, index, byte) + string.setbyte(index, byte) + end + + # Converts the given String to a UTF-8 byte buffer. + # + # string - The String to convert. + # + # Returns a new String with BINARY encoding, containing the UTF-8 + # bytes of the original string. + def self.convert_to_utf8_byte_buffer(string) + if string.encoding != Encoding::UTF_8 + # transcode to UTF-8 + string = string.encode(Encoding::UTF_8) + else + # encoding is already UTF-8, but a duplicate is needed + string = string.dup + end + string.force_encoding(Encoding::BINARY) + end + + # Converts the given UTF-8 byte buffer into a String + # + # utf8_buffer - A String, with BINARY encoding, containing UTF-8 bytes + # + # Returns a new String with UTF-8 encoding, + def self.convert_to_string(utf8_buffer) + # duplicate the buffer, force encoding to UTF-8 + utf8_buffer.dup.force_encoding(Encoding::UTF_8) + end + else + def self.empty_byte_buffer(size = nil) + if (size && size > 0) + "\0" * size + else + '' + end + end + + def self.force_binary_encoding(buffer) + buffer + end + + def self.get_string_byte(string, index) + string[index] + end + + def self.set_string_byte(string, index, byte) + string[index] = byte + end + + def self.convert_to_utf8_byte_buffer(string) + # This assumes $KCODE is 'UTF8'/'U', which would mean the String is already a UTF-8 byte buffer + # TODO consider handling other $KCODE values and transcoding with iconv + string + end + + def self.convert_to_string(utf8_buffer) + # See comment in 'convert_to_utf8_byte_buffer' for relevant assumptions. + utf8_buffer + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/client.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/client.rb new file mode 100644 index 00000000..64ef0595 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/client.rb @@ -0,0 +1,71 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + module Client + def initialize(iprot, oprot=nil) + @iprot = iprot + @oprot = oprot || iprot + @seqid = 0 + end + + def send_message(name, args_class, args = {}) + @oprot.write_message_begin(name, MessageTypes::CALL, @seqid) + send_message_args(args_class, args) + end + + def send_oneway_message(name, args_class, args = {}) + @oprot.write_message_begin(name, MessageTypes::ONEWAY, @seqid) + send_message_args(args_class, args) + end + + def send_message_args(args_class, args) + data = args_class.new + args.each do |k, v| + data.send("#{k.to_s}=", v) + end + begin + data.write(@oprot) + rescue StandardError => e + @oprot.trans.close + raise e + end + @oprot.write_message_end + @oprot.trans.flush + end + + def receive_message(result_klass) + fname, mtype, rseqid = @iprot.read_message_begin + handle_exception(mtype) + result = result_klass.new + result.read(@iprot) + @iprot.read_message_end + result + end + + def handle_exception(mtype) + if mtype == MessageTypes::EXCEPTION + x = ApplicationException.new + x.read(@iprot) + @iprot.read_message_end + raise x + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext.rb new file mode 100644 index 00000000..f763cd53 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext.rb @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +Dir[File.dirname(__FILE__) + "/core_ext/*.rb"].each do |file| + name = File.basename(file, '.rb') + require "thrift/core_ext/#{name}" +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb new file mode 100644 index 00000000..b4fc90dd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/core_ext/fixnum.rb @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Versions of ruby pre 1.8.7 do not have an .ord method available in the Fixnum +# class. +# +if RUBY_VERSION < "1.8.7" + class Fixnum + def ord + self + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/exceptions.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/exceptions.rb new file mode 100644 index 00000000..68cb9e03 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/exceptions.rb @@ -0,0 +1,87 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class Exception < StandardError + def initialize(message) + super + @message = message + end + + attr_reader :message + end + + class ApplicationException < Exception + + UNKNOWN = 0 + UNKNOWN_METHOD = 1 + INVALID_MESSAGE_TYPE = 2 + WRONG_METHOD_NAME = 3 + BAD_SEQUENCE_ID = 4 + MISSING_RESULT = 5 + INTERNAL_ERROR = 6 + PROTOCOL_ERROR = 7 + INVALID_TRANSFORM = 8 + INVALID_PROTOCOL = 9 + UNSUPPORTED_CLIENT_TYPE = 10 + + attr_reader :type + + def initialize(type=UNKNOWN, message=nil) + super(message) + @type = type + end + + def read(iprot) + iprot.read_struct_begin + while true + fname, ftype, fid = iprot.read_field_begin + if ftype == Types::STOP + break + end + if fid == 1 and ftype == Types::STRING + @message = iprot.read_string + elsif fid == 2 and ftype == Types::I32 + @type = iprot.read_i32 + else + iprot.skip(ftype) + end + iprot.read_field_end + end + iprot.read_struct_end + end + + def write(oprot) + oprot.write_struct_begin('Thrift::ApplicationException') + unless @message.nil? + oprot.write_field_begin('message', Types::STRING, 1) + oprot.write_string(@message) + oprot.write_field_end + end + unless @type.nil? + oprot.write_field_begin('type', Types::I32, 2) + oprot.write_i32(@type) + oprot.write_field_end + end + oprot.write_field_stop + oprot.write_struct_end + end + + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/multiplexed_processor.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/multiplexed_processor.rb new file mode 100644 index 00000000..c734c04b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/multiplexed_processor.rb @@ -0,0 +1,76 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require 'thrift/protocol/protocol_decorator' +require 'thrift/protocol/base_protocol' + +module Thrift + class MultiplexedProcessor + def initialize + @actual_processors = {} + end + + def register_processor(service_name, processor) + @actual_processors[service_name] = processor + end + + def process(iprot, oprot) + name, type, seqid = iprot.read_message_begin + check_type(type) + check_separator(name) + service_name, method = name.split(':') + processor(service_name).process(StoredMessageProtocol.new(iprot, [method, type, seqid]), oprot) + end + + protected + + def processor(service_name) + if @actual_processors.has_key?(service_name) + @actual_processors[service_name] + else + raise Thrift::Exception.new("Service name not found: #{service_name}. Did you forget to call #{self.class.name}#register_processor?") + end + end + + def check_type(type) + unless [MessageTypes::CALL, MessageTypes::ONEWAY].include?(type) + raise Thrift::Exception.new('This should not have happened!?') + end + end + + def check_separator(name) + if name.count(':') < 1 + raise Thrift::Exception.new("Service name not found in message name: #{name}. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?") + end + end + end + + class StoredMessageProtocol < BaseProtocol + + include ProtocolDecorator + + def initialize(protocol, message_begin) + super(protocol) + @message_begin = message_begin + end + + def read_message_begin + @message_begin + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/processor.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/processor.rb new file mode 100644 index 00000000..fd312eee --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/processor.rb @@ -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. +# + +module Thrift + module Processor + def initialize(handler) + @handler = handler + end + + def process(iprot, oprot) + name, type, seqid = iprot.read_message_begin + if respond_to?("process_#{name}") + begin + send("process_#{name}", seqid, iprot, oprot) + rescue => e + x = ApplicationException.new(ApplicationException::INTERNAL_ERROR, 'Internal error') + write_error(x, oprot, name, seqid) + end + true + else + iprot.skip(Types::STRUCT) + iprot.read_message_end + x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function '+name) + write_error(x, oprot, name, seqid) + false + end + end + + def read_args(iprot, args_class) + args = args_class.new + args.read(iprot) + iprot.read_message_end + args + end + + def write_result(result, oprot, name, seqid) + oprot.write_message_begin(name, MessageTypes::REPLY, seqid) + result.write(oprot) + oprot.write_message_end + oprot.trans.flush + end + + def write_error(err, oprot, name, seqid) + oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid) + err.write(oprot) + oprot.write_message_end + oprot.trans.flush + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb new file mode 100644 index 00000000..88f44d46 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/base_protocol.rb @@ -0,0 +1,379 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# this require is to make generated struct definitions happy +require 'set' + +module Thrift + class ProtocolException < Exception + + UNKNOWN = 0 + INVALID_DATA = 1 + NEGATIVE_SIZE = 2 + SIZE_LIMIT = 3 + BAD_VERSION = 4 + NOT_IMPLEMENTED = 5 + DEPTH_LIMIT = 6 + + attr_reader :type + + def initialize(type=UNKNOWN, message=nil) + super(message) + @type = type + end + end + + class BaseProtocol + + attr_reader :trans + + def initialize(trans) + @trans = trans + end + + def native? + puts "wrong method is being called!" + false + end + + def write_message_begin(name, type, seqid) + raise NotImplementedError + end + + def write_message_end; nil; end + + def write_struct_begin(name) + raise NotImplementedError + end + + def write_struct_end; nil; end + + def write_field_begin(name, type, id) + raise NotImplementedError + end + + def write_field_end; nil; end + + def write_field_stop + raise NotImplementedError + end + + def write_map_begin(ktype, vtype, size) + raise NotImplementedError + end + + def write_map_end; nil; end + + def write_list_begin(etype, size) + raise NotImplementedError + end + + def write_list_end; nil; end + + def write_set_begin(etype, size) + raise NotImplementedError + end + + def write_set_end; nil; end + + def write_bool(bool) + raise NotImplementedError + end + + def write_byte(byte) + raise NotImplementedError + end + + def write_i16(i16) + raise NotImplementedError + end + + def write_i32(i32) + raise NotImplementedError + end + + def write_i64(i64) + raise NotImplementedError + end + + def write_double(dub) + raise NotImplementedError + end + + # Writes a Thrift String. In Ruby 1.9+, the String passed will be transcoded to UTF-8. + # + # str - The String to write. + # + # Raises EncodingError if the transcoding to UTF-8 fails. + # + # Returns nothing. + def write_string(str) + raise NotImplementedError + end + + # Writes a Thrift Binary (Thrift String with no encoding). In Ruby 1.9+, the String passed + # will forced into BINARY encoding. + # + # buf - The String to write. + # + # Returns nothing. + def write_binary(buf) + raise NotImplementedError + end + + def read_message_begin + raise NotImplementedError + end + + def read_message_end; nil; end + + def read_struct_begin + raise NotImplementedError + end + + def read_struct_end; nil; end + + def read_field_begin + raise NotImplementedError + end + + def read_field_end; nil; end + + def read_map_begin + raise NotImplementedError + end + + def read_map_end; nil; end + + def read_list_begin + raise NotImplementedError + end + + def read_list_end; nil; end + + def read_set_begin + raise NotImplementedError + end + + def read_set_end; nil; end + + def read_bool + raise NotImplementedError + end + + def read_byte + raise NotImplementedError + end + + def read_i16 + raise NotImplementedError + end + + def read_i32 + raise NotImplementedError + end + + def read_i64 + raise NotImplementedError + end + + def read_double + raise NotImplementedError + end + + # Reads a Thrift String. In Ruby 1.9+, all Strings will be returned with an Encoding of UTF-8. + # + # Returns a String. + def read_string + raise NotImplementedError + end + + # Reads a Thrift Binary (Thrift String without encoding). In Ruby 1.9+, all Strings will be returned + # with an Encoding of BINARY. + # + # Returns a String. + def read_binary + raise NotImplementedError + end + + # Writes a field based on the field information, field ID and value. + # + # field_info - A Hash containing the definition of the field: + # :name - The name of the field. + # :type - The type of the field, which must be a Thrift::Types constant. + # :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding). + # fid - The ID of the field. + # value - The field's value to write; object type varies based on :type. + # + # Returns nothing. + def write_field(*args) + if args.size == 3 + # handles the documented method signature - write_field(field_info, fid, value) + field_info = args[0] + fid = args[1] + value = args[2] + elsif args.size == 4 + # handles the deprecated method signature - write_field(name, type, fid, value) + field_info = {:name => args[0], :type => args[1]} + fid = args[2] + value = args[3] + else + raise ArgumentError, "wrong number of arguments (#{args.size} for 3)" + end + + write_field_begin(field_info[:name], field_info[:type], fid) + write_type(field_info, value) + write_field_end + end + + # Writes a field value based on the field information. + # + # field_info - A Hash containing the definition of the field: + # :type - The Thrift::Types constant that determines how the value is written. + # :binary - A Boolean flag that indicates if Thrift::Types::STRING is a binary string (string without encoding). + # value - The field's value to write; object type varies based on field_info[:type]. + # + # Returns nothing. + def write_type(field_info, value) + # if field_info is a Fixnum, assume it is a Thrift::Types constant + # convert it into a field_info Hash for backwards compatibility + if field_info.is_a? Fixnum + field_info = {:type => field_info} + end + + case field_info[:type] + when Types::BOOL + write_bool(value) + when Types::BYTE + write_byte(value) + when Types::DOUBLE + write_double(value) + when Types::I16 + write_i16(value) + when Types::I32 + write_i32(value) + when Types::I64 + write_i64(value) + when Types::STRING + if field_info[:binary] + write_binary(value) + else + write_string(value) + end + when Types::STRUCT + value.write(self) + else + raise NotImplementedError + end + end + + # Reads a field value based on the field information. + # + # field_info - A Hash containing the pertinent data to write: + # :type - The Thrift::Types constant that determines how the value is written. + # :binary - A flag that indicates if Thrift::Types::STRING is a binary string (string without encoding). + # + # Returns the value read; object type varies based on field_info[:type]. + def read_type(field_info) + # if field_info is a Fixnum, assume it is a Thrift::Types constant + # convert it into a field_info Hash for backwards compatibility + if field_info.is_a? Fixnum + field_info = {:type => field_info} + end + + case field_info[:type] + when Types::BOOL + read_bool + when Types::BYTE + read_byte + when Types::DOUBLE + read_double + when Types::I16 + read_i16 + when Types::I32 + read_i32 + when Types::I64 + read_i64 + when Types::STRING + if field_info[:binary] + read_binary + else + read_string + end + else + raise NotImplementedError + end + end + + def skip(type) + case type + when Types::STOP + nil + when Types::BOOL + read_bool + when Types::BYTE + read_byte + when Types::I16 + read_i16 + when Types::I32 + read_i32 + when Types::I64 + read_i64 + when Types::DOUBLE + read_double + when Types::STRING + read_string + when Types::STRUCT + read_struct_begin + while true + name, type, id = read_field_begin + break if type == Types::STOP + skip(type) + read_field_end + end + read_struct_end + when Types::MAP + ktype, vtype, size = read_map_begin + size.times do + skip(ktype) + skip(vtype) + end + read_map_end + when Types::SET + etype, size = read_set_begin + size.times do + skip(etype) + end + read_set_end + when Types::LIST + etype, size = read_list_begin + size.times do + skip(etype) + end + read_list_end + end + end + end + + class BaseProtocolFactory + def get_protocol(trans) + raise NotImplementedError + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb new file mode 100644 index 00000000..e70b1e3a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol.rb @@ -0,0 +1,237 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class BinaryProtocol < BaseProtocol + VERSION_MASK = 0xffff0000 + VERSION_1 = 0x80010000 + TYPE_MASK = 0x000000ff + + attr_reader :strict_read, :strict_write + + def initialize(trans, strict_read=true, strict_write=true) + super(trans) + @strict_read = strict_read + @strict_write = strict_write + + # Pre-allocated read buffer for fixed-size read methods. Needs to be at least 8 bytes long for + # read_i64() and read_double(). + @rbuf = Bytes.empty_byte_buffer(8) + end + + def write_message_begin(name, type, seqid) + # this is necessary because we added (needed) bounds checking to + # write_i32, and 0x80010000 is too big for that. + if strict_write + write_i16(VERSION_1 >> 16) + write_i16(type) + write_string(name) + write_i32(seqid) + else + write_string(name) + write_byte(type) + write_i32(seqid) + end + end + + def write_struct_begin(name); nil; end + + def write_field_begin(name, type, id) + write_byte(type) + write_i16(id) + end + + def write_field_stop + write_byte(Thrift::Types::STOP) + end + + def write_map_begin(ktype, vtype, size) + write_byte(ktype) + write_byte(vtype) + write_i32(size) + end + + def write_list_begin(etype, size) + write_byte(etype) + write_i32(size) + end + + def write_set_begin(etype, size) + write_byte(etype) + write_i32(size) + end + + def write_bool(bool) + write_byte(bool ? 1 : 0) + end + + def write_byte(byte) + raise RangeError if byte < -2**31 || byte >= 2**32 + trans.write([byte].pack('c')) + end + + def write_i16(i16) + trans.write([i16].pack('n')) + end + + def write_i32(i32) + raise RangeError if i32 < -2**31 || i32 >= 2**31 + trans.write([i32].pack('N')) + end + + def write_i64(i64) + raise RangeError if i64 < -2**63 || i64 >= 2**64 + hi = i64 >> 32 + lo = i64 & 0xffffffff + trans.write([hi, lo].pack('N2')) + end + + def write_double(dub) + trans.write([dub].pack('G')) + end + + def write_string(str) + buf = Bytes.convert_to_utf8_byte_buffer(str) + write_binary(buf) + end + + def write_binary(buf) + write_i32(buf.bytesize) + trans.write(buf) + end + + def read_message_begin + version = read_i32 + if version < 0 + if (version & VERSION_MASK != VERSION_1) + raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Missing version identifier') + end + type = version & TYPE_MASK + name = read_string + seqid = read_i32 + [name, type, seqid] + else + if strict_read + raise ProtocolException.new(ProtocolException::BAD_VERSION, 'No version identifier, old protocol client?') + end + name = trans.read_all(version) + type = read_byte + seqid = read_i32 + [name, type, seqid] + end + end + + def read_struct_begin; nil; end + + def read_field_begin + type = read_byte + if (type == Types::STOP) + [nil, type, 0] + else + id = read_i16 + [nil, type, id] + end + end + + def read_map_begin + ktype = read_byte + vtype = read_byte + size = read_i32 + [ktype, vtype, size] + end + + def read_list_begin + etype = read_byte + size = read_i32 + [etype, size] + end + + def read_set_begin + etype = read_byte + size = read_i32 + [etype, size] + end + + def read_bool + byte = read_byte + byte != 0 + end + + def read_byte + val = trans.read_byte + if (val > 0x7f) + val = 0 - ((val - 1) ^ 0xff) + end + val + end + + def read_i16 + trans.read_into_buffer(@rbuf, 2) + val, = @rbuf.unpack('n') + if (val > 0x7fff) + val = 0 - ((val - 1) ^ 0xffff) + end + val + end + + def read_i32 + trans.read_into_buffer(@rbuf, 4) + val, = @rbuf.unpack('N') + if (val > 0x7fffffff) + val = 0 - ((val - 1) ^ 0xffffffff) + end + val + end + + def read_i64 + trans.read_into_buffer(@rbuf, 8) + hi, lo = @rbuf.unpack('N2') + if (hi > 0x7fffffff) + hi ^= 0xffffffff + lo ^= 0xffffffff + 0 - (hi << 32) - lo - 1 + else + (hi << 32) + lo + end + end + + def read_double + trans.read_into_buffer(@rbuf, 8) + val = @rbuf.unpack('G').first + val + end + + def read_string + buffer = read_binary + Bytes.convert_to_string(buffer) + end + + def read_binary + size = read_i32 + trans.read_all(size) + end + + end + + class BinaryProtocolFactory < BaseProtocolFactory + def get_protocol(trans) + return Thrift::BinaryProtocol.new(trans) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb new file mode 100644 index 00000000..70ea652c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/binary_protocol_accelerated.rb @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +=begin +The only change required for a transport to support BinaryProtocolAccelerated is to implement 2 methods: + * borrow(size), which takes an optional argument and returns atleast _size_ bytes from the transport, + or the default buffer size if no argument is given + * consume!(size), which removes size bytes from the front of the buffer + +See MemoryBuffer and BufferedTransport for examples. +=end + +module Thrift + class BinaryProtocolAcceleratedFactory < BaseProtocolFactory + def get_protocol(trans) + if (defined? BinaryProtocolAccelerated) + BinaryProtocolAccelerated.new(trans) + else + BinaryProtocol.new(trans) + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb new file mode 100644 index 00000000..605eea67 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/compact_protocol.rb @@ -0,0 +1,435 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class CompactProtocol < BaseProtocol + + PROTOCOL_ID = [0x82].pack('c').unpack('c').first + VERSION = 1 + VERSION_MASK = 0x1f + TYPE_MASK = 0xE0 + TYPE_BITS = 0x07 + TYPE_SHIFT_AMOUNT = 5 + + TSTOP = ["", Types::STOP, 0] + + # + # All of the on-wire type codes. + # + class CompactTypes + BOOLEAN_TRUE = 0x01 + BOOLEAN_FALSE = 0x02 + BYTE = 0x03 + I16 = 0x04 + I32 = 0x05 + I64 = 0x06 + DOUBLE = 0x07 + BINARY = 0x08 + LIST = 0x09 + SET = 0x0A + MAP = 0x0B + STRUCT = 0x0C + + def self.is_bool_type?(b) + (b & 0x0f) == BOOLEAN_TRUE || (b & 0x0f) == BOOLEAN_FALSE + end + + COMPACT_TO_TTYPE = { + Types::STOP => Types::STOP, + BOOLEAN_FALSE => Types::BOOL, + BOOLEAN_TRUE => Types::BOOL, + BYTE => Types::BYTE, + I16 => Types::I16, + I32 => Types::I32, + I64 => Types::I64, + DOUBLE => Types::DOUBLE, + BINARY => Types::STRING, + LIST => Types::LIST, + SET => Types::SET, + MAP => Types::MAP, + STRUCT => Types::STRUCT + } + + TTYPE_TO_COMPACT = { + Types::STOP => Types::STOP, + Types::BOOL => BOOLEAN_TRUE, + Types::BYTE => BYTE, + Types::I16 => I16, + Types::I32 => I32, + Types::I64 => I64, + Types::DOUBLE => DOUBLE, + Types::STRING => BINARY, + Types::LIST => LIST, + Types::SET => SET, + Types::MAP => MAP, + Types::STRUCT => STRUCT + } + + def self.get_ttype(compact_type) + val = COMPACT_TO_TTYPE[compact_type & 0x0f] + raise "don't know what type: #{compact_type & 0x0f}" unless val + val + end + + def self.get_compact_type(ttype) + val = TTYPE_TO_COMPACT[ttype] + raise "don't know what type: #{ttype & 0x0f}" unless val + val + end + end + + def initialize(transport) + super(transport) + + @last_field = [0] + @boolean_value = nil + + # Pre-allocated read buffer for read_double(). + @rbuf = Bytes.empty_byte_buffer(8) + end + + def write_message_begin(name, type, seqid) + write_byte(PROTOCOL_ID) + write_byte((VERSION & VERSION_MASK) | ((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK)) + write_varint32(seqid) + write_string(name) + nil + end + + def write_struct_begin(name) + @last_field.push(0) + nil + end + + def write_struct_end + @last_field.pop + nil + end + + def write_field_begin(name, type, id) + if type == Types::BOOL + # we want to possibly include the value, so we'll wait. + @boolean_field = [type, id] + else + write_field_begin_internal(type, id) + end + nil + end + + # + # The workhorse of writeFieldBegin. It has the option of doing a + # 'type override' of the type header. This is used specifically in the + # boolean field case. + # + def write_field_begin_internal(type, id, type_override=nil) + last_id = @last_field.pop + + # if there's a type override, use that. + typeToWrite = type_override || CompactTypes.get_compact_type(type) + + # check if we can use delta encoding for the field id + if id > last_id && id - last_id <= 15 + # write them together + write_byte((id - last_id) << 4 | typeToWrite) + else + # write them separate + write_byte(typeToWrite) + write_i16(id) + end + + @last_field.push(id) + nil + end + + def write_field_stop + write_byte(Types::STOP) + end + + def write_map_begin(ktype, vtype, size) + if (size == 0) + write_byte(0) + else + write_varint32(size) + write_byte(CompactTypes.get_compact_type(ktype) << 4 | CompactTypes.get_compact_type(vtype)) + end + end + + def write_list_begin(etype, size) + write_collection_begin(etype, size) + end + + def write_set_begin(etype, size) + write_collection_begin(etype, size); + end + + def write_bool(bool) + type = bool ? CompactTypes::BOOLEAN_TRUE : CompactTypes::BOOLEAN_FALSE + unless @boolean_field.nil? + # we haven't written the field header yet + write_field_begin_internal(@boolean_field.first, @boolean_field.last, type) + @boolean_field = nil + else + # we're not part of a field, so just write the value. + write_byte(type) + end + end + + def write_byte(byte) + @trans.write([byte].pack('c')) + end + + def write_i16(i16) + write_varint32(int_to_zig_zag(i16)) + end + + def write_i32(i32) + write_varint32(int_to_zig_zag(i32)) + end + + def write_i64(i64) + write_varint64(long_to_zig_zag(i64)) + end + + def write_double(dub) + @trans.write([dub].pack("G").reverse) + end + + def write_string(str) + buf = Bytes.convert_to_utf8_byte_buffer(str) + write_binary(buf) + end + + def write_binary(buf) + write_varint32(buf.bytesize) + @trans.write(buf) + end + + def read_message_begin + protocol_id = read_byte() + if protocol_id != PROTOCOL_ID + raise ProtocolException.new("Expected protocol id #{PROTOCOL_ID} but got #{protocol_id}") + end + + version_and_type = read_byte() + version = version_and_type & VERSION_MASK + if (version != VERSION) + raise ProtocolException.new("Expected version #{VERSION} but got #{version}"); + end + + type = (version_and_type >> TYPE_SHIFT_AMOUNT) & TYPE_BITS + seqid = read_varint32() + messageName = read_string() + [messageName, type, seqid] + end + + def read_struct_begin + @last_field.push(0) + "" + end + + def read_struct_end + @last_field.pop() + nil + end + + def read_field_begin + type = read_byte() + + # if it's a stop, then we can return immediately, as the struct is over. + if (type & 0x0f) == Types::STOP + TSTOP + else + field_id = nil + + # mask off the 4 MSB of the type header. it could contain a field id delta. + modifier = (type & 0xf0) >> 4 + if modifier == 0 + # not a delta. look ahead for the zigzag varint field id. + @last_field.pop + field_id = read_i16() + else + # has a delta. add the delta to the last read field id. + field_id = @last_field.pop + modifier + end + + # if this happens to be a boolean field, the value is encoded in the type + if CompactTypes.is_bool_type?(type) + # save the boolean value in a special instance variable. + @bool_value = (type & 0x0f) == CompactTypes::BOOLEAN_TRUE + end + + # push the new field onto the field stack so we can keep the deltas going. + @last_field.push(field_id) + ["", CompactTypes.get_ttype(type & 0x0f), field_id] + end + end + + def read_map_begin + size = read_varint32() + key_and_value_type = size == 0 ? 0 : read_byte() + [CompactTypes.get_ttype(key_and_value_type >> 4), CompactTypes.get_ttype(key_and_value_type & 0xf), size] + end + + def read_list_begin + size_and_type = read_byte() + size = (size_and_type >> 4) & 0x0f + if size == 15 + size = read_varint32() + end + type = CompactTypes.get_ttype(size_and_type) + [type, size] + end + + def read_set_begin + read_list_begin + end + + def read_bool + unless @bool_value.nil? + bv = @bool_value + @bool_value = nil + bv + else + read_byte() == CompactTypes::BOOLEAN_TRUE + end + end + + def read_byte + val = trans.read_byte + if (val > 0x7f) + val = 0 - ((val - 1) ^ 0xff) + end + val + end + + def read_i16 + zig_zag_to_int(read_varint32()) + end + + def read_i32 + zig_zag_to_int(read_varint32()) + end + + def read_i64 + zig_zag_to_long(read_varint64()) + end + + def read_double + trans.read_into_buffer(@rbuf, 8) + val = @rbuf.reverse.unpack('G').first + val + end + + def read_string + buffer = read_binary + Bytes.convert_to_string(buffer) + end + + def read_binary + size = read_varint32() + trans.read_all(size) + end + + private + + # + # Abstract method for writing the start of lists and sets. List and sets on + # the wire differ only by the type indicator. + # + def write_collection_begin(elem_type, size) + if size <= 14 + write_byte(size << 4 | CompactTypes.get_compact_type(elem_type)) + else + write_byte(0xf0 | CompactTypes.get_compact_type(elem_type)) + write_varint32(size) + end + end + + def write_varint32(n) + # int idx = 0; + while true + if (n & ~0x7F) == 0 + # i32buf[idx++] = (byte)n; + write_byte(n) + break + # return; + else + # i32buf[idx++] = (byte)((n & 0x7F) | 0x80); + write_byte((n & 0x7F) | 0x80) + n = n >> 7 + end + end + # trans_.write(i32buf, 0, idx); + end + + SEVEN_BIT_MASK = 0x7F + EVERYTHING_ELSE_MASK = ~SEVEN_BIT_MASK + + def write_varint64(n) + while true + if (n & EVERYTHING_ELSE_MASK) == 0 #TODO need to find a way to make this into a long... + write_byte(n) + break + else + write_byte((n & SEVEN_BIT_MASK) | 0x80) + n >>= 7 + end + end + end + + def read_varint32() + read_varint64() + end + + def read_varint64() + shift = 0 + result = 0 + while true + b = read_byte() + result |= (b & 0x7f) << shift + break if (b & 0x80) != 0x80 + shift += 7 + end + result + end + + def int_to_zig_zag(n) + (n << 1) ^ (n >> 31) + end + + def long_to_zig_zag(l) + # puts "zz encoded #{l} to #{(l << 1) ^ (l >> 63)}" + (l << 1) ^ (l >> 63) + end + + def zig_zag_to_int(n) + (n >> 1) ^ -(n & 1) + end + + def zig_zag_to_long(n) + (n >> 1) ^ -(n & 1) + end + end + + class CompactProtocolFactory < BaseProtocolFactory + def get_protocol(trans) + CompactProtocol.new(trans) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb new file mode 100644 index 00000000..2b8ac15d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/json_protocol.rb @@ -0,0 +1,778 @@ +# encoding: UTF-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'base64' + +module Thrift + class LookaheadReader + def initialize(trans) + @trans = trans + @hasData = false + @data = nil + end + + def read + if @hasData + @hasData = false + else + @data = @trans.read(1) + end + + return @data + end + + def peek + if !@hasData + @data = @trans.read(1) + end + @hasData = true + return @data + end + end + + # + # Class to serve as base JSON context and as base class for other context + # implementations + # + class JSONContext + @@kJSONElemSeparator = ',' + # + # Write context data to the trans. Default is to do nothing. + # + def write(trans) + end + + # + # Read context data from the trans. Default is to do nothing. + # + def read(reader) + end + + # + # Return true if numbers need to be escaped as strings in this context. + # Default behavior is to return false. + # + def escapeNum + return false + end + end + + # Context class for object member key-value pairs + class JSONPairContext < JSONContext + @@kJSONPairSeparator = ':' + + def initialize + @first = true + @colon = true + end + + def write(trans) + if (@first) + @first = false + @colon = true + else + trans.write(@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator) + @colon = !@colon + end + end + + def read(reader) + if (@first) + @first = false + @colon = true + else + ch = (@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator) + @colon = !@colon + JsonProtocol::read_syntax_char(reader, ch) + end + end + + # Numbers must be turned into strings if they are the key part of a pair + def escapeNum + return @colon + end + end + + # Context class for lists + class JSONListContext < JSONContext + + def initialize + @first = true + end + + def write(trans) + if (@first) + @first = false + else + trans.write(@@kJSONElemSeparator) + end + end + + def read(reader) + if (@first) + @first = false + else + JsonProtocol::read_syntax_char(reader, @@kJSONElemSeparator) + end + end + end + + class JsonProtocol < BaseProtocol + + @@kJSONObjectStart = '{' + @@kJSONObjectEnd = '}' + @@kJSONArrayStart = '[' + @@kJSONArrayEnd = ']' + @@kJSONNewline = '\n' + @@kJSONBackslash = '\\' + @@kJSONStringDelimiter = '"' + + @@kThriftVersion1 = 1 + + @@kThriftNan = "NaN" + @@kThriftInfinity = "Infinity" + @@kThriftNegativeInfinity = "-Infinity" + + def initialize(trans) + super(trans) + @context = JSONContext.new + @contexts = Array.new + @reader = LookaheadReader.new(trans) + end + + def get_type_name_for_type_id(id) + case id + when Types::BOOL + "tf" + when Types::BYTE + "i8" + when Types::I16 + "i16" + when Types::I32 + "i32" + when Types::I64 + "i64" + when Types::DOUBLE + "dbl" + when Types::STRING + "str" + when Types::STRUCT + "rec" + when Types::MAP + "map" + when Types::SET + "set" + when Types::LIST + "lst" + else + raise NotImplementedError + end + end + + def get_type_id_for_type_name(name) + if (name == "tf") + result = Types::BOOL + elsif (name == "i8") + result = Types::BYTE + elsif (name == "i16") + result = Types::I16 + elsif (name == "i32") + result = Types::I32 + elsif (name == "i64") + result = Types::I64 + elsif (name == "dbl") + result = Types::DOUBLE + elsif (name == "str") + result = Types::STRING + elsif (name == "rec") + result = Types::STRUCT + elsif (name == "map") + result = Types::MAP + elsif (name == "set") + result = Types::SET + elsif (name == "lst") + result = Types::LIST + else + result = Types::STOP + end + if (result == Types::STOP) + raise NotImplementedError + end + return result + end + + # Static helper functions + + # Read 1 character from the trans and verify that it is the expected character ch. + # Throw a protocol exception if it is not. + def self.read_syntax_char(reader, ch) + ch2 = reader.read + if (ch2 != ch) + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected \'#{ch}\' got \'#{ch2}\'.") + end + end + + # Return true if the character ch is in [-+0-9.Ee]; false otherwise + def is_json_numeric(ch) + case ch + when '+', '-', '.', '0' .. '9', 'E', "e" + return true + else + return false + end + end + + def push_context(context) + @contexts.push(@context) + @context = context + end + + def pop_context + @context = @contexts.pop + end + + # Write the character ch as a JSON escape sequence ("\u00xx") + def write_json_escape_char(ch) + trans.write('\\u') + ch_value = ch[0] + if (ch_value.kind_of? String) + ch_value = ch.bytes.first + end + trans.write(ch_value.to_s(16).rjust(4,'0')) + end + + # Write the character ch as part of a JSON string, escaping as appropriate. + def write_json_char(ch) + # This table describes the handling for the first 0x30 characters + # 0 : escape using "\u00xx" notation + # 1 : just output index + # : escape using "\" notation + kJSONCharTable = [ + # 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1 + 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2 + ] + + ch_value = ch[0] + if (ch_value.kind_of? String) + ch_value = ch.bytes.first + end + if (ch_value >= 0x30) + if (ch == @@kJSONBackslash) # Only special character >= 0x30 is '\' + trans.write(@@kJSONBackslash) + trans.write(@@kJSONBackslash) + else + trans.write(ch) + end + else + outCh = kJSONCharTable[ch_value]; + # Check if regular character, backslash escaped, or JSON escaped + if outCh.kind_of? String + trans.write(@@kJSONBackslash) + trans.write(outCh) + elsif outCh == 1 + trans.write(ch) + else + write_json_escape_char(ch) + end + end + end + + # Write out the contents of the string str as a JSON string, escaping characters as appropriate. + def write_json_string(str) + @context.write(trans) + trans.write(@@kJSONStringDelimiter) + str.split('').each do |ch| + write_json_char(ch) + end + trans.write(@@kJSONStringDelimiter) + end + + # Write out the contents of the string as JSON string, base64-encoding + # the string's contents, and escaping as appropriate + def write_json_base64(str) + @context.write(trans) + trans.write(@@kJSONStringDelimiter) + trans.write(Base64.strict_encode64(str)) + trans.write(@@kJSONStringDelimiter) + end + + # Convert the given integer type to a JSON number, or a string + # if the context requires it (eg: key in a map pair). + def write_json_integer(num) + @context.write(trans) + escapeNum = @context.escapeNum + if (escapeNum) + trans.write(@@kJSONStringDelimiter) + end + trans.write(num.to_s); + if (escapeNum) + trans.write(@@kJSONStringDelimiter) + end + end + + # Convert the given double to a JSON string, which is either the number, + # "NaN" or "Infinity" or "-Infinity". + def write_json_double(num) + @context.write(trans) + # Normalize output of boost::lexical_cast for NaNs and Infinities + special = false; + if (num.nan?) + special = true; + val = @@kThriftNan; + elsif (num.infinite?) + special = true; + val = @@kThriftInfinity; + if (num < 0.0) + val = @@kThriftNegativeInfinity; + end + else + val = num.to_s + end + + escapeNum = special || @context.escapeNum + if (escapeNum) + trans.write(@@kJSONStringDelimiter) + end + trans.write(val) + if (escapeNum) + trans.write(@@kJSONStringDelimiter) + end + end + + def write_json_object_start + @context.write(trans) + trans.write(@@kJSONObjectStart) + push_context(JSONPairContext.new); + end + + def write_json_object_end + pop_context + trans.write(@@kJSONObjectEnd) + end + + def write_json_array_start + @context.write(trans) + trans.write(@@kJSONArrayStart) + push_context(JSONListContext.new); + end + + def write_json_array_end + pop_context + trans.write(@@kJSONArrayEnd) + end + + def write_message_begin(name, type, seqid) + write_json_array_start + write_json_integer(@@kThriftVersion1) + write_json_string(name) + write_json_integer(type) + write_json_integer(seqid) + end + + def write_message_end + write_json_array_end + end + + def write_struct_begin(name) + write_json_object_start + end + + def write_struct_end + write_json_object_end + end + + def write_field_begin(name, type, id) + write_json_integer(id) + write_json_object_start + write_json_string(get_type_name_for_type_id(type)) + end + + def write_field_end + write_json_object_end + end + + def write_field_stop; nil; end + + def write_map_begin(ktype, vtype, size) + write_json_array_start + write_json_string(get_type_name_for_type_id(ktype)) + write_json_string(get_type_name_for_type_id(vtype)) + write_json_integer(size) + write_json_object_start + end + + def write_map_end + write_json_object_end + write_json_array_end + end + + def write_list_begin(etype, size) + write_json_array_start + write_json_string(get_type_name_for_type_id(etype)) + write_json_integer(size) + end + + def write_list_end + write_json_array_end + end + + def write_set_begin(etype, size) + write_json_array_start + write_json_string(get_type_name_for_type_id(etype)) + write_json_integer(size) + end + + def write_set_end + write_json_array_end + end + + def write_bool(bool) + write_json_integer(bool ? 1 : 0) + end + + def write_byte(byte) + write_json_integer(byte) + end + + def write_i16(i16) + write_json_integer(i16) + end + + def write_i32(i32) + write_json_integer(i32) + end + + def write_i64(i64) + write_json_integer(i64) + end + + def write_double(dub) + write_json_double(dub) + end + + def write_string(str) + write_json_string(str) + end + + def write_binary(str) + write_json_base64(str) + end + + ## + # Reading functions + ## + + # Reads 1 byte and verifies that it matches ch. + def read_json_syntax_char(ch) + JsonProtocol::read_syntax_char(@reader, ch) + end + + # Decodes the four hex parts of a JSON escaped string character and returns + # the character via out. + # + # Note - this only supports Unicode characters in the BMP (U+0000 to U+FFFF); + # characters above the BMP are encoded as two escape sequences (surrogate pairs), + # which is not yet implemented + def read_json_escape_char + str = @reader.read + str += @reader.read + str += @reader.read + str += @reader.read + if RUBY_VERSION >= '1.9' + str.hex.chr(Encoding::UTF_8) + else + str.hex.chr + end + end + + # Decodes a JSON string, including unescaping, and returns the string via str + def read_json_string(skipContext = false) + # This string's characters must match up with the elements in escape_char_vals. + # I don't have '/' on this list even though it appears on www.json.org -- + # it is not in the RFC -> it is. See RFC 4627 + escape_chars = "\"\\/bfnrt" + + # The elements of this array must match up with the sequence of characters in + # escape_chars + escape_char_vals = [ + "\"", "\\", "\/", "\b", "\f", "\n", "\r", "\t", + ] + + if !skipContext + @context.read(@reader) + end + read_json_syntax_char(@@kJSONStringDelimiter) + ch = "" + str = "" + while (true) + ch = @reader.read + if (ch == @@kJSONStringDelimiter) + break + end + if (ch == @@kJSONBackslash) + ch = @reader.read + if (ch == 'u') + ch = read_json_escape_char + else + pos = escape_chars.index(ch); + if (pos.nil?) # not found + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected control char, got \'#{ch}\'.") + end + ch = escape_char_vals[pos] + end + end + str += ch + end + return str + end + + # Reads a block of base64 characters, decoding it, and returns via str + def read_json_base64 + str = read_json_string + m = str.length % 4 + if m != 0 + # Add missing padding + (4 - m).times do + str += '=' + end + end + Base64.strict_decode64(str) + end + + # Reads a sequence of characters, stopping at the first one that is not + # a valid JSON numeric character. + def read_json_numeric_chars + str = "" + while (true) + ch = @reader.peek + if (!is_json_numeric(ch)) + break; + end + ch = @reader.read + str += ch + end + return str + end + + # Reads a sequence of characters and assembles them into a number, + # returning them via num + def read_json_integer + @context.read(@reader) + if (@context.escapeNum) + read_json_syntax_char(@@kJSONStringDelimiter) + end + str = read_json_numeric_chars + + begin + num = Integer(str); + rescue + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"") + end + + if (@context.escapeNum) + read_json_syntax_char(@@kJSONStringDelimiter) + end + + return num + end + + # Reads a JSON number or string and interprets it as a double. + def read_json_double + @context.read(@reader) + num = 0 + if (@reader.peek == @@kJSONStringDelimiter) + str = read_json_string(true) + # Check for NaN, Infinity and -Infinity + if (str == @@kThriftNan) + num = (+1.0/0.0)/(+1.0/0.0) + elsif (str == @@kThriftInfinity) + num = +1.0/0.0 + elsif (str == @@kThriftNegativeInfinity) + num = -1.0/0.0 + else + if (!@context.escapeNum) + # Raise exception -- we should not be in a string in this case + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Numeric data unexpectedly quoted") + end + begin + num = Float(str) + rescue + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"") + end + end + else + if (@context.escapeNum) + # This will throw - we should have had a quote if escapeNum == true + read_json_syntax_char(@@kJSONStringDelimiter) + end + str = read_json_numeric_chars + begin + num = Float(str) + rescue + raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"") + end + end + return num + end + + def read_json_object_start + @context.read(@reader) + read_json_syntax_char(@@kJSONObjectStart) + push_context(JSONPairContext.new) + nil + end + + def read_json_object_end + read_json_syntax_char(@@kJSONObjectEnd) + pop_context + nil + end + + def read_json_array_start + @context.read(@reader) + read_json_syntax_char(@@kJSONArrayStart) + push_context(JSONListContext.new) + nil + end + + def read_json_array_end + read_json_syntax_char(@@kJSONArrayEnd) + pop_context + nil + end + + def read_message_begin + read_json_array_start + version = read_json_integer + if (version != @@kThriftVersion1) + raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Message contained bad version.') + end + name = read_json_string + message_type = read_json_integer + seqid = read_json_integer + [name, message_type, seqid] + end + + def read_message_end + read_json_array_end + nil + end + + def read_struct_begin + read_json_object_start + nil + end + + def read_struct_end + read_json_object_end + nil + end + + def read_field_begin + # Check if we hit the end of the list + ch = @reader.peek + if (ch == @@kJSONObjectEnd) + field_type = Types::STOP + else + field_id = read_json_integer + read_json_object_start + field_type = get_type_id_for_type_name(read_json_string) + end + [nil, field_type, field_id] + end + + def read_field_end + read_json_object_end + end + + def read_map_begin + read_json_array_start + key_type = get_type_id_for_type_name(read_json_string) + val_type = get_type_id_for_type_name(read_json_string) + size = read_json_integer + read_json_object_start + [key_type, val_type, size] + end + + def read_map_end + read_json_object_end + read_json_array_end + end + + def read_list_begin + read_json_array_start + [get_type_id_for_type_name(read_json_string), read_json_integer] + end + + def read_list_end + read_json_array_end + end + + def read_set_begin + read_json_array_start + [get_type_id_for_type_name(read_json_string), read_json_integer] + end + + def read_set_end + read_json_array_end + end + + def read_bool + byte = read_byte + byte != 0 + end + + def read_byte + read_json_integer + end + + def read_i16 + read_json_integer + end + + def read_i32 + read_json_integer + end + + def read_i64 + read_json_integer + end + + def read_double + read_json_double + end + + def read_string + read_json_string + end + + def read_binary + read_json_base64 + end + end + + class JsonProtocolFactory < BaseProtocolFactory + def get_protocol(trans) + return Thrift::JsonProtocol.new(trans) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb new file mode 100644 index 00000000..13c9d93e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/multiplexed_protocol.rb @@ -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. + +require 'thrift/protocol/protocol_decorator' + +module Thrift + class MultiplexedProtocol < BaseProtocol + + include ProtocolDecorator + + def initialize(protocol, service_name) + super(protocol) + @service_name = service_name + end + + def write_message_begin(name, type, seqid) + case type + when MessageTypes::CALL, MessageTypes::ONEWAY + @protocol.write_message_begin("#{@service_name}:#{name}", type, seqid) + else + @protocol.write_message_begin(name, type, seqid) + end + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb new file mode 100644 index 00000000..b1e3c155 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/protocol/protocol_decorator.rb @@ -0,0 +1,194 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Thrift + module ProtocolDecorator + + def initialize(protocol) + @protocol = protocol + end + + def trans + @protocol.trans + end + + def write_message_begin(name, type, seqid) + @protocol.write_message_begin + end + + def write_message_end + @protocol.write_message_end + end + + def write_struct_begin(name) + @protocol.write_struct_begin(name) + end + + def write_struct_end + @protocol.write_struct_end + end + + def write_field_begin(name, type, id) + @protocol.write_field_begin(name, type, id) + end + + def write_field_end + @protocol.write_field_end + end + + def write_field_stop + @protocol.write_field_stop + end + + def write_map_begin(ktype, vtype, size) + @protocol.write_map_begin(ktype, vtype, size) + end + + def write_map_end + @protocol.write_map_end + end + + def write_list_begin(etype, size) + @protocol.write_list_begin(etype, size) + end + + def write_list_end + @protocol.write_list_end + end + + def write_set_begin(etype, size) + @protocol.write_set_begin(etype, size) + end + + def write_set_end + @protocol.write_set_end + end + + def write_bool(bool) + @protocol.write_bool(bool) + end + + def write_byte(byte) + @protocol.write_byte(byte) + end + + def write_i16(i16) + @protocol.write_i16(i16) + end + + def write_i32(i32) + @protocol.write_i32(i32) + end + + def write_i64(i64) + @protocol.write_i64(i64) + end + + def write_double(dub) + @protocol.write_double(dub) + end + + def write_string(str) + @protocol.write_string(str) + end + + def write_binary(buf) + @protocol.write_binary(buf) + end + + def read_message_begin + @protocol.read_message_begin + end + + def read_message_end + @protocol.read_message_end + end + + def read_struct_begin + @protocol.read_struct_begin + end + + def read_struct_end + @protocol.read_struct_end + end + + def read_field_begin + @protocol.read_field_begin + end + + def read_field_end + @protocol.read_field_end + end + + def read_map_begin + @protocol.read_map_begin + end + + def read_map_end + @protocol.read_map_end + end + + def read_list_begin + @protocol.read_list_begin + end + + def read_list_end + @protocol.read_list_end + end + + def read_set_begin + @protocol.read_set_begin + end + + def read_set_end + @protocol.read_set_end + end + + def read_bool + @protocol.read_bool + end + + def read_byte + @protocol.read_byte + end + + def read_i16 + @protocol.read_i16 + end + + def read_i32 + @protocol.read_i32 + end + + def read_i64 + @protocol.read_i64 + end + + def read_double + @protocol.read_double + end + + def read_string + @protocol.read_string + end + + def read_binary + @protocol.read_binary + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/deserializer.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/deserializer.rb new file mode 100644 index 00000000..d2ee325a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/deserializer.rb @@ -0,0 +1,33 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class Deserializer + def initialize(protocol_factory = BinaryProtocolFactory.new) + @transport = MemoryBufferTransport.new + @protocol = protocol_factory.get_protocol(@transport) + end + + def deserialize(base, buffer) + @transport.reset_buffer(buffer) + base.read(@protocol) + base + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/serializer.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/serializer.rb new file mode 100644 index 00000000..22316395 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/serializer/serializer.rb @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class Serializer + def initialize(protocol_factory = BinaryProtocolFactory.new) + @transport = MemoryBufferTransport.new + @protocol = protocol_factory.get_protocol(@transport) + end + + def serialize(base) + @transport.reset_buffer + base.write(@protocol) + @transport.read(@transport.available) + end + end +end + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/base_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/base_server.rb new file mode 100644 index 00000000..1ee12133 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/base_server.rb @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class BaseServer + def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil) + @processor = processor + @server_transport = server_transport + @transport_factory = transport_factory ? transport_factory : Thrift::BaseTransportFactory.new + @protocol_factory = protocol_factory ? protocol_factory : Thrift::BinaryProtocolFactory.new + end + + def serve; nil; end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb new file mode 100644 index 00000000..de354c8f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/mongrel_http_server.rb @@ -0,0 +1,60 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'mongrel' + +## Sticks a service on a URL, using mongrel to do the HTTP work +# DEPRECATED: Please use Thrift::ThinHTTPServer instead. +module Thrift + class MongrelHTTPServer < BaseServer + class Handler < Mongrel::HttpHandler + def initialize(processor, protocol_factory) + @processor = processor + @protocol_factory = protocol_factory + end + + def process(request, response) + if request.params["REQUEST_METHOD"] == "POST" + response.start(200) do |head, out| + head["Content-Type"] = "application/x-thrift" + transport = IOStreamTransport.new request.body, out + protocol = @protocol_factory.get_protocol transport + @processor.process protocol, protocol + end + else + response.start(404) { } + end + end + end + + def initialize(processor, opts={}) + Kernel.warn "[DEPRECATION WARNING] `Thrift::MongrelHTTPServer` is deprecated. Please use `Thrift::ThinHTTPServer` instead." + port = opts[:port] || 80 + ip = opts[:ip] || "0.0.0.0" + path = opts[:path] || "" + protocol_factory = opts[:protocol_factory] || BinaryProtocolFactory.new + @server = Mongrel::HttpServer.new ip, port + @server.register "/#{path}", Handler.new(processor, protocol_factory) + end + + def serve + @server.run.join + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb new file mode 100644 index 00000000..740f3417 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/nonblocking_server.rb @@ -0,0 +1,305 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'logger' +require 'thread' + +module Thrift + # this class expects to always use a FramedTransport for reading messages + class NonblockingServer < BaseServer + def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20, logger=nil) + super(processor, server_transport, transport_factory, protocol_factory) + @num_threads = num + if logger.nil? + @logger = Logger.new(STDERR) + @logger.level = Logger::WARN + else + @logger = logger + end + @shutdown_semaphore = Mutex.new + @transport_semaphore = Mutex.new + end + + def serve + @logger.info "Starting #{self}" + @server_transport.listen + @io_manager = start_io_manager + + begin + loop do + break if @server_transport.closed? + begin + rd, = select([@server_transport], nil, nil, 0.1) + rescue Errno::EBADF => e + # In Ruby 1.9, calling @server_transport.close in shutdown paths causes the select() to raise an + # Errno::EBADF. If this happens, ignore it and retry the loop. + break + end + next if rd.nil? + socket = @server_transport.accept + @logger.debug "Accepted socket: #{socket.inspect}" + @io_manager.add_connection socket + end + rescue IOError => e + end + # we must be shutting down + @logger.info "#{self} is shutting down, goodbye" + ensure + @transport_semaphore.synchronize do + @server_transport.close + end + @io_manager.ensure_closed unless @io_manager.nil? + end + + def shutdown(timeout = 0, block = true) + @shutdown_semaphore.synchronize do + return if @is_shutdown + @is_shutdown = true + end + # nonblocking is intended for calling from within a Handler + # but we can't change the order of operations here, so lets thread + shutdown_proc = lambda do + @io_manager.shutdown(timeout) + @transport_semaphore.synchronize do + @server_transport.close # this will break the accept loop + end + end + if block + shutdown_proc.call + else + Thread.new &shutdown_proc + end + end + + private + + def start_io_manager + iom = IOManager.new(@processor, @server_transport, @transport_factory, @protocol_factory, @num_threads, @logger) + iom.spawn + iom + end + + class IOManager # :nodoc: + DEFAULT_BUFFER = 2**20 + + def initialize(processor, server_transport, transport_factory, protocol_factory, num, logger) + @processor = processor + @server_transport = server_transport + @transport_factory = transport_factory + @protocol_factory = protocol_factory + @num_threads = num + @logger = logger + @connections = [] + @buffers = Hash.new { |h,k| h[k] = '' } + @signal_queue = Queue.new + @signal_pipes = IO.pipe + @signal_pipes[1].sync = true + @worker_queue = Queue.new + @shutdown_queue = Queue.new + end + + def add_connection(socket) + signal [:connection, socket] + end + + def spawn + @iom_thread = Thread.new do + @logger.debug "Starting #{self}" + run + end + end + + def shutdown(timeout = 0) + @logger.debug "#{self} is shutting down workers" + @worker_queue.clear + @num_threads.times { @worker_queue.push [:shutdown] } + signal [:shutdown, timeout] + @shutdown_queue.pop + @signal_pipes[0].close + @signal_pipes[1].close + @logger.debug "#{self} is shutting down, goodbye" + end + + def ensure_closed + kill_worker_threads if @worker_threads + @iom_thread.kill + end + + private + + def run + spin_worker_threads + + loop do + rd, = select([@signal_pipes[0], *@connections]) + if rd.delete @signal_pipes[0] + break if read_signals == :shutdown + end + rd.each do |fd| + begin + if fd.handle.eof? + remove_connection fd + else + read_connection fd + end + rescue Errno::ECONNRESET + remove_connection fd + end + end + end + join_worker_threads(@shutdown_timeout) + ensure + @shutdown_queue.push :shutdown + end + + def read_connection(fd) + @buffers[fd] << fd.read(DEFAULT_BUFFER) + while(frame = slice_frame!(@buffers[fd])) + @logger.debug "#{self} is processing a frame" + @worker_queue.push [:frame, fd, frame] + end + end + + def spin_worker_threads + @logger.debug "#{self} is spinning up worker threads" + @worker_threads = [] + @num_threads.times do + @worker_threads << spin_thread + end + end + + def spin_thread + Worker.new(@processor, @transport_factory, @protocol_factory, @logger, @worker_queue).spawn + end + + def signal(msg) + @signal_queue << msg + @signal_pipes[1].write " " + end + + def read_signals + # clear the signal pipe + # note that since read_nonblock is broken in jruby, + # we can only read up to a set number of signals at once + sigstr = @signal_pipes[0].readpartial(1024) + # now read the signals + begin + sigstr.length.times do + signal, obj = @signal_queue.pop(true) + case signal + when :connection + @connections << obj + when :shutdown + @shutdown_timeout = obj + return :shutdown + end + end + rescue ThreadError + # out of signals + # note that in a perfect world this would never happen, since we're + # only reading the number of signals pushed on the pipe, but given the lack + # of locks, in theory we could clear the pipe/queue while a new signal is being + # placed on the pipe, at which point our next read_signals would hit this error + end + end + + def remove_connection(fd) + # don't explicitly close it, a thread may still be writing to it + @connections.delete fd + @buffers.delete fd + end + + def join_worker_threads(shutdown_timeout) + start = Time.now + @worker_threads.each do |t| + if shutdown_timeout > 0 + timeout = (start + shutdown_timeout) - Time.now + break if timeout <= 0 + t.join(timeout) + else + t.join + end + end + kill_worker_threads + end + + def kill_worker_threads + @worker_threads.each do |t| + t.kill if t.status + end + @worker_threads.clear + end + + def slice_frame!(buf) + if buf.length >= 4 + size = buf.unpack('N').first + if buf.length >= size + 4 + buf.slice!(0, size + 4) + else + nil + end + else + nil + end + end + + class Worker # :nodoc: + def initialize(processor, transport_factory, protocol_factory, logger, queue) + @processor = processor + @transport_factory = transport_factory + @protocol_factory = protocol_factory + @logger = logger + @queue = queue + end + + def spawn + Thread.new do + @logger.debug "#{self} is spawning" + run + end + end + + private + + def run + loop do + cmd, *args = @queue.pop + case cmd + when :shutdown + @logger.debug "#{self} is shutting down, goodbye" + break + when :frame + fd, frame = args + begin + otrans = @transport_factory.get_transport(fd) + oprot = @protocol_factory.get_protocol(otrans) + membuf = MemoryBufferTransport.new(frame) + itrans = @transport_factory.get_transport(membuf) + iprot = @protocol_factory.get_protocol(itrans) + @processor.process(iprot, oprot) + rescue => e + @logger.error "#{Thread.current.inspect} raised error: #{e.inspect}\n#{e.backtrace.join("\n")}" + end + end + end + end + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/simple_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/simple_server.rb new file mode 100644 index 00000000..21e86592 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/simple_server.rb @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class SimpleServer < BaseServer + def serve + begin + @server_transport.listen + loop do + client = @server_transport.accept + trans = @transport_factory.get_transport(client) + prot = @protocol_factory.get_protocol(trans) + begin + loop do + @processor.process(prot, prot) + end + rescue Thrift::TransportException, Thrift::ProtocolException + ensure + trans.close + end + end + ensure + @server_transport.close + end + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thin_http_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thin_http_server.rb new file mode 100644 index 00000000..4a81c6d1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thin_http_server.rb @@ -0,0 +1,91 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'rack' +require 'thin' + +## +# Wraps the Thin web server to provide a Thrift server over HTTP. +module Thrift + class ThinHTTPServer < BaseServer + + ## + # Accepts a Thrift::Processor + # Options include: + # * :port + # * :ip + # * :path + # * :protocol_factory + def initialize(processor, options={}) + port = options[:port] || 80 + ip = options[:ip] || "0.0.0.0" + path = options[:path] || "/" + protocol_factory = options[:protocol_factory] || BinaryProtocolFactory.new + app = RackApplication.for(path, processor, protocol_factory) + @server = Thin::Server.new(ip, port, app) + end + + ## + # Starts the server + def serve + @server.start + end + + class RackApplication + + THRIFT_HEADER = "application/x-thrift" + + def self.for(path, processor, protocol_factory) + Rack::Builder.new do + use Rack::CommonLogger + use Rack::ShowExceptions + use Rack::Lint + map path do + run lambda { |env| + request = Rack::Request.new(env) + if RackApplication.valid_thrift_request?(request) + RackApplication.successful_request(request, processor, protocol_factory) + else + RackApplication.failed_request + end + } + end + end + end + + def self.successful_request(rack_request, processor, protocol_factory) + response = Rack::Response.new([], 200, {'Content-Type' => THRIFT_HEADER}) + transport = IOStreamTransport.new rack_request.body, response + protocol = protocol_factory.get_protocol transport + processor.process protocol, protocol + response + end + + def self.failed_request + Rack::Response.new(['Not Found'], 404, {'Content-Type' => THRIFT_HEADER}) + end + + def self.valid_thrift_request?(rack_request) + rack_request.post? && rack_request.env["CONTENT_TYPE"] == THRIFT_HEADER + end + + end + + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb new file mode 100644 index 00000000..8cec805a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/thread_pool_server.rb @@ -0,0 +1,75 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'thread' + +module Thrift + class ThreadPoolServer < BaseServer + def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20) + super(processor, server_transport, transport_factory, protocol_factory) + @thread_q = SizedQueue.new(num) + @exception_q = Queue.new + @running = false + end + + ## exceptions that happen in worker threads will be relayed here and + ## must be caught. 'retry' can be used to continue. (threads will + ## continue to run while the exception is being handled.) + def rescuable_serve + Thread.new { serve } unless @running + @running = true + raise @exception_q.pop + end + + ## exceptions that happen in worker threads simply cause that thread + ## to die and another to be spawned in its place. + def serve + @server_transport.listen + + begin + loop do + @thread_q.push(:token) + Thread.new do + begin + loop do + client = @server_transport.accept + trans = @transport_factory.get_transport(client) + prot = @protocol_factory.get_protocol(trans) + begin + loop do + @processor.process(prot, prot) + end + rescue Thrift::TransportException, Thrift::ProtocolException => e + ensure + trans.close + end + end + rescue => e + @exception_q.push(e) + ensure + @thread_q.pop # thread died! + end + end + end + ensure + @server_transport.close + end + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/threaded_server.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/threaded_server.rb new file mode 100644 index 00000000..a2c917cb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/server/threaded_server.rb @@ -0,0 +1,47 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'thread' + +module Thrift + class ThreadedServer < BaseServer + def serve + begin + @server_transport.listen + loop do + client = @server_transport.accept + trans = @transport_factory.get_transport(client) + prot = @protocol_factory.get_protocol(trans) + Thread.new(prot, trans) do |p, t| + begin + loop do + @processor.process(p, p) + end + rescue Thrift::TransportException, Thrift::ProtocolException + ensure + t.close + end + end + end + ensure + @server_transport.close + end + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct.rb new file mode 100644 index 00000000..df9d6561 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct.rb @@ -0,0 +1,237 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'set' + +module Thrift + module Struct + def initialize(d={}, &block) + # get a copy of the default values to work on, removing defaults in favor of arguments + fields_with_defaults = fields_with_default_values.dup + + # check if the defaults is empty, or if there are no parameters for this + # instantiation, and if so, don't bother overriding defaults. + unless fields_with_defaults.empty? || d.empty? + d.each_key do |name| + fields_with_defaults.delete(name.to_s) + end + end + + # assign all the user-specified arguments + unless d.empty? + d.each do |name, value| + unless name_to_id(name.to_s) + raise Exception, "Unknown key given to #{self.class}.new: #{name}" + end + Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking + instance_variable_set("@#{name}", value) + end + end + + # assign all the default values + unless fields_with_defaults.empty? + fields_with_defaults.each do |name, default_value| + instance_variable_set("@#{name}", (default_value.dup rescue default_value)) + end + end + + yield self if block_given? + end + + def fields_with_default_values + fields_with_default_values = self.class.instance_variable_get(:@fields_with_default_values) + unless fields_with_default_values + fields_with_default_values = {} + struct_fields.each do |fid, field_def| + unless field_def[:default].nil? + fields_with_default_values[field_def[:name]] = field_def[:default] + end + end + self.class.instance_variable_set(:@fields_with_default_values, fields_with_default_values) + end + fields_with_default_values + end + + def inspect(skip_optional_nulls = true) + fields = [] + each_field do |fid, field_info| + name = field_info[:name] + value = instance_variable_get("@#{name}") + unless skip_optional_nulls && field_info[:optional] && value.nil? + fields << "#{name}:#{inspect_field(value, field_info)}" + end + end + "<#{self.class} #{fields.join(", ")}>" + end + + def read(iprot) + iprot.read_struct_begin + loop do + fname, ftype, fid = iprot.read_field_begin + break if (ftype == Types::STOP) + handle_message(iprot, fid, ftype) + iprot.read_field_end + end + iprot.read_struct_end + validate + end + + def write(oprot) + validate + oprot.write_struct_begin(self.class.name) + each_field do |fid, field_info| + name = field_info[:name] + type = field_info[:type] + value = instance_variable_get("@#{name}") + unless value.nil? + if is_container? type + oprot.write_field_begin(name, type, fid) + write_container(oprot, value, field_info) + oprot.write_field_end + else + oprot.write_field(field_info, fid, value) + end + end + end + oprot.write_field_stop + oprot.write_struct_end + end + + def ==(other) + return false if other.nil? + each_field do |fid, field_info| + name = field_info[:name] + return false unless other.respond_to?(name) && self.send(name) == other.send(name) + end + true + end + + def eql?(other) + self.class == other.class && self == other + end + + # This implementation of hash() is inspired by Apache's Java HashCodeBuilder class. + def hash + total = 17 + each_field do |fid, field_info| + name = field_info[:name] + value = self.send(name) + total = (total * 37 + value.hash) & 0xffffffff + end + total + end + + def differences(other) + diffs = [] + unless other.is_a?(self.class) + diffs << "Different class!" + else + each_field do |fid, field_info| + name = field_info[:name] + diffs << "#{name} differs!" unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}") + end + end + diffs + end + + def self.field_accessor(klass, field_info) + field_name_sym = field_info[:name].to_sym + klass.send :attr_reader, field_name_sym + klass.send :define_method, "#{field_info[:name]}=" do |value| + Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking + instance_variable_set("@#{field_name_sym}", value) + end + end + + def self.generate_accessors(klass) + klass::FIELDS.values.each do |field_info| + field_accessor(klass, field_info) + qmark_isset_method(klass, field_info) + end + end + + def self.qmark_isset_method(klass, field_info) + klass.send :define_method, "#{field_info[:name]}?" do + !self.send(field_info[:name].to_sym).nil? + end + end + + def <=>(other) + if self.class == other.class + each_field do |fid, field_info| + v1 = self.send(field_info[:name]) + v1_set = !v1.nil? + v2 = other.send(field_info[:name]) + v2_set = !v2.nil? + if v1_set && !v2_set + return -1 + elsif !v1_set && v2_set + return 1 + elsif v1_set && v2_set + cmp = v1 <=> v2 + if cmp != 0 + return cmp + end + end + end + 0 + else + self.class <=> other.class + end + end + + protected + + def self.append_features(mod) + if mod.ancestors.include? ::Exception + mod.send :class_variable_set, :'@@__thrift_struct_real_initialize', mod.instance_method(:initialize) + super + # set up our custom initializer so `raise Xception, 'message'` works + mod.send :define_method, :struct_initialize, mod.instance_method(:initialize) + mod.send :define_method, :initialize, mod.instance_method(:exception_initialize) + else + super + end + end + + def exception_initialize(*args, &block) + if args.size == 1 and args.first.is_a? Hash + # looks like it's a regular Struct initialize + method(:struct_initialize).call(args.first) + else + # call the Struct initializer first with no args + # this will set our field default values + method(:struct_initialize).call() + # now give it to the exception + self.class.send(:class_variable_get, :'@@__thrift_struct_real_initialize').bind(self).call(*args, &block) if args.size > 0 + # self.class.instance_method(:initialize).bind(self).call(*args, &block) + end + end + + def handle_message(iprot, fid, ftype) + field = struct_fields[fid] + if field and field[:type] == ftype + value = read_field(iprot, field) + instance_variable_set("@#{field[:name]}", value) + else + iprot.skip(ftype) + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct_union.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct_union.rb new file mode 100644 index 00000000..e21b39eb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/struct_union.rb @@ -0,0 +1,192 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +require 'set' + +module Thrift + module Struct_Union + def name_to_id(name) + names_to_ids = self.class.instance_variable_get(:@names_to_ids) + unless names_to_ids + names_to_ids = {} + struct_fields.each do |fid, field_def| + names_to_ids[field_def[:name]] = fid + end + self.class.instance_variable_set(:@names_to_ids, names_to_ids) + end + names_to_ids[name] + end + + def sorted_field_ids + sorted_field_ids = self.class.instance_variable_get(:@sorted_field_ids) + unless sorted_field_ids + sorted_field_ids = struct_fields.keys.sort + self.class.instance_variable_set(:@sorted_field_ids, sorted_field_ids) + end + sorted_field_ids + end + + def each_field + sorted_field_ids.each do |fid| + data = struct_fields[fid] + yield fid, data + end + end + + def read_field(iprot, field = {}) + case field[:type] + when Types::STRUCT + value = field[:class].new + value.read(iprot) + when Types::MAP + key_type, val_type, size = iprot.read_map_begin + # Skip the map contents if the declared key or value types don't match the expected ones. + if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type])) + size.times do + iprot.skip(key_type) + iprot.skip(val_type) + end + value = nil + else + value = {} + size.times do + k = read_field(iprot, field_info(field[:key])) + v = read_field(iprot, field_info(field[:value])) + value[k] = v + end + end + iprot.read_map_end + when Types::LIST + e_type, size = iprot.read_list_begin + # Skip the list contents if the declared element type doesn't match the expected one. + if (e_type != field[:element][:type]) + size.times do + iprot.skip(e_type) + end + value = nil + else + value = Array.new(size) do |n| + read_field(iprot, field_info(field[:element])) + end + end + iprot.read_list_end + when Types::SET + e_type, size = iprot.read_set_begin + # Skip the set contents if the declared element type doesn't match the expected one. + if (e_type != field[:element][:type]) + size.times do + iprot.skip(e_type) + end + else + value = Set.new + size.times do + element = read_field(iprot, field_info(field[:element])) + value << element + end + end + iprot.read_set_end + else + value = iprot.read_type(field) + end + value + end + + def write_data(oprot, value, field) + if is_container? field[:type] + write_container(oprot, value, field) + else + oprot.write_type(field, value) + end + end + + def write_container(oprot, value, field = {}) + case field[:type] + when Types::MAP + oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size) + value.each do |k, v| + write_data(oprot, k, field[:key]) + write_data(oprot, v, field[:value]) + end + oprot.write_map_end + when Types::LIST + oprot.write_list_begin(field[:element][:type], value.size) + value.each do |elem| + write_data(oprot, elem, field[:element]) + end + oprot.write_list_end + when Types::SET + oprot.write_set_begin(field[:element][:type], value.size) + value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets + write_data(oprot, v, field[:element]) + end + oprot.write_set_end + else + raise "Not a container type: #{field[:type]}" + end + end + + CONTAINER_TYPES = [] + CONTAINER_TYPES[Types::LIST] = true + CONTAINER_TYPES[Types::MAP] = true + CONTAINER_TYPES[Types::SET] = true + def is_container?(type) + CONTAINER_TYPES[type] + end + + def field_info(field) + { :type => field[:type], + :class => field[:class], + :key => field[:key], + :value => field[:value], + :element => field[:element] } + end + + def inspect_field(value, field_info) + if enum_class = field_info[:enum_class] + "#{enum_class.const_get(:VALUE_MAP)[value]} (#{value})" + elsif value.is_a? Hash + if field_info[:type] == Types::MAP + map_buf = [] + value.each do |k, v| + map_buf << inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value]) + end + "{" + map_buf.join(", ") + "}" + else + # old-style set + inspect_collection(value.keys, field_info) + end + elsif value.is_a? Array + inspect_collection(value, field_info) + elsif value.is_a? Set + inspect_collection(value, field_info) + elsif value.is_a?(String) && field_info[:binary] + value.unpack("H*").first + else + value.inspect + end + end + + def inspect_collection(collection, field_info) + buf = [] + collection.each do |k| + buf << inspect_field(k, field_info[:element]) + end + "[" + buf.join(", ") + "]" + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/thrift_native.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/thrift_native.rb new file mode 100644 index 00000000..4d8df61f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/thrift_native.rb @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +begin + require "thrift_native" +rescue LoadError + puts "Unable to load thrift_native extension. Defaulting to pure Ruby libraries." +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb new file mode 100644 index 00000000..68c5af07 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_server_transport.rb @@ -0,0 +1,37 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class BaseServerTransport + def listen + raise NotImplementedError + end + + def accept + raise NotImplementedError + end + + def close; nil; end + + def closed? + raise NotImplementedError + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_transport.rb new file mode 100644 index 00000000..87903264 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/base_transport.rb @@ -0,0 +1,109 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class TransportException < Exception + UNKNOWN = 0 + NOT_OPEN = 1 + ALREADY_OPEN = 2 + TIMED_OUT = 3 + END_OF_FILE = 4 + + attr_reader :type + + def initialize(type=UNKNOWN, message=nil) + super(message) + @type = type + end + end + + module TransportUtils + # Deprecated: Use Thrift::Bytes instead + def self.get_string_byte(string, index) + Bytes.get_string_byte(string, index) + end + + # Deprecated: Use Thrift::Bytes instead + def self.set_string_byte(string, index, byte) + Bytes.set_string_byte(string, index, byte) + end + end + + class BaseTransport + def open?; end + + def open; end + + def close; end + + # Reads a number of bytes from the transports. In Ruby 1.9+, the String returned will have a BINARY (aka ASCII8BIT) encoding. + # + # sz - The number of bytes to read from the transport. + # + # Returns a String acting as a byte buffer. + def read(sz) + raise NotImplementedError + end + + # Returns an unsigned byte as a Fixnum in the range (0..255). + def read_byte + buf = read_all(1) + return Bytes.get_string_byte(buf, 0) + end + + # Reads size bytes and copies them into buffer[0..size]. + def read_into_buffer(buffer, size) + tmp = read_all(size) + i = 0 + tmp.each_byte do |byte| + Bytes.set_string_byte(buffer, i, byte) + i += 1 + end + i + end + + def read_all(size) + return Bytes.empty_byte_buffer if size <= 0 + buf = read(size) + while (buf.length < size) + chunk = read(size - buf.length) + buf << chunk + end + + buf + end + + # Writes the byte buffer to the transport. In Ruby 1.9+, the buffer will be forced into BINARY encoding. + # + # buf - A String acting as a byte buffer. + # + # Returns nothing. + def write(buf); end + alias_method :<<, :write + + def flush; end + end + + class BaseTransportFactory + def get_transport(trans) + return trans + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb new file mode 100644 index 00000000..781d3c69 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/buffered_transport.rb @@ -0,0 +1,114 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class BufferedTransport < BaseTransport + DEFAULT_BUFFER = 4096 + + def initialize(transport) + @transport = transport + @wbuf = Bytes.empty_byte_buffer + @rbuf = Bytes.empty_byte_buffer + @index = 0 + end + + def open? + return @transport.open? + end + + def open + @transport.open + end + + def close + flush + @transport.close + end + + def read(sz) + @index += sz + ret = @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer + + if ret.length == 0 + @rbuf = @transport.read([sz, DEFAULT_BUFFER].max) + @index = sz + ret = @rbuf.slice(0, sz) || Bytes.empty_byte_buffer + end + + ret + end + + def read_byte + # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it. + if @index >= @rbuf.size + @rbuf = @transport.read(DEFAULT_BUFFER) + @index = 0 + end + + # The read buffer has some data now, read a single byte. Using get_string_byte() avoids + # allocating a temp string of size 1 unnecessarily. + @index += 1 + return Bytes.get_string_byte(@rbuf, @index - 1) + end + + # Reads a number of bytes from the transport into the buffer passed. + # + # buffer - The String (byte buffer) to write data to; this is assumed to have a BINARY encoding. + # size - The number of bytes to read from the transport and write to the buffer. + # + # Returns the number of bytes read. + def read_into_buffer(buffer, size) + i = 0 + while i < size + # If the read buffer is exhausted, try to read up to DEFAULT_BUFFER more bytes into it. + if @index >= @rbuf.size + @rbuf = @transport.read(DEFAULT_BUFFER) + @index = 0 + end + + # The read buffer has some data now, so copy bytes over to the output buffer. + byte = Bytes.get_string_byte(@rbuf, @index) + Bytes.set_string_byte(buffer, i, byte) + @index += 1 + i += 1 + end + i + end + + def write(buf) + @wbuf << Bytes.force_binary_encoding(buf) + end + + def flush + unless @wbuf.empty? + @transport.write(@wbuf) + @wbuf = Bytes.empty_byte_buffer + end + + @transport.flush + end + end + + class BufferedTransportFactory < BaseTransportFactory + def get_transport(transport) + return BufferedTransport.new(transport) + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/framed_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/framed_transport.rb new file mode 100644 index 00000000..d806ce02 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/framed_transport.rb @@ -0,0 +1,117 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class FramedTransport < BaseTransport + def initialize(transport, read=true, write=true) + @transport = transport + @rbuf = Bytes.empty_byte_buffer + @wbuf = Bytes.empty_byte_buffer + @read = read + @write = write + @index = 0 + end + + def open? + @transport.open? + end + + def open + @transport.open + end + + def close + @transport.close + end + + def read(sz) + return @transport.read(sz) unless @read + + return Bytes.empty_byte_buffer if sz <= 0 + + read_frame if @index >= @rbuf.length + + @index += sz + @rbuf.slice(@index - sz, sz) || Bytes.empty_byte_buffer + end + + def read_byte + return @transport.read_byte() unless @read + + read_frame if @index >= @rbuf.length + + # The read buffer has some data now, read a single byte. Using get_string_byte() avoids + # allocating a temp string of size 1 unnecessarily. + @index += 1 + return Bytes.get_string_byte(@rbuf, @index - 1) + end + + def read_into_buffer(buffer, size) + i = 0 + while i < size + read_frame if @index >= @rbuf.length + + # The read buffer has some data now, so copy bytes over to the output buffer. + byte = Bytes.get_string_byte(@rbuf, @index) + Bytes.set_string_byte(buffer, i, byte) + @index += 1 + i += 1 + end + i + end + + def write(buf, sz=nil) + return @transport.write(buf) unless @write + + buf = Bytes.force_binary_encoding(buf) + @wbuf << (sz ? buf[0...sz] : buf) + end + + # + # Writes the output buffer to the stream in the format of a 4-byte length + # followed by the actual data. + # + def flush + return @transport.flush unless @write + + out = [@wbuf.length].pack('N') + # Array#pack should return a BINARY encoded String, so it shouldn't be necessary to force encoding + out << @wbuf + @transport.write(out) + @transport.flush + @wbuf = Bytes.empty_byte_buffer + end + + private + + def read_frame + sz = @transport.read_all(4).unpack('N').first + + @index = 0 + @rbuf = @transport.read_all(sz) + end + end + + class FramedTransportFactory < BaseTransportFactory + def get_transport(transport) + return FramedTransport.new(transport) + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb new file mode 100644 index 00000000..c9c4fec8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/http_client_transport.rb @@ -0,0 +1,57 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'net/http' +require 'net/https' +require 'openssl' +require 'uri' +require 'stringio' + +module Thrift + class HTTPClientTransport < BaseTransport + + def initialize(url, opts = {}) + @url = URI url + @headers = {'Content-Type' => 'application/x-thrift'} + @outbuf = Bytes.empty_byte_buffer + @ssl_verify_mode = opts.fetch(:ssl_verify_mode, OpenSSL::SSL::VERIFY_PEER) + end + + def open?; true end + def read(sz); @inbuf.read sz end + def write(buf); @outbuf << Bytes.force_binary_encoding(buf) end + + def add_headers(headers) + @headers = @headers.merge(headers) + end + + def flush + http = Net::HTTP.new @url.host, @url.port + http.use_ssl = @url.scheme == 'https' + http.verify_mode = @ssl_verify_mode if @url.scheme == 'https' + resp = http.post(@url.request_uri, @outbuf, @headers) + data = resp.body + data = Bytes.force_binary_encoding(data) + @inbuf = StringIO.new data + ensure + @outbuf = Bytes.empty_byte_buffer + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb new file mode 100644 index 00000000..e3c8379d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/io_stream_transport.rb @@ -0,0 +1,39 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Very very simple implementation of wrapping two objects, one with a #read +# method and one with a #write method, into a transport for thrift. +# +# Assumes both objects are open, remain open, don't require flushing, etc. +# +module Thrift + class IOStreamTransport < BaseTransport + def initialize(input, output) + @input = input + @output = output + end + + def open?; not @input.closed? or not @output.closed? end + def read(sz); @input.read(sz) end + def write(buf); @output.write(Bytes.force_binary_encoding(buf)) end + def close; @input.close; @output.close end + def to_io; @input end # we're assuming this is used in a IO.select for reading + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb new file mode 100644 index 00000000..ad5ad855 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/memory_buffer_transport.rb @@ -0,0 +1,125 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class MemoryBufferTransport < BaseTransport + GARBAGE_BUFFER_SIZE = 4*(2**10) # 4kB + + # If you pass a string to this, you should #dup that string + # unless you want it to be modified by #read and #write + #-- + # this behavior is no longer required. If you wish to change it + # go ahead, just make sure the specs pass + def initialize(buffer = nil) + @buf = buffer ? Bytes.force_binary_encoding(buffer) : Bytes.empty_byte_buffer + @index = 0 + end + + def open? + return true + end + + def open + end + + def close + end + + def peek + @index < @buf.size + end + + # this method does not use the passed object directly but copies it + def reset_buffer(new_buf = '') + @buf.replace Bytes.force_binary_encoding(new_buf) + @index = 0 + end + + def available + @buf.length - @index + end + + def read(len) + data = @buf.slice(@index, len) + @index += len + @index = @buf.size if @index > @buf.size + if @index >= GARBAGE_BUFFER_SIZE + @buf = @buf.slice(@index..-1) + @index = 0 + end + if data.size < len + raise EOFError, "Not enough bytes remain in buffer" + end + data + end + + def read_byte + raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size + val = Bytes.get_string_byte(@buf, @index) + @index += 1 + if @index >= GARBAGE_BUFFER_SIZE + @buf = @buf.slice(@index..-1) + @index = 0 + end + val + end + + def read_into_buffer(buffer, size) + i = 0 + while i < size + raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size + + # The read buffer has some data now, so copy bytes over to the output buffer. + byte = Bytes.get_string_byte(@buf, @index) + Bytes.set_string_byte(buffer, i, byte) + @index += 1 + i += 1 + end + if @index >= GARBAGE_BUFFER_SIZE + @buf = @buf.slice(@index..-1) + @index = 0 + end + i + end + + def write(wbuf) + @buf << Bytes.force_binary_encoding(wbuf) + end + + def flush + end + + def inspect_buffer + out = [] + for idx in 0...(@buf.size) + # if idx != 0 + # out << " " + # end + + if idx == @index + out << ">" + end + + out << @buf[idx].ord.to_s(16) + end + out.join(" ") + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/server_socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/server_socket.rb new file mode 100644 index 00000000..7feb9ab0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/server_socket.rb @@ -0,0 +1,63 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'socket' + +module Thrift + class ServerSocket < BaseServerTransport + # call-seq: initialize(host = nil, port) + def initialize(host_or_port, port = nil) + if port + @host = host_or_port + @port = port + else + @host = nil + @port = host_or_port + end + @handle = nil + end + + attr_reader :handle + + def listen + @handle = TCPServer.new(@host, @port) + end + + def accept + unless @handle.nil? + sock = @handle.accept + trans = Socket.new + trans.handle = sock + trans + end + end + + def close + @handle.close unless @handle.nil? or @handle.closed? + @handle = nil + end + + def closed? + @handle.nil? or @handle.closed? + end + + alias to_io handle + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/socket.rb new file mode 100644 index 00000000..517d112a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/socket.rb @@ -0,0 +1,141 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'socket' + +module Thrift + class Socket < BaseTransport + def initialize(host='localhost', port=9090, timeout=nil) + @host = host + @port = port + @timeout = timeout + @desc = "#{host}:#{port}" + @handle = nil + end + + attr_accessor :handle, :timeout + + def open + for addrinfo in ::Socket::getaddrinfo(@host, @port, nil, ::Socket::SOCK_STREAM) do + begin + socket = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0) + socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1) + sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3]) + begin + socket.connect_nonblock(sockaddr) + rescue Errno::EINPROGRESS + unless IO.select(nil, [ socket ], nil, @timeout) + next + end + begin + socket.connect_nonblock(sockaddr) + rescue Errno::EISCONN + end + end + return @handle = socket + rescue StandardError => e + next + end + end + raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}") + end + + def open? + !@handle.nil? and !@handle.closed? + end + + def write(str) + raise IOError, "closed stream" unless open? + str = Bytes.force_binary_encoding(str) + begin + if @timeout.nil? or @timeout == 0 + @handle.write(str) + else + len = 0 + start = Time.now + while Time.now - start < @timeout + rd, wr, = IO.select(nil, [@handle], nil, @timeout) + if wr and not wr.empty? + len += @handle.write_nonblock(str[len..-1]) + break if len >= str.length + end + end + if len < str.length + raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out writing #{str.length} bytes to #{@desc}") + else + len + end + end + rescue TransportException => e + # pass this on + raise e + rescue StandardError => e + @handle.close + @handle = nil + raise TransportException.new(TransportException::NOT_OPEN, e.message) + end + end + + def read(sz) + raise IOError, "closed stream" unless open? + + begin + if @timeout.nil? or @timeout == 0 + data = @handle.readpartial(sz) + else + # it's possible to interrupt select for something other than the timeout + # so we need to ensure we've waited long enough, but not too long + start = Time.now + timespent = 0 + rd = loop do + rd, = IO.select([@handle], nil, nil, @timeout - timespent) + timespent = Time.now - start + break rd if (rd and not rd.empty?) or timespent >= @timeout + end + if rd.nil? or rd.empty? + raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out reading #{sz} bytes from #{@desc}") + else + data = @handle.readpartial(sz) + end + end + rescue TransportException => e + # don't let this get caught by the StandardError handler + raise e + rescue StandardError => e + @handle.close unless @handle.closed? + @handle = nil + raise TransportException.new(TransportException::NOT_OPEN, e.message) + end + if (data.nil? or data.length == 0) + raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}") + end + data + end + + def close + @handle.close unless @handle.nil? or @handle.closed? + @handle = nil + end + + def to_io + @handle + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb new file mode 100644 index 00000000..abc13439 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_server_socket.rb @@ -0,0 +1,37 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'socket' + +module Thrift + class SSLServerSocket < ServerSocket + def initialize(host_or_port, port = nil, ssl_context = nil) + super(host_or_port, port) + @ssl_context = ssl_context + end + + attr_accessor :ssl_context + + def listen + socket = TCPServer.new(@host, @port) + @handle = OpenSSL::SSL::SSLServer.new(socket, @ssl_context) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb new file mode 100644 index 00000000..dbbcc94f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/ssl_socket.rb @@ -0,0 +1,47 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Thrift + class SSLSocket < Socket + def initialize(host='localhost', port=9090, timeout=nil, ssl_context=nil) + super(host, port, timeout) + @ssl_context = ssl_context + end + + attr_accessor :ssl_context + + def open + socket = super + @handle = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context) + begin + @handle.connect_nonblock + @handle.post_connection_check(@host) + @handle + rescue IO::WaitReadable + IO.select([ @handle ], nil, nil, @timeout) + retry + rescue IO::WaitWritable + IO.select(nil, [ @handle ], nil, @timeout) + retry + rescue StandardError => e + raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}") + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb new file mode 100644 index 00000000..a135d25f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_server_socket.rb @@ -0,0 +1,60 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'socket' + +module Thrift + class UNIXServerSocket < BaseServerTransport + def initialize(path) + @path = path + @handle = nil + end + + attr_accessor :handle + + def listen + @handle = ::UNIXServer.new(@path) + end + + def accept + unless @handle.nil? + sock = @handle.accept + trans = UNIXSocket.new(nil) + trans.handle = sock + trans + end + end + + def close + if @handle + @handle.close unless @handle.closed? + @handle = nil + # UNIXServer doesn't delete the socket file, so we have to do it ourselves + File.delete(@path) + end + end + + def closed? + @handle.nil? or @handle.closed? + end + + alias to_io handle + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_socket.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_socket.rb new file mode 100644 index 00000000..8f692e4c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/transport/unix_socket.rb @@ -0,0 +1,40 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'socket' + +module Thrift + class UNIXSocket < Socket + def initialize(path, timeout=nil) + @path = path + @timeout = timeout + @desc = @path # for read()'s error + @handle = nil + end + + def open + begin + @handle = ::UNIXSocket.new(@path) + rescue StandardError + raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}") + end + end + end +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/types.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/types.rb new file mode 100644 index 00000000..cac52691 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/types.rb @@ -0,0 +1,101 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'set' + +module Thrift + module Types + STOP = 0 + VOID = 1 + BOOL = 2 + BYTE = 3 + DOUBLE = 4 + I16 = 6 + I32 = 8 + I64 = 10 + STRING = 11 + STRUCT = 12 + MAP = 13 + SET = 14 + LIST = 15 + end + + class << self + attr_accessor :type_checking + end + + class TypeError < Exception + end + + def self.check_type(value, field, name, skip_nil=true) + return if value.nil? and skip_nil + klasses = case field[:type] + when Types::VOID + NilClass + when Types::BOOL + [TrueClass, FalseClass] + when Types::BYTE, Types::I16, Types::I32, Types::I64 + Integer + when Types::DOUBLE + Float + when Types::STRING + String + when Types::STRUCT + [Struct, Union] + when Types::MAP + Hash + when Types::SET + Set + when Types::LIST + Array + end + valid = klasses && [*klasses].any? { |klass| klass === value } + raise TypeError, "Expected #{type_name(field[:type])}, received #{value.class} for field #{name}" unless valid + # check elements now + case field[:type] + when Types::MAP + value.each_pair do |k,v| + check_type(k, field[:key], "#{name}.key", false) + check_type(v, field[:value], "#{name}.value", false) + end + when Types::SET, Types::LIST + value.each do |el| + check_type(el, field[:element], "#{name}.element", false) + end + when Types::STRUCT + raise TypeError, "Expected #{field[:class]}, received #{value.class} for field #{name}" unless field[:class] == value.class + end + end + + def self.type_name(type) + Types.constants.each do |const| + return "Types::#{const}" if Types.const_get(const) == type + end + nil + end + + module MessageTypes + CALL = 1 + REPLY = 2 + EXCEPTION = 3 + ONEWAY = 4 + end +end + +Thrift.type_checking = false if Thrift.type_checking.nil? diff --git a/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/union.rb b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/union.rb new file mode 100644 index 00000000..490c55c4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/lib/thrift/union.rb @@ -0,0 +1,176 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +module Thrift + class Union + def initialize(name=nil, value=nil) + if name + if name.is_a? Hash + if name.size > 1 + raise "#{self.class} cannot be instantiated with more than one field!" + end + + name, value = name.keys.first, name.values.first + end + + if Thrift.type_checking + raise Exception, "#{self.class} does not contain a field named #{name}!" unless name_to_id(name.to_s) + end + + if value.nil? + raise Exception, "Union #{self.class} cannot be instantiated with setfield and nil value!" + end + + Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking + elsif !value.nil? + raise Exception, "Value provided, but no name!" + end + @setfield = name + @value = value + end + + def inspect + if get_set_field + "<#{self.class} #{@setfield}: #{inspect_field(@value, struct_fields[name_to_id(@setfield.to_s)])}>" + else + "<#{self.class} >" + end + end + + def read(iprot) + iprot.read_struct_begin + fname, ftype, fid = iprot.read_field_begin + handle_message(iprot, fid, ftype) + iprot.read_field_end + + fname, ftype, fid = iprot.read_field_begin + raise "Too many fields for union" unless (ftype == Types::STOP) + + iprot.read_struct_end + validate + end + + def write(oprot) + validate + oprot.write_struct_begin(self.class.name) + + fid = self.name_to_id(@setfield.to_s) + + field_info = struct_fields[fid] + type = field_info[:type] + if is_container? type + oprot.write_field_begin(@setfield, type, fid) + write_container(oprot, @value, field_info) + oprot.write_field_end + else + oprot.write_field(@setfield, type, fid, @value) + end + + oprot.write_field_stop + oprot.write_struct_end + end + + def ==(other) + other.equal?(self) || other.instance_of?(self.class) && @setfield == other.get_set_field && @value == other.get_value + end + alias_method :eql?, :== + + def hash + [self.class.name, @setfield, @value].hash + end + + def self.field_accessor(klass, field_info) + klass.send :define_method, field_info[:name] do + if field_info[:name].to_sym == @setfield + @value + else + raise RuntimeError, "#{field_info[:name]} is not union's set field." + end + end + + klass.send :define_method, "#{field_info[:name]}=" do |value| + Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking + @setfield = field_info[:name].to_sym + @value = value + end + end + + def self.qmark_isset_method(klass, field_info) + klass.send :define_method, "#{field_info[:name]}?" do + get_set_field == field_info[:name].to_sym && !get_value.nil? + end + end + + def self.generate_accessors(klass) + klass::FIELDS.values.each do |field_info| + field_accessor(klass, field_info) + qmark_isset_method(klass, field_info) + end + end + + # get the symbol that indicates what the currently set field type is. + def get_set_field + @setfield + end + + # get the current value of this union, regardless of what the set field is. + # generally, you should only use this method when you don't know in advance + # what field to expect. + def get_value + @value + end + + def <=>(other) + if self.class == other.class + if get_set_field == other.get_set_field + if get_set_field.nil? + 0 + else + get_value <=> other.get_value + end + else + if get_set_field && other.get_set_field.nil? + -1 + elsif get_set_field.nil? && other.get_set_field + 1 + elsif get_set_field.nil? && other.get_set_field.nil? + 0 + else + name_to_id(get_set_field.to_s) <=> name_to_id(other.get_set_field.to_s) + end + end + else + self.class <=> other.class + end + end + + protected + + def handle_message(iprot, fid, ftype) + field = struct_fields[fid] + if field and field[:type] == ftype + @value = read_field(iprot, field) + name = field[:name].to_sym + @setfield = name + else + iprot.skip(ftype) + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/script/proto_benchmark.rb b/vendor/src/github.com/apache/thrift/lib/rb/script/proto_benchmark.rb new file mode 100644 index 00000000..bb49e2e4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/script/proto_benchmark.rb @@ -0,0 +1,121 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require File.dirname(__FILE__) + "/../spec/spec_helper.rb" + +require "benchmark" +# require "ruby-prof" + +obj = Fixtures::COMPACT_PROTOCOL_TEST_STRUCT + +HOW_MANY = 1_000 + +binser = Thrift::Serializer.new +bin_data = binser.serialize(obj) +bindeser = Thrift::Deserializer.new +accel_bin_ser = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new) +accel_bin_deser = Thrift::Deserializer.new(Thrift::BinaryProtocolAcceleratedFactory.new) + +compact_ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new) +compact_data = compact_ser.serialize(obj) +compact_deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new) + +Benchmark.bm(60) do |reporter| + reporter.report("binary protocol, write") do + HOW_MANY.times do + binser.serialize(obj) + end + end + + reporter.report("accelerated binary protocol, write") do + HOW_MANY.times do + accel_bin_ser.serialize(obj) + end + end + + reporter.report("compact protocol, write") do + # RubyProf.start + HOW_MANY.times do + compact_ser.serialize(obj) + end + # result = RubyProf.stop + # printer = RubyProf::GraphHtmlPrinter.new(result) + # file = File.open("profile.html", "w+") + # printer.print(file, 0) + # file.close + end + + reporter.report("binary protocol, read") do + HOW_MANY.times do + bindeser.deserialize(obj, bin_data) + end + end + + reporter.report("accelerated binary protocol, read") do + HOW_MANY.times do + accel_bin_deser.deserialize(obj, bin_data) + end + end + + reporter.report("compact protocol, read") do + HOW_MANY.times do + compact_deser.deserialize(obj, compact_data) + end + end + + + # f = File.new("/tmp/testfile", "w") + # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f)) + # reporter.report("accelerated binary protocol, write (to disk)") do + # HOW_MANY.times do + # obj.write(proto) + # end + # f.flush + # end + # f.close + # + # f = File.new("/tmp/testfile", "r") + # proto = Thrift::BinaryProtocolAccelerated.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new)) + # reporter.report("accelerated binary protocol, read (from disk)") do + # HOW_MANY.times do + # obj.read(proto) + # end + # end + # f.close + # + # f = File.new("/tmp/testfile", "w") + # reporter.report("compact protocol, write (to disk)") do + # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(Thrift::MemoryBufferTransport.new, f)) + # HOW_MANY.times do + # obj.write(proto) + # end + # f.flush + # end + # f.close + # + # f = File.new("/tmp/testfile", "r") + # reporter.report("compact protocol, read (from disk)") do + # proto = Thrift::CompactProtocol.new(Thrift::IOStreamTransport.new(f, Thrift::MemoryBufferTransport.new)) + # HOW_MANY.times do + # obj.read(proto) + # end + # end + # f.close + +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/script/read_struct.rb b/vendor/src/github.com/apache/thrift/lib/rb/script/read_struct.rb new file mode 100644 index 00000000..831fcec9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/script/read_struct.rb @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require "spec/spec_helper" + +path, factory_class = ARGV + +factory = eval(factory_class).new + +deser = Thrift::Deserializer.new(factory) + +cpts = CompactProtoTestStruct.new +CompactProtoTestStruct.constants.each do |const| + cpts.instance_variable_set("@#{const}", nil) +end + +data = File.read(path) + +deser.deserialize(cpts, data) + +if cpts == Fixtures::COMPACT_PROTOCOL_TEST_STRUCT + puts "Object verified successfully!" +else + puts "Object failed verification! Expected #{Fixtures::COMPACT_PROTOCOL_TEST_STRUCT.inspect} but got #{cpts.inspect}" + + puts cpts.differences(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT) +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/script/write_struct.rb b/vendor/src/github.com/apache/thrift/lib/rb/script/write_struct.rb new file mode 100644 index 00000000..da142197 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/script/write_struct.rb @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require "spec/spec_helper" + +path, factory_class = ARGV + +factory = eval(factory_class).new + +ser = Thrift::Serializer.new(factory) + +File.open(path, "w") do |file| + file.write(ser.serialize(Fixtures::COMPACT_PROTOCOL_TEST_STRUCT)) +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/BaseService.thrift b/vendor/src/github.com/apache/thrift/lib/rb/spec/BaseService.thrift new file mode 100644 index 00000000..5c7d32a6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/BaseService.thrift @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb Base + +struct Hello { + 1: string greeting = "hello world" +} + +service BaseService { + Hello greeting(1:bool english) +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/ExtendedService.thrift b/vendor/src/github.com/apache/thrift/lib/rb/spec/ExtendedService.thrift new file mode 100644 index 00000000..1a6b705a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/ExtendedService.thrift @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb Extended + +include "BaseService.thrift" + +service ExtendedService extends BaseService.BaseService { + void ping() +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/Referenced.thrift b/vendor/src/github.com/apache/thrift/lib/rb/spec/Referenced.thrift new file mode 100644 index 00000000..98f183fe --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/Referenced.thrift @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb OtherNamespace + +enum SomeEnum { + ONE + TWO +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift b/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift new file mode 100644 index 00000000..02f28895 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftNamespacedSpec.thrift @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb NamespacedSpecNamespace + +include "Referenced.thrift" + +struct Hello { + 1: string greeting = "hello world" +} + +service NamespacedNonblockingService { + Hello greeting(1:bool english) + bool block() + oneway void unblock(1:i32 n) + oneway void shutdown() + void sleep(1:double seconds) +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftSpec.thrift b/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftSpec.thrift new file mode 100644 index 00000000..b42481b3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/ThriftSpec.thrift @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +namespace rb SpecNamespace + +struct Hello { + 1: string greeting = "hello world" +} + +enum SomeEnum { + ONE + TWO +} + +struct StructWithSomeEnum { + 1: SomeEnum some_enum; +} + +union TestUnion { + /** + * A doc string + */ + 1: string string_field; + 2: i32 i32_field; + 3: i32 other_i32_field; + 4: SomeEnum enum_field; + 5: binary binary_field; +} + +struct Foo { + 1: i32 simple = 53, + 2: string words = "words", + 3: Hello hello = {'greeting' : "hello, world!"}, + 4: list ints = [1, 2, 2, 3], + 5: map> complex, + 6: set shorts = [5, 17, 239], + 7: optional string opt_string + 8: bool my_bool +} + +struct Foo2 { + 1: binary my_binary +} + +struct BoolStruct { + 1: bool yesno = 1 +} + +struct SimpleList { + 1: list bools, + 2: list bytes, + 3: list i16s, + 4: list i32s, + 5: list i64s, + 6: list doubles, + 7: list strings, + 8: list> maps, + 9: list> lists, + 10: list> sets, + 11: list hellos +} + +exception Xception { + 1: string message, + 2: i32 code = 1 +} + +service NonblockingService { + Hello greeting(1:bool english) + bool block() + oneway void unblock(1:i32 n) + oneway void shutdown() + void sleep(1:double seconds) +} + +union My_union { + 1: bool im_true, + 2: byte a_bite, + 3: i16 integer16, + 4: i32 integer32, + 5: i64 integer64, + 6: double double_precision, + 7: string some_characters, + 8: i32 other_i32 + 9: SomeEnum some_enum; + 10: map> my_map; +} + +struct Struct_with_union { + 1: My_union fun_union + 2: i32 integer32 + 3: string some_characters +} + +struct StructWithEnumMap { + 1: map> my_map; +} + +# Nested lists +struct NestedListInList { + 1: list> value +} + +struct NestedListInSet { + 1: set> value +} + +struct NestedListInMapKey { + 1: map, byte> value +} + +struct NestedListInMapValue { + 1: map> value +} + +# Nested sets +struct NestedSetInList { + 1: list> value +} + +struct NestedSetInSet { + 1: set> value +} + +struct NestedSetInMapKey { + 1: map, byte> value +} + +struct NestedSetInMapValue { + 1: map> value +} + +# Nested maps +struct NestedMapInList { + 1: list> value +} + +struct NestedMapInSet { + 1: set> value +} + +struct NestedMapInMapKey { + 2: map, byte> value +} + +struct NestedMapInMapValue { + 2: map> value +} diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/base_protocol_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/base_protocol_spec.rb new file mode 100644 index 00000000..ec50c482 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/base_protocol_spec.rb @@ -0,0 +1,217 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'BaseProtocol' do + + before(:each) do + @trans = mock("MockTransport") + @prot = Thrift::BaseProtocol.new(@trans) + end + + describe Thrift::BaseProtocol do + # most of the methods are stubs, so we can ignore them + + it "should make trans accessible" do + @prot.trans.should eql(@trans) + end + + it 'should write out a field nicely (deprecated write_field signature)' do + @prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered + @prot.should_receive(:write_type).with({:name => 'field', :type => 'type'}, 'value').ordered + @prot.should_receive(:write_field_end).ordered + @prot.write_field('field', 'type', 'fid', 'value') + end + + it 'should write out a field nicely' do + @prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered + @prot.should_receive(:write_type).with({:name => 'field', :type => 'type', :binary => false}, 'value').ordered + @prot.should_receive(:write_field_end).ordered + @prot.write_field({:name => 'field', :type => 'type', :binary => false}, 'fid', 'value') + end + + it 'should write out the different types (deprecated write_type signature)' do + @prot.should_receive(:write_bool).with('bool').ordered + @prot.should_receive(:write_byte).with('byte').ordered + @prot.should_receive(:write_double).with('double').ordered + @prot.should_receive(:write_i16).with('i16').ordered + @prot.should_receive(:write_i32).with('i32').ordered + @prot.should_receive(:write_i64).with('i64').ordered + @prot.should_receive(:write_string).with('string').ordered + struct = mock('Struct') + struct.should_receive(:write).with(@prot).ordered + @prot.write_type(Thrift::Types::BOOL, 'bool') + @prot.write_type(Thrift::Types::BYTE, 'byte') + @prot.write_type(Thrift::Types::DOUBLE, 'double') + @prot.write_type(Thrift::Types::I16, 'i16') + @prot.write_type(Thrift::Types::I32, 'i32') + @prot.write_type(Thrift::Types::I64, 'i64') + @prot.write_type(Thrift::Types::STRING, 'string') + @prot.write_type(Thrift::Types::STRUCT, struct) + # all other types are not implemented + [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type| + expect { @prot.write_type(type, type.to_s) }.to raise_error(NotImplementedError) + end + end + + it 'should write out the different types' do + @prot.should_receive(:write_bool).with('bool').ordered + @prot.should_receive(:write_byte).with('byte').ordered + @prot.should_receive(:write_double).with('double').ordered + @prot.should_receive(:write_i16).with('i16').ordered + @prot.should_receive(:write_i32).with('i32').ordered + @prot.should_receive(:write_i64).with('i64').ordered + @prot.should_receive(:write_string).with('string').ordered + @prot.should_receive(:write_binary).with('binary').ordered + struct = mock('Struct') + struct.should_receive(:write).with(@prot).ordered + @prot.write_type({:type => Thrift::Types::BOOL}, 'bool') + @prot.write_type({:type => Thrift::Types::BYTE}, 'byte') + @prot.write_type({:type => Thrift::Types::DOUBLE}, 'double') + @prot.write_type({:type => Thrift::Types::I16}, 'i16') + @prot.write_type({:type => Thrift::Types::I32}, 'i32') + @prot.write_type({:type => Thrift::Types::I64}, 'i64') + @prot.write_type({:type => Thrift::Types::STRING}, 'string') + @prot.write_type({:type => Thrift::Types::STRING, :binary => true}, 'binary') + @prot.write_type({:type => Thrift::Types::STRUCT}, struct) + # all other types are not implemented + [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type| + expect { @prot.write_type({:type => type}, type.to_s) }.to raise_error(NotImplementedError) + end + end + + it 'should read the different types (deprecated read_type signature)' do + @prot.should_receive(:read_bool).ordered + @prot.should_receive(:read_byte).ordered + @prot.should_receive(:read_i16).ordered + @prot.should_receive(:read_i32).ordered + @prot.should_receive(:read_i64).ordered + @prot.should_receive(:read_double).ordered + @prot.should_receive(:read_string).ordered + @prot.read_type(Thrift::Types::BOOL) + @prot.read_type(Thrift::Types::BYTE) + @prot.read_type(Thrift::Types::I16) + @prot.read_type(Thrift::Types::I32) + @prot.read_type(Thrift::Types::I64) + @prot.read_type(Thrift::Types::DOUBLE) + @prot.read_type(Thrift::Types::STRING) + # all other types are not implemented + [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, + Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type| + expect { @prot.read_type(type) }.to raise_error(NotImplementedError) + end + end + + it 'should read the different types' do + @prot.should_receive(:read_bool).ordered + @prot.should_receive(:read_byte).ordered + @prot.should_receive(:read_i16).ordered + @prot.should_receive(:read_i32).ordered + @prot.should_receive(:read_i64).ordered + @prot.should_receive(:read_double).ordered + @prot.should_receive(:read_string).ordered + @prot.should_receive(:read_binary).ordered + @prot.read_type({:type => Thrift::Types::BOOL}) + @prot.read_type({:type => Thrift::Types::BYTE}) + @prot.read_type({:type => Thrift::Types::I16}) + @prot.read_type({:type => Thrift::Types::I32}) + @prot.read_type({:type => Thrift::Types::I64}) + @prot.read_type({:type => Thrift::Types::DOUBLE}) + @prot.read_type({:type => Thrift::Types::STRING}) + @prot.read_type({:type => Thrift::Types::STRING, :binary => true}) + # all other types are not implemented + [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, + Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type| + expect { @prot.read_type({:type => type}) }.to raise_error(NotImplementedError) + end + end + + it "should skip the basic types" do + @prot.should_receive(:read_bool).ordered + @prot.should_receive(:read_byte).ordered + @prot.should_receive(:read_i16).ordered + @prot.should_receive(:read_i32).ordered + @prot.should_receive(:read_i64).ordered + @prot.should_receive(:read_double).ordered + @prot.should_receive(:read_string).ordered + @prot.skip(Thrift::Types::BOOL) + @prot.skip(Thrift::Types::BYTE) + @prot.skip(Thrift::Types::I16) + @prot.skip(Thrift::Types::I32) + @prot.skip(Thrift::Types::I64) + @prot.skip(Thrift::Types::DOUBLE) + @prot.skip(Thrift::Types::STRING) + @prot.skip(Thrift::Types::STOP) # should do absolutely nothing + end + + it "should skip structs" do + real_skip = @prot.method(:skip) + @prot.should_receive(:read_struct_begin).ordered + @prot.should_receive(:read_field_begin).exactly(4).times.and_return( + ['field 1', Thrift::Types::STRING, 1], + ['field 2', Thrift::Types::I32, 2], + ['field 3', Thrift::Types::MAP, 3], + [nil, Thrift::Types::STOP, 0] + ) + @prot.should_receive(:read_field_end).exactly(3).times + @prot.should_receive(:read_string).exactly(3).times + @prot.should_receive(:read_i32).ordered + @prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRING, 1]) + # @prot.should_receive(:read_string).exactly(2).times + @prot.should_receive(:read_map_end).ordered + @prot.should_receive(:read_struct_end).ordered + real_skip.call(Thrift::Types::STRUCT) + end + + it "should skip maps" do + real_skip = @prot.method(:skip) + @prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRUCT, 1]) + @prot.should_receive(:read_string).ordered + @prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"]) + @prot.should_receive(:read_field_begin).ordered.and_return([nil, Thrift::Types::STOP, nil]); + @prot.should_receive(:read_struct_end).ordered + @prot.should_receive(:read_map_end).ordered + real_skip.call(Thrift::Types::MAP) + end + + it "should skip sets" do + real_skip = @prot.method(:skip) + @prot.should_receive(:read_set_begin).ordered.and_return([Thrift::Types::I64, 9]) + @prot.should_receive(:read_i64).ordered.exactly(9).times + @prot.should_receive(:read_set_end) + real_skip.call(Thrift::Types::SET) + end + + it "should skip lists" do + real_skip = @prot.method(:skip) + @prot.should_receive(:read_list_begin).ordered.and_return([Thrift::Types::DOUBLE, 11]) + @prot.should_receive(:read_double).ordered.exactly(11).times + @prot.should_receive(:read_list_end) + real_skip.call(Thrift::Types::LIST) + end + end + + describe Thrift::BaseProtocolFactory do + it "should raise NotImplementedError" do + # returning nil since Protocol is just an abstract class + lambda {Thrift::BaseProtocolFactory.new.get_protocol(mock("MockTransport"))}.should raise_error(NotImplementedError) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/base_transport_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/base_transport_spec.rb new file mode 100644 index 00000000..4196572d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/base_transport_spec.rb @@ -0,0 +1,350 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'BaseTransport' do + + describe Thrift::TransportException do + it "should make type accessible" do + exc = Thrift::TransportException.new(Thrift::TransportException::ALREADY_OPEN, "msg") + exc.type.should == Thrift::TransportException::ALREADY_OPEN + exc.message.should == "msg" + end + end + + describe Thrift::BaseTransport do + it "should read the specified size" do + transport = Thrift::BaseTransport.new + transport.should_receive(:read).with(40).ordered.and_return("10 letters") + transport.should_receive(:read).with(30).ordered.and_return("fifteen letters") + transport.should_receive(:read).with(15).ordered.and_return("more characters") + transport.read_all(40).should == "10 lettersfifteen lettersmore characters" + end + + it "should stub out the rest of the methods" do + # can't test for stubbiness, so just make sure they're defined + [:open?, :open, :close, :read, :write, :flush].each do |sym| + Thrift::BaseTransport.method_defined?(sym).should be_true + end + end + + it "should alias << to write" do + Thrift::BaseTransport.instance_method(:<<).should == Thrift::BaseTransport.instance_method(:write) + end + end + + describe Thrift::BaseServerTransport do + it "should stub out its methods" do + [:listen, :accept, :close].each do |sym| + Thrift::BaseServerTransport.method_defined?(sym).should be_true + end + end + end + + describe Thrift::BaseTransportFactory do + it "should return the transport it's given" do + transport = mock("Transport") + Thrift::BaseTransportFactory.new.get_transport(transport).should eql(transport) + end + end + + describe Thrift::BufferedTransport do + it "should pass through everything but write/flush/read" do + trans = mock("Transport") + trans.should_receive(:open?).ordered.and_return("+ open?") + trans.should_receive(:open).ordered.and_return("+ open") + trans.should_receive(:flush).ordered # from the close + trans.should_receive(:close).ordered.and_return("+ close") + btrans = Thrift::BufferedTransport.new(trans) + btrans.open?.should == "+ open?" + btrans.open.should == "+ open" + btrans.close.should == "+ close" + end + + it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do + trans = mock("Transport") + trans.should_receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet") + btrans = Thrift::BufferedTransport.new(trans) + btrans.read(6).should == "lorum " + btrans.read(6).should == "ipsum " + btrans.read(6).should == "dolor " + btrans.read(6).should == "emet" + end + + it "should buffer writes and send them on flush" do + trans = mock("Transport") + btrans = Thrift::BufferedTransport.new(trans) + btrans.write("one/") + btrans.write("two/") + btrans.write("three/") + trans.should_receive(:write).with("one/two/three/").ordered + trans.should_receive(:flush).ordered + btrans.flush + end + + it "should only send buffered data once" do + trans = mock("Transport") + btrans = Thrift::BufferedTransport.new(trans) + btrans.write("one/") + btrans.write("two/") + btrans.write("three/") + trans.should_receive(:write).with("one/two/three/") + trans.stub!(:flush) + btrans.flush + # Nothing to flush with no data + btrans.flush + end + + it "should flush on close" do + trans = mock("Transport") + trans.should_receive(:close) + btrans = Thrift::BufferedTransport.new(trans) + btrans.should_receive(:flush) + btrans.close + end + + it "should not write to socket if there's no data" do + trans = mock("Transport") + trans.should_receive(:flush) + btrans = Thrift::BufferedTransport.new(trans) + btrans.flush + end + end + + describe Thrift::BufferedTransportFactory do + it "should wrap the given transport in a BufferedTransport" do + trans = mock("Transport") + btrans = mock("BufferedTransport") + Thrift::BufferedTransport.should_receive(:new).with(trans).and_return(btrans) + Thrift::BufferedTransportFactory.new.get_transport(trans).should == btrans + end + end + + describe Thrift::FramedTransport do + before(:each) do + @trans = mock("Transport") + end + + it "should pass through open?/open/close" do + ftrans = Thrift::FramedTransport.new(@trans) + @trans.should_receive(:open?).ordered.and_return("+ open?") + @trans.should_receive(:open).ordered.and_return("+ open") + @trans.should_receive(:close).ordered.and_return("+ close") + ftrans.open?.should == "+ open?" + ftrans.open.should == "+ open" + ftrans.close.should == "+ close" + end + + it "should pass through read when read is turned off" do + ftrans = Thrift::FramedTransport.new(@trans, false, true) + @trans.should_receive(:read).with(17).ordered.and_return("+ read") + ftrans.read(17).should == "+ read" + end + + it "should pass through write/flush when write is turned off" do + ftrans = Thrift::FramedTransport.new(@trans, true, false) + @trans.should_receive(:write).with("foo").ordered.and_return("+ write") + @trans.should_receive(:flush).ordered.and_return("+ flush") + ftrans.write("foo").should == "+ write" + ftrans.flush.should == "+ flush" + end + + it "should return a full frame if asked for >= the frame's length" do + frame = "this is a frame" + @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017") + @trans.should_receive(:read_all).with(frame.length).and_return(frame) + Thrift::FramedTransport.new(@trans).read(frame.length + 10).should == frame + end + + it "should return slices of the frame when asked for < the frame's length" do + frame = "this is a frame" + @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017") + @trans.should_receive(:read_all).with(frame.length).and_return(frame) + ftrans = Thrift::FramedTransport.new(@trans) + ftrans.read(4).should == "this" + ftrans.read(4).should == " is " + ftrans.read(16).should == "a frame" + end + + it "should return nothing if asked for <= 0" do + Thrift::FramedTransport.new(@trans).read(-2).should == "" + end + + it "should pull a new frame when the first is exhausted" do + frame = "this is a frame" + frame2 = "yet another frame" + @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021") + @trans.should_receive(:read_all).with(frame.length).and_return(frame) + @trans.should_receive(:read_all).with(frame2.length).and_return(frame2) + ftrans = Thrift::FramedTransport.new(@trans) + ftrans.read(4).should == "this" + ftrans.read(8).should == " is a fr" + ftrans.read(6).should == "ame" + ftrans.read(4).should == "yet " + ftrans.read(16).should == "another frame" + end + + it "should buffer writes" do + ftrans = Thrift::FramedTransport.new(@trans) + @trans.should_not_receive(:write) + ftrans.write("foo") + ftrans.write("bar") + ftrans.write("this is a frame") + end + + it "should write slices of the buffer" do + ftrans = Thrift::FramedTransport.new(@trans) + ftrans.write("foobar", 3) + ftrans.write("barfoo", 1) + @trans.stub!(:flush) + @trans.should_receive(:write).with("\000\000\000\004foob") + ftrans.flush + end + + it "should flush frames with a 4-byte header" do + ftrans = Thrift::FramedTransport.new(@trans) + @trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered + @trans.should_receive(:flush).ordered + ftrans.write("one/") + ftrans.write("two/") + ftrans.write("three/") + ftrans.write("this is a frame") + ftrans.flush + end + + it "should not flush the same buffered data twice" do + ftrans = Thrift::FramedTransport.new(@trans) + @trans.should_receive(:write).with("\000\000\000\007foo/bar") + @trans.stub!(:flush) + ftrans.write("foo") + ftrans.write("/bar") + ftrans.flush + @trans.should_receive(:write).with("\000\000\000\000") + ftrans.flush + end + end + + describe Thrift::FramedTransportFactory do + it "should wrap the given transport in a FramedTransport" do + trans = mock("Transport") + Thrift::FramedTransport.should_receive(:new).with(trans) + Thrift::FramedTransportFactory.new.get_transport(trans) + end + end + + describe Thrift::MemoryBufferTransport do + before(:each) do + @buffer = Thrift::MemoryBufferTransport.new + end + + it "should accept a buffer on input and use it directly" do + s = "this is a test" + @buffer = Thrift::MemoryBufferTransport.new(s) + @buffer.read(4).should == "this" + s.slice!(-4..-1) + @buffer.read(@buffer.available).should == " is a " + end + + it "should always remain open" do + @buffer.should be_open + @buffer.close + @buffer.should be_open + end + + it "should respond to peek and available" do + @buffer.write "some data" + @buffer.peek.should be_true + @buffer.available.should == 9 + @buffer.read(4) + @buffer.peek.should be_true + @buffer.available.should == 5 + @buffer.read(5) + @buffer.peek.should be_false + @buffer.available.should == 0 + end + + it "should be able to reset the buffer" do + @buffer.write "test data" + @buffer.reset_buffer("foobar") + @buffer.available.should == 6 + @buffer.read(@buffer.available).should == "foobar" + @buffer.reset_buffer + @buffer.available.should == 0 + end + + it "should copy the given string when resetting the buffer" do + s = "this is a test" + @buffer.reset_buffer(s) + @buffer.available.should == 14 + @buffer.read(10) + @buffer.available.should == 4 + s.should == "this is a test" + end + + it "should return from read what was given in write" do + @buffer.write "test data" + @buffer.read(4).should == "test" + @buffer.read(@buffer.available).should == " data" + @buffer.write "foo" + @buffer.write " bar" + @buffer.read(@buffer.available).should == "foo bar" + end + + it "should throw an EOFError when there isn't enough data in the buffer" do + @buffer.reset_buffer("") + lambda{@buffer.read(1)}.should raise_error(EOFError) + + @buffer.reset_buffer("1234") + lambda{@buffer.read(5)}.should raise_error(EOFError) + end + end + + describe Thrift::IOStreamTransport do + before(:each) do + @input = mock("Input", :closed? => false) + @output = mock("Output", :closed? => false) + @trans = Thrift::IOStreamTransport.new(@input, @output) + end + + it "should be open as long as both input or output are open" do + @trans.should be_open + @input.stub!(:closed?).and_return(true) + @trans.should be_open + @input.stub!(:closed?).and_return(false) + @output.stub!(:closed?).and_return(true) + @trans.should be_open + @input.stub!(:closed?).and_return(true) + @trans.should_not be_open + end + + it "should pass through read/write to input/output" do + @input.should_receive(:read).with(17).and_return("+ read") + @output.should_receive(:write).with("foobar").and_return("+ write") + @trans.read(17).should == "+ read" + @trans.write("foobar").should == "+ write" + end + + it "should close both input and output when closed" do + @input.should_receive(:close) + @output.should_receive(:close) + @trans.close + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb new file mode 100644 index 00000000..bac9ea7c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_accelerated_spec.rb @@ -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. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared") + +if defined? Thrift::BinaryProtocolAccelerated + + describe 'BinaryProtocolAccelerated' do + # since BinaryProtocolAccelerated should be directly equivalent to + # BinaryProtocol, we don't need any custom specs! + it_should_behave_like 'a binary protocol' + + def protocol_class + Thrift::BinaryProtocolAccelerated + end + + describe Thrift::BinaryProtocolAcceleratedFactory do + it "should create a BinaryProtocolAccelerated" do + Thrift::BinaryProtocolAcceleratedFactory.new.get_protocol(mock("MockTransport")).should be_instance_of(Thrift::BinaryProtocolAccelerated) + end + end + end +else + puts "skipping BinaryProtocolAccelerated spec because it is not defined." +end \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec.rb new file mode 100644 index 00000000..32772d3f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec.rb @@ -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. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/binary_protocol_spec_shared") + +describe 'BinaryProtocol' do + + it_should_behave_like 'a binary protocol' + + def protocol_class + Thrift::BinaryProtocol + end + + describe Thrift::BinaryProtocol do + + before(:each) do + @trans = Thrift::MemoryBufferTransport.new + @prot = protocol_class.new(@trans) + end + + it "should read a message header" do + @trans.write([protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::REPLY].pack('N')) + @trans.write([42].pack('N')) + @prot.should_receive(:read_string).and_return('testMessage') + @prot.read_message_begin.should == ['testMessage', Thrift::MessageTypes::REPLY, 42] + end + + it "should raise an exception if the message header has the wrong version" do + @prot.should_receive(:read_i32).and_return(-1) + lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'Missing version identifier') do |e| + e.type == Thrift::ProtocolException::BAD_VERSION + end + end + + it "should raise an exception if the message header does not exist and strict_read is enabled" do + @prot.should_receive(:read_i32).and_return(42) + @prot.should_receive(:strict_read).and_return(true) + lambda { @prot.read_message_begin }.should raise_error(Thrift::ProtocolException, 'No version identifier, old protocol client?') do |e| + e.type == Thrift::ProtocolException::BAD_VERSION + end + end + end + + describe Thrift::BinaryProtocolFactory do + it "should create a BinaryProtocol" do + Thrift::BinaryProtocolFactory.new.get_protocol(mock("MockTransport")).should be_instance_of(Thrift::BinaryProtocol) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec_shared.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec_shared.rb new file mode 100644 index 00000000..7a9d0287 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/binary_protocol_spec_shared.rb @@ -0,0 +1,455 @@ +# encoding: ascii-8bit +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +shared_examples_for 'a binary protocol' do + before(:each) do + @trans = Thrift::MemoryBufferTransport.new + @prot = protocol_class.new(@trans) + end + + it "should define the proper VERSION_1, VERSION_MASK AND TYPE_MASK" do + protocol_class.const_get(:VERSION_MASK).should == 0xffff0000 + protocol_class.const_get(:VERSION_1).should == 0x80010000 + protocol_class.const_get(:TYPE_MASK).should == 0x000000ff + end + + it "should make strict_read readable" do + @prot.strict_read.should eql(true) + end + + it "should make strict_write readable" do + @prot.strict_write.should eql(true) + end + + it "should write the message header" do + @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17) + @trans.read(@trans.available).should == [protocol_class.const_get(:VERSION_1) | Thrift::MessageTypes::CALL, "testMessage".size, "testMessage", 17].pack("NNa11N") + end + + it "should write the message header without version when writes are not strict" do + @prot = protocol_class.new(@trans, true, false) # no strict write + @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17) + @trans.read(@trans.available).should == "\000\000\000\vtestMessage\001\000\000\000\021" + end + + it "should write the message header with a version when writes are strict" do + @prot = protocol_class.new(@trans) # strict write + @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17) + @trans.read(@trans.available).should == "\200\001\000\001\000\000\000\vtestMessage\000\000\000\021" + end + + + # message footer is a noop + + it "should write the field header" do + @prot.write_field_begin('foo', Thrift::Types::DOUBLE, 3) + @trans.read(@trans.available).should == [Thrift::Types::DOUBLE, 3].pack("cn") + end + + # field footer is a noop + + it "should write the STOP field" do + @prot.write_field_stop + @trans.read(1).should == "\000" + end + + it "should write the map header" do + @prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, 17) + @trans.read(@trans.available).should == [Thrift::Types::STRING, Thrift::Types::LIST, 17].pack("ccN"); + end + + # map footer is a noop + + it "should write the list header" do + @prot.write_list_begin(Thrift::Types::I16, 42) + @trans.read(@trans.available).should == [Thrift::Types::I16, 42].pack("cN") + end + + # list footer is a noop + + it "should write the set header" do + @prot.write_set_begin(Thrift::Types::I16, 42) + @trans.read(@trans.available).should == [Thrift::Types::I16, 42].pack("cN") + end + + it "should write a bool" do + @prot.write_bool(true) + @prot.write_bool(false) + @trans.read(@trans.available).should == "\001\000" + end + + it "should treat a nil bool as false" do + @prot.write_bool(nil) + @trans.read(1).should == "\000" + end + + it "should write a byte" do + # byte is small enough, let's check -128..127 + (-128..127).each do |i| + @prot.write_byte(i) + @trans.read(1).should == [i].pack('c') + end + # handing it numbers out of signed range should clip + @trans.rspec_verify + (128..255).each do |i| + @prot.write_byte(i) + @trans.read(1).should == [i].pack('c') + end + # and lastly, a Bignum is going to error out + lambda { @prot.write_byte(2**65) }.should raise_error(RangeError) + end + + it "should error gracefully when trying to write a nil byte" do + lambda { @prot.write_byte(nil) }.should raise_error + end + + it "should write an i16" do + # try a random scattering of values + # include the signed i16 minimum/maximum + [-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i| + @prot.write_i16(i) + end + # and try something out of signed range, it should clip + @prot.write_i16(2**15 + 5) + + @trans.read(@trans.available).should == "\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005" + + # a Bignum should error + # lambda { @prot.write_i16(2**65) }.should raise_error(RangeError) + end + + it "should error gracefully when trying to write a nil i16" do + lambda { @prot.write_i16(nil) }.should raise_error + end + + it "should write an i32" do + # try a random scattering of values + # include the signed i32 minimum/maximum + [-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i| + @prot.write_i32(i) + end + # try something out of signed range, it should clip + @trans.read(@trans.available).should == "\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377" + [2 ** 31 + 5, 2 ** 65 + 5].each do |i| + lambda { @prot.write_i32(i) }.should raise_error(RangeError) + end + end + + it "should error gracefully when trying to write a nil i32" do + lambda { @prot.write_i32(nil) }.should raise_error + end + + it "should write an i64" do + # try a random scattering of values + # try the signed i64 minimum/maximum + [-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i| + @prot.write_i64(i) + end + # try something out of signed range, it should clip + @trans.read(@trans.available).should == ["\200\000\000\000\000\000\000\000", + "\377\377\364\303\035\244+]", + "\377\377\377\377\376\231:\341", + "\377\377\377\377\377\377\377\026", + "\000\000\000\000\000\000\000\000", + "\000\000\000\000\000\000\004\317", + "\000\000\000\000\000#\340\204", + "\000\000\000\002\340\311~\365", + "\177\377\377\377\377\377\377\377"].join("") + lambda { @prot.write_i64(2 ** 65 + 5) }.should raise_error(RangeError) + end + + it "should error gracefully when trying to write a nil i64" do + lambda { @prot.write_i64(nil) }.should raise_error + end + + it "should write a double" do + # try a random scattering of values, including min/max + values = [Float::MIN,-1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX] + values.each do |f| + @prot.write_double(f) + @trans.read(@trans.available).should == [f].pack("G") + end + end + + it "should error gracefully when trying to write a nil double" do + lambda { @prot.write_double(nil) }.should raise_error + end + + if RUBY_VERSION >= '1.9' + it 'should write a string' do + str = 'abc' + @prot.write_string(str) + a = @trans.read(@trans.available) + a.encoding.should == Encoding::BINARY + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63] + end + + it 'should write a string with unicode characters' do + str = "abc \u20AC \u20AD".encode('UTF-8') + @prot.write_string(str) + a = @trans.read(@trans.available) + a.encoding.should == Encoding::BINARY + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x0B, 0x61, 0x62, 0x63, 0x20, + 0xE2, 0x82, 0xAC, 0x20, 0xE2, 0x82, 0xAD] + end + + it 'should write should write a string with unicode characters and transcoding' do + str = "abc \u20AC".encode('ISO-8859-15') + @prot.write_string(str) + a = @trans.read(@trans.available) + a.encoding.should == Encoding::BINARY + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x07, 0x61, 0x62, 0x63, 0x20, 0xE2, 0x82, 0xAC] + end + + it 'should write a binary string' do + buffer = [0, 1, 2, 3].pack('C*') + @prot.write_binary(buffer) + a = @trans.read(@trans.available) + a.encoding.should == Encoding::BINARY + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03] + end + else + it 'should write a string' do + str = 'abc' + @prot.write_string(str) + a = @trans.read(@trans.available) + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63] + end + + it 'should write a binary string' do + buffer = [0, 1, 2, 3].pack('C*') + @prot.write_binary(buffer) + a = @trans.read(@trans.available) + a.unpack('C*').should == [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03] + end + end + + it "should error gracefully when trying to write a nil string" do + lambda { @prot.write_string(nil) }.should raise_error + end + + it "should write the message header without version when writes are not strict" do + @prot = protocol_class.new(@trans, true, false) # no strict write + @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17) + @trans.read(@trans.available).should == "\000\000\000\vtestMessage\001\000\000\000\021" + end + + it "should write the message header with a version when writes are strict" do + @prot = protocol_class.new(@trans) # strict write + @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 17) + @trans.read(@trans.available).should == "\200\001\000\001\000\000\000\vtestMessage\000\000\000\021" + end + + # message footer is a noop + + it "should read a field header" do + @trans.write([Thrift::Types::STRING, 3].pack("cn")) + @prot.read_field_begin.should == [nil, Thrift::Types::STRING, 3] + end + + # field footer is a noop + + it "should read a stop field" do + @trans.write([Thrift::Types::STOP].pack("c")); + @prot.read_field_begin.should == [nil, Thrift::Types::STOP, 0] + end + + it "should read a map header" do + @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 42].pack("ccN")) + @prot.read_map_begin.should == [Thrift::Types::DOUBLE, Thrift::Types::I64, 42] + end + + # map footer is a noop + + it "should read a list header" do + @trans.write([Thrift::Types::STRING, 17].pack("cN")) + @prot.read_list_begin.should == [Thrift::Types::STRING, 17] + end + + # list footer is a noop + + it "should read a set header" do + @trans.write([Thrift::Types::STRING, 17].pack("cN")) + @prot.read_set_begin.should == [Thrift::Types::STRING, 17] + end + + # set footer is a noop + + it "should read a bool" do + @trans.write("\001\000"); + @prot.read_bool.should == true + @prot.read_bool.should == false + end + + it "should read a byte" do + [-128, -57, -3, 0, 17, 24, 127].each do |i| + @trans.write([i].pack("c")) + @prot.read_byte.should == i + end + end + + it "should read an i16" do + # try a scattering of values, including min/max + [-2**15, -5237, -353, 0, 1527, 2234, 2**15-1].each do |i| + @trans.write([i].pack("n")); + @prot.read_i16.should == i + end + end + + it "should read an i32" do + # try a scattering of values, including min/max + [-2**31, -235125, -6236, 0, 2351, 123123, 2**31-1].each do |i| + @trans.write([i].pack("N")) + @prot.read_i32.should == i + end + end + + it "should read an i64" do + # try a scattering of values, including min/max + [-2**63, -123512312, -6346, 0, 32, 2346322323, 2**63-1].each do |i| + @trans.write([i >> 32, i & 0xFFFFFFFF].pack("NN")) + @prot.read_i64.should == i + end + end + + it "should read a double" do + # try a random scattering of values, including min/max + [Float::MIN, -231231.12351, -323.233513, 0, 123.2351235, 2351235.12351235, Float::MAX].each do |f| + @trans.write([f].pack("G")); + @prot.read_double.should == f + end + end + + if RUBY_VERSION >= '1.9' + it 'should read a string' do + # i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c' + buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*') + @trans.write(buffer) + a = @prot.read_string + a.should == 'abc'.encode('UTF-8') + a.encoding.should == Encoding::UTF_8 + end + + it 'should read a string containing unicode characters from UTF-8 encoded buffer' do + # i32 of value 3, followed by one character U+20AC made up of three bytes + buffer = [0x00, 0x00, 0x00, 0x03, 0xE2, 0x82, 0xAC].pack('C*') + @trans.write(buffer) + a = @prot.read_string + a.should == "\u20AC".encode('UTF-8') + a.encoding.should == Encoding::UTF_8 + end + + it 'should read a binary string' do + buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*') + @trans.write(buffer) + a = @prot.read_binary + a.should == [0x00, 0x01, 0x02, 0x03].pack('C*') + a.encoding.should == Encoding::BINARY + end + else + it 'should read a string' do + # i32 of value 3, followed by three characters/UTF-8 bytes 'a', 'b', 'c' + buffer = [0x00, 0x00, 0x00, 0x03, 0x61, 0x62, 0x63].pack('C*') + @trans.write(buffer) + @prot.read_string.should == 'abc' + end + + it 'should read a binary string' do + buffer = [0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03].pack('C*') + @trans.write(buffer) + a = @prot.read_binary + a.should == [0x00, 0x01, 0x02, 0x03].pack('C*') + end + end + + it "should perform a complete rpc with no args or return" do + srv_test( + proc {|client| client.send_voidMethod()}, + proc {|client| client.recv_voidMethod.should == nil} + ) + end + + it "should perform a complete rpc with a primitive return type" do + srv_test( + proc {|client| client.send_primitiveMethod()}, + proc {|client| client.recv_primitiveMethod.should == 1} + ) + end + + it "should perform a complete rpc with a struct return type" do + srv_test( + proc {|client| client.send_structMethod()}, + proc {|client| + result = client.recv_structMethod + result.set_byte_map = nil + result.map_byte_map = nil + result.should == Fixtures::COMPACT_PROTOCOL_TEST_STRUCT + } + ) + end + + def get_socket_connection + server = Thrift::ServerSocket.new("localhost", 9090) + server.listen + + clientside = Thrift::Socket.new("localhost", 9090) + clientside.open + serverside = server.accept + [clientside, serverside, server] + end + + def srv_test(firstblock, secondblock) + clientside, serverside, server = get_socket_connection + + clientproto = protocol_class.new(clientside) + serverproto = protocol_class.new(serverside) + + processor = Thrift::Test::Srv::Processor.new(SrvHandler.new) + + client = Thrift::Test::Srv::Client.new(clientproto, clientproto) + + # first block + firstblock.call(client) + + processor.process(serverproto, serverproto) + + # second block + secondblock.call(client) + ensure + clientside.close + serverside.close + server.close + end + + class SrvHandler + def voidMethod() + end + + def primitiveMethod + 1 + end + + def structMethod + Fixtures::COMPACT_PROTOCOL_TEST_STRUCT + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/bytes_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/bytes_spec.rb new file mode 100644 index 00000000..b82e304b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/bytes_spec.rb @@ -0,0 +1,160 @@ +# encoding: UTF-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe Thrift::Bytes do + if RUBY_VERSION >= '1.9' + describe '.empty_byte_buffer' do + it 'should create an empty buffer' do + b = Thrift::Bytes.empty_byte_buffer + b.length.should == 0 + b.encoding.should == Encoding::BINARY + end + + it 'should create an empty buffer of given size' do + b = Thrift::Bytes.empty_byte_buffer 2 + b.length.should == 2 + b.getbyte(0).should == 0 + b.getbyte(1).should == 0 + b.encoding.should == Encoding::BINARY + end + end + + describe '.force_binary_encoding' do + it 'should change encoding' do + e = 'STRING'.encode('UTF-8') + e.encoding.should_not == Encoding::BINARY + a = Thrift::Bytes.force_binary_encoding e + a.encoding.should == Encoding::BINARY + end + end + + describe '.get_string_byte' do + it 'should get the byte at index' do + s = "\x41\x42" + Thrift::Bytes.get_string_byte(s, 0).should == 0x41 + Thrift::Bytes.get_string_byte(s, 1).should == 0x42 + end + end + + describe '.set_string_byte' do + it 'should set byte value at index' do + s = "\x41\x42" + Thrift::Bytes.set_string_byte(s, 0, 0x43) + s.getbyte(0).should == 0x43 + s.should == 'CB' + end + end + + describe '.convert_to_utf8_byte_buffer' do + it 'should convert UTF-8 String to byte buffer' do + e = "\u20AC".encode('UTF-8') # a string with euro sign character U+20AC + e.length.should == 1 + + a = Thrift::Bytes.convert_to_utf8_byte_buffer e + a.encoding.should == Encoding::BINARY + a.length.should == 3 + a.unpack('C*').should == [0xE2, 0x82, 0xAC] + end + + it 'should convert ISO-8859-15 String to UTF-8 byte buffer' do + # Assumptions + e = "\u20AC".encode('ISO-8859-15') # a string with euro sign character U+20AC, then converted to ISO-8859-15 + e.length.should == 1 + e.unpack('C*').should == [0xA4] # euro sign is a different code point in ISO-8859-15 + + a = Thrift::Bytes.convert_to_utf8_byte_buffer e + a.encoding.should == Encoding::BINARY + a.length.should == 3 + a.unpack('C*').should == [0xE2, 0x82, 0xAC] + end + end + + describe '.convert_to_string' do + it 'should convert UTF-8 byte buffer to a UTF-8 String' do + e = [0xE2, 0x82, 0xAC].pack("C*") + e.encoding.should == Encoding::BINARY + a = Thrift::Bytes.convert_to_string e + a.encoding.should == Encoding::UTF_8 + a.should == "\u20AC" + end + end + + else # RUBY_VERSION + describe '.empty_byte_buffer' do + it 'should create an empty buffer' do + b = Thrift::Bytes.empty_byte_buffer + b.length.should == 0 + end + + it 'should create an empty buffer of given size' do + b = Thrift::Bytes.empty_byte_buffer 2 + b.length.should == 2 + b[0].should == 0 + b[1].should == 0 + end + end + + describe '.force_binary_encoding' do + it 'should be a no-op' do + e = 'STRING' + a = Thrift::Bytes.force_binary_encoding e + a.should == e + a.should be(e) + end + end + + describe '.get_string_byte' do + it 'should get the byte at index' do + s = "\x41\x42" + Thrift::Bytes.get_string_byte(s, 0).should == 0x41 + Thrift::Bytes.get_string_byte(s, 1).should == 0x42 + end + end + + describe '.set_string_byte' do + it 'should set byte value at index' do + s = "\x41\x42" + Thrift::Bytes.set_string_byte(s, 0, 0x43) + s[0].should == 0x43 + s.should == 'CB' + end + end + + describe '.convert_to_utf8_byte_buffer' do + it 'should be a no-op' do + e = 'STRING' + a = Thrift::Bytes.convert_to_utf8_byte_buffer e + a.should == e + a.should be(e) + end + end + + describe '.convert_to_string' do + it 'should be a no-op' do + e = 'STRING' + a = Thrift::Bytes.convert_to_string e + a.should == e + a.should be(e) + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/client_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/client_spec.rb new file mode 100644 index 00000000..f8ffe8a8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/client_spec.rb @@ -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. +# + +require 'spec_helper' + +describe 'Client' do + + class ClientSpec + include Thrift::Client + end + + before(:each) do + @prot = mock("MockProtocol") + @client = ClientSpec.new(@prot) + end + + describe Thrift::Client do + it "should re-use iprot for oprot if not otherwise specified" do + @client.instance_variable_get(:'@iprot').should eql(@prot) + @client.instance_variable_get(:'@oprot').should eql(@prot) + end + + it "should send a test message" do + @prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0) + mock_args = mock('#') + mock_args.should_receive(:foo=).with('foo') + mock_args.should_receive(:bar=).with(42) + mock_args.should_receive(:write).with(@prot) + @prot.should_receive(:write_message_end) + @prot.should_receive(:trans) do + mock('trans').tap do |trans| + trans.should_receive(:flush) + end + end + klass = stub("TestMessage_args", :new => mock_args) + @client.send_message('testMessage', klass, :foo => 'foo', :bar => 42) + end + + it "should increment the sequence id when sending messages" do + pending "it seems sequence ids are completely ignored right now" do + @prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered + @prot.should_receive(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered + @prot.should_receive(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered + @prot.stub!(:write_message_end) + @prot.stub!(:trans).and_return mock("trans").as_null_object + @client.send_message('testMessage', mock("args class").as_null_object) + @client.send_message('testMessage2', mock("args class").as_null_object) + @client.send_message('testMessage3', mock("args class").as_null_object) + end + end + + it "should receive a test message" do + @prot.should_receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::CALL, 0] + @prot.should_receive(:read_message_end) + mock_klass = mock("#") + mock_klass.should_receive(:read).with(@prot) + @client.receive_message(stub("MockClass", :new => mock_klass)) + end + + it "should handle received exceptions" do + @prot.should_receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::EXCEPTION, 0] + @prot.should_receive(:read_message_end) + Thrift::ApplicationException.should_receive(:new).and_return do + StandardError.new.tap do |mock_exc| + mock_exc.should_receive(:read).with(@prot) + end + end + lambda { @client.receive_message(nil) }.should raise_error(StandardError) + end + + it "should close the transport if an error occurs while sending a message" do + @prot.stub!(:write_message_begin) + @prot.should_not_receive(:write_message_end) + mock_args = mock("#") + mock_args.should_receive(:write).with(@prot).and_raise(StandardError) + trans = mock("MockTransport") + @prot.stub!(:trans).and_return(trans) + trans.should_receive(:close) + klass = mock("TestMessage_args", :new => mock_args) + lambda { @client.send_message("testMessage", klass) }.should raise_error(StandardError) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/compact_protocol_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/compact_protocol_spec.rb new file mode 100644 index 00000000..8a1a228d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/compact_protocol_spec.rb @@ -0,0 +1,143 @@ +# encoding: UTF-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe Thrift::CompactProtocol do + TESTS = { + :byte => (-127..127).to_a, + :i16 => (0..14).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort, + :i32 => (0..30).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort, + :i64 => (0..62).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort, + :string => ["", "1", "short", "fourteen123456", "fifteen12345678", "unicode characters: \u20AC \u20AD", "1" * 127, "1" * 3000], + :binary => ["", "\001", "\001" * 5, "\001" * 14, "\001" * 15, "\001" * 127, "\001" * 3000], + :double => [0.0, 1.0, -1.0, 1.1, -1.1, 10000000.1, 1.0/0.0, -1.0/0.0], + :bool => [true, false] + } + + it "should encode and decode naked primitives correctly" do + TESTS.each_pair do |primitive_type, test_values| + test_values.each do |value| + # puts "testing #{value}" if primitive_type == :i64 + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::CompactProtocol.new(trans) + + proto.send(writer(primitive_type), value) + # puts "buf: #{trans.inspect_buffer}" if primitive_type == :i64 + read_back = proto.send(reader(primitive_type)) + read_back.should == value + end + end + end + + it "should encode and decode primitives in fields correctly" do + TESTS.each_pair do |primitive_type, test_values| + final_primitive_type = primitive_type == :binary ? :string : primitive_type + thrift_type = Thrift::Types.const_get(final_primitive_type.to_s.upcase) + # puts primitive_type + test_values.each do |value| + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::CompactProtocol.new(trans) + + proto.write_field_begin(nil, thrift_type, 15) + proto.send(writer(primitive_type), value) + proto.write_field_end + + proto = Thrift::CompactProtocol.new(trans) + name, type, id = proto.read_field_begin + type.should == thrift_type + id.should == 15 + read_back = proto.send(reader(primitive_type)) + read_back.should == value + proto.read_field_end + end + end + end + + it "should encode and decode a monster struct correctly" do + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::CompactProtocol.new(trans) + + struct = Thrift::Test::CompactProtoTestStruct.new + # sets and maps don't hash well... not sure what to do here. + struct.write(proto) + + struct2 = Thrift::Test::CompactProtoTestStruct.new + struct2.read(proto) + struct2.should == struct + end + + it "should make method calls correctly" do + client_out_trans = Thrift::MemoryBufferTransport.new + client_out_proto = Thrift::CompactProtocol.new(client_out_trans) + + client_in_trans = Thrift::MemoryBufferTransport.new + client_in_proto = Thrift::CompactProtocol.new(client_in_trans) + + processor = Thrift::Test::Srv::Processor.new(JankyHandler.new) + + client = Thrift::Test::Srv::Client.new(client_in_proto, client_out_proto) + client.send_Janky(1) + # puts client_out_trans.inspect_buffer + processor.process(client_out_proto, client_in_proto) + client.recv_Janky.should == 2 + end + + it "should deal with fields following fields that have non-delta ids" do + brcp = Thrift::Test::BreaksRubyCompactProtocol.new( + :field1 => "blah", + :field2 => Thrift::Test::BigFieldIdStruct.new( + :field1 => "string1", + :field2 => "string2"), + :field3 => 3) + ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new) + bytes = ser.serialize(brcp) + + deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new) + brcp2 = Thrift::Test::BreaksRubyCompactProtocol.new + deser.deserialize(brcp2, bytes) + brcp2.should == brcp + end + + it "should deserialize an empty map to an empty hash" do + struct = Thrift::Test::SingleMapTestStruct.new(:i32_map => {}) + ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new) + bytes = ser.serialize(struct) + + deser = Thrift::Deserializer.new(Thrift::CompactProtocolFactory.new) + struct2 = Thrift::Test::SingleMapTestStruct.new + deser.deserialize(struct2, bytes) + struct.should == struct2 + end + + class JankyHandler + def Janky(i32arg) + i32arg * 2 + end + end + + def writer(sym) + "write_#{sym.to_s}" + end + + def reader(sym) + "read_#{sym.to_s}" + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/exception_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/exception_spec.rb new file mode 100644 index 00000000..d1da6217 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/exception_spec.rb @@ -0,0 +1,141 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'Exception' do + + describe Thrift::Exception do + it "should have an accessible message" do + e = Thrift::Exception.new("test message") + e.message.should == "test message" + end + end + + describe Thrift::ApplicationException do + it "should inherit from Thrift::Exception" do + Thrift::ApplicationException.superclass.should == Thrift::Exception + end + + it "should have an accessible type and message" do + e = Thrift::ApplicationException.new + e.type.should == Thrift::ApplicationException::UNKNOWN + e.message.should be_nil + e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message") + e.type.should == Thrift::ApplicationException::UNKNOWN_METHOD + e.message.should == "test message" + end + + it "should read a struct off of a protocol" do + prot = mock("MockProtocol") + prot.should_receive(:read_struct_begin).ordered + prot.should_receive(:read_field_begin).exactly(3).times.and_return( + ["message", Thrift::Types::STRING, 1], + ["type", Thrift::Types::I32, 2], + [nil, Thrift::Types::STOP, 0] + ) + prot.should_receive(:read_string).ordered.and_return "test message" + prot.should_receive(:read_i32).ordered.and_return Thrift::ApplicationException::BAD_SEQUENCE_ID + prot.should_receive(:read_field_end).exactly(2).times + prot.should_receive(:read_struct_end).ordered + + e = Thrift::ApplicationException.new + e.read(prot) + e.message.should == "test message" + e.type.should == Thrift::ApplicationException::BAD_SEQUENCE_ID + end + + it "should skip bad fields when reading a struct" do + prot = mock("MockProtocol") + prot.should_receive(:read_struct_begin).ordered + prot.should_receive(:read_field_begin).exactly(5).times.and_return( + ["type", Thrift::Types::I32, 2], + ["type", Thrift::Types::STRING, 2], + ["message", Thrift::Types::MAP, 1], + ["message", Thrift::Types::STRING, 3], + [nil, Thrift::Types::STOP, 0] + ) + prot.should_receive(:read_i32).and_return Thrift::ApplicationException::INVALID_MESSAGE_TYPE + prot.should_receive(:skip).with(Thrift::Types::STRING).twice + prot.should_receive(:skip).with(Thrift::Types::MAP) + prot.should_receive(:read_field_end).exactly(4).times + prot.should_receive(:read_struct_end).ordered + + e = Thrift::ApplicationException.new + e.read(prot) + e.message.should be_nil + e.type.should == Thrift::ApplicationException::INVALID_MESSAGE_TYPE + end + + it "should write a Thrift::ApplicationException struct to the oprot" do + prot = mock("MockProtocol") + prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered + prot.should_receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered + prot.should_receive(:write_string).with("test message").ordered + prot.should_receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered + prot.should_receive(:write_i32).with(Thrift::ApplicationException::UNKNOWN_METHOD).ordered + prot.should_receive(:write_field_end).twice + prot.should_receive(:write_field_stop).ordered + prot.should_receive(:write_struct_end).ordered + + e = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN_METHOD, "test message") + e.write(prot) + end + + it "should skip nil fields when writing to the oprot" do + prot = mock("MockProtocol") + prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered + prot.should_receive(:write_field_begin).with("message", Thrift::Types::STRING, 1).ordered + prot.should_receive(:write_string).with("test message").ordered + prot.should_receive(:write_field_end).ordered + prot.should_receive(:write_field_stop).ordered + prot.should_receive(:write_struct_end).ordered + + e = Thrift::ApplicationException.new(nil, "test message") + e.write(prot) + + prot = mock("MockProtocol") + prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered + prot.should_receive(:write_field_begin).with("type", Thrift::Types::I32, 2).ordered + prot.should_receive(:write_i32).with(Thrift::ApplicationException::BAD_SEQUENCE_ID).ordered + prot.should_receive(:write_field_end).ordered + prot.should_receive(:write_field_stop).ordered + prot.should_receive(:write_struct_end).ordered + + e = Thrift::ApplicationException.new(Thrift::ApplicationException::BAD_SEQUENCE_ID) + e.write(prot) + + prot = mock("MockProtocol") + prot.should_receive(:write_struct_begin).with("Thrift::ApplicationException").ordered + prot.should_receive(:write_field_stop).ordered + prot.should_receive(:write_struct_end).ordered + + e = Thrift::ApplicationException.new(nil) + e.write(prot) + end + end + + describe Thrift::ProtocolException do + it "should have an accessible type" do + prot = Thrift::ProtocolException.new(Thrift::ProtocolException::SIZE_LIMIT, "message") + prot.type.should == Thrift::ProtocolException::SIZE_LIMIT + prot.message.should == "message" + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/flat_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/flat_spec.rb new file mode 100644 index 00000000..f3787823 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/flat_spec.rb @@ -0,0 +1,62 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'generation' do + before do + require 'namespaced_nonblocking_service' + end + + it "did not generate the wrong files" do + prefix = File.expand_path("../gen-rb/flat", __FILE__) + ["namespaced_spec_namespace/namespaced_nonblocking_service.rb", + "namespaced_spec_namespace/thrift_namespaced_spec_constants.rb", + "namespaced_spec_namespace/thrift_namespaced_spec_types.rb", + "other_namespace/referenced_constants.rb", + "other_namespace/referenced_types.rb" + ].each do |name| + File.exist?(File.join(prefix, name)).should_not be_true + end + end + + it "generated the right files" do + prefix = File.expand_path("../gen-rb/flat", __FILE__) + ["namespaced_nonblocking_service.rb", + "thrift_namespaced_spec_constants.rb", + "thrift_namespaced_spec_types.rb", + "referenced_constants.rb", + "referenced_types.rb" + ].each do |name| + File.exist?(File.join(prefix, name)).should be_true + end + end + + it "has a service class in the right place" do + defined?(NamespacedSpecNamespace::NamespacedNonblockingService).should be_true + end + + it "has a struct in the right place" do + defined?(NamespacedSpecNamespace::Hello).should be_true + end + + it "required an included file" do + defined?(OtherNamespace::SomeEnum).should be_true + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/http_client_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/http_client_spec.rb new file mode 100644 index 00000000..5e8da24b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/http_client_spec.rb @@ -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. +# + +require 'spec_helper' + +describe 'Thrift::HTTPClientTransport' do + + describe Thrift::HTTPClientTransport do + before(:each) do + @client = Thrift::HTTPClientTransport.new("http://my.domain.com/path/to/service?param=value") + end + + it "should always be open" do + @client.should be_open + @client.close + @client.should be_open + end + + it "should post via HTTP and return the results" do + @client.write "a test" + @client.write " frame" + Net::HTTP.should_receive(:new).with("my.domain.com", 80).and_return do + mock("Net::HTTP").tap do |http| + http.should_receive(:use_ssl=).with(false) + http.should_receive(:post).with("/path/to/service?param=value", "a test frame", {"Content-Type"=>"application/x-thrift"}).and_return do + mock("Net::HTTPOK").tap do |response| + response.should_receive(:body).and_return "data" + end + end + end + end + @client.flush + @client.read(10).should == "data" + end + + it "should send custom headers if defined" do + @client.write "test" + custom_headers = {"Cookie" => "Foo"} + headers = {"Content-Type"=>"application/x-thrift"}.merge(custom_headers) + + @client.add_headers(custom_headers) + Net::HTTP.should_receive(:new).with("my.domain.com", 80).and_return do + mock("Net::HTTP").tap do |http| + http.should_receive(:use_ssl=).with(false) + http.should_receive(:post).with("/path/to/service?param=value", "test", headers).and_return do + mock("Net::HTTPOK").tap do |response| + response.should_receive(:body).and_return "data" + end + end + end + end + @client.flush + end + + it 'should reset the outbuf on HTTP failures' do + @client.write "test" + + Net::HTTP.should_receive(:new).with("my.domain.com", 80).and_return do + mock("Net::HTTP").tap do |http| + http.should_receive(:use_ssl=).with(false) + http.should_receive(:post).with("/path/to/service?param=value", "test", {"Content-Type"=>"application/x-thrift"}) { raise Net::ReadTimeout } + end + end + + @client.flush rescue + @client.instance_variable_get(:@outbuf).should eq(Thrift::Bytes.empty_byte_buffer) + end + + end + + describe 'ssl enabled' do + before(:each) do + @service_path = "/path/to/service?param=value" + @server_uri = "https://my.domain.com" + end + + it "should use SSL for https" do + client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}") + + client.write "test" + + Net::HTTP.should_receive(:new).with("my.domain.com", 443).and_return do + mock("Net::HTTP").tap do |http| + http.should_receive(:use_ssl=).with(true) + http.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) + http.should_receive(:post).with(@service_path, "test", + "Content-Type" => "application/x-thrift").and_return do + mock("Net::HTTPOK").tap do |response| + response.should_receive(:body).and_return "data" + end + end + end + end + client.flush + client.read(4).should == "data" + end + + it "should set SSL verify mode when specified" do + client = Thrift::HTTPClientTransport.new("#{@server_uri}#{@service_path}", + :ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE) + + client.write "test" + Net::HTTP.should_receive(:new).with("my.domain.com", 443).and_return do + mock("Net::HTTP").tap do |http| + http.should_receive(:use_ssl=).with(true) + http.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE) + http.should_receive(:post).with(@service_path, "test", + "Content-Type" => "application/x-thrift").and_return do + mock("Net::HTTPOK").tap do |response| + response.should_receive(:body).and_return "data" + end + end + end + end + client.flush + client.read(4).should == "data" + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/json_protocol_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/json_protocol_spec.rb new file mode 100644 index 00000000..b6b46bff --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/json_protocol_spec.rb @@ -0,0 +1,544 @@ +# encoding: UTF-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'JsonProtocol' do + + describe Thrift::JsonProtocol do + before(:each) do + @trans = Thrift::MemoryBufferTransport.new + @prot = Thrift::JsonProtocol.new(@trans) + end + + it "should write json escaped char" do + @prot.write_json_escape_char("\n") + @trans.read(@trans.available).should == '\u000a' + + @prot.write_json_escape_char(" ") + @trans.read(@trans.available).should == '\u0020' + end + + it "should write json char" do + @prot.write_json_char("\n") + @trans.read(@trans.available).should == '\\n' + + @prot.write_json_char(" ") + @trans.read(@trans.available).should == ' ' + + @prot.write_json_char("\\") + @trans.read(@trans.available).should == "\\\\" + + @prot.write_json_char("@") + @trans.read(@trans.available).should == '@' + end + + it "should write json string" do + @prot.write_json_string("this is a \\ json\nstring") + @trans.read(@trans.available).should == "\"this is a \\\\ json\\nstring\"" + end + + it "should write json base64" do + @prot.write_json_base64("this is a base64 string") + @trans.read(@trans.available).should == "\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"" + end + + it "should write json integer" do + @prot.write_json_integer(45) + @trans.read(@trans.available).should == "45" + + @prot.write_json_integer(33000) + @trans.read(@trans.available).should == "33000" + + @prot.write_json_integer(3000000000) + @trans.read(@trans.available).should == "3000000000" + + @prot.write_json_integer(6000000000) + @trans.read(@trans.available).should == "6000000000" + end + + it "should write json double" do + @prot.write_json_double(12.3) + @trans.read(@trans.available).should == "12.3" + + @prot.write_json_double(-3.21) + @trans.read(@trans.available).should == "-3.21" + + @prot.write_json_double(((+1.0/0.0)/(+1.0/0.0))) + @trans.read(@trans.available).should == "\"NaN\"" + + @prot.write_json_double((+1.0/0.0)) + @trans.read(@trans.available).should == "\"Infinity\"" + + @prot.write_json_double((-1.0/0.0)) + @trans.read(@trans.available).should == "\"-Infinity\"" + end + + it "should write json object start" do + @prot.write_json_object_start + @trans.read(@trans.available).should == "{" + end + + it "should write json object end" do + @prot.write_json_object_end + @trans.read(@trans.available).should == "}" + end + + it "should write json array start" do + @prot.write_json_array_start + @trans.read(@trans.available).should == "[" + end + + it "should write json array end" do + @prot.write_json_array_end + @trans.read(@trans.available).should == "]" + end + + it "should write message begin" do + @prot.write_message_begin("name", 12, 32) + @trans.read(@trans.available).should == "[1,\"name\",12,32" + end + + it "should write message end" do + @prot.write_message_end + @trans.read(@trans.available).should == "]" + end + + it "should write struct begin" do + @prot.write_struct_begin("name") + @trans.read(@trans.available).should == "{" + end + + it "should write struct end" do + @prot.write_struct_end + @trans.read(@trans.available).should == "}" + end + + it "should write field begin" do + @prot.write_field_begin("name", Thrift::Types::STRUCT, 32) + @trans.read(@trans.available).should == "32{\"rec\"" + end + + it "should write field end" do + @prot.write_field_end + @trans.read(@trans.available).should == "}" + end + + it "should write field stop" do + @prot.write_field_stop + @trans.read(@trans.available).should == "" + end + + it "should write map begin" do + @prot.write_map_begin(Thrift::Types::STRUCT, Thrift::Types::LIST, 32) + @trans.read(@trans.available).should == "[\"rec\",\"lst\",32,{" + end + + it "should write map end" do + @prot.write_map_end + @trans.read(@trans.available).should == "}]" + end + + it "should write list begin" do + @prot.write_list_begin(Thrift::Types::STRUCT, 32) + @trans.read(@trans.available).should == "[\"rec\",32" + end + + it "should write list end" do + @prot.write_list_end + @trans.read(@trans.available).should == "]" + end + + it "should write set begin" do + @prot.write_set_begin(Thrift::Types::STRUCT, 32) + @trans.read(@trans.available).should == "[\"rec\",32" + end + + it "should write set end" do + @prot.write_set_end + @trans.read(@trans.available).should == "]" + end + + it "should write bool" do + @prot.write_bool(true) + @trans.read(@trans.available).should == "1" + + @prot.write_bool(false) + @trans.read(@trans.available).should == "0" + end + + it "should write byte" do + @prot.write_byte(100) + @trans.read(@trans.available).should == "100" + end + + it "should write i16" do + @prot.write_i16(1000) + @trans.read(@trans.available).should == "1000" + end + + it "should write i32" do + @prot.write_i32(3000000000) + @trans.read(@trans.available).should == "3000000000" + end + + it "should write i64" do + @prot.write_i64(6000000000) + @trans.read(@trans.available).should == "6000000000" + end + + it "should write double" do + @prot.write_double(1.23) + @trans.read(@trans.available).should == "1.23" + + @prot.write_double(-32.1) + @trans.read(@trans.available).should == "-32.1" + + @prot.write_double(((+1.0/0.0)/(+1.0/0.0))) + @trans.read(@trans.available).should == "\"NaN\"" + + @prot.write_double((+1.0/0.0)) + @trans.read(@trans.available).should == "\"Infinity\"" + + @prot.write_double((-1.0/0.0)) + @trans.read(@trans.available).should == "\"-Infinity\"" + end + + if RUBY_VERSION >= '1.9' + it 'should write string' do + @prot.write_string('this is a test string') + a = @trans.read(@trans.available) + a.should == '"this is a test string"'.force_encoding(Encoding::BINARY) + a.encoding.should == Encoding::BINARY + end + + it 'should write string with unicode characters' do + @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD") + a = @trans.read(@trans.available) + a.should == "\"this is a test string with unicode characters: \u20AC \u20AD\"".force_encoding(Encoding::BINARY) + a.encoding.should == Encoding::BINARY + end + else + it 'should write string' do + @prot.write_string('this is a test string') + @trans.read(@trans.available).should == '"this is a test string"' + end + end + + it "should write binary" do + @prot.write_binary("this is a base64 string") + @trans.read(@trans.available).should == "\"dGhpcyBpcyBhIGJhc2U2NCBzdHJpbmc=\"" + end + + it "should write long binary" do + @prot.write_binary((0...256).to_a.pack('C*')) + @trans.read(@trans.available).should == "\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"" + end + + it "should get type name for type id" do + expect {@prot.get_type_name_for_type_id(Thrift::Types::STOP)}.to raise_error(NotImplementedError) + expect {@prot.get_type_name_for_type_id(Thrift::Types::VOID)}.to raise_error(NotImplementedError) + @prot.get_type_name_for_type_id(Thrift::Types::BOOL).should == "tf" + @prot.get_type_name_for_type_id(Thrift::Types::BYTE).should == "i8" + @prot.get_type_name_for_type_id(Thrift::Types::DOUBLE).should == "dbl" + @prot.get_type_name_for_type_id(Thrift::Types::I16).should == "i16" + @prot.get_type_name_for_type_id(Thrift::Types::I32).should == "i32" + @prot.get_type_name_for_type_id(Thrift::Types::I64).should == "i64" + @prot.get_type_name_for_type_id(Thrift::Types::STRING).should == "str" + @prot.get_type_name_for_type_id(Thrift::Types::STRUCT).should == "rec" + @prot.get_type_name_for_type_id(Thrift::Types::MAP).should == "map" + @prot.get_type_name_for_type_id(Thrift::Types::SET).should == "set" + @prot.get_type_name_for_type_id(Thrift::Types::LIST).should == "lst" + end + + it "should get type id for type name" do + expect {@prot.get_type_id_for_type_name("pp")}.to raise_error(NotImplementedError) + @prot.get_type_id_for_type_name("tf").should == Thrift::Types::BOOL + @prot.get_type_id_for_type_name("i8").should == Thrift::Types::BYTE + @prot.get_type_id_for_type_name("dbl").should == Thrift::Types::DOUBLE + @prot.get_type_id_for_type_name("i16").should == Thrift::Types::I16 + @prot.get_type_id_for_type_name("i32").should == Thrift::Types::I32 + @prot.get_type_id_for_type_name("i64").should == Thrift::Types::I64 + @prot.get_type_id_for_type_name("str").should == Thrift::Types::STRING + @prot.get_type_id_for_type_name("rec").should == Thrift::Types::STRUCT + @prot.get_type_id_for_type_name("map").should == Thrift::Types::MAP + @prot.get_type_id_for_type_name("set").should == Thrift::Types::SET + @prot.get_type_id_for_type_name("lst").should == Thrift::Types::LIST + end + + it "should read json syntax char" do + @trans.write('F') + expect {@prot.read_json_syntax_char('G')}.to raise_error(Thrift::ProtocolException) + @trans.write('H') + @prot.read_json_syntax_char('H') + end + + it "should read json escape char" do + @trans.write('0054') + @prot.read_json_escape_char.should == 'T' + + @trans.write("\"\\\"\"") + @prot.read_json_string(false).should == "\"" + + @trans.write("\"\\\\\"") + @prot.read_json_string(false).should == "\\" + + @trans.write("\"\\/\"") + @prot.read_json_string(false).should == "\/" + + @trans.write("\"\\b\"") + @prot.read_json_string(false).should == "\b" + + @trans.write("\"\\f\"") + @prot.read_json_string(false).should == "\f" + + @trans.write("\"\\n\"") + @prot.read_json_string(false).should == "\n" + + @trans.write("\"\\r\"") + @prot.read_json_string(false).should == "\r" + + @trans.write("\"\\t\"") + @prot.read_json_string(false).should == "\t" + end + + it "should read json string" do + @trans.write("\"\\P") + expect {@prot.read_json_string(false)}.to raise_error(Thrift::ProtocolException) + + @trans.write("\"this is a test string\"") + @prot.read_json_string.should == "this is a test string" + end + + it "should read json base64" do + @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"") + @prot.read_json_base64.should == "this is a test string" + end + + it "should is json numeric" do + @prot.is_json_numeric("A").should == false + @prot.is_json_numeric("+").should == true + @prot.is_json_numeric("-").should == true + @prot.is_json_numeric(".").should == true + @prot.is_json_numeric("0").should == true + @prot.is_json_numeric("1").should == true + @prot.is_json_numeric("2").should == true + @prot.is_json_numeric("3").should == true + @prot.is_json_numeric("4").should == true + @prot.is_json_numeric("5").should == true + @prot.is_json_numeric("6").should == true + @prot.is_json_numeric("7").should == true + @prot.is_json_numeric("8").should == true + @prot.is_json_numeric("9").should == true + @prot.is_json_numeric("E").should == true + @prot.is_json_numeric("e").should == true + end + + it "should read json numeric chars" do + @trans.write("1.453E45T") + @prot.read_json_numeric_chars.should == "1.453E45" + end + + it "should read json integer" do + @trans.write("1.45\"\"") + expect {@prot.read_json_integer}.to raise_error(Thrift::ProtocolException) + @prot.read_string + + @trans.write("1453T") + @prot.read_json_integer.should == 1453 + end + + it "should read json double" do + @trans.write("1.45e3e01\"\"") + expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException) + @prot.read_string + + @trans.write("\"1.453e01\"") + expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException) + + @trans.write("1.453e01\"\"") + @prot.read_json_double.should == 14.53 + @prot.read_string + + @trans.write("\"NaN\"") + @prot.read_json_double.nan?.should == true + + @trans.write("\"Infinity\"") + @prot.read_json_double.should == +1.0/0.0 + + @trans.write("\"-Infinity\"") + @prot.read_json_double.should == -1.0/0.0 + end + + it "should read json object start" do + @trans.write("{") + @prot.read_json_object_start.should == nil + end + + it "should read json object end" do + @trans.write("}") + @prot.read_json_object_end.should == nil + end + + it "should read json array start" do + @trans.write("[") + @prot.read_json_array_start.should == nil + end + + it "should read json array end" do + @trans.write("]") + @prot.read_json_array_end.should == nil + end + + it "should read_message_begin" do + @trans.write("[2,") + expect {@prot.read_message_begin}.to raise_error(Thrift::ProtocolException) + + @trans.write("[1,\"name\",12,32\"\"") + @prot.read_message_begin.should == ["name", 12, 32] + end + + it "should read message end" do + @trans.write("]") + @prot.read_message_end.should == nil + end + + it "should read struct begin" do + @trans.write("{") + @prot.read_struct_begin.should == nil + end + + it "should read struct end" do + @trans.write("}") + @prot.read_struct_end.should == nil + end + + it "should read field begin" do + @trans.write("1{\"rec\"") + @prot.read_field_begin.should == [nil, 12, 1] + end + + it "should read field end" do + @trans.write("}") + @prot.read_field_end.should == nil + end + + it "should read map begin" do + @trans.write("[\"rec\",\"lst\",2,{") + @prot.read_map_begin.should == [12, 15, 2] + end + + it "should read map end" do + @trans.write("}]") + @prot.read_map_end.should == nil + end + + it "should read list begin" do + @trans.write("[\"rec\",2\"\"") + @prot.read_list_begin.should == [12, 2] + end + + it "should read list end" do + @trans.write("]") + @prot.read_list_end.should == nil + end + + it "should read set begin" do + @trans.write("[\"rec\",2\"\"") + @prot.read_set_begin.should == [12, 2] + end + + it "should read set end" do + @trans.write("]") + @prot.read_set_end.should == nil + end + + it "should read bool" do + @trans.write("0\"\"") + @prot.read_bool.should == false + @prot.read_string + + @trans.write("1\"\"") + @prot.read_bool.should == true + end + + it "should read byte" do + @trans.write("60\"\"") + @prot.read_byte.should == 60 + end + + it "should read i16" do + @trans.write("1000\"\"") + @prot.read_i16.should == 1000 + end + + it "should read i32" do + @trans.write("3000000000\"\"") + @prot.read_i32.should == 3000000000 + end + + it "should read i64" do + @trans.write("6000000000\"\"") + @prot.read_i64.should == 6000000000 + end + + it "should read double" do + @trans.write("12.23\"\"") + @prot.read_double.should == 12.23 + end + + if RUBY_VERSION >= '1.9' + it 'should read string' do + @trans.write('"this is a test string"'.force_encoding(Encoding::BINARY)) + a = @prot.read_string + a.should == 'this is a test string' + a.encoding.should == Encoding::UTF_8 + end + + it 'should read string with unicode characters' do + @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.force_encoding(Encoding::BINARY)) + a = @prot.read_string + a.should == "this is a test string with unicode characters: \u20AC \u20AD" + a.encoding.should == Encoding::UTF_8 + end + else + it 'should read string' do + @trans.write('"this is a test string"') + @prot.read_string.should == 'this is a test string' + end + end + + it "should read binary" do + @trans.write("\"dGhpcyBpcyBhIHRlc3Qgc3RyaW5n\"") + @prot.read_binary.should == "this is a test string" + end + + it "should read long binary" do + @trans.write("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"") + @prot.read_binary.bytes.to_a.should == (0...256).to_a + end + end + + describe Thrift::JsonProtocolFactory do + it "should create a JsonProtocol" do + Thrift::JsonProtocolFactory.new.get_protocol(mock("MockTransport")).should be_instance_of(Thrift::JsonProtocol) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/namespaced_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/namespaced_spec.rb new file mode 100644 index 00000000..31379d96 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/namespaced_spec.rb @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'namespaced generation' do + before do + require 'namespaced_spec_namespace/namespaced_nonblocking_service' + end + + it "generated the right files" do + prefix = File.expand_path("../gen-rb", __FILE__) + ["namespaced_spec_namespace/namespaced_nonblocking_service.rb", + "namespaced_spec_namespace/thrift_namespaced_spec_constants.rb", + "namespaced_spec_namespace/thrift_namespaced_spec_types.rb", + "other_namespace/referenced_constants.rb", + "other_namespace/referenced_types.rb" + ].each do |name| + File.exist?(File.join(prefix, name)).should be_true + end + end + + it "did not generate the wrong files" do + prefix = File.expand_path("../gen-rb", __FILE__) + ["namespaced_nonblocking_service.rb", + "thrift_namespaced_spec_constants.rb", + "thrift_namespaced_spec_types.rb", + "referenced_constants.rb", + "referenced_types.rb" + ].each do |name| + File.exist?(File.join(prefix, name)).should_not be_true + end + end + + it "has a service class in the right place" do + defined?(NamespacedSpecNamespace::NamespacedNonblockingService).should be_true + end + + it "has a struct in the right place" do + defined?(NamespacedSpecNamespace::Hello).should be_true + end + + it "required an included file" do + defined?(OtherNamespace::SomeEnum).should be_true + end + + it "extended a service" do + require "extended/extended_service" + end + +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/nonblocking_server_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/nonblocking_server_spec.rb new file mode 100644 index 00000000..712cf45c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/nonblocking_server_spec.rb @@ -0,0 +1,263 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'NonblockingServer' do + + class Handler + def initialize + @queue = Queue.new + end + + attr_accessor :server + + def greeting(english) + if english + SpecNamespace::Hello.new + else + SpecNamespace::Hello.new(:greeting => "Aloha!") + end + end + + def block + @queue.pop + end + + def unblock(n) + n.times { @queue.push true } + end + + def sleep(time) + Kernel.sleep time + end + + def shutdown + @server.shutdown(0, false) + end + end + + class SpecTransport < Thrift::BaseTransport + def initialize(transport, queue) + @transport = transport + @queue = queue + @flushed = false + end + + def open? + @transport.open? + end + + def open + @transport.open + end + + def close + @transport.close + end + + def read(sz) + @transport.read(sz) + end + + def write(buf,sz=nil) + @transport.write(buf, sz) + end + + def flush + @queue.push :flushed unless @flushed or @queue.nil? + @flushed = true + @transport.flush + end + end + + class SpecServerSocket < Thrift::ServerSocket + def initialize(host, port, queue) + super(host, port) + @queue = queue + end + + def listen + super + @queue.push :listen + end + end + + describe Thrift::NonblockingServer do + before(:each) do + @port = 43251 + handler = Handler.new + processor = SpecNamespace::NonblockingService::Processor.new(handler) + queue = Queue.new + @transport = SpecServerSocket.new('localhost', @port, queue) + transport_factory = Thrift::FramedTransportFactory.new + logger = Logger.new(STDERR) + logger.level = Logger::WARN + @server = Thrift::NonblockingServer.new(processor, @transport, transport_factory, nil, 5, logger) + handler.server = @server + @server_thread = Thread.new(Thread.current) do |master_thread| + begin + @server.serve + rescue => e + p e + puts e.backtrace * "\n" + master_thread.raise e + end + end + queue.pop + + @clients = [] + @catch_exceptions = false + end + + after(:each) do + @clients.each { |client, trans| trans.close } + # @server.shutdown(1) + @server_thread.kill + @transport.close + end + + def setup_client(queue = nil) + transport = SpecTransport.new(Thrift::FramedTransport.new(Thrift::Socket.new('localhost', @port)), queue) + protocol = Thrift::BinaryProtocol.new(transport) + client = SpecNamespace::NonblockingService::Client.new(protocol) + transport.open + @clients << [client, transport] + client + end + + def setup_client_thread(result) + queue = Queue.new + Thread.new do + begin + client = setup_client + while (cmd = queue.pop) + msg, *args = cmd + case msg + when :block + result << client.block + when :unblock + client.unblock(args.first) + when :hello + result << client.greeting(true) # ignore result + when :sleep + client.sleep(args[0] || 0.5) + result << :slept + when :shutdown + client.shutdown + when :exit + result << :done + break + end + end + @clients.each { |c,t| t.close and break if c == client } #close the transport + rescue => e + raise e unless @catch_exceptions + end + end + queue + end + + it "should handle basic message passing" do + client = setup_client + client.greeting(true).should == SpecNamespace::Hello.new + client.greeting(false).should == SpecNamespace::Hello.new(:greeting => 'Aloha!') + @server.shutdown + end + + it "should handle concurrent clients" do + queue = Queue.new + trans_queue = Queue.new + 4.times do + Thread.new(Thread.current) do |main_thread| + begin + queue.push setup_client(trans_queue).block + rescue => e + main_thread.raise e + end + end + end + 4.times { trans_queue.pop } + setup_client.unblock(4) + 4.times { queue.pop.should be_true } + @server.shutdown + end + + it "should handle messages from more than 5 long-lived connections" do + queues = [] + result = Queue.new + 7.times do |i| + queues << setup_client_thread(result) + Thread.pass if i == 4 # give the server time to accept connections + end + client = setup_client + # block 4 connections + 4.times { |i| queues[i] << :block } + queues[4] << :hello + queues[5] << :hello + queues[6] << :hello + 3.times { result.pop.should == SpecNamespace::Hello.new } + client.greeting(true).should == SpecNamespace::Hello.new + queues[5] << [:unblock, 4] + 4.times { result.pop.should be_true } + queues[2] << :hello + result.pop.should == SpecNamespace::Hello.new + client.greeting(false).should == SpecNamespace::Hello.new(:greeting => 'Aloha!') + 7.times { queues.shift << :exit } + client.greeting(true).should == SpecNamespace::Hello.new + @server.shutdown + end + + it "should shut down when asked" do + # connect first to ensure it's running + client = setup_client + client.greeting(false) # force a message pass + @server.shutdown + @server_thread.join(2).should be_an_instance_of(Thread) + end + + it "should continue processing active messages when shutting down" do + result = Queue.new + client = setup_client_thread(result) + client << :sleep + sleep 0.1 # give the server time to start processing the client's message + @server.shutdown + @server_thread.join(2).should be_an_instance_of(Thread) + result.pop.should == :slept + end + + it "should kill active messages when they don't expire while shutting down" do + result = Queue.new + client = setup_client_thread(result) + client << [:sleep, 10] + sleep 0.1 # start processing the client's message + @server.shutdown(1) + @catch_exceptions = true + @server_thread.join(3).should_not be_nil + result.should be_empty + end + + it "should allow shutting down in response to a message" do + client = setup_client + client.greeting(true).should == SpecNamespace::Hello.new + client.shutdown + @server_thread.join(2).should_not be_nil + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/processor_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/processor_spec.rb new file mode 100644 index 00000000..989f5cca --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/processor_spec.rb @@ -0,0 +1,80 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'Processor' do + + class ProcessorSpec + include Thrift::Processor + end + + describe Thrift::Processor do + before(:each) do + @processor = ProcessorSpec.new(mock("MockHandler")) + @prot = mock("MockProtocol") + end + + def mock_trans(obj) + obj.should_receive(:trans).ordered.and_return do + mock("trans").tap do |trans| + trans.should_receive(:flush).ordered + end + end + end + + it "should call process_ when it receives that message" do + @prot.should_receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 17] + @processor.should_receive(:process_testMessage).with(17, @prot, @prot).ordered + @processor.process(@prot, @prot).should == true + end + + it "should raise an ApplicationException when the received message cannot be processed" do + @prot.should_receive(:read_message_begin).ordered.and_return ['testMessage', Thrift::MessageTypes::CALL, 4] + @prot.should_receive(:skip).with(Thrift::Types::STRUCT).ordered + @prot.should_receive(:read_message_end).ordered + @prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::EXCEPTION, 4).ordered + e = mock(Thrift::ApplicationException) + e.should_receive(:write).with(@prot).ordered + Thrift::ApplicationException.should_receive(:new).with(Thrift::ApplicationException::UNKNOWN_METHOD, "Unknown function testMessage").and_return(e) + @prot.should_receive(:write_message_end).ordered + mock_trans(@prot) + @processor.process(@prot, @prot) + end + + it "should pass args off to the args class" do + args_class = mock("MockArgsClass") + args = mock("#").tap do |args| + args.should_receive(:read).with(@prot).ordered + end + args_class.should_receive(:new).and_return args + @prot.should_receive(:read_message_end).ordered + @processor.read_args(@prot, args_class).should eql(args) + end + + it "should write out a reply when asked" do + @prot.should_receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::REPLY, 23).ordered + result = mock("MockResult") + result.should_receive(:write).with(@prot).ordered + @prot.should_receive(:write_message_end).ordered + mock_trans(@prot) + @processor.write_result(result, @prot, 'testMessage', 23) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/serializer_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/serializer_spec.rb new file mode 100644 index 00000000..599b454b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/serializer_spec.rb @@ -0,0 +1,67 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'Serializer' do + + describe Thrift::Serializer do + it "should serialize structs to binary by default" do + serializer = Thrift::Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new) + data = serializer.serialize(SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!")) + data.should == "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00" + end + + it "should serialize structs to the given protocol" do + protocol = Thrift::BaseProtocol.new(mock("transport")) + protocol.should_receive(:write_struct_begin).with("SpecNamespace::Hello") + protocol.should_receive(:write_field_begin).with("greeting", Thrift::Types::STRING, 1) + protocol.should_receive(:write_string).with("Good day") + protocol.should_receive(:write_field_end) + protocol.should_receive(:write_field_stop) + protocol.should_receive(:write_struct_end) + protocol_factory = mock("ProtocolFactory") + protocol_factory.stub!(:get_protocol).and_return(protocol) + serializer = Thrift::Serializer.new(protocol_factory) + serializer.serialize(SpecNamespace::Hello.new(:greeting => "Good day")) + end + end + + describe Thrift::Deserializer do + it "should deserialize structs from binary by default" do + deserializer = Thrift::Deserializer.new + data = "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00" + deserializer.deserialize(SpecNamespace::Hello.new, data).should == SpecNamespace::Hello.new(:greeting => "'Ello guv'nor!") + end + + it "should deserialize structs from the given protocol" do + protocol = Thrift::BaseProtocol.new(mock("transport")) + protocol.should_receive(:read_struct_begin).and_return("SpecNamespace::Hello") + protocol.should_receive(:read_field_begin).and_return(["greeting", Thrift::Types::STRING, 1], + [nil, Thrift::Types::STOP, 0]) + protocol.should_receive(:read_string).and_return("Good day") + protocol.should_receive(:read_field_end) + protocol.should_receive(:read_struct_end) + protocol_factory = mock("ProtocolFactory") + protocol_factory.stub!(:get_protocol).and_return(protocol) + deserializer = Thrift::Deserializer.new(protocol_factory) + deserializer.deserialize(SpecNamespace::Hello.new, "").should == SpecNamespace::Hello.new(:greeting => "Good day") + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/server_socket_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/server_socket_spec.rb new file mode 100644 index 00000000..1301d540 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/server_socket_spec.rb @@ -0,0 +1,79 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared") + +describe 'Thrift::ServerSocket' do + + describe Thrift::ServerSocket do + before(:each) do + @socket = Thrift::ServerSocket.new(1234) + end + + it "should create a handle when calling listen" do + TCPServer.should_receive(:new).with(nil, 1234) + @socket.listen + end + + it "should accept an optional host argument" do + @socket = Thrift::ServerSocket.new('localhost', 1234) + TCPServer.should_receive(:new).with('localhost', 1234) + @socket.listen + end + + it "should create a Thrift::Socket to wrap accepted sockets" do + handle = mock("TCPServer") + TCPServer.should_receive(:new).with(nil, 1234).and_return(handle) + @socket.listen + sock = mock("sock") + handle.should_receive(:accept).and_return(sock) + trans = mock("Socket") + Thrift::Socket.should_receive(:new).and_return(trans) + trans.should_receive(:handle=).with(sock) + @socket.accept.should == trans + end + + it "should close the handle when closed" do + handle = mock("TCPServer", :closed? => false) + TCPServer.should_receive(:new).with(nil, 1234).and_return(handle) + @socket.listen + handle.should_receive(:close) + @socket.close + end + + it "should return nil when accepting if there is no handle" do + @socket.accept.should be_nil + end + + it "should return true for closed? when appropriate" do + handle = mock("TCPServer", :closed? => false) + TCPServer.stub!(:new).and_return(handle) + @socket.listen + @socket.should_not be_closed + handle.stub!(:close) + @socket.close + @socket.should be_closed + @socket.listen + @socket.should_not be_closed + handle.stub!(:closed?).and_return(true) + @socket.should be_closed + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/server_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/server_spec.rb new file mode 100644 index 00000000..eaecbda0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/server_spec.rb @@ -0,0 +1,147 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +require 'spec_helper' + +describe 'Server' do + + describe Thrift::BaseServer do + it "should default to BaseTransportFactory and BinaryProtocolFactory when not specified" do + server = Thrift::BaseServer.new(mock("Processor"), mock("BaseServerTransport")) + server.instance_variable_get(:'@transport_factory').should be_an_instance_of(Thrift::BaseTransportFactory) + server.instance_variable_get(:'@protocol_factory').should be_an_instance_of(Thrift::BinaryProtocolFactory) + end + + # serve is a noop, so can't test that + end + + describe Thrift::SimpleServer do + before(:each) do + @processor = mock("Processor") + @serverTrans = mock("ServerTransport") + @trans = mock("BaseTransport") + @prot = mock("BaseProtocol") + @client = mock("Client") + @server = described_class.new(@processor, @serverTrans, @trans, @prot) + end + + it "should serve in the main thread" do + @serverTrans.should_receive(:listen).ordered + @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client) + @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans) + @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot) + x = 0 + @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do + case (x += 1) + when 1 then raise Thrift::TransportException + when 2 then raise Thrift::ProtocolException + when 3 then throw :stop + end + end + @trans.should_receive(:close).exactly(3).times + @serverTrans.should_receive(:close).ordered + lambda { @server.serve }.should throw_symbol(:stop) + end + end + + describe Thrift::ThreadedServer do + before(:each) do + @processor = mock("Processor") + @serverTrans = mock("ServerTransport") + @trans = mock("BaseTransport") + @prot = mock("BaseProtocol") + @client = mock("Client") + @server = described_class.new(@processor, @serverTrans, @trans, @prot) + end + + it "should serve using threads" do + @serverTrans.should_receive(:listen).ordered + @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client) + @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans) + @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot) + Thread.should_receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans) + x = 0 + @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do + case (x += 1) + when 1 then raise Thrift::TransportException + when 2 then raise Thrift::ProtocolException + when 3 then throw :stop + end + end + @trans.should_receive(:close).exactly(3).times + @serverTrans.should_receive(:close).ordered + lambda { @server.serve }.should throw_symbol(:stop) + end + end + + describe Thrift::ThreadPoolServer do + before(:each) do + @processor = mock("Processor") + @server_trans = mock("ServerTransport") + @trans = mock("BaseTransport") + @prot = mock("BaseProtocol") + @client = mock("Client") + @server = described_class.new(@processor, @server_trans, @trans, @prot) + end + + it "should serve inside a thread" do + exception_q = @server.instance_variable_get(:@exception_q) + described_class.any_instance.should_receive(:serve) do + exception_q.push(StandardError.new('ERROR')) + end + expect { @server.rescuable_serve }.to(raise_error('ERROR')) + end + + it "should avoid running the server twice when retrying rescuable_serve" do + exception_q = @server.instance_variable_get(:@exception_q) + described_class.any_instance.should_receive(:serve) do + exception_q.push(StandardError.new('ERROR1')) + exception_q.push(StandardError.new('ERROR2')) + end + expect { @server.rescuable_serve }.to(raise_error('ERROR1')) + expect { @server.rescuable_serve }.to(raise_error('ERROR2')) + end + + it "should serve using a thread pool" do + thread_q = mock("SizedQueue") + exception_q = mock("Queue") + @server.instance_variable_set(:@thread_q, thread_q) + @server.instance_variable_set(:@exception_q, exception_q) + @server_trans.should_receive(:listen).ordered + thread_q.should_receive(:push).with(:token) + thread_q.should_receive(:pop) + Thread.should_receive(:new).and_yield + @server_trans.should_receive(:accept).exactly(3).times.and_return(@client) + @trans.should_receive(:get_transport).exactly(3).times.and_return(@trans) + @prot.should_receive(:get_protocol).exactly(3).times.and_return(@prot) + x = 0 + error = RuntimeError.new("Stopped") + @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do + case (x += 1) + when 1 then raise Thrift::TransportException + when 2 then raise Thrift::ProtocolException + when 3 then raise error + end + end + @trans.should_receive(:close).exactly(3).times + exception_q.should_receive(:push).with(error).and_throw(:stop) + @server_trans.should_receive(:close) + expect { @server.serve }.to(throw_symbol(:stop)) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec.rb new file mode 100644 index 00000000..8e1ef50b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec.rb @@ -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. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared") + +describe 'Socket' do + + describe Thrift::Socket do + before(:each) do + @socket = Thrift::Socket.new + @handle = mock("Handle", :closed? => false) + @handle.stub!(:close) + @handle.stub!(:connect_nonblock) + @handle.stub!(:setsockopt) + ::Socket.stub!(:new).and_return(@handle) + end + + it_should_behave_like "a socket" + + it "should raise a TransportException when it cannot open a socket" do + ::Socket.should_receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]]) + lambda { @socket.open }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN } + end + + it "should open a ::Socket with default args" do + ::Socket.should_receive(:new).and_return(mock("Handle", :connect_nonblock => true, :setsockopt => nil)) + ::Socket.should_receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]]) + ::Socket.should_receive(:sockaddr_in) + @socket.open + end + + it "should accept host/port options" do + ::Socket.should_receive(:new).and_return(mock("Handle", :connect_nonblock => true, :setsockopt => nil)) + ::Socket.should_receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]]) + ::Socket.should_receive(:sockaddr_in) + Thrift::Socket.new('my.domain', 1234).open + end + + it "should accept an optional timeout" do + ::Socket.stub!(:new) + Thrift::Socket.new('localhost', 8080, 5).timeout.should == 5 + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec_shared.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec_shared.rb new file mode 100644 index 00000000..5fddc16a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/socket_spec_shared.rb @@ -0,0 +1,104 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +shared_examples_for "a socket" do + it "should open a socket" do + @socket.open.should == @handle + end + + it "should be open whenever it has a handle" do + @socket.should_not be_open + @socket.open + @socket.should be_open + @socket.handle = nil + @socket.should_not be_open + @socket.handle = @handle + @socket.close + @socket.should_not be_open + end + + it "should write data to the handle" do + @socket.open + @handle.should_receive(:write).with("foobar") + @socket.write("foobar") + @handle.should_receive(:write).with("fail").and_raise(StandardError) + lambda { @socket.write("fail") }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN } + end + + it "should raise an error when it cannot read from the handle" do + @socket.open + @handle.should_receive(:readpartial).with(17).and_raise(StandardError) + lambda { @socket.read(17) }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN } + end + + it "should return the data read when reading from the handle works" do + @socket.open + @handle.should_receive(:readpartial).with(17).and_return("test data") + @socket.read(17).should == "test data" + end + + it "should declare itself as closed when it has an error" do + @socket.open + @handle.should_receive(:write).with("fail").and_raise(StandardError) + @socket.should be_open + lambda { @socket.write("fail") }.should raise_error + @socket.should_not be_open + end + + it "should raise an error when the stream is closed" do + @socket.open + @handle.stub!(:closed?).and_return(true) + @socket.should_not be_open + lambda { @socket.write("fail") }.should raise_error(IOError, "closed stream") + lambda { @socket.read(10) }.should raise_error(IOError, "closed stream") + end + + it "should support the timeout accessor for read" do + @socket.timeout = 3 + @socket.open + IO.should_receive(:select).with([@handle], nil, nil, 3).and_return([[@handle], [], []]) + @handle.should_receive(:readpartial).with(17).and_return("test data") + @socket.read(17).should == "test data" + end + + it "should support the timeout accessor for write" do + @socket.timeout = 3 + @socket.open + IO.should_receive(:select).with(nil, [@handle], nil, 3).twice.and_return([[], [@handle], []]) + @handle.should_receive(:write_nonblock).with("test data").and_return(4) + @handle.should_receive(:write_nonblock).with(" data").and_return(5) + @socket.write("test data").should == 9 + end + + it "should raise an error when read times out" do + @socket.timeout = 0.5 + @socket.open + IO.should_receive(:select).once {sleep(0.5); nil} + lambda { @socket.read(17) }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::TIMED_OUT } + end + + it "should raise an error when write times out" do + @socket.timeout = 0.5 + @socket.open + IO.should_receive(:select).with(nil, [@handle], nil, 0.5).any_number_of_times.and_return(nil) + lambda { @socket.write("test data") }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::TIMED_OUT } + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/spec_helper.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/spec_helper.rb new file mode 100644 index 00000000..5bf98d07 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/spec_helper.rb @@ -0,0 +1,64 @@ +# encoding: UTF-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'rubygems' +require 'rspec' + +$:.unshift File.join(File.dirname(__FILE__), *%w[.. ext]) + +# pretend we already loaded fastthread, otherwise the nonblocking_server_spec +# will get screwed up +# $" << 'fastthread.bundle' + +require 'thrift' + +unless Object.method_defined? :tap + # if Object#tap isn't defined, then add it; this should only happen in Ruby < 1.8.7 + class Object + def tap(&block) + block.call(self) + self + end + end +end + +RSpec.configure do |configuration| + configuration.before(:each) do + Thrift.type_checking = true + end +end + +$:.unshift File.join(File.dirname(__FILE__), *%w[.. test debug_proto gen-rb]) +require 'srv' +require 'debug_proto_test_constants' + +$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb]) +require 'thrift_spec_types' +require 'nonblocking_service' + +module Fixtures + COMPACT_PROTOCOL_TEST_STRUCT = Thrift::Test::COMPACT_TEST.dup + COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0,1,2,3,4,5,6,7,8].pack('c*') + COMPACT_PROTOCOL_TEST_STRUCT.set_byte_map = nil + COMPACT_PROTOCOL_TEST_STRUCT.map_byte_map = nil +end + +$:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb/flat]) + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/ssl_socket_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/ssl_socket_spec.rb new file mode 100644 index 00000000..a8bc7854 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/ssl_socket_spec.rb @@ -0,0 +1,74 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared") + +describe 'SSLSocket' do + + describe Thrift::SSLSocket do + before(:each) do + @context = OpenSSL::SSL::SSLContext.new + @socket = Thrift::SSLSocket.new + @simple_socket_handle = mock("Handle", :closed? => false) + @simple_socket_handle.stub!(:close) + @simple_socket_handle.stub!(:connect_nonblock) + @simple_socket_handle.stub!(:setsockopt) + + @handle = mock(mock("SSLHandle", :connect_nonblock => true, :post_connection_check => true), :closed? => false) + @handle.stub!(:connect_nonblock) + @handle.stub!(:close) + @handle.stub!(:post_connection_check) + + ::Socket.stub!(:new).and_return(@simple_socket_handle) + OpenSSL::SSL::SSLSocket.stub!(:new).and_return(@handle) + end + + it_should_behave_like "a socket" + + it "should raise a TransportException when it cannot open a ssl socket" do + ::Socket.should_receive(:getaddrinfo).with("localhost", 9090, nil, ::Socket::SOCK_STREAM).and_return([[]]) + lambda { @socket.open }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN } + end + + it "should open a ::Socket with default args" do + OpenSSL::SSL::SSLSocket.should_receive(:new).with(@simple_socket_handle, nil).and_return(@handle) + @handle.should_receive(:post_connection_check).with('localhost') + @socket.open + end + + it "should accept host/port options" do + handle = mock("Handle", :connect_nonblock => true, :setsockopt => nil) + ::Socket.stub!(:new).and_return(handle) + ::Socket.should_receive(:getaddrinfo).with("my.domain", 1234, nil, ::Socket::SOCK_STREAM).and_return([[]]) + ::Socket.should_receive(:sockaddr_in) + OpenSSL::SSL::SSLSocket.should_receive(:new).with(handle, nil).and_return(@handle) + @handle.should_receive(:post_connection_check).with('my.domain') + Thrift::SSLSocket.new('my.domain', 1234, 6000, nil).open + end + + it "should accept an optional timeout" do + Thrift::SSLSocket.new('localhost', 8080, 5).timeout.should == 5 + end + + it "should accept an optional context" do + Thrift::SSLSocket.new('localhost', 8080, 5, @context).ssl_context.should == @context + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_nested_containers_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_nested_containers_spec.rb new file mode 100644 index 00000000..dc8ce5f5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_nested_containers_spec.rb @@ -0,0 +1,191 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'StructNestedContainers' do + + def with_type_checking + saved_type_checking, Thrift.type_checking = Thrift.type_checking, true + begin + yield + ensure + Thrift.type_checking = saved_type_checking + end + end + + describe Thrift::Struct do + # Nested container tests, see THRIFT-369. + it "should support nested lists inside lists" do + with_type_checking do + a, b = SpecNamespace::NestedListInList.new, SpecNamespace::NestedListInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ] + thrift_struct.validate + end + a.should == b + b.value.push [3, 4, 5] + a.should_not == b + end + end + + it "should support nested lists inside sets" do + with_type_checking do + a, b = SpecNamespace::NestedListInSet.new, SpecNamespace::NestedListInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ].to_set + thrift_struct.validate + end + a.should == b + b.value.add [3, 4, 5] + a.should_not == b + end + end + + it "should support nested lists in map keys" do + with_type_checking do + a, b = SpecNamespace::NestedListInMapKey.new, SpecNamespace::NestedListInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { [1, 2, 3] => 1, [2, 3, 4] => 2 } + thrift_struct.validate + end + a.should == b + b.value[[3, 4, 5]] = 3 + a.should_not == b + end + end + + it "should support nested lists in map values" do + with_type_checking do + a, b = SpecNamespace::NestedListInMapValue.new, SpecNamespace::NestedListInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => [1, 2, 3], 2 => [2, 3, 4] } + thrift_struct.validate + end + a.should == b + b.value[3] = [3, 4, 5] + a.should_not == b + end + end + + it "should support nested sets inside lists" do + with_type_checking do + a, b = SpecNamespace::NestedSetInList.new, SpecNamespace::NestedSetInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ] + thrift_struct.validate + end + a.should == b + b.value.push([3, 4, 5].to_set) + a.should_not == b + end + end + + it "should support nested sets inside sets" do + with_type_checking do + a, b = SpecNamespace::NestedSetInSet.new, SpecNamespace::NestedSetInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ].to_set + thrift_struct.validate + end + a.should == b + b.value.add([3, 4, 5].to_set) + a.should_not == b + end + end + + it "should support nested sets in map keys" do + with_type_checking do + a, b = SpecNamespace::NestedSetInMapKey.new, SpecNamespace::NestedSetInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { [1, 2, 3].to_set => 1, [2, 3, 4].to_set => 2 } + thrift_struct.validate + end + a.should == b + b.value[[3, 4, 5].to_set] = 3 + a.should_not == b + end + end + + it "should support nested sets in map values" do + with_type_checking do + a, b = SpecNamespace::NestedSetInMapValue.new, SpecNamespace::NestedSetInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => [1, 2, 3].to_set, 2 => [2, 3, 4].to_set } + thrift_struct.validate + end + a.should == b + b.value[3] = [3, 4, 5].to_set + a.should_not == b + end + end + + it "should support nested maps inside lists" do + with_type_checking do + a, b = SpecNamespace::NestedMapInList.new, SpecNamespace::NestedMapInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ] + thrift_struct.validate + end + a.should == b + b.value.push({ 3 => 4, 5 => 6 }) + a.should_not == b + end + end + + it "should support nested maps inside sets" do + with_type_checking do + a, b = SpecNamespace::NestedMapInSet.new, SpecNamespace::NestedMapInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ].to_set + thrift_struct.validate + end + a.should == b + b.value.add({ 3 => 4, 5 => 6 }) + a.should_not == b + end + end + + it "should support nested maps in map keys" do + with_type_checking do + a, b = SpecNamespace::NestedMapInMapKey.new, SpecNamespace::NestedMapInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5} => 2 } + thrift_struct.validate + end + a.should == b + b.value[{3 => 4, 5 => 6}] = 3 + a.should_not == b + end + end + + it "should support nested maps in map values" do + with_type_checking do + a, b = SpecNamespace::NestedMapInMapValue.new, SpecNamespace::NestedMapInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => { 1 => 2, 3 => 4}, 2 => {2 => 3, 4 => 5} } + thrift_struct.validate + end + a.should == b + b.value[3] = { 3 => 4, 5 => 6 } + a.should_not == b + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_spec.rb new file mode 100644 index 00000000..6534d616 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/struct_spec.rb @@ -0,0 +1,293 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'Struct' do + + describe Thrift::Struct do + it "should iterate over all fields properly" do + fields = {} + SpecNamespace::Foo.new.each_field { |fid,field_info| fields[fid] = field_info } + fields.should == SpecNamespace::Foo::FIELDS + end + + it "should initialize all fields to defaults" do + validate_default_arguments(SpecNamespace::Foo.new) + end + + it "should initialize all fields to defaults and accept a block argument" do + SpecNamespace::Foo.new do |f| + validate_default_arguments(f) + end + end + + def validate_default_arguments(object) + object.simple.should == 53 + object.words.should == "words" + object.hello.should == SpecNamespace::Hello.new(:greeting => 'hello, world!') + object.ints.should == [1, 2, 2, 3] + object.complex.should be_nil + object.shorts.should == Set.new([5, 17, 239]) + end + + it "should not share default values between instances" do + begin + struct = SpecNamespace::Foo.new + struct.ints << 17 + SpecNamespace::Foo.new.ints.should == [1,2,2,3] + ensure + # ensure no leakage to other tests + SpecNamespace::Foo::FIELDS[4][:default] = [1,2,2,3] + end + end + + it "should properly initialize boolean values" do + struct = SpecNamespace::BoolStruct.new(:yesno => false) + struct.yesno.should be_false + end + + it "should have proper == semantics" do + SpecNamespace::Foo.new.should_not == SpecNamespace::Hello.new + SpecNamespace::Foo.new.should == SpecNamespace::Foo.new + SpecNamespace::Foo.new(:simple => 52).should_not == SpecNamespace::Foo.new + end + + it "should print enum value names in inspect" do + SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect.should == "" + + SpecNamespace::StructWithEnumMap.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect.should == "" + end + + it "should pretty print binary fields" do + SpecNamespace::Foo2.new(:my_binary => "\001\002\003").inspect.should == "" + end + + it "should offer field? methods" do + SpecNamespace::Foo.new.opt_string?.should be_false + SpecNamespace::Foo.new(:simple => 52).simple?.should be_true + SpecNamespace::Foo.new(:my_bool => false).my_bool?.should be_true + SpecNamespace::Foo.new(:my_bool => true).my_bool?.should be_true + end + + it "should be comparable" do + s1 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::ONE) + s2 = SpecNamespace::StructWithSomeEnum.new(:some_enum => SpecNamespace::SomeEnum::TWO) + + (s1 <=> s2).should == -1 + (s2 <=> s1).should == 1 + (s1 <=> s1).should == 0 + (s1 <=> SpecNamespace::StructWithSomeEnum.new()).should == -1 + end + + it "should read itself off the wire" do + struct = SpecNamespace::Foo.new + prot = Thrift::BaseProtocol.new(mock("transport")) + prot.should_receive(:read_struct_begin).twice + prot.should_receive(:read_struct_end).twice + prot.should_receive(:read_field_begin).and_return( + ['complex', Thrift::Types::MAP, 5], # Foo + ['words', Thrift::Types::STRING, 2], # Foo + ['hello', Thrift::Types::STRUCT, 3], # Foo + ['greeting', Thrift::Types::STRING, 1], # Hello + [nil, Thrift::Types::STOP, 0], # Hello + ['simple', Thrift::Types::I32, 1], # Foo + ['ints', Thrift::Types::LIST, 4], # Foo + ['shorts', Thrift::Types::SET, 6], # Foo + [nil, Thrift::Types::STOP, 0] # Hello + ) + prot.should_receive(:read_field_end).exactly(7).times + prot.should_receive(:read_map_begin).and_return( + [Thrift::Types::I32, Thrift::Types::MAP, 2], # complex + [Thrift::Types::STRING, Thrift::Types::DOUBLE, 2], # complex/1/value + [Thrift::Types::STRING, Thrift::Types::DOUBLE, 1] # complex/2/value + ) + prot.should_receive(:read_map_end).exactly(3).times + prot.should_receive(:read_list_begin).and_return([Thrift::Types::I32, 4]) + prot.should_receive(:read_list_end) + prot.should_receive(:read_set_begin).and_return([Thrift::Types::I16, 2]) + prot.should_receive(:read_set_end) + prot.should_receive(:read_i32).and_return( + 1, 14, # complex keys + 42, # simple + 4, 23, 4, 29 # ints + ) + prot.should_receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?") + prot.should_receive(:read_double).and_return(Math::PI, Math::E, 4.669201609) + prot.should_receive(:read_i16).and_return(2, 3) + prot.should_not_receive(:skip) + struct.read(prot) + + struct.simple.should == 42 + struct.complex.should == {1 => {"pi" => Math::PI, "e" => Math::E}, 14 => {"feigenbaum" => 4.669201609}} + struct.hello.should == SpecNamespace::Hello.new(:greeting => "what's up?") + struct.words.should == "apple banana" + struct.ints.should == [4, 23, 4, 29] + struct.shorts.should == Set.new([3, 2]) + end + + it "should serialize false boolean fields correctly" do + b = SpecNamespace::BoolStruct.new(:yesno => false) + prot = Thrift::BinaryProtocol.new(Thrift::MemoryBufferTransport.new) + prot.should_receive(:write_bool).with(false) + b.write(prot) + end + + it "should skip unexpected fields in structs and use default values" do + struct = SpecNamespace::Foo.new + prot = Thrift::BaseProtocol.new(mock("transport")) + prot.should_receive(:read_struct_begin) + prot.should_receive(:read_struct_end) + prot.should_receive(:read_field_begin).and_return( + ['simple', Thrift::Types::I32, 1], + ['complex', Thrift::Types::STRUCT, 5], + ['thinz', Thrift::Types::MAP, 7], + ['foobar', Thrift::Types::I32, 3], + ['words', Thrift::Types::STRING, 2], + [nil, Thrift::Types::STOP, 0] + ) + prot.should_receive(:read_field_end).exactly(5).times + prot.should_receive(:read_i32).and_return(42) + prot.should_receive(:read_string).and_return("foobar") + prot.should_receive(:skip).with(Thrift::Types::STRUCT) + prot.should_receive(:skip).with(Thrift::Types::MAP) + # prot.should_receive(:read_map_begin).and_return([Thrift::Types::I32, Thrift::Types::I32, 0]) + # prot.should_receive(:read_map_end) + prot.should_receive(:skip).with(Thrift::Types::I32) + struct.read(prot) + + struct.simple.should == 42 + struct.complex.should be_nil + struct.words.should == "foobar" + struct.hello.should == SpecNamespace::Hello.new(:greeting => 'hello, world!') + struct.ints.should == [1, 2, 2, 3] + struct.shorts.should == Set.new([5, 17, 239]) + end + + it "should write itself to the wire" do + prot = Thrift::BaseProtocol.new(mock("transport")) #mock("Protocol") + prot.should_receive(:write_struct_begin).with("SpecNamespace::Foo") + prot.should_receive(:write_struct_begin).with("SpecNamespace::Hello") + prot.should_receive(:write_struct_end).twice + prot.should_receive(:write_field_begin).with('ints', Thrift::Types::LIST, 4) + prot.should_receive(:write_i32).with(1) + prot.should_receive(:write_i32).with(2).twice + prot.should_receive(:write_i32).with(3) + prot.should_receive(:write_field_begin).with('complex', Thrift::Types::MAP, 5) + prot.should_receive(:write_i32).with(5) + prot.should_receive(:write_string).with('foo') + prot.should_receive(:write_double).with(1.23) + prot.should_receive(:write_field_begin).with('shorts', Thrift::Types::SET, 6) + prot.should_receive(:write_i16).with(5) + prot.should_receive(:write_i16).with(17) + prot.should_receive(:write_i16).with(239) + prot.should_receive(:write_field_stop).twice + prot.should_receive(:write_field_end).exactly(6).times + prot.should_receive(:write_field_begin).with('simple', Thrift::Types::I32, 1) + prot.should_receive(:write_i32).with(53) + prot.should_receive(:write_field_begin).with('hello', Thrift::Types::STRUCT, 3) + prot.should_receive(:write_field_begin).with('greeting', Thrift::Types::STRING, 1) + prot.should_receive(:write_string).with('hello, world!') + prot.should_receive(:write_map_begin).with(Thrift::Types::I32, Thrift::Types::MAP, 1) + prot.should_receive(:write_map_begin).with(Thrift::Types::STRING, Thrift::Types::DOUBLE, 1) + prot.should_receive(:write_map_end).twice + prot.should_receive(:write_list_begin).with(Thrift::Types::I32, 4) + prot.should_receive(:write_list_end) + prot.should_receive(:write_set_begin).with(Thrift::Types::I16, 3) + prot.should_receive(:write_set_end) + + struct = SpecNamespace::Foo.new + struct.words = nil + struct.complex = {5 => {"foo" => 1.23}} + struct.write(prot) + end + + it "should raise an exception if presented with an unknown container" do + # yeah this is silly, but I'm going for code coverage here + struct = SpecNamespace::Foo.new + lambda { struct.send :write_container, nil, nil, {:type => "foo"} }.should raise_error(StandardError, "Not a container type: foo") + end + + it "should support optional type-checking in Thrift::Struct.new" do + Thrift.type_checking = true + begin + lambda { SpecNamespace::Hello.new(:greeting => 3) }.should raise_error(Thrift::TypeError, "Expected Types::STRING, received Fixnum for field greeting") + ensure + Thrift.type_checking = false + end + lambda { SpecNamespace::Hello.new(:greeting => 3) }.should_not raise_error(Thrift::TypeError) + end + + it "should support optional type-checking in field accessors" do + Thrift.type_checking = true + begin + hello = SpecNamespace::Hello.new + lambda { hello.greeting = 3 }.should raise_error(Thrift::TypeError, "Expected Types::STRING, received Fixnum for field greeting") + ensure + Thrift.type_checking = false + end + lambda { hello.greeting = 3 }.should_not raise_error(Thrift::TypeError) + end + + it "should raise an exception when unknown types are given to Thrift::Struct.new" do + lambda { SpecNamespace::Hello.new(:fish => 'salmon') }.should raise_error(Exception, "Unknown key given to SpecNamespace::Hello.new: fish") + end + + it "should support `raise Xception, 'message'` for Exception structs" do + begin + raise SpecNamespace::Xception, "something happened" + rescue Thrift::Exception => e + e.message.should == "something happened" + e.code.should == 1 + # ensure it gets serialized properly, this is the really important part + prot = Thrift::BaseProtocol.new(mock("trans")) + prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception") + prot.should_receive(:write_struct_end) + prot.should_receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)#, "something happened") + prot.should_receive(:write_string).with("something happened") + prot.should_receive(:write_field_begin).with('code', Thrift::Types::I32, 2)#, 1) + prot.should_receive(:write_i32).with(1) + prot.should_receive(:write_field_stop) + prot.should_receive(:write_field_end).twice + + e.write(prot) + end + end + + it "should support the regular initializer for exception structs" do + begin + raise SpecNamespace::Xception, :message => "something happened", :code => 5 + rescue Thrift::Exception => e + e.message.should == "something happened" + e.code.should == 5 + prot = Thrift::BaseProtocol.new(mock("trans")) + prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception") + prot.should_receive(:write_struct_end) + prot.should_receive(:write_field_begin).with('message', Thrift::Types::STRING, 1) + prot.should_receive(:write_string).with("something happened") + prot.should_receive(:write_field_begin).with('code', Thrift::Types::I32, 2) + prot.should_receive(:write_i32).with(5) + prot.should_receive(:write_field_stop) + prot.should_receive(:write_field_end).twice + + e.write(prot) + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/thin_http_server_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/thin_http_server_spec.rb new file mode 100644 index 00000000..55208392 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/thin_http_server_spec.rb @@ -0,0 +1,141 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' +require 'rack/test' +require 'thrift/server/thin_http_server' + +describe Thrift::ThinHTTPServer do + + let(:processor) { mock('processor') } + + describe "#initialize" do + + context "when using the defaults" do + + it "binds to port 80, with host 0.0.0.0, a path of '/'" do + Thin::Server.should_receive(:new).with('0.0.0.0', 80, an_instance_of(Rack::Builder)) + Thrift::ThinHTTPServer.new(processor) + end + + it 'creates a ThinHTTPServer::RackApplicationContext' do + Thrift::ThinHTTPServer::RackApplication.should_receive(:for).with("/", processor, an_instance_of(Thrift::BinaryProtocolFactory)).and_return(anything) + Thrift::ThinHTTPServer.new(processor) + end + + it "uses the BinaryProtocolFactory" do + Thrift::BinaryProtocolFactory.should_receive(:new) + Thrift::ThinHTTPServer.new(processor) + end + + end + + context "when using the options" do + + it 'accepts :ip, :port, :path' do + ip = "192.168.0.1" + port = 3000 + path = "/thin" + Thin::Server.should_receive(:new).with(ip, port, an_instance_of(Rack::Builder)) + Thrift::ThinHTTPServer.new(processor, + :ip => ip, + :port => port, + :path => path) + end + + it 'creates a ThinHTTPServer::RackApplicationContext with a different protocol factory' do + Thrift::ThinHTTPServer::RackApplication.should_receive(:for).with("/", processor, an_instance_of(Thrift::JsonProtocolFactory)).and_return(anything) + Thrift::ThinHTTPServer.new(processor, + :protocol_factory => Thrift::JsonProtocolFactory.new) + end + + end + + end + + describe "#serve" do + + it 'starts the Thin server' do + underlying_thin_server = mock('thin server', :start => true) + Thin::Server.stub(:new).and_return(underlying_thin_server) + + thin_thrift_server = Thrift::ThinHTTPServer.new(processor) + + underlying_thin_server.should_receive(:start) + thin_thrift_server.serve + end + end + +end + +describe Thrift::ThinHTTPServer::RackApplication do + include Rack::Test::Methods + + let(:processor) { mock('processor') } + let(:protocol_factory) { mock('protocol factory') } + + def app + Thrift::ThinHTTPServer::RackApplication.for("/", processor, protocol_factory) + end + + context "404 response" do + + it 'receives a non-POST' do + header('Content-Type', "application/x-thrift") + get "/" + last_response.status.should be 404 + end + + it 'receives a header other than application/x-thrift' do + header('Content-Type', "application/json") + post "/" + last_response.status.should be 404 + end + + end + + context "200 response" do + + before do + protocol_factory.stub(:get_protocol) + processor.stub(:process) + end + + it 'creates an IOStreamTransport' do + header('Content-Type', "application/x-thrift") + Thrift::IOStreamTransport.should_receive(:new).with(an_instance_of(Rack::Lint::InputWrapper), an_instance_of(Rack::Response)) + post "/" + end + + it 'fetches the right protocol based on the Transport' do + header('Content-Type', "application/x-thrift") + protocol_factory.should_receive(:get_protocol).with(an_instance_of(Thrift::IOStreamTransport)) + post "/" + end + + it 'status code 200' do + header('Content-Type', "application/x-thrift") + post "/" + last_response.ok?.should be_true + end + + end + +end + diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/types_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/types_spec.rb new file mode 100644 index 00000000..b2c3a200 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/types_spec.rb @@ -0,0 +1,115 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe Thrift::Types do + + before(:each) do + Thrift.type_checking = true + end + + after(:each) do + Thrift.type_checking = false + end + + context 'type checking' do + it "should return the proper name for each type" do + Thrift.type_name(Thrift::Types::I16).should == "Types::I16" + Thrift.type_name(Thrift::Types::VOID).should == "Types::VOID" + Thrift.type_name(Thrift::Types::LIST).should == "Types::LIST" + Thrift.type_name(42).should be_nil + end + + it "should check types properly" do + # lambda { Thrift.check_type(nil, Thrift::Types::STOP) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3, {:type => Thrift::Types::STOP}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::VOID}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3, {:type => Thrift::Types::VOID}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(true, {:type => Thrift::Types::BOOL}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3, {:type => Thrift::Types::BOOL}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(42, {:type => Thrift::Types::BYTE}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(42, {:type => Thrift::Types::I16}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(42, {:type => Thrift::Types::I32}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(42, {:type => Thrift::Types::I64}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3.14, {:type => Thrift::Types::I32}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3.14, {:type => Thrift::Types::DOUBLE}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3, {:type => Thrift::Types::DOUBLE}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type("3", {:type => Thrift::Types::STRING}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.should raise_error(Thrift::TypeError) + hello = SpecNamespace::Hello.new + lambda { Thrift.check_type(hello, {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type("foo", {:type => Thrift::Types::STRUCT}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type({:foo => 1}, {:type => Thrift::Types::MAP}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type([1], {:type => Thrift::Types::MAP}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type([1], {:type => Thrift::Types::LIST}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type({:foo => 1}, {:type => Thrift::Types::LIST}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(Set.new([1,2]), {:type => Thrift::Types::SET}, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type([1,2], {:type => Thrift::Types::SET}, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type({:foo => true}, {:type => Thrift::Types::SET}, :foo) }.should raise_error(Thrift::TypeError) + end + + it "should error out if nil is passed and skip_types is false" do + lambda { Thrift.check_type(nil, {:type => Thrift::Types::BOOL}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::BYTE}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::I16}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::I32}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::I64}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::DOUBLE}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::STRING}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::STRUCT}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::LIST}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::SET}, :foo, false) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(nil, {:type => Thrift::Types::MAP}, :foo, false) }.should raise_error(Thrift::TypeError) + end + + it "should check element types on containers" do + field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}} + lambda { Thrift.check_type([1, 2], field, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type([1, nil, 2], field, :foo) }.should raise_error(Thrift::TypeError) + field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}} + lambda { Thrift.check_type({1 => "one", 2 => "two"}, field, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type({1 => "one", nil => "nil"}, field, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type({1 => nil, 2 => "two"}, field, :foo) }.should raise_error(Thrift::TypeError) + field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}} + lambda { Thrift.check_type(Set.new([1, 2]), field, :foo) }.should_not raise_error(Thrift::TypeError) + lambda { Thrift.check_type(Set.new([1, nil, 2]), field, :foo) }.should raise_error(Thrift::TypeError) + lambda { Thrift.check_type(Set.new([1, 2.3, 2]), field, :foo) }.should raise_error(Thrift::TypeError) + + field = {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello} + lambda { Thrift.check_type(SpecNamespace::BoolStruct, field, :foo) }.should raise_error(Thrift::TypeError) + end + + it "should give the Thrift::TypeError a readable message" do + msg = "Expected Types::STRING, received Fixnum for field foo" + lambda { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.should raise_error(Thrift::TypeError, msg) + msg = "Expected Types::STRING, received Fixnum for field foo.element" + field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::STRING}} + lambda { Thrift.check_type([3], field, :foo) }.should raise_error(Thrift::TypeError, msg) + msg = "Expected Types::I32, received NilClass for field foo.element.key" + field = {:type => Thrift::Types::LIST, + :element => {:type => Thrift::Types::MAP, + :key => {:type => Thrift::Types::I32}, + :value => {:type => Thrift::Types::I32}}} + lambda { Thrift.check_type([{nil => 3}], field, :foo) }.should raise_error(Thrift::TypeError, msg) + msg = "Expected Types::I32, received NilClass for field foo.element.value" + lambda { Thrift.check_type([{1 => nil}], field, :foo) }.should raise_error(Thrift::TypeError, msg) + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/union_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/union_spec.rb new file mode 100644 index 00000000..6ad31940 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/union_spec.rb @@ -0,0 +1,215 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' + +describe 'Union' do + + describe Thrift::Union do + it "should return nil value in unset union" do + union = SpecNamespace::My_union.new + union.get_set_field.should == nil + union.get_value.should == nil + end + + it "should set a field and be accessible through get_value and the named field accessor" do + union = SpecNamespace::My_union.new + union.integer32 = 25 + union.get_set_field.should == :integer32 + union.get_value.should == 25 + union.integer32.should == 25 + end + + it "should work correctly when instantiated with static field constructors" do + union = SpecNamespace::My_union.integer32(5) + union.get_set_field.should == :integer32 + union.integer32.should == 5 + end + + it "should raise for wrong set field" do + union = SpecNamespace::My_union.new + union.integer32 = 25 + lambda { union.some_characters }.should raise_error(RuntimeError, "some_characters is not union's set field.") + end + + it "should raise for wrong set field when hash initialized and type checking is off" do + Thrift.type_checking = false + union = SpecNamespace::My_union.new({incorrect_field: :incorrect}) + example = lambda { Thrift::Serializer.new.serialize(union) } + example.should raise_error(RuntimeError, "set_field is not valid for this union!") + end + + it "should not be equal to nil" do + union = SpecNamespace::My_union.new + union.should_not == nil + end + + it "should not be equal with an empty String" do + union = SpecNamespace::My_union.new + union.should_not == '' + end + + it "should not equate two different unions, i32 vs. string" do + union = SpecNamespace::My_union.new(:integer32, 25) + other_union = SpecNamespace::My_union.new(:some_characters, "blah!") + union.should_not == other_union + end + + it "should properly reset setfield and setvalue" do + union = SpecNamespace::My_union.new(:integer32, 25) + union.get_set_field.should == :integer32 + union.some_characters = "blah!" + union.get_set_field.should == :some_characters + union.get_value.should == "blah!" + lambda { union.integer32 }.should raise_error(RuntimeError, "integer32 is not union's set field.") + end + + it "should not equate two different unions with different values" do + union = SpecNamespace::My_union.new(:integer32, 25) + other_union = SpecNamespace::My_union.new(:integer32, 400) + union.should_not == other_union + end + + it "should not equate two different unions with different fields" do + union = SpecNamespace::My_union.new(:integer32, 25) + other_union = SpecNamespace::My_union.new(:other_i32, 25) + union.should_not == other_union + end + + it "should inspect properly" do + union = SpecNamespace::My_union.new(:integer32, 25) + union.inspect.should == "" + end + + it "should not allow setting with instance_variable_set" do + union = SpecNamespace::My_union.new(:integer32, 27) + union.instance_variable_set(:@some_characters, "hallo!") + union.get_set_field.should == :integer32 + union.get_value.should == 27 + lambda { union.some_characters }.should raise_error(RuntimeError, "some_characters is not union's set field.") + end + + it "should serialize to binary correctly" do + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::BinaryProtocol.new(trans) + + union = SpecNamespace::My_union.new(:integer32, 25) + union.write(proto) + + other_union = SpecNamespace::My_union.new(:integer32, 25) + other_union.read(proto) + other_union.should == union + end + + it "should serialize to json correctly" do + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::JsonProtocol.new(trans) + + union = SpecNamespace::My_union.new(:integer32, 25) + union.write(proto) + + other_union = SpecNamespace::My_union.new(:integer32, 25) + other_union.read(proto) + other_union.should == union + end + + it "should raise when validating unset union" do + union = SpecNamespace::My_union.new + lambda { union.validate }.should raise_error(StandardError, "Union fields are not set.") + + other_union = SpecNamespace::My_union.new(:integer32, 1) + lambda { other_union.validate }.should_not raise_error(StandardError, "Union fields are not set.") + end + + it "should validate an enum field properly" do + union = SpecNamespace::TestUnion.new(:enum_field, 3) + union.get_set_field.should == :enum_field + lambda { union.validate }.should raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!") + + other_union = SpecNamespace::TestUnion.new(:enum_field, 1) + lambda { other_union.validate }.should_not raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!") + end + + it "should properly serialize and match structs with a union" do + union = SpecNamespace::My_union.new(:integer32, 26) + swu = SpecNamespace::Struct_with_union.new(:fun_union => union) + + trans = Thrift::MemoryBufferTransport.new + proto = Thrift::CompactProtocol.new(trans) + + swu.write(proto) + + other_union = SpecNamespace::My_union.new(:some_characters, "hello there") + swu2 = SpecNamespace::Struct_with_union.new(:fun_union => other_union) + + swu2.should_not == swu + + swu2.read(proto) + swu2.should == swu + end + + it "should support old style constructor" do + union = SpecNamespace::My_union.new(:integer32 => 26) + union.get_set_field.should == :integer32 + union.get_value.should == 26 + end + + it "should not throw an error when inspected and unset" do + lambda{SpecNamespace::TestUnion.new().inspect}.should_not raise_error + end + + it "should print enum value name when inspected" do + SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).inspect.should == "" + + SpecNamespace::My_union.new(:my_map => {SpecNamespace::SomeEnum::ONE => [SpecNamespace::SomeEnum::TWO]}).inspect.should == "" + end + + it "should offer field? methods" do + SpecNamespace::My_union.new.some_enum?.should be_false + SpecNamespace::My_union.new(:some_enum => SpecNamespace::SomeEnum::ONE).some_enum?.should be_true + SpecNamespace::My_union.new(:im_true => false).im_true?.should be_true + SpecNamespace::My_union.new(:im_true => true).im_true?.should be_true + end + + it "should pretty print binary fields" do + SpecNamespace::TestUnion.new(:binary_field => "\001\002\003").inspect.should == "" + end + + it "should be comparable" do + relationships = [ + [0, -1, -1, -1], + [1, 0, -1, -1], + [1, 1, 0, -1], + [1, 1, 1, 0]] + + objs = [ + SpecNamespace::TestUnion.new(:string_field, "blah"), + SpecNamespace::TestUnion.new(:string_field, "blahblah"), + SpecNamespace::TestUnion.new(:i32_field, 1), + SpecNamespace::TestUnion.new()] + + for y in 0..3 + for x in 0..3 + # puts "#{objs[y].inspect} <=> #{objs[x].inspect} should == #{relationships[y][x]}" + (objs[y] <=> objs[x]).should == relationships[y][x] + end + end + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/spec/unix_socket_spec.rb b/vendor/src/github.com/apache/thrift/lib/rb/spec/unix_socket_spec.rb new file mode 100644 index 00000000..cb6cff3f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/spec/unix_socket_spec.rb @@ -0,0 +1,107 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +require 'spec_helper' +require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared") + +describe 'UNIXSocket' do + + describe Thrift::UNIXSocket do + before(:each) do + @path = '/tmp/thrift_spec_socket' + @socket = Thrift::UNIXSocket.new(@path) + @handle = mock("Handle", :closed? => false) + @handle.stub!(:close) + ::UNIXSocket.stub!(:new).and_return(@handle) + end + + it_should_behave_like "a socket" + + it "should raise a TransportException when it cannot open a socket" do + ::UNIXSocket.should_receive(:new).and_raise(StandardError) + lambda { @socket.open }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN } + end + + it "should accept an optional timeout" do + ::UNIXSocket.stub!(:new) + Thrift::UNIXSocket.new(@path, 5).timeout.should == 5 + end + end + + describe Thrift::UNIXServerSocket do + before(:each) do + @path = '/tmp/thrift_spec_socket' + @socket = Thrift::UNIXServerSocket.new(@path) + end + + it "should create a handle when calling listen" do + UNIXServer.should_receive(:new).with(@path) + @socket.listen + end + + it "should create a Thrift::UNIXSocket to wrap accepted sockets" do + handle = mock("UNIXServer") + UNIXServer.should_receive(:new).with(@path).and_return(handle) + @socket.listen + sock = mock("sock") + handle.should_receive(:accept).and_return(sock) + trans = mock("UNIXSocket") + Thrift::UNIXSocket.should_receive(:new).and_return(trans) + trans.should_receive(:handle=).with(sock) + @socket.accept.should == trans + end + + it "should close the handle when closed" do + handle = mock("UNIXServer", :closed? => false) + UNIXServer.should_receive(:new).with(@path).and_return(handle) + @socket.listen + handle.should_receive(:close) + File.stub!(:delete) + @socket.close + end + + it "should delete the socket when closed" do + handle = mock("UNIXServer", :closed? => false) + UNIXServer.should_receive(:new).with(@path).and_return(handle) + @socket.listen + handle.stub!(:close) + File.should_receive(:delete).with(@path) + @socket.close + end + + it "should return nil when accepting if there is no handle" do + @socket.accept.should be_nil + end + + it "should return true for closed? when appropriate" do + handle = mock("UNIXServer", :closed? => false) + UNIXServer.stub!(:new).and_return(handle) + File.stub!(:delete) + @socket.listen + @socket.should_not be_closed + handle.stub!(:close) + @socket.close + @socket.should be_closed + @socket.listen + @socket.should_not be_closed + handle.stub!(:closed?).and_return(true) + @socket.should be_closed + end + end +end diff --git a/vendor/src/github.com/apache/thrift/lib/rb/thrift.gemspec b/vendor/src/github.com/apache/thrift/lib/rb/thrift.gemspec new file mode 100644 index 00000000..0ceebd22 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/rb/thrift.gemspec @@ -0,0 +1,37 @@ +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) + +Gem::Specification.new do |s| + s.name = 'thrift' + s.version = '0.10.0.0' + s.authors = ['Thrift Developers'] + s.email = ['dev@thrift.apache.org'] + s.homepage = 'http://thrift.apache.org' + s.summary = %q{Ruby bindings for Apache Thrift} + s.description = %q{Ruby bindings for the Apache Thrift RPC system} + s.license = 'Apache 2.0' + s.extensions = ['ext/extconf.rb'] + + s.has_rdoc = true + s.rdoc_options = %w[--line-numbers --inline-source --title Thrift --main README] + + s.rubyforge_project = 'thrift' + + dir = File.expand_path(File.dirname(__FILE__)) + + s.files = Dir.glob("{lib,spec}/**/*") + s.test_files = Dir.glob("{test,spec,benchmark}/**/*") + s.executables = Dir.glob("{bin}/**/*") + + s.extra_rdoc_files = %w[README.md] + Dir.glob("{ext,lib}/**/*.{c,h,rb}") + + s.require_paths = %w[lib ext] + + s.add_development_dependency 'rspec', ['>= 2.10.0', '< 2.14.0'] + s.add_development_dependency "rack", "~> 1.5" + s.add_development_dependency "rack-test", "~> 0.6" + s.add_development_dependency "thin", "~> 1.5" + s.add_development_dependency "bundler", "~> 1" + s.add_development_dependency 'rake', '~> 10.5' +end + diff --git a/vendor/src/github.com/apache/thrift/lib/st/README.md b/vendor/src/github.com/apache/thrift/lib/st/README.md new file mode 100644 index 00000000..5b5fdeef --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/st/README.md @@ -0,0 +1,39 @@ +Thrift SmallTalk Software Library + +Last updated Nov 2007 + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Contains some contributions under the Thrift Software License. +Please see doc/old-thrift-license.txt in the Thrift distribution for +details. + +Library +======= + +To get started, just file in thrift.st with Squeak, run thrift -st +on the tutorial .thrift files (and file in the resulting code), and +then: + +calc := CalculatorClient binaryOnHost: 'localhost' port: '9090' +calc addNum1: 10 num2: 15 + +Tested in Squeak 3.7, but should work fine with anything later. diff --git a/vendor/src/github.com/apache/thrift/lib/st/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/st/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/st/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/st/package.xml b/vendor/src/github.com/apache/thrift/lib/st/package.xml new file mode 100644 index 00000000..8a7fe174 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/st/package.xml @@ -0,0 +1,26 @@ + + + + + libthrift-st + thrift.st + thrift.st + + diff --git a/vendor/src/github.com/apache/thrift/lib/st/thrift.st b/vendor/src/github.com/apache/thrift/lib/st/thrift.st new file mode 100644 index 00000000..fdb66dfc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/st/thrift.st @@ -0,0 +1,815 @@ +" +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +License); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +Contains some contributions under the Thrift Software License. +Please see doc/old-thrift-license.txt in the Thrift distribution for +details. +" + +SystemOrganization addCategory: #Thrift! +SystemOrganization addCategory: #'Thrift-Protocol'! +SystemOrganization addCategory: #'Thrift-Transport'! + +Error subclass: #TError + instanceVariableNames: 'code' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift'! + +!TError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'! +signalWithCode: anInteger + self new code: anInteger; signal! ! + +!TError methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'! +code + ^ code! ! + +!TError methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'! +code: anInteger + code := anInteger! ! + +TError subclass: #TProtocolError + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'! +badVersion + ^ 4! ! + +!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'! +invalidData + ^ 1! ! + +!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:39'! +negativeSize + ^ 2! ! + +!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:40'! +sizeLimit + ^ 3! ! + +!TProtocolError class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:40'! +unknown + ^ 0! ! + +TError subclass: #TTransportError + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Transport'! + +TTransportError subclass: #TTransportClosedError + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Transport'! + +Object subclass: #TClient + instanceVariableNames: 'iprot oprot seqid remoteSeqid' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift'! + +!TClient class methodsFor: 'as yet unclassified' stamp: 'pc 11/7/2007 06:00'! +binaryOnHost: aString port: anInteger + | sock | + sock := TSocket new host: aString; port: anInteger; open; yourself. + ^ self new + inProtocol: (TBinaryProtocol new transport: sock); + yourself! ! + +!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 23:03'! +inProtocol: aProtocol + iprot := aProtocol. + oprot ifNil: [oprot := aProtocol]! ! + +!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 04:28'! +nextSeqid + ^ seqid + ifNil: [seqid := 0] + ifNotNil: [seqid := seqid + 1]! ! + +!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:51'! +outProtocol: aProtocol + oprot := aProtocol! ! + +!TClient methodsFor: 'as yet unclassified' stamp: 'pc 10/28/2007 15:32'! +validateRemoteMessage: aMsg + remoteSeqid + ifNil: [remoteSeqid := aMsg seqid] + ifNotNil: + [(remoteSeqid + 1) = aMsg seqid ifFalse: + [TProtocolError signal: 'Bad seqid: ', aMsg seqid asString, + '; wanted: ', remoteSeqid asString]. + remoteSeqid := aMsg seqid]! ! + +Object subclass: #TField + instanceVariableNames: 'name type id' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'! +id + ^ id ifNil: [0]! ! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'! +id: anInteger + id := anInteger! ! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'! +name + ^ name ifNil: ['']! ! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'! +name: anObject + name := anObject! ! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'! +type + ^ type ifNil: [TType stop]! ! + +!TField methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:44'! +type: anInteger + type := anInteger! ! + +Object subclass: #TMessage + instanceVariableNames: 'name seqid type' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +TMessage subclass: #TCallMessage + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TCallMessage methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:53'! +type + ^ 1! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'! +name + ^ name ifNil: ['']! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'! +name: aString + name := aString! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:05'! +seqid + ^ seqid ifNil: [0]! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'! +seqid: anInteger + seqid := anInteger! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:06'! +type + ^ type ifNil: [0]! ! + +!TMessage methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:35'! +type: anInteger + type := anInteger! ! + +Object subclass: #TProtocol + instanceVariableNames: 'transport' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +TProtocol subclass: #TBinaryProtocol + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:24'! +intFromByteArray: buf + | vals | + vals := Array new: buf size. + 1 to: buf size do: [:n | vals at: n put: ((buf at: n) bitShift: (buf size - n) * 8)]. + ^ vals sum! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 18:46'! +readBool + ^ self readByte isZero not! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/25/2007 00:02'! +readByte + ^ (self transport read: 1) first! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/28/2007 16:24'! +readDouble + | val | + val := Float new: 2. + ^ val basicAt: 1 put: (self readRawInt: 4); + basicAt: 2 put: (self readRawInt: 4); + yourself! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 20:02'! +readFieldBegin + | field | + field := TField new type: self readByte. + + ^ field type = TType stop + ifTrue: [field] + ifFalse: [field id: self readI16; yourself]! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:15'! +readI16 + ^ self readInt: 2! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:20'! +readI32 + ^ self readInt: 4! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:20'! +readI64 + ^ self readInt: 8! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 02:35'! +readInt: size + | buf val | + buf := transport read: size. + val := self intFromByteArray: buf. + ^ buf first > 16r7F + ifTrue: [self unsignedInt: val size: size] + ifFalse: [val]! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:57'! +readListBegin + ^ TList new + elemType: self readByte; + size: self readI32! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:58'! +readMapBegin + ^ TMap new + keyType: self readByte; + valueType: self readByte; + size: self readI32! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:22'! +readMessageBegin + | version | + version := self readI32. + + (version bitAnd: self versionMask) = self version1 + ifFalse: [TProtocolError signalWithCode: TProtocolError badVersion]. + + ^ TMessage new + type: (version bitAnd: 16r000000FF); + name: self readString; + seqid: self readI32! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 10/28/2007 16:24'! +readRawInt: size + ^ self intFromByteArray: (transport read: size)! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 00:59'! +readSetBegin + "element type, size" + ^ TSet new + elemType: self readByte; + size: self readI32! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 02/07/2009 19:00'! +readString + | sz | + sz := self readI32. + ^ sz > 0 ifTrue: [(transport read: sz) asString] ifFalse: ['']! ! + +!TBinaryProtocol methodsFor: 'reading' stamp: 'pc 11/1/2007 04:22'! +unsignedInt: val size: size + ^ 0 - ((val - 1) bitXor: ((2 raisedTo: (size * 8)) - 1))! ! + +!TBinaryProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:13'! +version1 + ^ 16r80010000 ! ! + +!TBinaryProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 18:01'! +versionMask + ^ 16rFFFF0000! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:35'! +write: aString + transport write: aString! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:23'! +writeBool: bool + bool ifTrue: [self writeByte: 1] + ifFalse: [self writeByte: 0]! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/26/2007 09:31'! +writeByte: aNumber + aNumber > 16rFF ifTrue: [TError signal: 'writeByte too big']. + transport write: (Array with: aNumber)! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/28/2007 16:16'! +writeDouble: aDouble + self writeI32: (aDouble basicAt: 1); + writeI32: (aDouble basicAt: 2)! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:56'! +writeField: aField + self writeByte: aField type; + writeI16: aField id! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/25/2007 00:01'! +writeFieldBegin: aField + self writeByte: aField type. + self writeI16: aField id! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:04'! +writeFieldStop + self writeByte: TType stop! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'! +writeI16: i16 + self writeInt: i16 size: 2! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'! +writeI32: i32 + self writeInt: i32 size: 4! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 02:06'! +writeI64: i64 + self writeInt: i64 size: 8! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 04:23'! +writeInt: val size: size + 1 to: size do: [:n | self writeByte: ((val bitShift: (size negated + n) * 8) bitAnd: 16rFF)]! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 00:48'! +writeListBegin: aList + self writeByte: aList elemType; writeI32: aList size! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:55'! +writeMapBegin: aMap + self writeByte: aMap keyType; + writeByte: aMap valueType; + writeI32: aMap size! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 20:36'! +writeMessageBegin: msg + self writeI32: (self version1 bitOr: msg type); + writeString: msg name; + writeI32: msg seqid! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 11/1/2007 00:56'! +writeSetBegin: aSet + self writeByte: aSet elemType; writeI32: aSet size! ! + +!TBinaryProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 18:35'! +writeString: aString + self writeI32: aString size; + write: aString! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readBool! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readByte! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readDouble! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readFieldBegin! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readFieldEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readI16! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readI32! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readI64! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readListBegin! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readListEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readMapBegin! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readMapEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:39'! +readMessageBegin! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:39'! +readMessageEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readSetBegin! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readSetEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/25/2007 16:10'! +readSimpleType: aType + aType = TType bool ifTrue: [^ self readBool]. + aType = TType byte ifTrue: [^ self readByte]. + aType = TType double ifTrue: [^ self readDouble]. + aType = TType i16 ifTrue: [^ self readI16]. + aType = TType i32 ifTrue: [^ self readI32]. + aType = TType i64 ifTrue: [^ self readI64]. + aType = TType list ifTrue: [^ self readBool].! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readString! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readStructBegin + ! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/24/2007 19:40'! +readStructEnd! ! + +!TProtocol methodsFor: 'reading' stamp: 'pc 10/26/2007 21:34'! +skip: aType + aType = TType stop ifTrue: [^ self]. + aType = TType bool ifTrue: [^ self readBool]. + aType = TType byte ifTrue: [^ self readByte]. + aType = TType i16 ifTrue: [^ self readI16]. + aType = TType i32 ifTrue: [^ self readI32]. + aType = TType i64 ifTrue: [^ self readI64]. + aType = TType string ifTrue: [^ self readString]. + aType = TType double ifTrue: [^ self readDouble]. + aType = TType struct ifTrue: + [| field | + self readStructBegin. + [(field := self readFieldBegin) type = TType stop] whileFalse: + [self skip: field type. self readFieldEnd]. + ^ self readStructEnd]. + aType = TType map ifTrue: + [| map | + map := self readMapBegin. + map size timesRepeat: [self skip: map keyType. self skip: map valueType]. + ^ self readMapEnd]. + aType = TType list ifTrue: + [| list | + list := self readListBegin. + list size timesRepeat: [self skip: list elemType]. + ^ self readListEnd]. + aType = TType set ifTrue: + [| set | + set := self readSetBegin. + set size timesRepeat: [self skip: set elemType]. + ^ self readSetEnd]. + + self error: 'Unknown type'! ! + +!TProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 23:02'! +transport + ^ transport! ! + +!TProtocol methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:28'! +transport: aTransport + transport := aTransport! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeBool: aBool! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeByte: aByte! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'! +writeDouble: aFloat! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'! +writeFieldBegin: aField! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeFieldEnd! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeFieldStop! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeI16: i16! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeI32: i32! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeI64: i64! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'! +writeListBegin: aList! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeListEnd! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'! +writeMapBegin: aMap! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeMapEnd! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:36'! +writeMessageBegin! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:36'! +writeMessageEnd! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:39'! +writeSetBegin: aSet! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeSetEnd! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'! +writeString: aString! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:38'! +writeStructBegin: aStruct! ! + +!TProtocol methodsFor: 'writing' stamp: 'pc 10/24/2007 19:37'! +writeStructEnd! ! + +Object subclass: #TResult + instanceVariableNames: 'success oprot iprot exception' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift'! + +!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 21:35'! +exception + ^ exception! ! + +!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 21:35'! +exception: anError + exception := anError! ! + +!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 14:43'! +success + ^ success! ! + +!TResult methodsFor: 'as yet unclassified' stamp: 'pc 10/26/2007 14:43'! +success: anObject + success := anObject! ! + +Object subclass: #TSizedObject + instanceVariableNames: 'size' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +TSizedObject subclass: #TList + instanceVariableNames: 'elemType' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TList methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'! +elemType + ^ elemType ifNil: [TType stop]! ! + +!TList methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:42'! +elemType: anInteger + elemType := anInteger! ! + +TList subclass: #TSet + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +TSizedObject subclass: #TMap + instanceVariableNames: 'keyType valueType' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'! +keyType + ^ keyType ifNil: [TType stop]! ! + +!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:45'! +keyType: anInteger + keyType := anInteger! ! + +!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 20:04'! +valueType + ^ valueType ifNil: [TType stop]! ! + +!TMap methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:45'! +valueType: anInteger + valueType := anInteger! ! + +!TSizedObject methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:03'! +size + ^ size ifNil: [0]! ! + +!TSizedObject methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:06'! +size: anInteger + size := anInteger! ! + +Object subclass: #TSocket + instanceVariableNames: 'host port stream' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Transport'! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:34'! +close + self isOpen ifTrue: [stream close]! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:23'! +connect + ^ (self socketStream openConnectionToHost: + (NetNameResolver addressForName: host) port: port) + timeout: 180; + binary; + yourself! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:35'! +flush + stream flush! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:08'! +host: aString + host := aString! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 20:34'! +isOpen + ^ stream isNil not + and: [stream socket isConnected] + and: [stream socket isOtherEndClosed not]! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:22'! +open + stream := self connect! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:09'! +port: anInteger + port := anInteger! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:17'! +read: size + | data | + [data := stream next: size. + data isEmpty ifTrue: [TTransportError signal: 'Could not read ', size asString, ' bytes']. + ^ data] + on: ConnectionClosed + do: [TTransportClosedError signal]! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:18'! +socketStream + ^ Smalltalk at: #FastSocketStream ifAbsent: [SocketStream] ! ! + +!TSocket methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 22:17'! +write: aCollection + [stream nextPutAll: aCollection] + on: ConnectionClosed + do: [TTransportClosedError signal]! ! + +Object subclass: #TStruct + instanceVariableNames: 'name' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Protocol'! + +!TStruct methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:47'! +name + ^ name! ! + +!TStruct methodsFor: 'accessing' stamp: 'pc 10/24/2007 19:47'! +name: aString + name := aString! ! + +Object subclass: #TTransport + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift-Transport'! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'! +close + self subclassResponsibility! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'! +flush + self subclassResponsibility! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'! +isOpen + self subclassResponsibility! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'! +open + self subclassResponsibility! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:18'! +read: anInteger + self subclassResponsibility! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'! +readAll: anInteger + ^ String streamContents: [:str | + [str size < anInteger] whileTrue: + [str nextPutAll: (self read: anInteger - str size)]]! ! + +!TTransport methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:22'! +write: aString + self subclassResponsibility! ! + +Object subclass: #TType + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'Thrift'! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'! +bool + ^ 2! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'! +byte + ^ 3! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:55'! +codeOf: aTypeName + self typeMap do: [:each | each first = aTypeName ifTrue: [^ each second]]. + ^ nil! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'! +double + ^ 4! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +i16 + ^ 6! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +i32 + ^ 8! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +i64 + ^ 10! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +list + ^ 15! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +map + ^ 13! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:56'! +nameOf: aTypeCode + self typeMap do: [:each | each second = aTypeCode ifTrue: [^ each first]]. + ^ nil! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +set + ^ 14! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'! +stop + ^ 0! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +string + ^ 11! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:04'! +struct + ^ 12! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/25/2007 15:51'! +typeMap + ^ #((bool 2) (byte 3) (double 4) (i16 6) (i32 8) (i64 10) (list 15) + (map 13) (set 15) (stop 0) (string 11) (struct 12) (void 1))! ! + +!TType class methodsFor: 'as yet unclassified' stamp: 'pc 10/24/2007 17:03'! +void + ^ 1! ! diff --git a/vendor/src/github.com/apache/thrift/lib/ts/coding_standards.md b/vendor/src/github.com/apache/thrift/lib/ts/coding_standards.md new file mode 100644 index 00000000..fa0390bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ts/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/vendor/src/github.com/apache/thrift/lib/ts/thrift.d.ts b/vendor/src/github.com/apache/thrift/lib/ts/thrift.d.ts new file mode 100644 index 00000000..0ba46c91 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/ts/thrift.d.ts @@ -0,0 +1,699 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +declare module Thrift { + /** + * Thrift JavaScript library version. + */ + var Version: string; + + /** + * Thrift IDL type string to Id mapping. + * @property {number} STOP - End of a set of fields. + * @property {number} VOID - No value (only legal for return types). + * @property {number} BOOL - True/False integer. + * @property {number} BYTE - Signed 8 bit integer. + * @property {number} I08 - Signed 8 bit integer. + * @property {number} DOUBLE - 64 bit IEEE 854 floating point. + * @property {number} I16 - Signed 16 bit integer. + * @property {number} I32 - Signed 32 bit integer. + * @property {number} I64 - Signed 64 bit integer. + * @property {number} STRING - Array of bytes representing a string of characters. + * @property {number} UTF7 - Array of bytes representing a string of UTF7 encoded characters. + * @property {number} STRUCT - A multifield type. + * @property {number} MAP - A collection type (map/associative-array/dictionary). + * @property {number} SET - A collection type (unordered and without repeated values). + * @property {number} LIST - A collection type (unordered). + * @property {number} UTF8 - Array of bytes representing a string of UTF8 encoded characters. + * @property {number} UTF16 - Array of bytes representing a string of UTF16 encoded characters. + */ + interface Type { + 'STOP': number; + 'VOID': number; + 'BOOL': number; + 'BYTE': number; + 'I08': number; + 'DOUBLE': number; + 'I16': number; + 'I32': number; + 'I64': number; + 'STRING': number; + 'UTF7': number; + 'STRUCT': number; + 'MAP': number; + 'SET': number; + 'LIST': number; + 'UTF8': number; + 'UTF16': number; + } + var Type: Type; + + /** + * Thrift RPC message type string to Id mapping. + * @property {number} CALL - RPC call sent from client to server. + * @property {number} REPLY - RPC call normal response from server to client. + * @property {number} EXCEPTION - RPC call exception response from server to client. + * @property {number} ONEWAY - Oneway RPC call from client to server with no response. + */ + interface MessageType { + 'CALL': number; + 'REPLY': number; + 'EXCEPTION': number; + 'ONEWAY': number; + } + var MessageType: MessageType; + + /** + * Utility function returning the count of an object's own properties. + * @param {object} obj - Object to test. + * @returns {number} number of object's own properties + */ + function objectLength(obj: Object): number; + + /** + * Utility function to establish prototype inheritance. + * @param {function} constructor - Contstructor function to set as derived. + * @param {function} superConstructor - Contstructor function to set as base. + * @param {string} [name] - Type name to set as name property in derived prototype. + */ + function inherits(constructor: Function, superConstructor: Function, name?: string): void; + + /** + * TException is the base class for all Thrift exceptions types. + */ + class TException implements Error { + name: string; + message: string; + + /** + * Initializes a Thrift TException instance. + * @param {string} message - The TException message (distinct from the Error message). + */ + constructor(message: string); + + /** + * Returns the message set on the exception. + * @returns {string} exception message + */ + getMessage(): string; + } + + /** + * Thrift Application Exception type string to Id mapping. + * @property {number} UNKNOWN - Unknown/undefined. + * @property {number} UNKNOWN_METHOD - Client attempted to call a method unknown to the server. + * @property {number} INVALID_MESSAGE_TYPE - Client passed an unknown/unsupported MessageType. + * @property {number} WRONG_METHOD_NAME - Unused. + * @property {number} BAD_SEQUENCE_ID - Unused in Thrift RPC, used to flag proprietary sequence number errors. + * @property {number} MISSING_RESULT - Raised by a server processor if a handler fails to supply the required return result. + * @property {number} INTERNAL_ERROR - Something bad happened. + * @property {number} PROTOCOL_ERROR - The protocol layer failed to serialize or deserialize data. + * @property {number} INVALID_TRANSFORM - Unused. + * @property {number} INVALID_PROTOCOL - The protocol (or version) is not supported. + * @property {number} UNSUPPORTED_CLIENT_TYPE - Unused. + */ + interface TApplicationExceptionType { + 'UNKNOWN': number; + 'UNKNOWN_METHOD': number; + 'INVALID_MESSAGE_TYPE': number; + 'WRONG_METHOD_NAME': number; + 'BAD_SEQUENCE_ID': number; + 'MISSING_RESULT': number; + 'INTERNAL_ERROR': number; + 'PROTOCOL_ERROR': number; + 'INVALID_TRANSFORM': number; + 'INVALID_PROTOCOL': number; + 'UNSUPPORTED_CLIENT_TYPE': number; + } + var TApplicationExceptionType: TApplicationExceptionType; + + /** + * TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client. + */ + class TApplicationException extends TException { + message: string; + code: number; + + /** + * Initializes a Thrift TApplicationException instance. + * @param {string} message - The TApplicationException message (distinct from the Error message). + * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code. + */ + constructor(message: string, code?: number); + + /** + * Read a TApplicationException from the supplied protocol. + * @param {object} input - The input protocol to read from. + */ + read(input: Object): void; + + /** + * Write a TApplicationException to the supplied protocol. + * @param {object} output - The output protocol to write to. + */ + write(output: Object): void; + + /** + * Returns the application exception code set on the exception. + * @returns {Thrift.TApplicationExceptionType} exception code + */ + getCode(): number; + } + + /** + * The Apache Thrift Transport layer performs byte level I/O between RPC + * clients and servers. The JavaScript Transport object type uses Http[s]/XHR and is + * the sole browser based Thrift transport. Target servers must implement the http[s] + * transport (see: node.js example server). + */ + class TXHRTransport { + url: string; + wpos: number; + rpos: number; + useCORS: any; + send_buf: string; + recv_buf: string; + + /** + * If you do not specify a url then you must handle XHR operations on + * your own. This type can also be constructed using the Transport alias + * for backward compatibility. + * @param {string} [url] - The URL to connect to. + * @param {object} [options] - Options. + */ + constructor(url?: string, options?: Object); + + /** + * Gets the browser specific XmlHttpRequest Object. + * @returns {object} the browser XHR interface object + */ + getXmlHttpRequestObject(): Object; + + /** + * Sends the current XRH request if the transport was created with a URL and + * the async parameter if false. If the transport was not created with a URL + * or the async parameter is True or the URL is an empty string, the current + * send buffer is returned. + * @param {object} async - If true the current send buffer is returned. + * @param {function} callback - Optional async completion callback. + * @returns {undefined|string} Nothing or the current send buffer. + */ + flush(async: any, callback?: Function): string; + + /** + * Creates a jQuery XHR object to be used for a Thrift server call. + * @param {object} client - The Thrift Service client object generated by the IDL compiler. + * @param {object} postData - The message to send to the server. + * @param {function} args - The function to call if the request succeeds. + * @param {function} recv_method - The Thrift Service Client receive method for the call. + * @returns {object} A new jQuery XHR object. + */ + jqRequest(client: Object, postData: any, args: Function, recv_method: Function): Object; + + /** + * Sets the buffer to use when receiving server responses. + * @param {string} buf - The buffer to receive server responses. + */ + setRecvBuffer(buf: string): void; + + /** + * Returns true if the transport is open, in browser based JavaScript + * this function always returns true. + * @returns {boolean} Always True. + */ + isOpen(): boolean; + + /** + * Opens the transport connection, in browser based JavaScript + * this function is a nop. + */ + open(): void; + + /** + * Closes the transport connection, in browser based JavaScript + * this function is a nop. + */ + close(): void; + + /** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ + read(len: number): string; + + /** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ + readAll(): string; + + /** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ + write(buf: string): void; + + /** + * Returns the send buffer. + * @returns {string} The send buffer. + */ + getSendBuffer(): string; + } + + /** + * Old alias of the TXHRTransport for backwards compatibility. + */ + class Transport extends TXHRTransport { } + + /** + * The Apache Thrift Transport layer performs byte level I/O + * between RPC clients and servers. The JavaScript TWebSocketTransport object + * uses the WebSocket protocol. Target servers must implement WebSocket. + */ + class TWebSocketTransport { + url: string; //Where to connect + socket: any; //The web socket + callbacks: Function[]; //Pending callbacks + send_pending: any[]; //Buffers/Callback pairs waiting to be sent + send_buf: string; //Outbound data, immutable until sent + recv_buf: string; //Inbound data + rb_wpos: number; //Network write position in receive buffer + rb_rpos: number; //Client read position in receive buffer + + /** + * Constructor Function for the WebSocket transport. + * @param {string } [url] - The URL to connect to. + */ + constructor(url: string); + + __reset(url: string): void; + + /** + * Sends the current WS request and registers callback. The async + * parameter is ignored (WS flush is always async) and the callback + * function parameter is required. + * @param {object} async - Ignored. + * @param {function} callback - The client completion callback. + * @returns {undefined|string} Nothing (undefined) + */ + flush(async: any, callback: Function): string; + + __onOpen(): void; + + __onClose(): void; + + __onMessage(): void; + + __onError(): void; + + /** + * Sets the buffer to use when receiving server responses. + * @param {string} buf - The buffer to receive server responses. + */ + setRecvBuffer(buf: string): void; + + /** + * Returns true if the transport is open + * @returns {boolean} + */ + isOpen(): boolean; + + /** + * Opens the transport connection + */ + open(): void; + + /** + * Closes the transport connection + */ + close(): void; + + /** + * Returns the specified number of characters from the response + * buffer. + * @param {number} len - The number of characters to return. + * @returns {string} Characters sent by the server. + */ + read(len: number): string; + + /** + * Returns the entire response buffer. + * @returns {string} Characters sent by the server. + */ + readAll(): string; + + /** + * Sets the send buffer to buf. + * @param {string} buf - The buffer to send. + */ + write(buf: string): void; + + /** + * Returns the send buffer. + * @returns {string} The send buffer. + */ + getSendBuffer(): string; + } + + /** + * Apache Thrift Protocols perform serialization which enables cross + * language RPC. The Protocol type is the JavaScript browser implementation + * of the Apache Thrift TJSONProtocol. + */ + class TJSONProtocol { + transport: Object; + + /** + * Thrift IDL type Id to string mapping. + * The mapping table looks as follows: + * Thrift.Type.BOOL -> "tf": True/False integer. + * Thrift.Type.BYTE -> "i8": Signed 8 bit integer. + * Thrift.Type.I16 -> "i16": Signed 16 bit integer. + * Thrift.Type.I32 -> "i32": Signed 32 bit integer. + * Thrift.Type.I64 -> "i64": Signed 64 bit integer. + * Thrift.Type.DOUBLE -> "dbl": 64 bit IEEE 854 floating point. + * Thrift.Type.STRUCT -> "rec": A multifield type. + * Thrift.Type.STRING -> "str": Array of bytes representing a string of characters. + * Thrift.Type.MAP -> "map": A collection type (map/associative-array/dictionary). + * Thrift.Type.LIST -> "lst": A collection type (unordered). + * Thrift.Type.SET -> "set": A collection type (unordered and without repeated values). + */ + Type: { [k: number]: string }; + + /** + * Thrift IDL type string to Id mapping. + * The mapping table looks as follows: + * "tf" -> Thrift.Type.BOOL + * "i8" -> Thrift.Type.BYTE + * "i16" -> Thrift.Type.I16 + * "i32" -> Thrift.Type.I32 + * "i64" -> Thrift.Type.I64 + * "dbl" -> Thrift.Type.DOUBLE + * "rec" -> Thrift.Type.STRUCT + * "str" -> Thrift.Type.STRING + * "map" -> Thrift.Type.MAP + * "lst" -> Thrift.Type.LIST + * "set" -> Thrift.Type.SET + */ + RType: { [k: string]: number }; + + /** + * The TJSONProtocol version number. + */ + Version: number; + + /** + * Initializes a Thrift JSON protocol instance. + * @param {Thrift.Transport} transport - The transport to serialize to/from. + */ + constructor(transport: Object); + + /** + * Returns the underlying transport. + * @returns {Thrift.Transport} The underlying transport. + */ + getTransport(): Object; + + /** + * Serializes the beginning of a Thrift RPC message. + * @param {string} name - The service method to call. + * @param {Thrift.MessageType} messageType - The type of method call. + * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift). + */ + writeMessageBegin(name: string, messageType: number, seqid: number): void; + + /** + * Serializes the end of a Thrift RPC message. + */ + writeMessageEnd(): void; + + /** + * Serializes the beginning of a struct. + * @param {string} name - The name of the struct. + */ + writeStructBegin(name?: string): void; + + /** + * Serializes the end of a struct. + */ + writeStructEnd(): void; + + /** + * Serializes the beginning of a struct field. + * @param {string} name - The name of the field. + * @param {Thrift.Protocol.Type} fieldType - The data type of the field. + * @param {number} fieldId - The field's unique identifier. + */ + writeFieldBegin(name: string, fieldType: number, fieldId: number): void; + + /** + * Serializes the end of a field. + */ + writeFieldEnd(): void; + + /** + * Serializes the end of the set of fields for a struct. + */ + writeFieldStop(): void; + + /** + * Serializes the beginning of a map collection. + * @param {Thrift.Type} keyType - The data type of the key. + * @param {Thrift.Type} valType - The data type of the value. + * @param {number} [size] - The number of elements in the map (ignored). + */ + writeMapBegin(keyType: number, valType: number, size?: number): void; + + /** + * Serializes the end of a map. + */ + writeMapEnd(): void; + + /** + * Serializes the beginning of a list collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ + writeListBegin(elemType: number, size: number): void; + + /** + * Serializes the end of a list. + */ + writeListEnd(): void; + + /** + * Serializes the beginning of a set collection. + * @param {Thrift.Type} elemType - The data type of the elements. + * @param {number} size - The number of elements in the list. + */ + writeSetBegin(elemType: number, size: number): void; + + /** + * Serializes the end of a set. + */ + writeSetEnd(): void; + + /** Serializes a boolean */ + writeBool(value: boolean): void; + + /** Serializes a number */ + writeByte(i8: number): void; + + /** Serializes a number */ + writeI16(i16: number): void; + + /** Serializes a number */ + writeI32(i32: number): void; + + /** Serializes a number */ + writeI64(i64: number): void; + + /** Serializes a number */ + writeDouble(dbl: number): void; + + /** Serializes a string */ + writeString(str: string): void; + + /** Serializes a string */ + writeBinary(str: string): void; + + /** + @class + @name AnonReadMessageBeginReturn + @property {string} fname - The name of the service method. + @property {Thrift.MessageType} mtype - The type of message call. + @property {number} rseqid - The sequence number of the message (0 in Thrift RPC). + */ + /** + * Deserializes the beginning of a message. + * @returns {AnonReadMessageBeginReturn} + */ + readMessageBegin(): { fname: string; mtype: number; rseqid: number }; + + /** Deserializes the end of a message. */ + readMessageEnd(): void; + + /** + * Deserializes the beginning of a struct. + * @param {string} [name] - The name of the struct (ignored). + * @returns {object} - An object with an empty string fname property. + */ + readStructBegin(name?: string): { fname: string }; + + /** Deserializes the end of a struct. */ + readStructEnd(): void; + + /** + @class + @name AnonReadFieldBeginReturn + @property {string} fname - The name of the field (always ''). + @property {Thrift.Type} ftype - The data type of the field. + @property {number} fid - The unique identifier of the field. + */ + /** + * Deserializes the beginning of a field. + * @returns {AnonReadFieldBeginReturn} + */ + readFieldBegin(): { fname: string; ftype: number; fid: number }; + + /** Deserializes the end of a field. */ + readFieldEnd(): void; + + /** + @class + @name AnonReadMapBeginReturn + @property {Thrift.Type} ktype - The data type of the key. + @property {Thrift.Type} vtype - The data type of the value. + @property {number} size - The number of elements in the map. + */ + /** + * Deserializes the beginning of a map. + * @returns {AnonReadMapBeginReturn} + */ + readMapBegin(): { ktype: number; vtype: number; size: number }; + + /** Deserializes the end of a map. */ + readMapEnd(): void; + + /** + @class + @name AnonReadColBeginReturn + @property {Thrift.Type} etype - The data type of the element. + @property {number} size - The number of elements in the collection. + */ + /** + * Deserializes the beginning of a list. + * @returns {AnonReadColBeginReturn} + */ + readListBegin(): { etype: number; size: number }; + + /** Deserializes the end of a list. */ + readListEnd(): void; + + /** + * Deserializes the beginning of a set. + * @param {Thrift.Type} elemType - The data type of the elements (ignored). + * @param {number} size - The number of elements in the list (ignored). + * @returns {AnonReadColBeginReturn} + */ + readSetBegin(elemType?: number, size?: number): { etype: number; size: number }; + + /** Deserializes the end of a set. */ + readSetEnd(): void; + + /** Returns an object with a value property set to + * False unless the next number in the protocol buffer + * is 1, in which case the value property is True. */ + readBool(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readByte(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readI16(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readI32(f?: any): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readI64(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readDouble(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readString(): Object; + + /** Returns an object with a value property set to the + next value found in the protocol buffer. */ + readBinary(): Object; + + /** + * Method to arbitrarily skip over data (not implemented). + */ + skip(type: number): void; + } + + /** + * Old alias of the TXHRTransport for backwards compatibility. + */ + class Protocol extends TJSONProtocol { } + + class MultiplexProtocol extends TJSONProtocol { + serviceName: string; + + /** + * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol. + * @param {string} srvName + * @param {Thrift.Transport} trans + * @param {any} [strictRead] + * @param {any} [strictWrite] + */ + constructor(srvName: string, trans: Object, strictRead?: any, strictWrite?: any); + + /** + * Override writeMessageBegin method of prototype + * Serializes the beginning of a Thrift RPC message. + * @param {string} name - The service method to call. + * @param {Thrift.MessageType} messageType - The type of method call. + * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift). + */ + writeMessageBegin(name: string, type: number, seqid: number): void; + } + + class Multiplexer { + seqid: number; + + /** + * Instantiates a multiplexed client for a specific service. + * @param {String} serviceName - The transport to serialize to/from. + * @param {Thrift.ServiceClient} SCl - The Service Client Class. + * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port. + */ + createClient(serviceName: string, SCl: any, transport: Object): any; + } +} diff --git a/vendor/src/github.com/apache/thrift/lib/xml/Makefile.am b/vendor/src/github.com/apache/thrift/lib/xml/Makefile.am new file mode 100644 index 00000000..bcad6bdd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/xml/Makefile.am @@ -0,0 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = + +if WITH_JAVA +# Schema validation test depends on java +SUBDIRS += test +endif + +EXTRA_DIST = \ + thrift-idl.xsd \ + test diff --git a/vendor/src/github.com/apache/thrift/lib/xml/test/Makefile.am b/vendor/src/github.com/apache/thrift/lib/xml/test/Makefile.am new file mode 100644 index 00000000..bb87a520 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/xml/test/Makefile.am @@ -0,0 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +check: + $(ANT) $(ANT_FLAGS) test + +# Make sure this doesn't fail if ant is not configured. +clean-local: + ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ + $$ANT $(ANT_FLAGS) clean diff --git a/vendor/src/github.com/apache/thrift/lib/xml/test/build.xml b/vendor/src/github.com/apache/thrift/lib/xml/test/build.xml new file mode 100644 index 00000000..f0e95cf0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/xml/test/build.xml @@ -0,0 +1,112 @@ + + + + XML Schema Validation Test + + + + + + + + + + + + + + + + + + + + + Thrift compiler is missing ! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/lib/xml/thrift-idl.xsd b/vendor/src/github.com/apache/thrift/lib/xml/thrift-idl.xsd new file mode 100644 index 00000000..09dd695e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/lib/xml/thrift-idl.xsd @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/package.json b/vendor/src/github.com/apache/thrift/package.json new file mode 100644 index 00000000..edfc3553 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/package.json @@ -0,0 +1,54 @@ +{ + "name": "thrift", + "description": "node.js bindings for the Apache Thrift RPC system", + "homepage": "http://thrift.apache.org/", + "repository": { + "type": "git", + "url": "https://git-wip-us.apache.org/repos/asf/thrift.git" + }, + "version": "0.10.0", + "author": { + "name": "Apache Thrift Developers", + "email": "dev@thrift.apache.org", + "url": "http://thrift.apache.org" + }, + "license": "Apache-2.0", + "licenses": [ + { + "type": "Apache-2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + ], + "bugs": { + "mail": "dev@thrift.apache.org", + "url": "https://issues.apache.org/jira/browse/THRIFT" + }, + "files": [ + "lib/nodejs/lib/thrift", + "lib/nodejs/README.md" + ], + "directories": { + "lib": "./lib/nodejs/lib/thrift" + }, + "main": "./lib/nodejs/lib/thrift", + "engines": { + "node": ">= 0.2.4" + }, + "dependencies": { + "node-int64": "~0.3.0", + "q": "1.0.x", + "ws": "~0.4.32" + }, + "devDependencies": { + "buffer-equals": "^1.0.3", + "commander": "2.1.x", + "connect": "2.7.x", + "istanbul": "^0.3.5", + "run-browser": "^2.0.1", + "tape": "~3.5.0" + }, + "scripts": { + "cover": "lib/nodejs/test/testAll.sh COVER", + "test": "lib/nodejs/test/testAll.sh" + } +} diff --git a/vendor/src/github.com/apache/thrift/sonar-project.properties b/vendor/src/github.com/apache/thrift/sonar-project.properties new file mode 100644 index 00000000..6e6c5db9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/sonar-project.properties @@ -0,0 +1,140 @@ +# Apache Thrift © The Apache Software Foundation +# http://www.apache.org/licenses/LICENSE-2.0 +# SPDX-License-Identifier: Apache-2.0 + +# File: sonar-project.properties +# Apache Thrift configuration file for Sonar https://analysis.apache.org/ +# Sonar is an open platform to manage code quality http://www.sonarsource.org/ + + +# required metadata +sonar.projectKey=org.apache.thrift +sonar.projectName=Apache Thrift +sonar.projectDescription= +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 all major languages. + +# Apache Thrift Version +sonar.projectVersion=0.10.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 +#sonar.projectDate=yyyy-MM-dd + +# TODO add website (sonar.projectUrl does not work) +#sonar.XXXX=http//thrift.apache.org + +# Some properties that will be inherited by the modules +sonar.sources=src +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 + +# cppcheck -q --error-exitcode=0 --xml . 2> cppcheck-result.xml +sonar.cxx.cppcheck.reportPath=cppcheck-result.xml + +# List of the module identifiers +sonar.modules=module1,module3,module4,module5,module6,module7,module8,module9,module10,module11,module12,module14 + + + +# we need sonar-runner 2.1 for this, see http://jira.codehaus.org/browse/SONARPLUGINS-2421 +#sonar.modules=module2 + +# delph plugin is broken +#sonar.modules=module13 + +# phpunit plugin is broken +#sonar.modules=module15 + +module1.sonar.projectName=Apache Thrift - Java Library +module1.sonar.projectBaseDir=lib/java +module1.sonar.sources=src +module1.sonar.tests=test +module1.sonar.binaries=build/libthrift-0.10.0.jar +module1.sonar.libraries=build/lib/*.jar +module1.sonar.language=java + +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/lib/*.jar,lib/java/build/libthrift-1.0.0.jar +module2.sonar.language=java + +module3.sonar.projectName=Apache Thrift - JavaScript Library +module3.sonar.projectBaseDir=lib/js +module3.sonar.sources=. +module3.sonar.exclusions=test/**/* +module3.sonar.language=js + +module4.sonar.projectName=Apache Thrift - JavaScript Tutorial +module4.sonar.projectBaseDir=tutorial/js +module4.sonar.sources=. +module4.sonar.language=web + +module5.sonar.projectName=Apache Thrift - C++ Library +module5.sonar.projectBaseDir=lib/cpp +module5.sonar.sources=src +module5.sonar.tests=test +module5.sonar.language=c++ + +module6.sonar.projectName=Apache Thrift - C++ Tutorial +module6.sonar.projectBaseDir=tutorial/cpp +module6.sonar.sources=. +module6.sonar.exclusions=gen-cpp/**/* +module6.sonar.language=c++ + +module7.sonar.projectName=Apache Thrift - C++ Cross Language Test +module7.sonar.projectBaseDir=test/cpp +module7.sonar.sources=src +module7.sonar.language=c++ + +module8.sonar.projectName=Apache Thrift - Compiler +module8.sonar.projectBaseDir=compiler/cpp +module8.sonar.sources=src +module8.sonar.language=c++ + +module9.sonar.projectName=Apache Thrift - Python Library +module9.sonar.projectBaseDir=lib/py +module9.sonar.sources=src +module9.sonar.language=py + +module10.sonar.projectName=Apache Thrift - Python Tutorial +module10.sonar.projectBaseDir=tutorial/py +module10.sonar.sources=. +module10.sonar.exclusions=gen-py/**/* +module10.sonar.language=py + +module11.sonar.projectName=Apache Thrift - Python Cross Language Test +module11.sonar.projectBaseDir=test/py +module11.sonar.sources=. +module11.sonar.exclusions=gen-*/**/* +module11.sonar.language=py + +module12.sonar.projectName=Apache Thrift - c_glib Library +module12.sonar.projectBaseDir=lib/c_glib +module12.sonar.sources=src +module12.sonar.language=c + +module13.sonar.projectName=Apache Thrift - Delphi Library +module13.sonar.projectBaseDir=lib/delphi +module13.sonar.sources=src +module13.sonar.tests=test +module13.sonar.language=delph + +module14.sonar.projectName=Apache Thrift - Flex (as3) Library +module14.sonar.projectBaseDir=lib/as3 +module14.sonar.sources=src +module14.sonar.language=flex + +module15.sonar.projectName=Apache Thrift - PHP Library +module15.sonar.projectBaseDir=lib/php +module15.sonar.sources=src +module15.sonar.language=php + +# TODO add some more languages here + diff --git a/vendor/src/github.com/apache/thrift/test/AnnotationTest.thrift b/vendor/src/github.com/apache/thrift/test/AnnotationTest.thrift new file mode 100644 index 00000000..191995ac --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/AnnotationTest.thrift @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +typedef list ( cpp.template = "std::list" ) int_linked_list + +struct foo { + 1: i32 bar ( presence = "required" ); + 2: i32 baz ( presence = "manual", cpp.use_pointer = "", ); + 3: i32 qux; + 4: i32 bop; +} ( + cpp.type = "DenseFoo", + python.type = "DenseFoo", + java.final = "", + annotation.without.value, +) + +exception foo_error { + 1: i32 error_code ( foo="bar" ) + 2: string error_msg +} (foo = "bar") + +typedef string ( unicode.encoding = "UTF-16" ) non_latin_string (foo="bar") +typedef list< double ( cpp.fixed_point = "16" ) > tiny_float_list + +enum weekdays { + SUNDAY ( weekend = "yes" ), + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY ( weekend = "yes" ) +} (foo.bar="baz") + +/* Note that annotations on senum values are not supported. */ +senum seasons { + "Spring", + "Summer", + "Fall", + "Winter" +} ( foo = "bar" ) + +service foo_service { + void foo() ( foo = "bar" ) +} (a.b="c") + diff --git a/vendor/src/github.com/apache/thrift/test/BrokenConstants.thrift b/vendor/src/github.com/apache/thrift/test/BrokenConstants.thrift new file mode 100644 index 00000000..c5aab4ab --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/BrokenConstants.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. + */ + +const i64 myint = 68719476736 +const i64 broken = 9876543210987654321 // A little over 2^63 + +enum foo { + bar = 68719476736 +} diff --git a/vendor/src/github.com/apache/thrift/test/ConstantsDemo.thrift b/vendor/src/github.com/apache/thrift/test/ConstantsDemo.thrift new file mode 100644 index 00000000..b99bdb2f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/ConstantsDemo.thrift @@ -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. + */ + +namespace cpp yozone + +struct thing { + 1: i32 hello, + 2: i32 goodbye +} + +enum enumconstants { + ONE = 1, + TWO = 2 +} + +// struct thing2 { +// /** standard docstring */ +// 1: enumconstants val = TWO +// } + +typedef i32 myIntType +const myIntType myInt = 3 + +//const map GEN_ENUM_NAMES = {ONE : "HOWDY", TWO: "PARTNER"} + +const i32 hex_const = 0x0001F +const i32 negative_hex_constant = -0x0001F + +const i32 GEN_ME = -3523553 +const double GEn_DUB = 325.532 +const double GEn_DU = 085.2355 +const string GEN_STRING = "asldkjasfd" + +const double e10 = 1e10 // fails with 0.9.3 and earlier +const double e11 = -1e10 + +const map GEN_MAP = { 35532 : 233, 43523 : 853 } +const list GEN_LIST = [ 235235, 23598352, 3253523 ] + +const map> GEN_MAPMAP = { 235 : { 532 : 53255, 235:235}} + +const map GEN_MAP2 = { "hello" : 233, "lkj98d" : 853, 'lkjsdf' : 098325 } + +const thing GEN_THING = { 'hello' : 325, 'goodbye' : 325352 } + +const map GEN_WHAT = { 35 : { 'hello' : 325, 'goodbye' : 325352 } } + +const set GEN_SET = [ 235, 235, 53235 ] + +exception Blah { + 1: i32 bing } + +exception Gak {} + +service yowza { + void blingity(), + i32 blangity() throws (1: Blah hoot ) +} diff --git a/vendor/src/github.com/apache/thrift/test/DebugProtoTest.thrift b/vendor/src/github.com/apache/thrift/test/DebugProtoTest.thrift new file mode 100644 index 00000000..b6a76595 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/DebugProtoTest.thrift @@ -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. + */ + +namespace c_glib TTest +namespace cpp thrift.test.debug +namespace java thrift.test +namespace rb thrift.test + +struct Doubles { + 1: double nan, + 2: double inf, + 3: double neginf, + 4: double repeating, + 5: double big, + 6: double tiny, + 7: double zero, + 8: double negzero, +} + +struct OneOfEach { + 1: bool im_true, + 2: bool im_false, + 3: i8 a_bite = 0x7f, + 4: i16 integer16 = 0x7fff, + 5: i32 integer32, + 6: i64 integer64 = 10000000000, + 7: double double_precision, + 8: string some_characters, + 9: string zomg_unicode, + 10: bool what_who, + 11: binary base64, + 12: list byte_list = [1, 2, 3], + 13: list i16_list = [1,2,3], + 14: list i64_list = [1,2,3] +} + +struct Bonk { + 1: i32 type, + 2: string message, +} + +struct Nesting { + 1: Bonk my_bonk, + 2: OneOfEach my_ooe, +} + +struct HolyMoley { + 1: list big, + 2: set (python.immutable = "")> contain, + 3: map> bonks, +} + +struct Backwards { + 2: i32 first_tag2, + 1: i32 second_tag1, +} + +struct Empty { +} ( + python.immutable = "", +) + +struct Wrapper { + 1: Empty foo +} ( + python.immutable = "", +) + +struct RandomStuff { + 1: i32 a, + 2: i32 b, + 3: i32 c, + 4: i32 d, + 5: list myintlist, + 6: map maps, + 7: i64 bigint, + 8: double triple, +} + +struct Base64 { + 1: i32 a, + 2: binary b1, + 3: binary b2, + 4: binary b3, + 5: binary b4, + 6: binary b5, + 7: binary b6, +} + +struct CompactProtoTestStruct { + // primitive fields + 1: i8 a_byte; + 2: i16 a_i16; + 3: i32 a_i32; + 4: i64 a_i64; + 5: double a_double; + 6: string a_string; + 7: binary a_binary; + 8: bool true_field; + 9: bool false_field; + 10: Empty empty_struct_field; + + // primitives in lists + 11: list byte_list; + 12: list i16_list; + 13: list i32_list; + 14: list i64_list; + 15: list double_list; + 16: list string_list; + 17: list binary_list; + 18: list boolean_list; + 19: list struct_list; + + // primitives in sets + 20: set byte_set; + 21: set i16_set; + 22: set i32_set; + 23: set i64_set; + 24: set double_set; + 25: set string_set; + 26: set binary_set; + 27: set boolean_set; + 28: set struct_set; + + // maps + // primitives as keys + 29: map byte_byte_map; + 30: map i16_byte_map; + 31: map i32_byte_map; + 32: map i64_byte_map; + 33: map double_byte_map; + 34: map string_byte_map; + 35: map binary_byte_map; + 36: map boolean_byte_map; + // primitives as values + 37: map byte_i16_map; + 38: map byte_i32_map; + 39: map byte_i64_map; + 40: map byte_double_map; + 41: map byte_string_map; + 42: map byte_binary_map; + 43: map byte_boolean_map; + // collections as keys + 44: map (python.immutable = ""), i8> list_byte_map; + 45: map (python.immutable = ""), i8> set_byte_map; + 46: map (python.immutable = ""), i8> map_byte_map; + // collections as values + 47: map> byte_map_map; + 48: map> byte_set_map; + 49: map> byte_list_map; +} + +// To be used to test the serialization of an empty map +struct SingleMapTestStruct { + 1: required map i32_map; +} + +const CompactProtoTestStruct COMPACT_TEST = { + 'a_byte' : 127, + 'a_i16' : 32000, + 'a_i32' : 1000000000, + 'a_i64' : 0xffffffffff, + 'a_double' : 5.6789, + 'a_string' : "my string", +//'a_binary,' + 'true_field' : 1, + 'false_field' : 0, + 'empty_struct_field' : {}, + 'byte_list' : [-127, -1, 0, 1, 127], + 'i16_list' : [-1, 0, 1, 0x7fff], + 'i32_list' : [-1, 0, 0xff, 0xffff, 0xffffff, 0x7fffffff], + 'i64_list' : [-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff], + 'double_list' : [0.1, 0.2, 0.3], + 'string_list' : ["first", "second", "third"], +//'binary_list,' + 'boolean_list' : [1, 1, 1, 0, 0, 0], + 'struct_list' : [{}, {}], + 'byte_set' : [-127, -1, 0, 1, 127], + 'i16_set' : [-1, 0, 1, 0x7fff], + 'i32_set' : [1, 2, 3], + 'i64_set' : [-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff], + 'double_set' : [0.1, 0.2, 0.3], + 'string_set' : ["first", "second", "third"], +//'binary_set,' + 'boolean_set' : [1, 0], + 'struct_set' : [{}], + 'byte_byte_map' : {1 : 2}, + 'i16_byte_map' : {1 : 1, -1 : 1, 0x7fff : 1}, + 'i32_byte_map' : {1 : 1, -1 : 1, 0x7fffffff : 1}, + 'i64_byte_map' : {0 : 1, 1 : 1, -1 : 1, 0x7fffffffffffffff : 1}, + 'double_byte_map' : {-1.1 : 1, 1.1 : 1}, + 'string_byte_map' : {"first" : 1, "second" : 2, "third" : 3, "" : 0}, +//'binary_byte_map,' + 'boolean_byte_map' : {1 : 1, 0 : 0}, + 'byte_i16_map' : {1 : 1, 2 : -1, 3 : 0x7fff}, + 'byte_i32_map' : {1 : 1, 2 : -1, 3 : 0x7fffffff}, + 'byte_i64_map' : {1 : 1, 2 : -1, 3 : 0x7fffffffffffffff}, + 'byte_double_map' : {1 : 0.1, 2 : -0.1, 3 : 1000000.1}, + 'byte_string_map' : {1 : "", 2 : "blah", 3 : "loooooooooooooong string"}, +//'byte_binary_map,' + 'byte_boolean_map' : {1 : 1, 2 : 0}, + 'list_byte_map' : {[1, 2, 3] : 1, [0, 1] : 2, [] : 0}, + 'set_byte_map' : {[1, 2, 3] : 1, [0, 1] : 2, [] : 0}, + 'map_byte_map' : {{1 : 1} : 1, {2 : 2} : 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]}, +} + + +const i32 MYCONST = 2 + + +exception ExceptionWithAMap { + 1: string blah; + 2: map map_field; +} + +service ServiceForExceptionWithAMap { + void methodThatThrowsAnException() throws (1: ExceptionWithAMap xwamap); +} + +service Srv { + i32 Janky(1: i32 arg); + + // return type only methods + + void voidMethod(); + i32 primitiveMethod(); + CompactProtoTestStruct structMethod(); + + void methodWithDefaultArgs(1: i32 something = MYCONST); + + oneway void onewayMethod(); + + bool declaredExceptionMethod(1: bool shouldThrow) throws (1: ExceptionWithAMap xwamap); +} + +service Inherited extends Srv { + i32 identity(1: i32 arg) +} + +service EmptyService {} + +// The only purpose of this thing is to increase the size of the generated code +// so that ZlibTest has more highly compressible data to play with. +struct BlowUp { + 1: map(python.immutable = ""),set (python.immutable = "")>> b1; + 2: map(python.immutable = ""),set (python.immutable = "")>> b2; + 3: map(python.immutable = ""),set (python.immutable = "")>> b3; + 4: map(python.immutable = ""),set (python.immutable = "")>> b4; +} + + +struct ReverseOrderStruct { + 4: string first; + 3: i16 second; + 2: i32 third; + 1: i64 fourth; +} + +service ReverseOrderService { + void myMethod(4: string first, 3: i16 second, 2: i32 third, 1: i64 fourth); +} + +enum SomeEnum { + ONE = 1 + TWO = 2 +} + +/** This is a docstring on a constant! */ +const SomeEnum MY_SOME_ENUM = SomeEnum.ONE + +const SomeEnum MY_SOME_ENUM_1 = 1 +/*const SomeEnum MY_SOME_ENUM_2 = 7*/ + +const map MY_ENUM_MAP = { + SomeEnum.ONE : SomeEnum.TWO +} + +struct StructWithSomeEnum { + 1: SomeEnum blah; +} + +const map EXTRA_CRAZY_MAP = { + SomeEnum.ONE : {"blah" : SomeEnum.TWO} +} + +union TestUnion { + /** + * A doc string + */ + 1: string string_field; + 2: i32 i32_field; + 3: OneOfEach struct_field; + 4: list struct_list; + 5: i32 other_i32_field; + 6: SomeEnum enum_field; + 7: set i32_set; + 8: map i32_map; +} + +union TestUnionMinusStringField { + 2: i32 i32_field; + 3: OneOfEach struct_field; + 4: list struct_list; + 5: i32 other_i32_field; + 6: SomeEnum enum_field; + 7: set i32_set; + 8: map i32_map; +} + +union ComparableUnion { + 1: string string_field; + 2: binary binary_field; +} + +struct StructWithAUnion { + 1: TestUnion test_union; +} + +struct PrimitiveThenStruct { + 1: i32 blah; + 2: i32 blah2; + 3: Backwards bw; +} + +typedef map SomeMap + +struct StructWithASomemap { + 1: required SomeMap somemap_field; +} + +struct BigFieldIdStruct { + 1: string field1; + 45: string field2; +} + +struct BreaksRubyCompactProtocol { + 1: string field1; + 2: BigFieldIdStruct field2; + 3: i32 field3; +} + +struct TupleProtocolTestStruct { + optional i32 field1; + optional i32 field2; + optional i32 field3; + optional i32 field4; + optional i32 field5; + optional i32 field6; + optional i32 field7; + optional i32 field8; + optional i32 field9; + optional i32 field10; + optional i32 field11; + optional i32 field12; +} + +struct ListDoublePerf { + 1: list field; +} diff --git a/vendor/src/github.com/apache/thrift/test/DenseLinkingTest.thrift b/vendor/src/github.com/apache/thrift/test/DenseLinkingTest.thrift new file mode 100644 index 00000000..3a5b9574 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/DenseLinkingTest.thrift @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* +../compiler/cpp/thrift -gen cpp:dense DebugProtoTest.thrift +../compiler/cpp/thrift -gen cpp:dense DenseLinkingTest.thrift +g++ -Wall -g -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \ + DebugProtoTest.cpp gen-cpp/DebugProtoTest_types.cpp \ + gen-cpp/DenseLinkingTest_types.cpp \ + ../lib/cpp/.libs/libthrift.a -o DebugProtoTest +./DebugProtoTest +*/ + +/* +The idea of this test is that everything is structurally identical to DebugProtoTest. +If I messed up the naming of the reflection local typespecs, +then compiling this should give errors because of doubly defined symbols. +*/ + +namespace cpp thrift.test +namespace java thrift.test + +struct OneOfEachZZ { + 1: bool im_true, + 2: bool im_false, + 3: byte a_bite, + 4: i16 integer16, + 5: i32 integer32, + 6: i64 integer64, + 7: double double_precision, + 8: string some_characters, + 9: string zomg_unicode, + 10: bool what_who, +} + +struct BonkZZ { + 1: i32 type, + 2: string message, +} + +struct NestingZZ { + 1: BonkZZ my_bonk, + 2: OneOfEachZZ my_ooe, +} + +struct HolyMoleyZZ { + 1: list big, + 2: set> contain, + 3: map> bonks, +} + +struct BackwardsZZ { + 2: i32 first_tag2, + 1: i32 second_tag1, +} + +struct EmptyZZ { +} + +struct WrapperZZ { + 1: EmptyZZ foo +} + +struct RandomStuffZZ { + 1: i32 a, + 2: i32 b, + 3: i32 c, + 4: i32 d, + 5: list myintlist, + 6: map maps, + 7: i64 bigint, + 8: double triple, +} + +service Srv { + i32 Janky(1: i32 arg) +} + +service UnderscoreSrv { + i64 some_rpc_call(1: string message) +} + diff --git a/vendor/src/github.com/apache/thrift/test/DocTest.thrift b/vendor/src/github.com/apache/thrift/test/DocTest.thrift new file mode 100644 index 00000000..d702b2c2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/DocTest.thrift @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Program doctext. + * + * Seriously, this is the documentation for this whole program. + */ + +namespace java thrift.test +namespace cpp thrift.test + +// C++ comment +/* c style comment */ + +# the new unix comment + +/** Some doc text goes here. Wow I am [nesting these] (no more nesting.) */ +enum Numberz +{ + + /** This is how to document a parameter */ + ONE = 1, + + /** And this is a doc for a parameter that has no specific value assigned */ + TWO, + + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +/** This is how you would do a typedef doc */ +typedef i64 UserId + +/** And this is where you would document a struct */ +struct Xtruct +{ + + /** And the members of a struct */ + 1: string string_thing + + /** doct text goes before a comma */ + 4: i8 byte_thing, + + 9: i32 i32_thing, + 11: i64 i64_thing +} + +/** + * You can document constants now too. Yeehaw! + */ +const i32 INT32CONSTANT = 9853 +const i16 INT16CONSTANT = 1616 +/** Everyone get in on the docu-action! */ +const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} + +struct Xtruct2 +{ + 1: i8 byte_thing, + 2: Xtruct struct_thing, + 3: i32 i32_thing +} + +/** Struct insanity */ +struct Insanity +{ + + /** This is doc for field 1 */ + 1: map userMap, + + /** And this is doc for field 2 */ + 2: list xtructs +} + +exception Xception { + 1: i32 errorCode, + 2: string message +} + +exception Xception2 { + 1: i32 errorCode, + 2: Xtruct struct_thing +} + +/* C1 */ +/** Doc */ +/* C2 */ +/* C3 */ +struct EmptyStruct {} + +struct OneField { + 1: EmptyStruct field +} + +/** This is where you would document a Service */ +service ThriftTest +{ + + /** And this is how you would document functions in a service */ + void testVoid(), + string testString(1: string thing), + i8 testByte(1: byte thing), + i32 testI32(1: i32 thing), + + /** Like this one */ + i64 testI64(1: i64 thing), + double testDouble(1: double thing), + Xtruct testStruct(1: Xtruct thing), + Xtruct2 testNest(1: Xtruct2 thing), + map testMap(1: map thing), + set testSet(1: set thing), + list testList(1: list thing), + + /** This is an example of a function with params documented */ + Numberz testEnum( + + /** This param is a thing */ + 1: Numberz thing + + ), + + UserId testTypedef(1: UserId thing), + + map> testMapMap(1: i32 hello), + + /* So you think you've got this all worked, out eh? */ + map> testInsanity(1: Insanity argument), + +} + +/// This style of Doxy-comment doesn't work. +typedef i32 SorryNoGo + +/** + * This is a trivial example of a multiline docstring. + */ +typedef i32 TrivialMultiLine + +/** + * This is the canonical example + * of a multiline docstring. + */ +typedef i32 StandardMultiLine + +/** + * The last line is non-blank. + * I said non-blank! */ +typedef i32 LastLine + +/** Both the first line + * are non blank. ;-) + * and the last line */ +typedef i32 FirstAndLastLine + +/** + * INDENTED TITLE + * The text is less indented. + */ +typedef i32 IndentedTitle + +/** First line indented. + * Unfortunately, this does not get indented. + */ +typedef i32 FirstLineIndent + + +/** + * void code_in_comment() { + * printf("hooray code!"); + * } + */ +typedef i32 CodeInComment + + /** + * Indented Docstring. + * This whole docstring is indented. + * This line is indented further. + */ +typedef i32 IndentedDocstring + +/** Irregular docstring. + * We will have to punt + * on this thing */ +typedef i32 Irregular1 + +/** + * note the space + * before these lines +* but not this + * one + */ +typedef i32 Irregular2 + +/** +* Flush against +* the left. +*/ +typedef i32 Flush + +/** + No stars in this one. + It should still work fine, though. + Including indenting. + */ +typedef i32 NoStars + +/** Trailing whitespace +Sloppy trailing whitespace +is truncated. */ +typedef i32 TrailingWhitespace + +/** + * This is a big one. + * + * We'll have some blank lines in it. + * + * void as_well_as(some code) { + * puts("YEEHAW!"); + * } + */ +typedef i32 BigDog + +/** +* +* +*/ +typedef i32 TotallyDegenerate + +/**no room for newline here*/ + +/* * / */ +typedef i32 TestFor3501a + +/** + * / + */ +typedef i32 TestFor3501b + + +/* Comment-end tokens can of course have more than one asterisk */ +struct TestFor3709_00 { /* ? */ 1: i32 foo } +/* Comment-end tokens can of course have more than one asterisk **/ +struct TestFor3709_01 { /* ? */ 1: i32 foo } +/* Comment-end tokens can of course have more than one asterisk ***/ +struct TestFor3709_02 { /* ? */ 1: i32 foo } +/** Comment-end tokens can of course have more than one asterisk */ +struct TestFor3709_03 { /* ? */ 1: i32 foo } +/** Comment-end tokens can of course have more than one asterisk **/ +struct TestFor3709_04 { /* ? */ 1: i32 foo } +/** Comment-end tokens can of course have more than one asterisk ***/ +struct TestFor3709_05 { /* ? */ 1: i32 foo } +/*** Comment-end tokens can of course have more than one asterisk */ +struct TestFor3709_06 { /* ? */ 1: i32 foo } +/*** Comment-end tokens can of course have more than one asterisk **/ +struct TestFor3709_07 { /* ? */ 1: i32 foo } +/*** Comment-end tokens can of course have more than one asterisk ***/ +struct TestFor3709_08 { /* ? */ 1: i32 foo } + +struct TestFor3709 { + /** This is a comment */ + 1: required string id, + /** This is also a comment **/ + 2: required string typeId, + /** Yet another comment! */ + 3: required i32 endTimestamp +} + + +/* THE END */ diff --git a/vendor/src/github.com/apache/thrift/test/EnumTest.thrift b/vendor/src/github.com/apache/thrift/test/EnumTest.thrift new file mode 100644 index 00000000..f38cec30 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/EnumTest.thrift @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +namespace c_glib TTest + +enum MyEnum1 { + ME1_0 = 0, + ME1_1 = 1, + ME1_2, + ME1_3, + ME1_5 = 5, + ME1_6, +} + +enum MyEnum2 { + ME2_0, + ME2_1, + ME2_2, +} + +enum MyEnum2_again { + // enum value identifiers may appear again in another enum type + ME0_1, + ME1_1, + ME2_1, + ME3_1, +} + +enum MyEnum3 { + ME3_0, + ME3_1, + ME3_N2 = -2, + ME3_N1, + ME3_D0, + ME3_D1, + ME3_9 = 9, + ME3_10, +} + +enum MyEnum4 { + ME4_A = 0x7ffffffd + ME4_B + ME4_C + // attempting to define another enum value here fails + // with an overflow error, as we overflow values that can be + // represented with an i32. +} + +enum MyEnum5 { + e1 // fails with 0.9.3 and earlier + e2 = 42 // fails with 0.9.3 and earlier +} + +struct MyStruct { + 1: MyEnum2 me2_2 = MyEnum1.ME2_2 + 2: MyEnum3 me3_n2 = MyEnum3.ME3_N2 + 3: MyEnum3 me3_d1 = MyEnum3.ME3_D1 +} + +struct EnumTestStruct { + 1: MyEnum3 a_enum; + 2: list enum_list; + 3: set enum_set; + 4: map enum_enum_map; + // collections as keys + 44: map (python.immutable = ""), MyEnum3> list_enum_map; + 45: map (python.immutable = ""), MyEnum3> set_enum_map; + 46: map (python.immutable = ""), MyEnum3> map_enum_map; + // collections as values + 47: map> enum_map_map; + 48: map> enum_set_map; + 49: map> enum_list_map; +} + +const EnumTestStruct ENUM_TEST = { + 'a_enum': MyEnum3.ME3_D1, + 'enum_list': [MyEnum3.ME3_D1, MyEnum3.ME3_0, MyEnum3.ME3_N2], + 'enum_set': [MyEnum3.ME3_D1, MyEnum3.ME3_N1], + 'enum_enum_map': {MyEnum3.ME3_D1: MyEnum3.ME3_0, MyEnum3.ME3_0: MyEnum3.ME3_D1}, + 'list_enum_map': {[MyEnum3.ME3_D1, MyEnum3.ME3_0]: MyEnum3.ME3_0, [MyEnum3.ME3_D1]: MyEnum3.ME3_0, [MyEnum3.ME3_0]: MyEnum3.ME3_D1}, + 'set_enum_map': {[MyEnum3.ME3_D1, MyEnum3.ME3_0]: MyEnum3.ME3_0, [MyEnum3.ME3_D1]: MyEnum3.ME3_0}, + 'map_enum_map': {{MyEnum3.ME3_N1: MyEnum3.ME3_10}: MyEnum3.ME3_1}, + 'enum_map_map': {MyEnum3.ME3_N1: {MyEnum3.ME3_D1: MyEnum3.ME3_D1}}, + 'enum_set_map': {MyEnum3.ME3_N2: [MyEnum3.ME3_D1, MyEnum3.ME3_N1], MyEnum3.ME3_10: [MyEnum3.ME3_D1, MyEnum3.ME3_N1]}, + 'enum_list_map': {MyEnum3.ME3_D1: [MyEnum3.ME3_10], MyEnum3.ME3_0: [MyEnum3.ME3_9, MyEnum3.ME3_10]}, +} + +service EnumTestService { + MyEnum3 testEnum(1: MyEnum3 enum1), + list testEnumList(1: list enum1), + set testEnumSet(1: set enum1), + map testEnumMap(1: map enum1), + EnumTestStruct testEnumStruct(1: EnumTestStruct enum1), +} diff --git a/vendor/src/github.com/apache/thrift/test/FullCamelTest.thrift b/vendor/src/github.com/apache/thrift/test/FullCamelTest.thrift new file mode 100644 index 00000000..cee4dd8f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/FullCamelTest.thrift @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace java thrift.test.fullcamel + +struct OneOfEachZZ { + 1: bool im_true, + 2: bool im_false, + 3: byte a_bite, + 4: i16 integer16, + 5: i32 integer32, + 6: i64 integer64, + 7: double double_precision, + 8: string some_characters, + 9: string zomg_unicode, + 10: bool what_who, +} + +service UnderscoreSrv { + i64 some_rpc_call(1: string message) +} + diff --git a/vendor/src/github.com/apache/thrift/test/Include.thrift b/vendor/src/github.com/apache/thrift/test/Include.thrift new file mode 100644 index 00000000..562319b2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/Include.thrift @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +include "ThriftTest.thrift" + +struct IncludeTest { + 1: required ThriftTest.Bools bools +} \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/test/JavaBeansTest.thrift b/vendor/src/github.com/apache/thrift/test/JavaBeansTest.thrift new file mode 100644 index 00000000..b6c3ea86 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/JavaBeansTest.thrift @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace java thrift.test + +struct OneOfEachBeans { + 1: bool boolean_field, + 2: byte a_bite, + 3: i16 integer16, + 4: i32 integer32, + 5: i64 integer64, + 6: double double_precision, + 7: string some_characters, + 8: binary base64, + 9: list byte_list, + 10: list i16_list, + 11: list i64_list +} + + +service Service { + i64 mymethod(i64 blah); +} \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/test/JavaTypes.thrift b/vendor/src/github.com/apache/thrift/test/JavaTypes.thrift new file mode 100644 index 00000000..fcb0ab2a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/JavaTypes.thrift @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace java thrift.test + +struct Integer { + 1: i32 val +} + +struct String { + 1: string val +} + +struct Boolean { + 1: bool val +} + +struct Double { + 1: double val +} + +struct Long { + 1: i64 val +} + +struct Byte { + 1: byte val +} + +struct Float { + 1: double val +} + +struct List { + 1: list vals +} + +struct ArrayList { + 1: list vals +} + +struct SortedMap { + 1: map vals +} + +struct TreeMap { + 1: map vals +} + +struct HashMap { + 1: map vals +} + +struct Map { + 1: map vals +} + +struct Object { + 1: Integer integer, + 2: String str, + 3: Boolean boolean_field, + 4: Double dbl, + 5: Byte bite, + 6: map intmap, + 7: Map somemap, +} + +exception Exception { + 1: string msg +} + +service AsyncNonblockingService { + Object mymethod( + 1: Integer integer, + 2: String str, + 3: Boolean boolean_field, + 4: Double dbl, + 5: Byte bite, + 6: map intmap, + 7: Map somemap, + ) throws (1:Exception ex); +} diff --git a/vendor/src/github.com/apache/thrift/test/JsDeepConstructorTest.thrift b/vendor/src/github.com/apache/thrift/test/JsDeepConstructorTest.thrift new file mode 100644 index 00000000..ef5126fa --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/JsDeepConstructorTest.thrift @@ -0,0 +1,18 @@ +struct Simple { + 1: string value +} + +struct Complex { + 1: Simple struct_field + 2: list struct_list_field + 3: set struct_set_field + 4: map struct_map_field + 5: list>>> struct_nested_containers_field + 6: map> > struct_nested_containers_field2 + 7: list> list_of_list_field + 8: list>> list_of_list_of_list_field +} + +struct ComplexList { + 1: list struct_list_field; +} diff --git a/vendor/src/github.com/apache/thrift/test/Makefile.am b/vendor/src/github.com/apache/thrift/test/Makefile.am new file mode 100644 index 00000000..ff780c39 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/Makefile.am @@ -0,0 +1,150 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = features +PRECROSS_TARGET = + +if WITH_C_GLIB +SUBDIRS += c_glib +PRECROSS_TARGET += precross-c_glib +endif + +if WITH_MONO +SUBDIRS += csharp +PRECROSS_TARGET += precross-csharp +endif + +if WITH_CPP +SUBDIRS += cpp +PRECROSS_TARGET += precross-cpp +endif + +if WITH_PERL +SUBDIRS += perl +PRECROSS_TARGET += precross-perl +endif + +if WITH_PHP +SUBDIRS += php +PRECROSS_TARGET += precross-php +endif + +if WITH_DART +SUBDIRS += dart +PRECROSS_TARGET += precross-dart +endif + +if WITH_PYTHON +SUBDIRS += py +PRECROSS_TARGET += precross-py +SUBDIRS += py.tornado +if WITH_TWISTED_TEST +SUBDIRS += py.twisted +endif +endif + +if WITH_RUBY +SUBDIRS += rb +PRECROSS_TARGET += precross-rb +endif + +if WITH_HASKELL +SUBDIRS += hs +endif + +if WITH_HAXE +SUBDIRS += haxe +endif + +if WITH_GO +SUBDIRS += go +PRECROSS_TARGET += precross-go +endif + +if WITH_ERLANG +SUBDIRS += erl +PRECROSS_TARGET += precross-erl +endif + +if WITH_LUA +SUBDIRS += lua +PRECROSS_TARGET += precross-lua +endif + +# +# generate html for ThriftTest.thrift +# +check-local: + $(top_builddir)/compiler/cpp/thrift --gen html -r $(top_srcdir)/test/ThriftTest.thrift + +clean-local: + rm -rf $(top_srcdir)/test/gen-html + +EXTRA_DIST = \ + audit \ + crossrunner \ + keys \ + c_glib \ + cpp \ + dart \ + erl \ + hs \ + lua \ + ocaml \ + perl \ + php \ + py \ + py.twisted \ + py.tornado \ + rb \ + threads \ + AnnotationTest.thrift \ + BrokenConstants.thrift \ + ConstantsDemo.thrift \ + DebugProtoTest.thrift \ + DenseLinkingTest.thrift \ + DocTest.thrift \ + EnumTest.thrift \ + FullCamelTest.thrift \ + Include.thrift \ + JavaBeansTest.thrift \ + JavaTypes.thrift \ + JsDeepConstructorTest.thrift \ + ManyOptionals.thrift \ + ManyTypedefs.thrift \ + NameConflictTest.thrift \ + OptionalRequiredTest.thrift \ + Recursive.thrift \ + ReuseObjects.thrift \ + SmallTest.thrift \ + StressTest.thrift \ + ThriftTest.thrift \ + TypedefTest.thrift \ + known_failures_Linux.json \ + test.py \ + tests.json \ + rebuild_known_failures.sh \ + result.js \ + index.html \ + README.md \ + valgrind.suppress + +precross-%: + $(MAKE) -C $* precross +precross: $(PRECROSS_TARGET) diff --git a/vendor/src/github.com/apache/thrift/test/ManyOptionals.thrift b/vendor/src/github.com/apache/thrift/test/ManyOptionals.thrift new file mode 100644 index 00000000..3fb1d68b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/ManyOptionals.thrift @@ -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. + */ + +// The java codegenerator has a few different codepaths depending +// on how many optionals the struct has; this attempts to exercise +// them. + +namespace java thrift.test + +struct Opt4 { + 1: i32 def1; + 2: i32 def2; + 3: i32 def3; + 4: i32 def4; +} + +struct Opt13 { + 1: i32 def1; + 2: i32 def2; + 3: i32 def3; + 4: i32 def4; + 5: i32 def5; + 6: i32 def6; + 7: i32 def7; + 8: i32 def8; + 9: i32 def9; + 10: i32 def10; + 11: i32 def11; + 12: i32 def12; + 13: i32 def13; +} + +struct Opt30 { + 1: i32 def1; + 2: i32 def2; + 3: i32 def3; + 4: i32 def4; + 5: i32 def5; + 6: i32 def6; + 7: i32 def7; + 8: i32 def8; + 9: i32 def9; + 10: i32 def10; + 11: i32 def11; + 12: i32 def12; + 13: i32 def13; + 14: i32 def14; + 15: i32 def15; + 16: i32 def16; + 17: i32 def17; + 18: i32 def18; + 19: i32 def19; + 20: i32 def20; + 21: i32 def21; + 22: i32 def22; + 23: i32 def23; + 24: i32 def24; + 25: i32 def25; + 26: i32 def26; + 27: i32 def27; + 28: i32 def28; + 29: i32 def29; + 30: i32 def30; +} + +struct Opt64 { + 1: i32 def1; + 2: i32 def2; + 3: i32 def3; + 4: i32 def4; + 5: i32 def5; + 6: i32 def6; + 7: i32 def7; + 8: i32 def8; + 9: i32 def9; + 10: i32 def10; + 11: i32 def11; + 12: i32 def12; + 13: i32 def13; + 14: i32 def14; + 15: i32 def15; + 16: i32 def16; + 17: i32 def17; + 18: i32 def18; + 19: i32 def19; + 20: i32 def20; + 21: i32 def21; + 22: i32 def22; + 23: i32 def23; + 24: i32 def24; + 25: i32 def25; + 26: i32 def26; + 27: i32 def27; + 28: i32 def28; + 29: i32 def29; + 30: i32 def30; + 31: i32 def31; + 32: i32 def32; + 33: i32 def33; + 34: i32 def34; + 35: i32 def35; + 36: i32 def36; + 37: i32 def37; + 38: i32 def38; + 39: i32 def39; + 40: i32 def40; + 41: i32 def41; + 42: i32 def42; + 43: i32 def43; + 44: i32 def44; + 45: i32 def45; + 46: i32 def46; + 47: i32 def47; + 48: i32 def48; + 49: i32 def49; + 50: i32 def50; + 51: i32 def51; + 52: i32 def52; + 53: i32 def53; + 54: i32 def54; + 55: i32 def55; + 56: i32 def56; + 57: i32 def57; + 58: i32 def58; + 59: i32 def59; + 60: i32 def60; + 61: i32 def61; + 62: i32 def62; + 63: i32 def63; + 64: i32 def64; +} + +struct Opt80 { + 1: i32 def1; + 2: i32 def2; + 3: i32 def3; + 4: i32 def4; + 5: i32 def5; + 6: i32 def6; + 7: i32 def7; + 8: i32 def8; + 9: i32 def9; + 10: i32 def10; + 11: i32 def11; + 12: i32 def12; + 13: i32 def13; + 14: i32 def14; + 15: i32 def15; + 16: i32 def16; + 17: i32 def17; + 18: i32 def18; + 19: i32 def19; + 20: i32 def20; + 21: i32 def21; + 22: i32 def22; + 23: i32 def23; + 24: i32 def24; + 25: i32 def25; + 26: i32 def26; + 27: i32 def27; + 28: i32 def28; + 29: i32 def29; + 30: i32 def30; + 31: i32 def31; + 32: i32 def32; + 33: i32 def33; + 34: i32 def34; + 35: i32 def35; + 36: i32 def36; + 37: i32 def37; + 38: i32 def38; + 39: i32 def39; + 40: i32 def40; + 41: i32 def41; + 42: i32 def42; + 43: i32 def43; + 44: i32 def44; + 45: i32 def45; + 46: i32 def46; + 47: i32 def47; + 48: i32 def48; + 49: i32 def49; + 50: i32 def50; + 51: i32 def51; + 52: i32 def52; + 53: i32 def53; + 54: i32 def54; + 55: i32 def55; + 56: i32 def56; + 57: i32 def57; + 58: i32 def58; + 59: i32 def59; + 60: i32 def60; + 61: i32 def61; + 62: i32 def62; + 63: i32 def63; + 64: i32 def64; + 65: i32 def65; + 66: i32 def66; + 67: i32 def67; + 68: i32 def68; + 69: i32 def69; + 70: i32 def70; + 71: i32 def71; + 72: i32 def72; + 73: i32 def73; + 74: i32 def74; + 75: i32 def75; + 76: i32 def76; + 77: i32 def77; + 78: i32 def78; + 79: i32 def79; + 80: i32 def80; +} + diff --git a/vendor/src/github.com/apache/thrift/test/ManyTypedefs.thrift b/vendor/src/github.com/apache/thrift/test/ManyTypedefs.thrift new file mode 100644 index 00000000..d194b63c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/ManyTypedefs.thrift @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// This is to make sure you don't mess something up when you change typedef code. +// Generate it with the old and new thrift and make sure they are the same. +/* +rm -rf gen-* orig-* +mkdir old new +thrift --gen cpp --gen java --gen php --gen phpi --gen py --gen rb --gen xsd --gen perl --gen ocaml --gen erl --gen hs --strict ManyTypedefs.thrift +mv gen-* old +../compiler/cpp/thrift --gen cpp --gen java --gen php --gen phpi --gen py --gen rb --gen xsd --gen perl --gen ocaml --gen erl --gen hs --strict ManyTypedefs.thrift +mv gen-* new +diff -ur old new +rm -rf old new +# There should be no output. +*/ + +typedef i32 int32 +typedef list> biglist + +struct struct1 { + 1: int32 myint; + 2: biglist mylist; +} + +exception exception1 { + 1: biglist alist; + 2: struct1 mystruct; +} + +service AService { + struct1 method1(1: int32 myint) throws (1: exception1 exn); + biglist method2(); +} diff --git a/vendor/src/github.com/apache/thrift/test/NameConflictTest.thrift b/vendor/src/github.com/apache/thrift/test/NameConflictTest.thrift new file mode 100644 index 00000000..d3efb474 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/NameConflictTest.thrift @@ -0,0 +1,110 @@ +// Naming testcases, sepcifically for these tickets (but not limited to them) +// THRIFT-2508 Uncompileable C# code due to language keywords in IDL +// THRIFT-2557 error CS0542 member names cannot be the same as their enclosing type + + +struct using { + 1: double single + 2: double integer +} + +struct delegate { + 1: string partial + 2: delegate delegate +} + +struct get { + 1: bool sbyte +} + +struct partial { + 1: using using + 2: bool read + 3: bool write +} + +enum Maybe { + JUST = 1, + TRUE = 2, + FALSE = 3 +} + +enum Either { + LEFT = 1, + RIGHT = 2 +} + +struct foldr { + 1: string id +} + +struct of { + 1: string let + 2: string where +} + +struct ofOf { + 1: of Of +} + + +struct ClassAndProp { + 1: bool ClassAndProp + 2: bool ClassAndProp_ + 3: bool ClassAndProp__ + 4: bool ClassAndProper +} + +struct second_chance { + 1: bool SECOND_CHANCE + 2: bool SECOND_CHANCE_ + 3: bool SECOND_CHANCE__ + 4: bool SECOND_CHANCES +} + +struct NOW_EAT_THIS { + 1: bool now_eat_this + 2: bool now_eat_this_ + 3: bool now_eat_this__ + 4: bool now_eat_this_and_this +} + +struct TheEdgeCase { + 1: bool theEdgeCase + 2: bool theEdgeCase_ + 3: bool theEdgeCase__ + 4: bool TheEdgeCase + 5: bool TheEdgeCase_ + 6: bool TheEdgeCase__ +} + +struct Tricky_ { + 1: bool tricky + 2: bool Tricky +} + +struct Nested { + 1: ClassAndProp ClassAndProp + 2: second_chance second_chance + 3: NOW_EAT_THIS NOW_EAT_THIS + 4: TheEdgeCase TheEdgeCase + 5: Tricky_ Tricky_ + 6: Nested Nested +} + +exception Problem_ { + 1: bool problem + 2: bool Problem +} + + +service extern { + delegate event(1: partial get) + void Foo(1: Nested Foo_args) throws (1: Problem_ Foo_result) +} + +service qualified { + Maybe maybe(1: Maybe foldr) + Either either(1: foldr of) +} +// eof diff --git a/vendor/src/github.com/apache/thrift/test/OptionalRequiredTest.thrift b/vendor/src/github.com/apache/thrift/test/OptionalRequiredTest.thrift new file mode 100644 index 00000000..a608898f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/OptionalRequiredTest.thrift @@ -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. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +namespace c_glib TTest +namespace cpp thrift.test +namespace java thrift.test + +struct OldSchool { + 1: i16 im_int; + 2: string im_str; + 3: list> im_big; +} + +struct Simple { + 1: /* :) */ i16 im_default; + 2: required i16 im_required; + 3: optional i16 im_optional; +} + +struct Tricky1 { + 1: /* :) */ i16 im_default; +} + +struct Tricky2 { + 1: optional i16 im_optional; +} + +struct Tricky3 { + 1: required i16 im_required; +} + +struct OptionalDefault { + 1: optional i16 opt_int = 1234; + 2: optional string opt_str = "default"; +} + +struct Complex { + 1: i16 cp_default; + 2: required i16 cp_required; + 3: optional i16 cp_optional; + 4: map the_map; + 5: required Simple req_simp; + 6: optional Simple opt_simp; +} + +struct ManyOpt { + 1: optional i32 opt1; + 2: optional i32 opt2; + 3: optional i32 opt3; + 4: i32 def4; + 5: optional i32 opt5; + 6: optional i32 opt6; +} + +struct JavaTestHelper { + 1: required i32 req_int; + 2: optional i32 opt_int; + 3: required string req_obj; + 4: optional string opt_obj; + 5: required binary req_bin; + 6: optional binary opt_bin; +} + +struct Binaries { + 4: binary bin; + 5: required binary req_bin; + 6: optional binary opt_bin; +} diff --git a/vendor/src/github.com/apache/thrift/test/README.md b/vendor/src/github.com/apache/thrift/test/README.md new file mode 100644 index 00000000..0682f5d9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/README.md @@ -0,0 +1,185 @@ +# Apache Thrift - integration test suite + +This is the cross everything integration test suite for Apache Thrift. + +## Run + +### A. Using Make + +The test can be executed by: + + make cross + +This starts the [test.py](test.py) script which does the real cross test with +different transports, protocols and languages. + +Note that this skips any language that is not built locally. It also skips +tests that are known to be failing. If you need more control over which tests +to run, read following section. + +### B. Using test script directly + +Alternatively, you can invoke [test.py](test.py) directly. You need to run`make +precross` once before executing it for the first time. + +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 ... + make precross -j8 + test/test.py --server cpp,java --client nodejs + test/test.py --server nodejs --client cpp,java + +Another useful flag is --regex. For example, to run all tests that involve +Java TBinaryProtocol: + + test/test.py --regex "java.*binary" + +## Test case definition file + +The cross test cases are defined in [tests.json](tests.json). +The root element is collection of test target definitions. +Each test target definition looks like this: + + { + "name": "somelib", + + "client": { + "command": ["somelib_client_executable"], + "workdir": "somelib/bin", + "protocols": ["binary"], + "transports": ["buffered"], + "sockets": ["ip"], + }, + "server": { + "command": ["somelib_server_executable"], + "workdir": "somelib/bin", + "protocols": ["binary"], + "transports": ["buffered"], + "sockets": ["ip", "ip-ssl"], + } + } + +Either client or server definition or both should be present. + +Parameters that are common to both `client` and `server` can be put to target +definition root: + + { + "name": "somelib", + + "workdir": "somelib/bin", + "protocols": ["binary"], + "transports": ["buffered"], + "sockets": ["ip"], + + "client": { "command": ["somelib_client_executable"] }, + "server": { + "command": ["somelib_server_executable"], + "sockets": ["ip-ssl"] + } + } + +For the complete list of supported keys and their effect, see source code +comment at the opt of [crossrunner/collect.py](crossrunner/collect.py). + + +## List of known failures + +Since many cross tests currently fail (mainly due to partial incompatibility +around exception handling), the test script specifically report for "not known +before" failures. + +For this purpose, test cases known to (occasionally) fail are listed in +`known_failures_.json` where `` matches with python +`platform.system()` string. + +Currently, only Linux version is included. + +FYI, the file is initially generated by + + test/test.py --update-expected-failures=overwrite + +after a full test run, then repeatedly + + test/test.py --skip-known-failures + test/test.py --update-expected-failures=merge + +to update the known failures, run + + make fail + +## Test executable specification + +### Command line parameters + +Unit tests for languages are usually located under lib//test/ +cross language tests according to [ThriftTest.thrift](ThriftTest.thrift) shall be +provided for every language including executables with the following command +line interface: + +**Server command line interface:** + + $ ./cpp/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 + +**Client command line interface:** + + $ ./cpp/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 + +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. + +### Return code + +The return code (exit code) shall be 0 on success, or an integer in the range 1 - 255 on errors. +In order to signal failed tests, the return code shall be composed from these bits to indicate +failing tests: + + #define TEST_BASETYPES 1 // 0000 0001 + #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_TIMEOUT 128 // 1000 0000 + #define TEST_NOTUSED 48 // 0011 0000 (reserved bits) + +Tests that have not been executed at all count as errors. + +**Example:** + +During tests, the test client notices that some of the Struct tests fail. +Furthermore, due to some other problem none of the Exception tests is executed. +Therefore, the test client returns the code `10 = 2 | 8`, indicating the failure +of both test 2 (TEST_STRUCTS) and test 8 (TEST_EXCEPTIONS). + + +## SSL +Test Keys and Certificates are provided in multiple formats under the following +directory [test/keys](keys) diff --git a/vendor/src/github.com/apache/thrift/test/Recursive.thrift b/vendor/src/github.com/apache/thrift/test/Recursive.thrift new file mode 100644 index 00000000..c9825821 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/Recursive.thrift @@ -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. + */ + +struct RecTree { + 1: list children + 2: i16 item +} + +struct RecList { + 1: RecList & nextitem + 3: i16 item +} + +struct CoRec { + 1: CoRec2 & other +} + +struct CoRec2 { + 1: CoRec other +} + +struct VectorTest { + 1: list lister; +} + +service TestService +{ + RecTree echoTree(1:RecTree tree) + RecList echoList(1:RecList lst) + CoRec echoCoRec(1:CoRec item) +} diff --git a/vendor/src/github.com/apache/thrift/test/ReuseObjects.thrift b/vendor/src/github.com/apache/thrift/test/ReuseObjects.thrift new file mode 100644 index 00000000..2dd6c6ec --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/ReuseObjects.thrift @@ -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. + */ + +// The java codegenerator has option to reuse objects for deserialization + +namespace java thrift.test + +include "ThriftTest.thrift" + +struct Reuse { + 1: i32 val1; + 2: set val2; +} + diff --git a/vendor/src/github.com/apache/thrift/test/SmallTest.thrift b/vendor/src/github.com/apache/thrift/test/SmallTest.thrift new file mode 100644 index 00000000..d0821c7f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/SmallTest.thrift @@ -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. + */ + + +namespace rb TestNamespace + +struct Goodbyez { + 1: i32 val = 325; +} + +senum Thinger { + "ASDFKJ", + "r32)*F#@", + "ASDFLJASDF" +} + +struct BoolPasser { + 1: bool value = 1 +} + +struct Hello { + 1: i32 simple = 53, + 2: map complex = {23:532, 6243:632, 2355:532}, + 3: map> complexer, + 4: string words = "words", + 5: Goodbyez thinz = {'val' : 36632} +} + +const map> CMAP = { 235: {235:235}, 53:{53:53} } +const i32 CINT = 325; +const Hello WHOA = {'simple' : 532} + +exception Goodbye { + 1: i32 simple, + 2: map complex, + 3: map> complexer, +} + +service SmallService { + Thinger testThinger(1:Thinger bootz), + Hello testMe(1:i32 hello=64, 2: Hello wonk) throws (1: Goodbye g), + void testVoid() throws (1: Goodbye g), + i32 testI32(1:i32 boo) +} diff --git a/vendor/src/github.com/apache/thrift/test/StressTest.thrift b/vendor/src/github.com/apache/thrift/test/StressTest.thrift new file mode 100644 index 00000000..431811b8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/StressTest.thrift @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace cpp test.stress +namespace d thrift.test.stress +namespace go stress + +service Service { + + void echoVoid(), + i8 echoByte(1: i8 arg), + i32 echoI32(1: i32 arg), + i64 echoI64(1: i64 arg), + string echoString(1: string arg), + list echoList(1: list arg), + set echoSet(1: set arg), + map echoMap(1: map arg), +} + diff --git a/vendor/src/github.com/apache/thrift/test/ThriftTest.thrift b/vendor/src/github.com/apache/thrift/test/ThriftTest.thrift new file mode 100644 index 00000000..ca708737 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/ThriftTest.thrift @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +namespace c_glib TTest +namespace java thrift.test +namespace cpp thrift.test +namespace rb Thrift.Test +namespace perl ThriftTest +namespace csharp Thrift.Test +namespace js ThriftTest +namespace st 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 xsd test (uri = 'http://thrift.apache.org/ns/ThriftTest') + +// Presence of namespaces and sub-namespaces for which there is +// no generator should compile with warnings only +namespace noexist ThriftTest +namespace cpp.noexist ThriftTest + +namespace * thrift.test + +/** + * Docstring! + */ +enum Numberz +{ + ONE = 1, + TWO, + THREE, + FIVE = 5, + SIX, + EIGHT = 8 +} + +const Numberz myNumberz = Numberz.ONE; +// the following is expected to fail: +// const Numberz urNumberz = ONE; + +typedef i64 UserId + +struct Bonk +{ + 1: string message, + 2: i32 type +} + +typedef map MapType + +struct Bools { + 1: bool im_true, + 2: bool im_false, +} + +struct Xtruct +{ + 1: string string_thing, + 4: i8 byte_thing, + 9: i32 i32_thing, + 11: i64 i64_thing +} + +struct Xtruct2 +{ + 1: i8 byte_thing, // used to be byte, hence the name + 2: Xtruct struct_thing, + 3: i32 i32_thing +} + +struct Xtruct3 +{ + 1: string string_thing, + 4: i32 changed, + 9: i32 i32_thing, + 11: i64 i64_thing +} + + +struct Insanity +{ + 1: map userMap, + 2: list xtructs +} (python.immutable= "") + +struct CrazyNesting { + 1: string string_field, + 2: optional set set_field, + // Do not insert line break as test/go/Makefile.am is removing this line with pattern match + 3: required list (python.immutable = ""), map(python.immutable = "")> (python.immutable = "")>>>> list_field, + 4: binary binary_field +} + +union SomeUnion { + 1: map map_thing, + 2: string string_thing, + 3: i32 i32_thing, + 4: Xtruct3 xtruct_thing, + 5: Insanity insanity_thing +} + +exception Xception { + 1: i32 errorCode, + 2: string message +} + +exception Xception2 { + 1: i32 errorCode, + 2: Xtruct struct_thing +} + +struct EmptyStruct {} + +struct OneField { + 1: EmptyStruct field +} + +service ThriftTest +{ + /** + * Prints "testVoid()" and returns nothing. + */ + void testVoid(), + + /** + * Prints 'testString("%s")' with thing as '%s' + * @param string thing - the string to print + * @return string - returns the string 'thing' + */ + string testString(1: string thing), + + /** + * Prints 'testBool("%s")' where '%s' with thing as 'true' or 'false' + * @param bool thing - the bool data to print + * @return bool - returns the bool 'thing' + */ + bool testBool(1: bool thing), + + /** + * Prints 'testByte("%d")' with thing as '%d' + * The types i8 and byte are synonyms, use of i8 is encouraged, byte still exists for the sake of compatibility. + * @param byte thing - the i8/byte to print + * @return i8 - returns the i8/byte 'thing' + */ + i8 testByte(1: i8 thing), + + /** + * Prints 'testI32("%d")' with thing as '%d' + * @param i32 thing - the i32 to print + * @return i32 - returns the i32 'thing' + */ + i32 testI32(1: i32 thing), + + /** + * Prints 'testI64("%d")' with thing as '%d' + * @param i64 thing - the i64 to print + * @return i64 - returns the i64 'thing' + */ + i64 testI64(1: i64 thing), + + /** + * Prints 'testDouble("%f")' with thing as '%f' + * @param double thing - the double to print + * @return double - returns the double 'thing' + */ + double testDouble(1: double thing), + + /** + * Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data + * @param binary thing - the binary data to print + * @return binary - returns the binary 'thing' + */ + binary testBinary(1: binary thing), + + /** + * Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values + * @param Xtruct thing - the Xtruct to print + * @return Xtruct - returns the Xtruct 'thing' + */ + Xtruct testStruct(1: Xtruct thing), + + /** + * Prints 'testNest("{%s}")' where thing has been formatted into a string of the nested struct + * @param Xtruct2 thing - the Xtruct2 to print + * @return Xtruct2 - returns the Xtruct2 'thing' + */ + Xtruct2 testNest(1: Xtruct2 thing), + + /** + * 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' + */ + map testMap(1: map thing), + + /** + * 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' + */ + map testStringMap(1: map thing), + + /** + * 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' + */ + set testSet(1: set thing), + + /** + * 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' + */ + list testList(1: list thing), + + /** + * Prints 'testEnum("%d")' where thing has been formatted into it's numeric value + * @param Numberz thing - the Numberz to print + * @return Numberz - returns the Numberz 'thing' + */ + Numberz testEnum(1: Numberz thing), + + /** + * Prints 'testTypedef("%d")' with thing as '%d' + * @param UserId thing - the UserId to print + * @return UserId - returns the UserId 'thing' + */ + UserId testTypedef(1: UserId thing), + + /** + * Prints 'testMapMap("%d")' with hello as '%d' + * @param i32 hello - the i32 to print + * @return map> - returns a dictionary with these values: + * {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, } + */ + map> testMapMap(1: i32 hello), + + /** + * So you think you've got this all worked, out eh? + * + * Creates a the returned map with these values and prints it out: + * { 1 => { 2 => argument, + * 3 => argument, + * }, + * 2 => { 6 => , }, + * } + * @return map> - a map with the above values + */ + map> testInsanity(1: Insanity argument), + + /** + * Prints 'testMulti()' + * @param i8 arg0 - + * @param i32 arg1 - + * @param i64 arg2 - + * @param map arg3 - + * @param Numberz arg4 - + * @param UserId arg5 - + * @return Xtruct - returns an Xtruct with string_thing = "Hello2, byte_thing = arg0, i32_thing = arg1 + * and i64_thing = arg2 + */ + Xtruct testMulti(1: i8 arg0, 2: i32 arg1, 3: i64 arg2, 4: map arg3, 5: Numberz arg4, 6: UserId arg5), + + /** + * 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 + */ + 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 + * 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 do not throw anything + * @return Xtruct - an Xtruct with string_thing = arg1 + */ + Xtruct testMultiException(1: string arg0, 2: string arg1) throws(1: Xception err1, 2: Xception2 err2) + + /** + * Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d' + * sleep 'secondsToSleep' + * Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d' + * @param i32 secondsToSleep - the number of seconds to sleep + */ + oneway void testOneway(1:i32 secondsToSleep) +} + +service SecondService +{ + void blahBlah() + /** + * Prints 'testString("%s")' with thing as '%s' + * @param string thing - the string to print + * @return string - returns the string 'thing' + */ + string secondtestString(1: string thing), +} + +struct VersioningTestV1 { + 1: i32 begin_in_both, + 3: string old_string, + 12: i32 end_in_both +} + +struct VersioningTestV2 { + 1: i32 begin_in_both, + + 2: i32 newint, + 3: i8 newbyte, + 4: i16 newshort, + 5: i64 newlong, + 6: double newdouble + 7: Bonk newstruct, + 8: list newlist, + 9: set newset, + 10: map newmap, + 11: string newstring, + 12: i32 end_in_both +} + +struct ListTypeVersioningV1 { + 1: list myints; + 2: string hello; +} + +struct ListTypeVersioningV2 { + 1: list strings; + 2: string hello; +} + +struct GuessProtocolStruct { + 7: map map_field, +} + +struct LargeDeltas { + 1: Bools b1, + 10: Bools b10, + 100: Bools b100, + 500: bool check_true, + 1000: Bools b1000, + 1500: bool check_false, + 2000: VersioningTestV2 vertwo2000, + 2500: set a_set2500, + 3000: VersioningTestV2 vertwo3000, + 4000: list big_numbers +} + +struct NestedListsI32x2 { + 1: list> integerlist +} +struct NestedListsI32x3 { + 1: list>> integerlist +} +struct NestedMixedx2 { + 1: list> int_set_list + 2: map> map_int_strset + 3: list>> map_int_strset_list +} +struct ListBonks { + 1: list bonk +} +struct NestedListsBonk { + 1: list>> bonk +} + +struct BoolTest { + 1: optional bool b = true; + 2: optional string s = "true"; +} + +struct StructA { + 1: required string s; +} + +struct StructB { + 1: optional StructA aa; + 2: required StructA ab; +} diff --git a/vendor/src/github.com/apache/thrift/test/TypedefTest.thrift b/vendor/src/github.com/apache/thrift/test/TypedefTest.thrift new file mode 100644 index 00000000..94374785 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/TypedefTest.thrift @@ -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. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +namespace cpp thrift.test + +typedef i32 MyInt32 +typedef string MyString; + +struct TypedefTestStruct { + 1: MyInt32 field_MyInt32; + 2: MyString field_MyString; + 3: i32 field_Int32; + 4: string field_String; +} + +typedef TypedefTestStruct MyStruct, \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/test/audit/README.md b/vendor/src/github.com/apache/thrift/test/audit/README.md new file mode 100644 index 00000000..412f8d5b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/README.md @@ -0,0 +1,40 @@ +Typical usage +============= +``` +thrift.exe --audit +``` +Example run +=========== +``` +> thrift.exe --audit test.thrift break1.thrift +[Thrift Audit Failure:break1.thrift] New Thrift File has missing function base_function3 +[Thrift Audit Warning:break1.thrift] Constant const3 has different value +``` + +Problems that the audit tool can catch +====================================== +Errors +* Removing an enum value +* Changing the type of a struct field +* Changing the required-ness of a struct field +* Removing a struct field +* Adding a required struct field +* Adding a struct field 'in the middle'. This usually indicates an old ID has been recycled +* Struct removed +* Oneway-ness change +* Return type change +* Missing function +* Missing service +* Change in service inheritance + +Warnings +* Removing a language namespace declaration +* Changing a namespace +* Changing an enum value's name +* Removing an enum class +* Default value changed +* Struct field name change +* Removed constant +* Type of constant changed +* Value of constant changed + \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/test/audit/break1.thrift b/vendor/src/github.com/apache/thrift/test/audit/break1.thrift new file mode 100644 index 00000000..f77f6722 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break1.thrift @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//Thrift Method removed from service base. + +namespace cpp test + +//constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3= [23, 32], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break10.thrift b/vendor/src/github.com/apache/thrift/test/audit/break10.thrift new file mode 100644 index 00000000..00690aaf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break10.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break10 - Struct field removed from struct2 id =1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break11.thrift b/vendor/src/github.com/apache/thrift/test/audit/break11.thrift new file mode 100644 index 00000000..a4e0a7d2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break11.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break11 - Struct field removed from struct3 id =7 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break12.thrift b/vendor/src/github.com/apache/thrift/test/audit/break12.thrift new file mode 100644 index 00000000..e5522edc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break12.thrift @@ -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. + */ + +// derived1_function1 return type changed from enum1 to enum2 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum2 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break13.thrift b/vendor/src/github.com/apache/thrift/test/audit/break13.thrift new file mode 100644 index 00000000..66975cd0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break13.thrift @@ -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. + */ + +// derived1_function6 return type changed from struct1 to struct2 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct2 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break14.thrift b/vendor/src/github.com/apache/thrift/test/audit/break14.thrift new file mode 100644 index 00000000..4ccd503c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break14.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// derived1_function6 return type changed from string to double + +namespace cpp test +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + double derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break15.thrift b/vendor/src/github.com/apache/thrift/test/audit/break15.thrift new file mode 100644 index 00000000..95f69e6a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break15.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// break15 - derived2_function1 return type changed from list to list +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break16.thrift b/vendor/src/github.com/apache/thrift/test/audit/break16.thrift new file mode 100644 index 00000000..cdcff7d8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break16.thrift @@ -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. + */ + +// break 16 - derived2_function5 return type changed from map to map + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break17.thrift b/vendor/src/github.com/apache/thrift/test/audit/break17.thrift new file mode 100644 index 00000000..353b1422 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break17.thrift @@ -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. + */ + +//break17 - derived2_function6 return type changed from map to map + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break18.thrift b/vendor/src/github.com/apache/thrift/test/audit/break18.thrift new file mode 100644 index 00000000..c778b6a0 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break18.thrift @@ -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. + */ + +//break18- oneway removed from base_oneway + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break19.thrift b/vendor/src/github.com/apache/thrift/test/audit/break19.thrift new file mode 100644 index 00000000..1a0b2296 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break19.thrift @@ -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. + */ + +//break19 - oneway added to base_function1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + oneway void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break2.thrift b/vendor/src/github.com/apache/thrift/test/audit/break2.thrift new file mode 100644 index 00000000..6f4fe2dd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break2.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//Struct field changed in test_struct1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i32 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break20.thrift b/vendor/src/github.com/apache/thrift/test/audit/break20.thrift new file mode 100644 index 00000000..9ae5f001 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break20.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// break 20 - first enum value removed from enum1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break21.thrift b/vendor/src/github.com/apache/thrift/test/audit/break21.thrift new file mode 100644 index 00000000..f7da4002 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break21.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break21- last enum value removed from enum2 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break22.thrift b/vendor/src/github.com/apache/thrift/test/audit/break22.thrift new file mode 100644 index 00000000..38083494 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break22.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break22 - in-between enum value removed from enum1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break23.thrift b/vendor/src/github.com/apache/thrift/test/audit/break23.thrift new file mode 100644 index 00000000..ff95a426 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break23.thrift @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break23 - required struct field added to struct4 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2, + 3: required i64 struct4_member3 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break24.thrift b/vendor/src/github.com/apache/thrift/test/audit/break24.thrift new file mode 100644 index 00000000..bb4d5b93 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break24.thrift @@ -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. + */ + +//break24 - removed inheritance from derived1. + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break25.thrift b/vendor/src/github.com/apache/thrift/test/audit/break25.thrift new file mode 100644 index 00000000..6efe97e6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break25.thrift @@ -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. + */ + +//Changed inheritance of derived2 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends derived1 { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break26.thrift b/vendor/src/github.com/apache/thrift/test/audit/break26.thrift new file mode 100644 index 00000000..6576d9b6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break26.thrift @@ -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. + */ + +//break26 - Field type changed in base_function1 argument id=3 +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: double function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} + diff --git a/vendor/src/github.com/apache/thrift/test/audit/break27.thrift b/vendor/src/github.com/apache/thrift/test/audit/break27.thrift new file mode 100644 index 00000000..b556706d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break27.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// break27 - argument changed base_function2 list to list id =8 +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break28.thrift b/vendor/src/github.com/apache/thrift/test/audit/break28.thrift new file mode 100644 index 00000000..c64e5580 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break28.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break28- derived1_function5 arguement type changed map to list +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: list function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break29.thrift b/vendor/src/github.com/apache/thrift/test/audit/break29.thrift new file mode 100644 index 00000000..52f30811 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break29.thrift @@ -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. + */ + +//break29 - base_function2 arguemnt type changed list to string + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: string function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break3.thrift b/vendor/src/github.com/apache/thrift/test/audit/break3.thrift new file mode 100644 index 00000000..ded9972d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break3.thrift @@ -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. + */ + +//break3 - Struct field changed in test_struct1(enum1 to enum2) + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum2 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break30.thrift b/vendor/src/github.com/apache/thrift/test/audit/break30.thrift new file mode 100644 index 00000000..818dd6e4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break30.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// break30- derived1_function6 argument changed struct1 to map +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + map derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break31.thrift b/vendor/src/github.com/apache/thrift/test/audit/break31.thrift new file mode 100644 index 00000000..7ca38046 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break31.thrift @@ -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. + */ + +//break31 - Exception removed to base_function2 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break32.thrift b/vendor/src/github.com/apache/thrift/test/audit/break32.thrift new file mode 100644 index 00000000..ca3f8a8b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break32.thrift @@ -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. + */ + +//break32- Exception1 field type changed for id =1 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i64 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break33.thrift b/vendor/src/github.com/apache/thrift/test/audit/break33.thrift new file mode 100644 index 00000000..42dbb824 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break33.thrift @@ -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. + */ + +//break33 - derived1_function1 exception type changed. + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception1 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break34.thrift b/vendor/src/github.com/apache/thrift/test/audit/break34.thrift new file mode 100644 index 00000000..af93e650 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break34.thrift @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break34 - Field added to struct with Field ID being in between two existing field IDs + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 6: map struct3_member6, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break4.thrift b/vendor/src/github.com/apache/thrift/test/audit/break4.thrift new file mode 100644 index 00000000..6a28ec05 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break4.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//Field type changed in test_struct1(bool to string) +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: string struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 =[23, 32], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break5.thrift b/vendor/src/github.com/apache/thrift/test/audit/break5.thrift new file mode 100644 index 00000000..18c22d16 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break5.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// member field type changed in test_struct1(bool to list) + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: list struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break6.thrift b/vendor/src/github.com/apache/thrift/test/audit/break6.thrift new file mode 100644 index 00000000..9b7a3004 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break6.thrift @@ -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. + */ + +// Field type changed in test_struct2 (list to list) + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break7.thrift b/vendor/src/github.com/apache/thrift/test/audit/break7.thrift new file mode 100644 index 00000000..b31c2dff --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break7.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break7 - requiredness removed in struct6 + +namespace cpp test +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break8.thrift b/vendor/src/github.com/apache/thrift/test/audit/break8.thrift new file mode 100644 index 00000000..9acac09e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break8.thrift @@ -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. + */ + +//break8 - requiredness addedd in struct5 + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: required string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/break9.thrift b/vendor/src/github.com/apache/thrift/test/audit/break9.thrift new file mode 100644 index 00000000..62b319d6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/break9.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//break9 - Struct field removed from struct1 + + +namespace cpp test +//Constants + +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/test.thrift b/vendor/src/github.com/apache/thrift/test/audit/test.thrift new file mode 100644 index 00000000..e9834b38 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/test.thrift @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +namespace cpp test + +//Constants +const i32 const1 = 123; +const double const2 = 23.3; +const map const3 = {"hello":"world", "thrift":"audit"}; + + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.5, + 5: string struct1_member5 = "Audit test", + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 struct1_member9 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list struct2_member2, + 3: list struct2_member3 = [23, 32 ], + 4: list struct2_member4, + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:2, 3:4}, + 2: map struct3_member2 = {10:1.1, 20:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1, + 2: string struct5_member2 = "Thrift Audit Test" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base { + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/thrift_audit_test.pl b/vendor/src/github.com/apache/thrift/test/audit/thrift_audit_test.pl new file mode 100644 index 00000000..69ed4dcc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/thrift_audit_test.pl @@ -0,0 +1,261 @@ +#!/usr/bin/perl -w + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + +#break1 - Thrift method removed from service base +#break2 - Struct field changed in test_struct1(i16 to i32) +#break3 - Struct field changed in test_struct1(enum1 to enum2) +#break4 - Field type changed in test_struct1(bool to string) +#break5- member field type changed in test_struct1(bool to list) +#break6- Field type changed in test_struct2 (list to list) +#break7 - requiredness removed in struct6 +#break8 - requiredness addedd in struct5 +#break9 - Struct field removed from struct1 +#break10 - Struct field removed from struct2 id = 1 +#break11 - Struct field removed from struct3 last id +#break12 - derived1_function1 return type changed from enum1 to enum2 +#break13 - derived1_function6 return type changed from struct1 to struct2 +#break14 - derived1_function4 return type changed from string to double +#break15 - derived2_function1 return type changed from list to list +#break16 - derived2_function5 return type changed from map to map +#break17 - derived2_function6 return type changed from map to map +#break18- oneway removed from base_oneway +#break19 - oneway added to base_function1 +#break20 - first enum value removed from enum1 +#break21- last enum value removed from enum2 +#break22 - in-between enum value removed from enum1 +#break23 - required struct field added to struct4 +#break24 - removed inheritance of derived1. +#break25 - changed inheritance of derived2. +#break26 - Field type changed in base_function1 argument id=3 +#break27 - argument changed base_function2 list to list id =8 +#break28- derived1_function5 arguement type changed map to list +#break29 - base_function2 arguemnt type changed list to string +#break30- derived1_function6 argument changed struct1 to map +#break31 - Exception removed to base_function2 +#break32- Exception1 field type changed for id =1 +#break33 - derived1_function1 exception type changed. +#break34 - Field added to struct with Field ID being in between two existing field IDs + +#warning.thrift +#Changing defaults +#Id=1 struct5 +#id=2 struct5 +#id=4 struct2(list) +#id=3 struct2(list default values removed) +#id 4 struct1 change in double value +#id 5 struct1 (default string value removed) +#id=1 struct3 (change in map values) +#id2 struct3 (change in map keys) + +#change in inheritance for derived1 and derived2 + +#change in struct field names +#id9 struct1 +#id2 struct2 + +use strict; +use warnings; +use Getopt::Std; + +# globals +my $gArguments = ""; # arguments that will be passed to AuditTool +my $gAuditToolPath = ""; +my $gPreviousThriftPath; # previous thrift path +my $gCurrentThriftPath; # current thrift path +my $gThriftFileFolder; +my $gBreakingFilesCount =34; + +my $gVerbose = 0; +#functions +sub auditBreakingChanges; +sub auditNonBreakingChanges; + +main(); + +sub main +{ + parseOptions(); + auditBreakingChanges(); + auditNonBreakingChanges(); +} + +sub parseOptions +{ + my %options = (); + if ( getopts ('vf:o:t:',\%options) ) + { + # current (new) thrift folder + if ($options{'f'}) + { + $gThriftFileFolder = $options{'f'}; + $gPreviousThriftPath = $gThriftFileFolder."/test.thrift"; + } + else + { + die "Missing Folder containing thrift files\n"; + } + + if($options{'t'}) + { + $gAuditToolPath = $options{'t'}; + } + else + { + die "Audit Tool Path required \n"; + } + + if ($options{'v'}) + { + $gVerbose = 1; + } + + } +} + +sub auditBreakingChanges +{ + my $breakingFileBaseName = $gThriftFileFolder."/break"; + my $newThriftFile; + for(my $i=1; $i <= $gBreakingFilesCount; $i++) + { + $newThriftFile = $breakingFileBaseName."$i.thrift"; + my $arguments = $gPreviousThriftPath." ".$newThriftFile; + my ($exitCode, $output) = callThriftAuditTool($arguments); + print $output if $gVerbose eq 1; + + if($exitCode == 1) + { + # thrift_audit returns 1 when it is not able to find files or other non-audit failures + print "exiting with exit code =1 i = ".$i."\n"; + print $output; + exit $exitCode; + } + if($exitCode != 2) + { + # thrift-audit return 2 for audit failures. So for Breaking changes we should get 2 as return value. + print $output; + die "\nTEST FAILURE: Breaking Change not detected for thrift file $newThriftFile, code=$exitCode \n"; + } + if(index($output,getMessageSubString("break$i")) == -1) + { + #Audit tool detected failure, but not the expected one. The change in breaking thrift file does not match getMessageSubString() + print $output; + die "\nTest FAILURE: Audit tool detected failure, but not the expected one!\n"; + } + else + { + #Thrift audit tool has detected audit failure and has returned exited to status code 2 + print "Test Pass: Audit Failure detected for thrift file break$i.thrift \n"; + } + } + +} + +sub auditNonBreakingChanges +{ + my $breakingFileBaseName = $gThriftFileFolder."/warning"; + my $newThriftFile; + $newThriftFile = $breakingFileBaseName.".thrift"; + my $arguments = $gPreviousThriftPath." ".$newThriftFile; + my ($exitCode, $output) = callThriftAuditTool($arguments); + print $output if $gVerbose eq 1; + + if($exitCode == 1) + { + # thrift_audit returns 1 when it is not able to find files or other non-audit failures + print "exiting with exit code = 1 for file warning.thrift\n"; + exit $exitCode; + } + elsif($exitCode != 0) + { + # thrift-audit return 0 if there are no audit failures. + die "\nTEST FAILURE: Non Breaking changes returned failure for thrift file $newThriftFile \n"; + } + else + { + #Thrift audit tool has exited with status 0. + print "Test Pass: Audit tool exits with success for warnings \n"; + } + + +} + +# ----------------------------------------------------------------------------------------------------- +# call thriftAuditTool script +sub callThriftAuditTool ( $ ) +{ + my $args = shift; + + my $command = "$gAuditToolPath --audit $args"; + my $output = `$command 2>&1`; + my $exitCode = $? >> 8; + + return ($exitCode,$output); +} + +sub getMessageSubString( $ ) +{ + my $fileName = shift; + my %lookupTable = ( + "break1" => "base_function3", + "break2" => "test_struct1", + "break3" => "test_struct1", + "break4" => "test_struct1", + "break5" => "test_struct1", + "break6" => "test_struct2", + "break7" => "test_struct6", + "break8" => "test_struct5", + "break9" => "test_struct1", + "break10" => "test_struct2", + "break11" => "test_struct3", + "break12" => "derived1_function1", + "break13" => "derived1_function6", + "break14" => "derived1_function4", + "break15" => "derived2_function1", + "break16" => "derived2_function5", + "break17" => "derived2_function6", + "break18" => "base_oneway", + "break19" => "base_function1", + "break20" => "test_enum1", + "break21" => "test_enum2", + "break22" => "test_enum1", + "break23" => "test_struct4", + "break24" => "derived1", + "break25" => "derived2", + "break26" => "base_function1", + "break27" => "base_function2_args", + "break28" => "derived1_function5_args", + "break29" => "base_function2_args", + "break30" => "derived1_function6", + "break31" => "base_function2_exception", + "break32" => "test_exception1", + "break33" => "derived1_function1_exception", + "break34" => "test_struct3", + ); + if (not exists $lookupTable{ $fileName }) + { + print "in the null case\n"; + return "NULL"; + } + + my $retval = $lookupTable{ $fileName }; + print "$fileName => $retval\n"; + return $lookupTable{ $fileName }; +} diff --git a/vendor/src/github.com/apache/thrift/test/audit/warning.thrift b/vendor/src/github.com/apache/thrift/test/audit/warning.thrift new file mode 100644 index 00000000..5392d5cc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/audit/warning.thrift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +namespace cpp test + +//Constants + +const i32 const1 = 123; +const double const2 = 23.2; +const map const3 = {"hello":"class", "thrift":"audit"}; + +//Exception +exception test_exception1 { + 1: i32 code; + 2: string json; +} +exception test_exception2 { + 1: i32 code; + 2: string json; +} + +//Enums + +enum test_enum1 { + enum1_value0 = 0, + enum1_value1 = 1, + enum1_value2 = 2, + enum1_value5 = 5, + enum1_value7 = 7, + enum1_value8 = 8 +} + +enum test_enum2 { + enum2_value0 = 0, + enum2_value1 = 1, + enum2_value2 = 2, + enum2_value3 = 3 +} + +enum test_enum3 { + enum3_value1 = 0, + enum3_value2 = 1 +} + +struct test_struct1 { + 1: i16 struct1_member1, + 2: i32 struct1_member2, + 3: i64 struct1_member3, + 4: double struct1_member4 = 2.4, + 5: string struct1_member5, + 6: bool struct1_member6, + 7: byte struct1_member7, + 8: binary struct1_member8, + 9: test_enum1 changed19 +} + +struct test_struct2 { + 1: list struct2_member1, + 2: list changed22, + 3: list struct2_member3, + 4: list struct2_member4 =[1.0, 2.1], + 5: list struct2_member5, + 6: list struct2_member6, + 7: list struct2_member7, + 8: list struct2_member8, + 9: list struct2_member9 +} + +struct test_struct3 { + 1: map struct3_member1 = {1:10, 2:20}, + 2: map struct3_member2 = {1:1.1, 2:2.1}, + 3: map struct3_member3, + 4: map struct3_member4, + 5: map struct3_member5, + 7: map struct3_member7 +} + +struct test_struct4 { + 1: i32 struct4_member1, + 2: optional i32 struct4_member2 +} + +struct test_struct5{ + 1: double struct5_member1 = 1.1, + 2: string struct5_member2 = "Thrift Audit Tess" +} +struct test_struct6 { + 1: i32 struct6_member1, + 2: required i32 struct6_member2 +} + +service base { + oneway void base_oneway( + 1: i32 arg1), + + void base_function1( + 1: i16 function1_arg1, + 2: i32 function1_arg2, + 3: i64 function1_arg3, + 4: double function1_arg4, + 5: string function1_arg5, + 6: bool function1_arg6, + 7: test_enum1 function1_arg7, + 8: test_struct1 function1_arg8), + + void base_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5, + 6: list function2_arg6, + 7: list function2_arg7, + 8: list function2_arg8, + 9: list function2_arg9) throws (1:test_exception2 e), + + void base_function3(), + +} + +service derived1 extends base{ + + test_enum1 derived1_function1( + 1: i64 function1_arg1, + 2: double function1_arg2, + 3: test_enum1 function1_arg3) throws (1:test_exception2 e), + + i64 derived1_function2( + 1: list function2_arg1, + 2: list function2_arg2, + 3: list function2_arg3, + 4: list function2_arg4, + 5: list function2_arg5) throws (1:test_exception2 e), + + double derived1_function3( + 1: string function3_arg1, + 2: bool function3_arg2) throws (1:test_exception2 e), + + string derived1_function4( + 1: string function4_arg1, + 2: bool function4_arg2) throws (1:test_exception2 e), + + + bool derived1_function5( + 1: map function5_arg1, + 2: map function5_arg2, + 3: map function5_arg3) throws (1:test_exception2 e), + + test_struct1 derived1_function6( + 1: double function6_arg1) throws (1:test_exception2 e), +} + +service derived2 extends base { + + list derived2_function1( + 1: i32 function1_arg1) throws (1:test_exception2 e), + + list derived2_function2( + 1:i64 function2_arg2) throws (1:test_exception2 e), + + list derived2_function3( + 1:double function3_arg1) throws(1:test_exception2 e), + + map derived2_function4( + 1:string function4_arg1) throws(1:test_exception2 e), + + map derived2_function5( + 1:bool function5_arg1) throws(1:test_exception2 e), + + map derived2_function6( + 1:bool function6_arg1) throws(1:test_exception2 e), + +} diff --git a/vendor/src/github.com/apache/thrift/test/c_glib/Makefile.am b/vendor/src/github.com/apache/thrift/test/c_glib/Makefile.am new file mode 100644 index 00000000..4f9a119f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/c_glib/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. +# +AUTOMAKE_OPTIONS = subdir-objects serial-tests + +noinst_LTLIBRARIES = libtestcglib.la +nodist_libtestcglib_la_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 + +libtestcglib_la_LIBADD = $(top_builddir)/lib/c_glib/libthrift_c_glib.la + +precross: libtestcglib.la test_client test_server + +check_PROGRAMS = \ + test_client \ + test_server + +test_client_SOURCES = \ + src/test_client.c + +test_client_LDADD = \ + libtestcglib.la \ + $(top_builddir)/lib/c_glib/libthrift_c_glib.la + +test_server_SOURCES = \ + src/thrift_test_handler.c \ + src/thrift_test_handler.h \ + src/test_server.c + +test_server_LDADD = \ + libtestcglib.la \ + $(top_builddir)/lib/c_glib/libthrift_c_glib.la + +# +# Common thrift code generation rules +# +THRIFT = $(top_builddir)/compiler/cpp/thrift + +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: $(top_srcdir)/test/ThriftTest.thrift $(THRIFT) + $(THRIFT) --gen c_glib -r $< + +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_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) @GCOV_LDFLAGS@ + +clean-local: + $(RM) gen-c_glib/* + +EXTRA_DIST = \ + src/test_client.c \ + src/thrift_test_handler.c \ + src/thrift_test_handler.h \ + src/test_server.c diff --git a/vendor/src/github.com/apache/thrift/test/c_glib/src/test_client.c b/vendor/src/github.com/apache/thrift/test/c_glib/src/test_client.c new file mode 100644 index 00000000..3ae9325d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/c_glib/src/test_client.c @@ -0,0 +1,1600 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include +#include +#include + +#include "../gen-c_glib/t_test_thrift_test.h" + +/* Handle SIGPIPE signals (indicating the server has closed the + connection prematurely) by outputting an error message before + exiting. */ +static void +sigpipe_handler (int signal_number) +{ + THRIFT_UNUSED_VAR (signal_number); + + /* Flush standard output to make sure the test results so far are + logged */ + fflush (stdout); + + fputs ("Broken pipe (server closed connection prematurely)\n", stderr); + fflush (stderr); + + /* Re-raise the signal, this time invoking the default signal + handler, to terminate the program */ + raise (SIGPIPE); +} + +/* Compare two gint32 values. Used for sorting and finding integer + values within a GList. */ +static gint +gint32_compare (gconstpointer a, gconstpointer b) +{ + gint32 int32_a = *(gint32 *)a; + gint32 int32_b = *(gint32 *)b; + int result = 0; + + if (int32_a < int32_b) + result = -1; + else if (int32_a > int32_b) + result = 1; + + return result; +} + +int +main (int argc, char **argv) +{ + static gchar *host = NULL; + static gint port = 9090; + static gchar *transport_option = NULL; + static gchar *protocol_option = NULL; + static gint num_tests = 1; + + static + GOptionEntry option_entries[] ={ + { "host", 0, 0, G_OPTION_ARG_STRING, &host, + "Host to connect (=localhost)", NULL }, + { "port", 0, 0, G_OPTION_ARG_INT, &port, + "Port number to connect (=9090)", NULL }, + { "transport", 0, 0, G_OPTION_ARG_STRING, &transport_option, + "Transport: buffered, framed (=buffered)", NULL }, + { "protocol", 0, 0, G_OPTION_ARG_STRING, &protocol_option, + "Protocol: binary, compact (=binary)", NULL }, + { "testloops", 'n', 0, G_OPTION_ARG_INT, &num_tests, + "Number of tests (=1)", NULL }, + { NULL } + }; + + struct sigaction sigpipe_action; + + GType transport_type = THRIFT_TYPE_BUFFERED_TRANSPORT; + gchar *transport_name = "buffered"; + GType protocol_type = THRIFT_TYPE_BINARY_PROTOCOL; + gchar *protocol_name = "binary"; + + ThriftSocket *socket; + ThriftTransport *transport; + ThriftProtocol *protocol; + + TTestThriftTestIf *test_client; + + struct timeval time_start, time_stop, time_elapsed; + guint64 time_elapsed_usec, time_total_usec = 0; + guint64 time_min_usec = G_MAXUINT64, time_max_usec = 0, time_avg_usec; + + GOptionContext *option_context; + gboolean options_valid = TRUE; + int test_num = 0; + int fail_count = 0; + GError *error = NULL; + +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + /* Configure and parse our command-line options */ + option_context = g_option_context_new (NULL); + g_option_context_add_main_entries (option_context, + option_entries, + NULL); + if (!g_option_context_parse (option_context, + &argc, + &argv, + &error)) { + fprintf (stderr, "%s\n", error->message); + return 255; + } + g_option_context_free (option_context); + + /* Set remaining default values for unspecified options */ + if (host == NULL) + host = g_strdup ("localhost"); + + /* Validate the parsed options */ + if (protocol_option != NULL) { + if (strncmp (protocol_option, "compact", 8) == 0) { + protocol_type = THRIFT_TYPE_COMPACT_PROTOCOL; + protocol_name = "compact"; + } + else if (strncmp (protocol_option, "binary", 7) != 0) { + fprintf (stderr, "Unknown protocol type %s\n", protocol_option); + options_valid = FALSE; + } + } + + if (transport_option != NULL) { + if (strncmp (transport_option, "framed", 7) == 0) { + transport_type = THRIFT_TYPE_FRAMED_TRANSPORT; + transport_name = "framed"; + } + else if (strncmp (transport_option, "buffered", 9) != 0) { + fprintf (stderr, "Unknown transport type %s\n", transport_option); + options_valid = FALSE; + } + } + + if (!options_valid) + return 254; + + printf ("Connecting (%s/%s) to: %s:%d\n", + transport_name, + protocol_name, + host, + port); + + /* Install our SIGPIPE handler, which outputs an error message to + standard error before exiting so testers can know what + happened */ + memset (&sigpipe_action, 0, sizeof (sigpipe_action)); + sigpipe_action.sa_handler = sigpipe_handler; + sigpipe_action.sa_flags = SA_RESETHAND; + sigaction (SIGPIPE, &sigpipe_action, NULL); + + /* Establish all our connection objects */ + socket = g_object_new (THRIFT_TYPE_SOCKET, + "hostname", host, + "port", port, + NULL); + transport = g_object_new (transport_type, + "transport", socket, + NULL); + protocol = g_object_new (protocol_type, + "transport", transport, + NULL); + test_client = g_object_new (T_TEST_TYPE_THRIFT_TEST_CLIENT, + "input_protocol", protocol, + "output_protocol", protocol, + NULL); + + /* Execute the actual tests */ + for (test_num = 0; test_num < num_tests; ++test_num) { + if (thrift_transport_open (transport, &error)) { + gchar *string = NULL; + gboolean boolean = 0; + gint8 byte = 0; + gint32 int32 = 0; + gint64 int64 = 0; + gdouble dub = 0; + + gint byte_thing, i32_thing, inner_byte_thing, inner_i32_thing; + gint64 i64_thing, inner_i64_thing; + + TTestXtruct *xtruct_out, *xtruct_out2, *xtruct_in, *inner_xtruct_in; + TTestXtruct2 *xtruct2_out, *xtruct2_in; + + GHashTable *map_out, *map_in, *inner_map_in; + GHashTable *set_out, *set_in; + gpointer key, value; + gint32 *i32_key_ptr, *i32_value_ptr; + GHashTableIter hash_table_iter, inner_hash_table_iter; + GList *keys_out, *keys_in, *keys_elem; + + GArray *list_out, *list_in; + + TTestNumberz numberz; + TTestNumberz numberz2; + + TTestUserId user_id, *user_id_ptr, *user_id_ptr2; + + TTestInsanity *insanity_out, *insanity_in; + GHashTable *user_map; + GHashTableIter user_map_iter; + GPtrArray *xtructs; + + TTestXception *xception = NULL; + TTestXception2 *xception2 = NULL; + + gboolean oneway_result; + struct timeval oneway_start, oneway_end, oneway_elapsed; + gint oneway_elapsed_usec; + + gboolean first; + gint32 i, j; + + 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 + client, care being taken to ensure their output remains as + close as possible to the original to facilitate diffs. + + For simplicity comments have been omitted, but every routine + has the same basic structure: + + - Create and populate data structures as necessary. + + - Format and output (to the console) a representation of the + outgoing data. + + - Issue the remote method call to the server. + + - Format and output a representation of the returned data. + + - Verify the returned data matches what was expected. + + - Deallocate any created data structures. + + Note the recognized values and expected behaviour of each + remote method are described in ThriftTest.thrift, which + you'll find in the top-level "test" folder. */ + + /** + * VOID TEST + */ + printf ("testVoid()"); + if (t_test_thrift_test_if_test_void (test_client, &error)) { + printf (" = void\n"); + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * STRING TEST + */ + printf ("testString(\"Test\")"); + if (t_test_thrift_test_if_test_string (test_client, + &string, + "Test", + &error)) { + printf (" = \"%s\"\n", string); + if (strncmp (string, "Test", 5) != 0) + fail_count++; + + g_free (string); + string = NULL; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * BOOL TEST + */ + printf ("testByte(true)"); + if (t_test_thrift_test_if_test_bool (test_client, + &boolean, + 1, + &error)) { + printf (" = %s\n", boolean ? "true" : "false"); + if (boolean != 1) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + printf ("testByte(false)"); + if (t_test_thrift_test_if_test_bool (test_client, + &boolean, + 0, + &error)) { + printf (" = %s\n", boolean ? "true" : "false"); + if (boolean != 0) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * BYTE TEST + */ + printf ("testByte(1)"); + if (t_test_thrift_test_if_test_byte (test_client, + &byte, + 1, + &error)) { + printf (" = %d\n", byte); + if (byte != 1) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + printf ("testByte(-1)"); + if (t_test_thrift_test_if_test_byte (test_client, + &byte, + -1, + &error)) { + printf (" = %d\n", byte); + if (byte != -1) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * I32 TEST + */ + printf ("testI32(-1)"); + if (t_test_thrift_test_if_test_i32 (test_client, + &int32, + -1, + &error)) { + printf (" = %d\n", int32); + if (int32 != -1) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * I64 TEST + */ + printf ("testI64(-34359738368)"); + if (t_test_thrift_test_if_test_i64 (test_client, + &int64, + (gint64)-34359738368, + &error)) { + printf (" = %" PRId64 "\n", int64); + if (int64 != (gint64)-34359738368) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * DOUBLE TEST + */ + printf("testDouble(-5.2098523)"); + if (t_test_thrift_test_if_test_double (test_client, + &dub, + -5.2098523, + &error)) { + printf (" = %f\n", dub); + if ((dub - (-5.2098523)) > 0.001) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + // TODO: add testBinary() + + /** + * STRUCT TEST + */ + printf ("testStruct({\"Zero\", 1, -3, -5})"); + xtruct_out = g_object_new (T_TEST_TYPE_XTRUCT, + "string_thing", "Zero", + "byte_thing", 1, + "i32_thing", -3, + "i64_thing", -5LL, + NULL); + xtruct_in = g_object_new (T_TEST_TYPE_XTRUCT, NULL); + + if (t_test_thrift_test_if_test_struct (test_client, + &xtruct_in, + xtruct_out, + &error)) { + g_object_get (xtruct_in, + "string_thing", &string, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + + printf (" = {\"%s\", %d, %d, %" PRId64 "}\n", + string, + byte_thing, + i32_thing, + i64_thing); + if ((string == NULL || strncmp (string, "Zero", 5) != 0) || + byte_thing != 1 || + i32_thing != -3 || + i64_thing != (gint64)-5) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + g_object_unref (xtruct_in); + + /** + * NESTED STRUCT TEST + */ + printf ("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); + xtruct2_out = g_object_new (T_TEST_TYPE_XTRUCT2, + "byte_thing", 1, + "struct_thing", xtruct_out, + "i32_thing", 5, + NULL); + xtruct2_in = g_object_new (T_TEST_TYPE_XTRUCT2, NULL); + + if (t_test_thrift_test_if_test_nest (test_client, + &xtruct2_in, + xtruct2_out, + &error)) { + g_object_get (xtruct2_in, + "byte_thing", &byte_thing, + "struct_thing", &xtruct_in, + "i32_thing", &i32_thing, + NULL); + g_object_get (xtruct_in, + "string_thing", &string, + "byte_thing", &inner_byte_thing, + "i32_thing", &inner_i32_thing, + "i64_thing", &inner_i64_thing, + NULL); + + printf (" = {%d, {\"%s\", %d, %d, %" PRId64 "}, %d}\n", + byte_thing, + string, + inner_byte_thing, + inner_i32_thing, + inner_i64_thing, + i32_thing); + if (byte_thing != 1 || + (string == NULL || strncmp (string, "Zero", 5) != 0) || + inner_byte_thing != 1 || + inner_i32_thing != -3 || + inner_i64_thing != (gint64)-5 || + i32_thing != 5) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_object_unref (xtruct_in); + g_object_unref (xtruct2_in); + g_object_unref (xtruct2_out); + g_object_unref (xtruct_out); + + /** + * MAP TEST + */ + map_out = g_hash_table_new_full (g_int_hash, + g_int_equal, + g_free, + g_free); + for (i = 0; i < 5; ++i) { + i32_key_ptr = g_malloc (sizeof *i32_key_ptr); + i32_value_ptr = g_malloc (sizeof *i32_value_ptr); + + *i32_key_ptr = i; + *i32_value_ptr = i - 10; + + g_hash_table_insert (map_out, i32_key_ptr, i32_value_ptr); + } + printf ("testMap({"); + first = TRUE; + g_hash_table_iter_init (&hash_table_iter, map_out); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d => %d", *(gint32 *)key, *(gint32 *)value); + } + printf ("})"); + + map_in = g_hash_table_new_full (g_int_hash, + g_int_equal, + g_free, + g_free); + + if (t_test_thrift_test_if_test_map (test_client, + &map_in, + map_out, + &error)) { + printf (" = {"); + first = TRUE; + g_hash_table_iter_init (&hash_table_iter, map_in); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d => %d", *(gint32 *)key, *(gint32 *)value); + } + printf ("}\n"); + + if (g_hash_table_size (map_in) != g_hash_table_size (map_out)) + fail_count++; + else { + g_hash_table_iter_init (&hash_table_iter, map_out); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + gpointer in_value = g_hash_table_lookup (map_in, key); + if (in_value == NULL || + *(gint32 *)in_value != *(gint32 *)value) { + fail_count++; + break; + } + } + } + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_hash_table_unref (map_in); + g_hash_table_unref (map_out); + + /** + * STRING MAP TEST + */ + map_out = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + NULL); + g_hash_table_insert (map_out, "a", "2"); + g_hash_table_insert (map_out, "b", "blah"); + g_hash_table_insert (map_out, "some", "thing"); + printf ("testStringMap({"); + first = TRUE; + g_hash_table_iter_init (&hash_table_iter, map_out); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("\"%s\" => \"%s\"", (gchar *)key, (gchar *)value); + } + printf (")}"); + + map_in = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + + if (t_test_thrift_test_if_test_string_map (test_client, + &map_in, + map_out, + &error)) { + printf (" = {"); + first = TRUE; + g_hash_table_iter_init (&hash_table_iter, map_in); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("\"%s\" => \"%s\"", (gchar *)key, (gchar *)value); + } + printf ("}\n"); + + if (g_hash_table_size (map_in) != g_hash_table_size (map_out)) + fail_count++; + else { + g_hash_table_iter_init (&hash_table_iter, map_out); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + gpointer in_value = g_hash_table_lookup (map_in, key); + if (in_value == NULL || + strcmp ((gchar *)in_value, (gchar *)value) != 0) { + fail_count++; + break; + } + } + } + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_hash_table_unref (map_in); + g_hash_table_unref (map_out); + + /** + * SET TEST + */ + set_out = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL); + for (i = -2; i < 3; ++i) { + i32_key_ptr = g_malloc (sizeof *i32_key_ptr); + *i32_key_ptr = i; + + g_hash_table_insert (set_out, i32_key_ptr, NULL); + } + printf ("testSet({"); + first = TRUE; + keys_out = g_hash_table_get_keys (set_out); + keys_elem = keys_out; + while (keys_elem != NULL) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d", *(gint32 *)keys_elem->data); + + keys_elem = keys_elem->next; + } + printf ("})"); + + set_in = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL); + + if (t_test_thrift_test_if_test_set (test_client, + &set_in, + set_out, + &error)) { + printf(" = {"); + first = TRUE; + keys_in = g_hash_table_get_keys (set_in); + keys_elem = keys_in; + while (keys_elem != NULL) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d", *(gint32 *)keys_elem->data); + + keys_elem = keys_elem->next; + } + printf ("}\n"); + + if (g_list_length (keys_in) != g_list_length (keys_out)) + fail_count++; + else { + keys_elem = keys_out; + while (keys_elem != NULL) { + if (g_list_find_custom (keys_in, + keys_elem->data, + gint32_compare) == NULL) { + fail_count++; + break; + } + + keys_elem = keys_elem->next; + } + } + + g_list_free (keys_in); + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_hash_table_unref (set_in); + g_list_free (keys_out); + g_hash_table_unref (set_out); + + /** + * LIST TEST + */ + list_out = g_array_new (FALSE, TRUE, sizeof (gint32)); + for (i = -2; i < 3; ++i) { + g_array_append_val (list_out, i); + } + printf ("testList({"); + first = TRUE; + for (i = 0; i < (gint32)list_out->len; ++i) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d", g_array_index (list_out, gint32, i)); + } + printf ("})"); + + list_in = g_array_new (FALSE, TRUE, sizeof (gint32)); + + if (t_test_thrift_test_if_test_list (test_client, + &list_in, + list_out, + &error)) { + printf (" = {"); + first = TRUE; + for (i = 0; i < (gint32)list_in->len; ++i) { + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d", g_array_index (list_in, gint32, i)); + } + printf ("}\n"); + + if (list_in->len != list_out->len || + memcmp (list_in->data, + list_out->data, + list_in->len * sizeof (gint32)) != 0) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_array_unref (list_in); + g_array_unref (list_out); + + /** + * ENUM TEST + */ + printf("testEnum(ONE)"); + if (t_test_thrift_test_if_test_enum (test_client, + &numberz, + T_TEST_NUMBERZ_ONE, + &error)) { + printf(" = %d\n", numberz); + if (numberz != T_TEST_NUMBERZ_ONE) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + printf("testEnum(TWO)"); + if (t_test_thrift_test_if_test_enum (test_client, + &numberz, + T_TEST_NUMBERZ_TWO, + &error)) { + printf(" = %d\n", numberz); + if (numberz != T_TEST_NUMBERZ_TWO) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + printf("testEnum(THREE)"); + if (t_test_thrift_test_if_test_enum (test_client, + &numberz, + T_TEST_NUMBERZ_THREE, + &error)) { + printf(" = %d\n", numberz); + if (numberz != T_TEST_NUMBERZ_THREE) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + printf("testEnum(FIVE)"); + if (t_test_thrift_test_if_test_enum (test_client, + &numberz, + T_TEST_NUMBERZ_FIVE, + &error)) { + printf(" = %d\n", numberz); + if (numberz != T_TEST_NUMBERZ_FIVE) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + printf("testEnum(EIGHT)"); + if (t_test_thrift_test_if_test_enum (test_client, + &numberz, + T_TEST_NUMBERZ_EIGHT, + &error)) { + printf(" = %d\n", numberz); + if (numberz != T_TEST_NUMBERZ_EIGHT) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * TYPEDEF TEST + */ + printf ("testTypedef(309858235082523)"); + if (t_test_thrift_test_if_test_typedef (test_client, + &user_id, + 309858235082523LL, + &error)) { + printf(" = %" PRId64 "\n", user_id); + if (user_id != 309858235082523LL) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * NESTED MAP TEST + */ + printf ("testMapMap(1)"); + map_in = g_hash_table_new_full (g_int_hash, + g_int_equal, + g_free, + (GDestroyNotify)g_hash_table_unref); + if (t_test_thrift_test_if_test_map_map (test_client, + &map_in, + 1, + &error)) { + g_hash_table_iter_init (&hash_table_iter, map_in); + + printf (" = {"); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + printf ("%d => {", *(gint32 *)key); + + g_hash_table_iter_init (&inner_hash_table_iter, + (GHashTable *)value); + while (g_hash_table_iter_next (&inner_hash_table_iter, + &key, + &value)) { + printf ("%d => %d, ", *(gint32 *)key, *(gint32 *)value); + } + + printf ("}, "); + } + printf ("}\n"); + + if (g_hash_table_size (map_in) != 2) + fail_count++; + else { + gint32 inner_keys[] = {1, 2, 3, 4}; + gint32 i32_key; + + i32_key = -4; + inner_map_in = g_hash_table_lookup (map_in, &i32_key); + if (inner_map_in == NULL || + g_hash_table_size (inner_map_in) != 4) + fail_count++; + else { + keys_in = g_hash_table_get_keys (inner_map_in); + keys_in = g_list_sort (keys_in, gint32_compare); + + for (i = 0; i < 4; i++) { + keys_elem = g_list_nth (keys_in, 3 - i); + + if (*(gint32 *)keys_elem->data != (-1 * inner_keys[i]) || + *(gint32 *)g_hash_table_lookup (inner_map_in, + keys_elem->data) != + (-1 * inner_keys[i])) { + fail_count++; + break; + } + } + + g_list_free (keys_in); + } + + i32_key = 4; + inner_map_in = g_hash_table_lookup (map_in, &i32_key); + if (inner_map_in == NULL || + g_hash_table_size (inner_map_in) != 4) + fail_count++; + else { + keys_in = g_hash_table_get_keys (inner_map_in); + keys_in = g_list_sort (keys_in, gint32_compare); + + for (i = 0; i < 4; i++) { + keys_elem = g_list_nth (keys_in, i); + + if (*(gint32 *)keys_elem->data != inner_keys[i] || + *(gint32 *)g_hash_table_lookup (inner_map_in, + keys_elem->data) != + inner_keys[i]) { + fail_count++; + break; + } + } + + g_list_free (keys_in); + } + } + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_hash_table_unref (map_in); + + /** + * INSANITY TEST + */ + insanity_out = g_object_new (T_TEST_TYPE_INSANITY, NULL); + g_object_get (insanity_out, + "userMap", &user_map, + "xtructs", &xtructs, + NULL); + + numberz = T_TEST_NUMBERZ_FIVE; + numberz2 = T_TEST_NUMBERZ_EIGHT; + user_id_ptr = g_malloc (sizeof *user_id_ptr); + *user_id_ptr = 5; + user_id_ptr2 = g_malloc (sizeof *user_id_ptr); + *user_id_ptr2 = 8; + g_hash_table_insert (user_map, (gpointer)numberz, user_id_ptr); + g_hash_table_insert (user_map, (gpointer)numberz2, user_id_ptr2); + g_hash_table_unref (user_map); + + xtruct_out = g_object_new (T_TEST_TYPE_XTRUCT, + "string_thing", "Hello2", + "byte_thing", 2, + "i32_thing", 2, + "i64_thing", 2LL, + NULL); + xtruct_out2 = g_object_new (T_TEST_TYPE_XTRUCT, + "string_thing", "Goodbye4", + "byte_thing", 4, + "i32_thing", 4, + "i64_thing", 4LL, + NULL); + g_ptr_array_add (xtructs, xtruct_out2); + g_ptr_array_add (xtructs, xtruct_out); + g_ptr_array_unref (xtructs); + + map_in = g_hash_table_new_full (g_int64_hash, + g_int64_equal, + g_free, + (GDestroyNotify)g_hash_table_unref); + + printf("testInsanity()"); + if (t_test_thrift_test_if_test_insanity (test_client, + &map_in, + insanity_out, + &error)) { + printf (" = {"); + g_hash_table_iter_init (&hash_table_iter, map_in); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + printf ("%" PRId64 " => {", *(TTestUserId *)key); + + g_hash_table_iter_init (&inner_hash_table_iter, + (GHashTable *)value); + while (g_hash_table_iter_next (&inner_hash_table_iter, + &key, + &value)) { + printf ("%d => {", (TTestNumberz)key); + + g_object_get ((TTestInsanity *)value, + "userMap", &user_map, + "xtructs", &xtructs, + NULL); + + printf ("{"); + g_hash_table_iter_init (&user_map_iter, user_map); + while (g_hash_table_iter_next (&user_map_iter, + &key, + &value)) { + printf ("%d => %" PRId64 ", ", + (TTestNumberz)key, + *(TTestUserId *)value); + } + printf ("}, "); + g_hash_table_unref (user_map); + + printf("{"); + for (i = 0; i < (gint32)xtructs->len; ++i) { + xtruct_in = g_ptr_array_index (xtructs, i); + g_object_get (xtruct_in, + "string_thing", &string, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + + printf ("{\"%s\", %d, %d, %" PRId64 "}, ", + string, + byte_thing, + i32_thing, + i64_thing); + } + printf ("}"); + g_ptr_array_unref (xtructs); + + printf ("}, "); + } + printf("}, "); + } + printf("}\n"); + + if (g_hash_table_size (map_in) != 2) + fail_count++; + else { + TTestNumberz numberz_key_values[] = { + T_TEST_NUMBERZ_TWO, T_TEST_NUMBERZ_THREE + }; + gint user_map_values[] = { 5, 8 }; + TTestUserId user_id_key; + + user_id_key = 1; + inner_map_in = g_hash_table_lookup (map_in, &user_id_key); + if (inner_map_in == NULL || + g_hash_table_size (inner_map_in) != 2) + fail_count++; + else { + TTestNumberz numberz_key; + + for (i = 0; i < 2; ++i) { + numberz_key = numberz_key_values[i]; + insanity_in = + g_hash_table_lookup (inner_map_in, + (gconstpointer)numberz_key); + if (insanity_in == NULL) + fail_count++; + else { + g_object_get (insanity_in, + "userMap", &user_map, + "xtructs", &xtructs, + NULL); + + if (user_map == NULL) + fail_count++; + else { + if (g_hash_table_size (user_map) != 2) + fail_count++; + else { + for (j = 0; j < 2; ++j) { + numberz_key = (TTestNumberz)user_map_values[j]; + + value = + g_hash_table_lookup (user_map, + (gconstpointer)numberz_key); + if (value == NULL || + *(TTestUserId *)value != (TTestUserId)user_map_values[j]) + fail_count++; + } + } + + g_hash_table_unref (user_map); + } + + if (xtructs == NULL) + fail_count++; + else { + if (xtructs->len != 2) + fail_count++; + else { + xtruct_in = g_ptr_array_index (xtructs, 0); + g_object_get (xtruct_in, + "string_thing", &string, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + if ((string == NULL || + strncmp (string, "Goodbye4", 9) != 0) || + byte_thing != 4 || + i32_thing != 4 || + i64_thing != 4) + fail_count++; + + if (string != NULL) + g_free (string); + + xtruct_in = g_ptr_array_index (xtructs, 1); + g_object_get (xtruct_in, + "string_thing", &string, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + if ((string == NULL || + strncmp (string, "Hello2", 7) != 0) || + byte_thing != 2 || + i32_thing != 2 || + i64_thing != 2) + fail_count++; + + if (string != NULL) + g_free (string); + } + + g_ptr_array_unref (xtructs); + } + } + } + } + + user_id_key = 2; + inner_map_in = g_hash_table_lookup (map_in, &user_id_key); + if (inner_map_in == NULL || + g_hash_table_size (inner_map_in) != 1) + fail_count++; + else { + insanity_in = + g_hash_table_lookup (inner_map_in, + (gconstpointer)T_TEST_NUMBERZ_SIX); + if (insanity_in == NULL) + fail_count++; + else { + g_object_get (insanity_in, + "userMap", &user_map, + "xtructs", &xtructs, + NULL); + + if (user_map == NULL) + fail_count++; + else { + if (g_hash_table_size (user_map) != 0) + fail_count++; + + g_hash_table_unref (user_map); + } + + if (xtructs == NULL) + fail_count++; + else { + if (xtructs->len != 0) + fail_count++; + + g_ptr_array_unref (xtructs); + } + } + } + } + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + g_hash_table_unref (map_in); + g_object_unref (insanity_out); + + /* test exception */ + printf ("testClient.testException(\"Xception\") =>"); + if (!t_test_thrift_test_if_test_exception (test_client, + "Xception", + &xception, + &error) && + xception != NULL) { + g_object_get (xception, + "errorCode", &int32, + "message", &string, + NULL); + printf (" {%u, \"%s\"}\n", int32, string); + g_free (string); + + g_object_unref (xception); + xception = NULL; + + g_error_free (error); + error = NULL; + } + else { + printf (" void\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + if (error != NULL) { + g_error_free (error); + error = NULL; + } + } + + printf ("testClient.testException(\"TException\") =>"); + if (!t_test_thrift_test_if_test_exception (test_client, + "TException", + &xception, + &error) && + xception == NULL && + error != NULL) { + printf (" Caught TException\n"); + + g_error_free (error); + error = NULL; + } + else { + printf (" void\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + if (error != NULL) { + g_error_free (error); + error = NULL; + } + } + + printf ("testClient.testException(\"success\") =>"); + if (t_test_thrift_test_if_test_exception (test_client, + "success", + &xception, + &error)) + printf (" void\n"); + else { + printf (" void\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + g_error_free (error); + error = NULL; + } + + g_assert (error == NULL); + + /* test multi exception */ + printf ("testClient.testMultiException(\"Xception\", \"test 1\") =>"); + xtruct_in = g_object_new (T_TEST_TYPE_XTRUCT, NULL); + if (!t_test_thrift_test_if_test_multi_exception (test_client, + &xtruct_in, + "Xception", + "test 1", + &xception, + &xception2, + &error) && + xception != NULL && + xception2 == NULL) { + g_object_get (xception, + "errorCode", &int32, + "message", &string, + NULL); + printf (" {%u, \"%s\"}\n", int32, string); + g_free (string); + + g_object_unref (xception); + xception = NULL; + + g_error_free (error); + error = NULL; + } + else { + printf (" result\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + if (xception2 != NULL) { + g_object_unref (xception2); + xception = NULL; + } + + if (error != NULL) { + g_error_free (error); + error = NULL; + } + } + g_object_unref (xtruct_in); + + printf ("testClient.testMultiException(\"Xception2\", \"test 2\") =>"); + xtruct_in = g_object_new (T_TEST_TYPE_XTRUCT, NULL); + if (!t_test_thrift_test_if_test_multi_exception (test_client, + &xtruct_in, + "Xception2", + "test 2", + &xception, + &xception2, + &error) && + xception == NULL && + xception2 != NULL) { + g_object_get (xception2, + "errorCode", &int32, + "struct_thing", &inner_xtruct_in, + NULL); + g_object_get (inner_xtruct_in, + "string_thing", &string, + NULL); + printf (" {%u, {\"%s\"}}\n", int32, string); + g_free (string); + + g_object_unref (inner_xtruct_in); + inner_xtruct_in = NULL; + + g_object_unref (xception2); + xception2 = NULL; + + g_error_free (error); + error = NULL; + } + else { + printf (" result\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + if (xception2 != NULL) { + g_object_unref (xception2); + xception = NULL; + } + + if (error != NULL) { + g_error_free (error); + error = NULL; + } + } + g_object_unref (xtruct_in); + + printf ("testClient.testMultiException(\"success\", \"test 3\") =>"); + xtruct_in = g_object_new (T_TEST_TYPE_XTRUCT, NULL); + if (t_test_thrift_test_if_test_multi_exception (test_client, + &xtruct_in, + "success", + "test 3", + &xception, + &xception2, + &error) && + xception == NULL && + xception2 == NULL) { + g_object_get (xtruct_in, + "string_thing", &string, + NULL); + printf (" {{\"%s\"}}\n", string); + g_free (string); + } + else { + printf (" result\nFAILURE\n"); + fail_count++; + + if (xception != NULL) { + g_object_unref (xception); + xception = NULL; + } + + if (xception2 != NULL) { + g_object_unref (xception2); + xception = NULL; + } + + if (error != NULL) { + g_error_free (error); + error = NULL; + } + } + g_object_unref (xtruct_in); + + /* test oneway void */ + printf ("testClient.testOneway(1) =>"); + gettimeofday (&oneway_start, NULL); + oneway_result = t_test_thrift_test_if_test_oneway (test_client, + 1, + &error); + gettimeofday (&oneway_end, NULL); + timersub (&oneway_end, &oneway_start, &oneway_elapsed); + oneway_elapsed_usec = + oneway_elapsed.tv_sec * 1000 * 1000 + oneway_elapsed.tv_usec; + + if (oneway_result) { + if (oneway_elapsed_usec > 200 * 1000) { + printf (" FAILURE - took %.2f ms\n", + (double)oneway_elapsed_usec / 1000.0); + fail_count++; + } + else + printf (" success - took %.2f ms\n", + (double)oneway_elapsed_usec / 1000.0); + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + /** + * redo a simple test after the oneway to make sure we aren't "off by + * one" -- if the server treated oneway void like normal void, this next + * test will fail since it will get the void confirmation rather than + * the correct result. In this circumstance, the client will receive the + * error: + * + * application error: Wrong method name + */ + /** + * I32 TEST + */ + printf ("re-test testI32(-1)"); + if (t_test_thrift_test_if_test_i32 (test_client, + &int32, + -1, + &error)) { + printf (" = %d\n", int32); + if (int32 != -1) + fail_count++; + } + else { + printf ("%s\n", error->message); + g_error_free (error); + error = NULL; + + fail_count++; + } + + gettimeofday (&time_stop, NULL); + timersub (&time_stop, &time_start, &time_elapsed); + time_elapsed_usec = + time_elapsed.tv_sec * 1000 * 1000 + time_elapsed.tv_usec; + + printf("Total time: %" PRIu64 " us\n", time_elapsed_usec); + + time_total_usec += time_elapsed_usec; + if (time_elapsed_usec < time_min_usec) + time_min_usec = time_elapsed_usec; + if (time_elapsed_usec > time_max_usec) + time_max_usec = time_elapsed_usec; + + thrift_transport_close (transport, &error); + } + else { + printf ("Connect failed: %s\n", error->message); + g_error_free (error); + error = NULL; + + return 1; + } + } + + /* All done---output statistics */ + puts ("\nAll tests done."); + + time_avg_usec = time_total_usec / num_tests; + + printf ("Min time: %" PRIu64 " us\n", time_min_usec); + printf ("Max time: %" PRIu64 " us\n", time_max_usec); + printf ("Avg time: %" PRIu64 " us\n", time_avg_usec); + + g_object_unref (test_client); + g_object_unref (protocol); + g_object_unref (transport); + g_free (host); + + return fail_count; +} diff --git a/vendor/src/github.com/apache/thrift/test/c_glib/src/test_server.c b/vendor/src/github.com/apache/thrift/test/c_glib/src/test_server.c new file mode 100644 index 00000000..7f41d3f8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/c_glib/src/test_server.c @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../gen-c_glib/t_test_thrift_test.h" + +#include "thrift_test_handler.h" + +/* Our server object, declared globally so it is accessible within the SIGINT + signal handler */ +ThriftServer *server = NULL; + +/* A flag that indicates whether the server was interrupted with SIGINT + (i.e. Ctrl-C) so we can tell whether its termination was abnormal */ +gboolean sigint_received = FALSE; + +/* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the server */ +static void +sigint_handler (int signal_number) +{ + THRIFT_UNUSED_VAR (signal_number); + + /* Take note we were called */ + sigint_received = TRUE; + + /* Shut down the server gracefully */ + if (server != NULL) + thrift_server_stop (server); +} + +int +main (int argc, char **argv) +{ + static gint port = 9090; + static gchar *server_type_option = NULL; + static gchar *transport_option = NULL; + static gchar *protocol_option = NULL; + static gint string_limit = 0; + static gint container_limit = 0; + + static + GOptionEntry option_entries[] = { + { "port", 0, 0, G_OPTION_ARG_INT, &port, + "Port number to connect (=9090)", 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, + "Transport: buffered, framed (=buffered)", NULL }, + { "protocol", 0, 0, G_OPTION_ARG_STRING, &protocol_option, + "Protocol: binary, compact (=binary)", NULL }, + { "string-limit", 0, 0, G_OPTION_ARG_INT, &string_limit, + "Max string length (=none)", NULL }, + { "container-limit", 0, 0, G_OPTION_ARG_INT, &container_limit, + "Max container length (=none)", NULL }, + { NULL } + }; + + gchar *server_name = "simple"; + gchar *transport_name = "buffered"; + GType transport_factory_type = THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY; + gchar *protocol_name = "binary"; + GType protocol_factory_type = THRIFT_TYPE_BINARY_PROTOCOL_FACTORY; + + TTestThriftTestHandler *handler; + ThriftProcessor *processor; + ThriftServerTransport *server_transport; + ThriftTransportFactory *transport_factory; + ThriftProtocolFactory *protocol_factory; + + struct sigaction sigint_action; + + GOptionContext *option_context; + gboolean options_valid = TRUE; + + GError *error = NULL; + +#if (!GLIB_CHECK_VERSION (2, 36, 0)) + g_type_init (); +#endif + + /* Configure and parse our command-line options */ + option_context = g_option_context_new (NULL); + g_option_context_add_main_entries (option_context, + option_entries, + NULL); + if (g_option_context_parse (option_context, + &argc, + &argv, + &error) == FALSE) { + fprintf (stderr, "%s\n", error->message); + return 255; + } + g_option_context_free (option_context); + + /* Validate the parsed options */ + if (server_type_option != NULL && + strncmp (server_type_option, "simple", 7) != 0) { + fprintf (stderr, "Unknown server type %s\n", protocol_option); + options_valid = FALSE; + } + + if (protocol_option != NULL) { + if (strncmp (protocol_option, "compact", 8) == 0) { + protocol_factory_type = THRIFT_TYPE_COMPACT_PROTOCOL_FACTORY; + protocol_name = "compact"; + } + else if (strncmp (protocol_option, "binary", 7) != 0) { + fprintf (stderr, "Unknown protocol type %s\n", protocol_option); + options_valid = FALSE; + } + } + + if (transport_option != NULL) { + if (strncmp (transport_option, "framed", 7) == 0) { + transport_factory_type = THRIFT_TYPE_FRAMED_TRANSPORT_FACTORY; + transport_name = "framed"; + } + else if (strncmp (transport_option, "buffered", 9) != 0) { + fprintf (stderr, "Unknown transport type %s\n", transport_option); + options_valid = FALSE; + } + } + + if (!options_valid) + return 254; + + /* Establish all our connection objects */ + handler = g_object_new (TYPE_THRIFT_TEST_HANDLER, + NULL); + processor = g_object_new (T_TEST_TYPE_THRIFT_TEST_PROCESSOR, + "handler", handler, + NULL); + server_transport = g_object_new (THRIFT_TYPE_SERVER_SOCKET, + "port", port, + NULL); + transport_factory = g_object_new (transport_factory_type, + NULL); + + if (strncmp (protocol_name, "compact", 8) == 0) { + protocol_factory = g_object_new (protocol_factory_type, + "string_limit", string_limit, + "container_limit", container_limit, + NULL); + } else { + protocol_factory = g_object_new (protocol_factory_type, + NULL); + } + + server = g_object_new (THRIFT_TYPE_SIMPLE_SERVER, + "processor", processor, + "server_transport", server_transport, + "input_transport_factory", transport_factory, + "output_transport_factory", transport_factory, + "input_protocol_factory", protocol_factory, + "output_protocol_factory", protocol_factory, + NULL); + + /* Install our SIGINT handler, which handles Ctrl-C being pressed by stopping + the server gracefully */ + memset (&sigint_action, 0, sizeof (sigint_action)); + sigint_action.sa_handler = sigint_handler; + 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); + fflush (stdout); + + /* Serve clients until SIGINT is received (Ctrl-C is pressed) */ + thrift_server_serve (server, &error); + + /* If the server stopped for any reason other than being interrupted by the + user, report the error */ + if (!sigint_received) { + g_message ("thrift_server_serve: %s", + error != NULL ? error->message : "(null)"); + g_clear_error (&error); + } + + puts ("done."); + + g_object_unref (server); + g_object_unref (protocol_factory); + g_object_unref (transport_factory); + g_object_unref (server_transport); + g_object_unref (processor); + g_object_unref (handler); + + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.c b/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.c new file mode 100644 index 00000000..1d8bcb25 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.c @@ -0,0 +1,837 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 "thrift_test_handler.h" + +/* A handler that implements the TTestThriftTestIf interface */ + +G_DEFINE_TYPE (ThriftTestHandler, + thrift_test_handler, + T_TEST_TYPE_THRIFT_TEST_HANDLER); + +gboolean +thrift_test_handler_test_void (TTestThriftTestIf *iface, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testVoid()\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_string (TTestThriftTestIf *iface, + gchar **_return, + const gchar *thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testString(\"%s\")\n", thing); + *_return = g_strdup (thing); + + return TRUE; +} + +gboolean +thrift_test_handler_test_bool (TTestThriftTestIf *iface, + gboolean *_return, + const gboolean thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testBool(%s)\n", thing ? "true" : "false"); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_byte (TTestThriftTestIf *iface, + gint8 *_return, + const gint8 thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testByte(%d)\n", (gint)thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_i32 (TTestThriftTestIf *iface, + gint32 *_return, + const gint32 thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testI32(%d)\n", thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_i64 (TTestThriftTestIf *iface, + gint64 *_return, + const gint64 thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testI64(%" PRId64 ")\n", thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_double (TTestThriftTestIf *iface, + gdouble *_return, + const gdouble thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testDouble(%f)\n", thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_binary (TTestThriftTestIf *iface, + GByteArray ** _return, + const GByteArray * thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testBinary()\n"); // TODO: hex output + g_byte_array_ref((GByteArray *)thing); + *_return = (GByteArray *)thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_struct (TTestThriftTestIf *iface, + TTestXtruct **_return, + const TTestXtruct *thing, + GError **error) +{ + gchar *string_thing = NULL; + gint byte_thing; + gint i32_thing; + gint64 i64_thing; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + g_object_get ((TTestXtruct *)thing, + "string_thing", &string_thing, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + + printf ("testStruct({\"%s\", %d, %d, %" PRId64 "})\n", + string_thing, + (gint)byte_thing, + i32_thing, + i64_thing); + + g_object_set (*_return, + "string_thing", string_thing, + "byte_thing", byte_thing, + "i32_thing", i32_thing, + "i64_thing", i64_thing, + NULL); + + if (string_thing != NULL) + g_free (string_thing); + + return TRUE; +} + +gboolean +thrift_test_handler_test_nest (TTestThriftTestIf *iface, + TTestXtruct2 **_return, + const TTestXtruct2 *thing, + GError **error) +{ + gchar *inner_string_thing = NULL; + gint byte_thing, inner_byte_thing; + gint i32_thing, inner_i32_thing; + gint64 inner_i64_thing; + TTestXtruct *struct_thing; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + g_object_get ((TTestXtruct2 *)thing, + "byte_thing", &byte_thing, + "struct_thing", &struct_thing, + "i32_thing", &i32_thing, + NULL); + g_object_get (struct_thing, + "string_thing", &inner_string_thing, + "byte_thing", &inner_byte_thing, + "i32_thing", &inner_i32_thing, + "i64_thing", &inner_i64_thing, + NULL); + + printf ("testNest({%d, {\"%s\", %d, %d, %" PRId64 "}, %d})\n", + byte_thing, + inner_string_thing, + inner_byte_thing, + inner_i32_thing, + inner_i64_thing, + i32_thing); + + g_object_set (*_return, + "byte_thing", byte_thing, + "struct_thing", struct_thing, + "i32_thing", i32_thing, + NULL); + + if (inner_string_thing != NULL) + g_free (inner_string_thing); + g_object_unref (struct_thing); + + return TRUE; +} + +gboolean +thrift_test_handler_test_map (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error) +{ + GHashTableIter hash_table_iter; + gpointer key; + gpointer value; + gboolean first = TRUE; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testMap({"); + g_hash_table_iter_init (&hash_table_iter, (GHashTable *)thing); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + gint32 *new_key; + gint32 *new_value; + + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d => %d", *(gint32 *)key, *(gint32 *)value); + + new_key = g_malloc (sizeof *new_key); + *new_key = *(gint32 *)key; + new_value = g_malloc (sizeof *new_value); + *new_value = *(gint32 *)value; + g_hash_table_insert (*_return, new_key, new_value); + } + printf ("})\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_string_map (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error) +{ + GHashTableIter hash_table_iter; + gpointer key; + gpointer value; + gboolean first = TRUE; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testStringMap({"); + g_hash_table_iter_init (&hash_table_iter, (GHashTable *)thing); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + gchar *new_key; + gchar *new_value; + + if (first) + first = FALSE; + else + printf (", "); + + printf ("%s => %s", (gchar *)key, (gchar *)value); + + new_key = g_strdup ((gchar *)key); + new_value = g_strdup ((gchar *)value); + g_hash_table_insert (*_return, new_key, new_value); + } + printf ("})\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_set (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error) +{ + GHashTableIter hash_table_iter; + gpointer key; + gboolean first = TRUE; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testSet({"); + g_hash_table_iter_init (&hash_table_iter, (GHashTable *)thing); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + NULL)) { + gint32 *new_key; + + if (first) + first = FALSE; + else + printf (", "); + + printf ("%d", *(gint32 *)key); + + new_key = g_malloc (sizeof *new_key); + *new_key = *(gint32 *)key; + g_hash_table_insert (*_return, new_key, NULL); + } + printf ("})\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_list (TTestThriftTestIf *iface, + GArray **_return, + const GArray *thing, + GError **error) +{ + guint i; + gboolean first = TRUE; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testList({"); + for (i = 0; i < thing->len; i += 1) { + gint32 value; + gint32 *new_value; + + if (first) + first = FALSE; + else + printf (", "); + + 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); + } + printf ("})\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_enum (TTestThriftTestIf *iface, + TTestNumberz *_return, + const TTestNumberz thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testEnum(%d)\n", thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_typedef (TTestThriftTestIf *iface, + TTestUserId *_return, + const TTestUserId thing, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testTypedef(%" PRId64 ")\n", thing); + *_return = thing; + + return TRUE; +} + +gboolean +thrift_test_handler_test_map_map (TTestThriftTestIf *iface, + GHashTable **_return, + const gint32 hello, + GError **error) +{ + GHashTable *positive; + GHashTable *negative; + gint32 *key; + gint32 *value; + guint i; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testMapMap(%d)\n", hello); + + positive = g_hash_table_new_full (g_int_hash, + g_int_equal, + g_free, + g_free); + negative = g_hash_table_new_full (g_int_hash, + g_int_equal, + g_free, + g_free); + + for (i = 1; i < 5; i += 1) { + key = g_malloc (sizeof *key); + value = g_malloc (sizeof *value); + *key = i; + *value = i; + g_hash_table_insert (positive, key, value); + + key = g_malloc (sizeof *key); + value = g_malloc (sizeof *value); + *key = -i; + *value = -i; + g_hash_table_insert (negative, key, value); + } + + key = g_malloc (sizeof *key); + *key = 4; + g_hash_table_insert (*_return, key, positive); + + key = g_malloc (sizeof *key); + *key = -4; + g_hash_table_insert (*_return, key, negative); + + return TRUE; +} + +gboolean +thrift_test_handler_test_insanity (TTestThriftTestIf *iface, + GHashTable **_return, + const TTestInsanity *argument, + GError **error) +{ + TTestXtruct *xtruct_in; + + gchar *string_thing = NULL; + gint byte_thing; + gint i32_thing; + gint64 i64_thing; + + GPtrArray *xtructs; + + TTestInsanity *looney; + + GHashTable *user_map; + GHashTable *first_map; + GHashTable *second_map; + + GHashTableIter hash_table_iter; + GHashTableIter inner_hash_table_iter; + GHashTableIter user_map_iter; + + gpointer key; + gpointer value; + + TTestUserId *user_id; + + guint i; + + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testInsanity()\n"); + + first_map = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + second_map = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); + + g_hash_table_insert (first_map, + GINT_TO_POINTER (T_TEST_NUMBERZ_TWO), + (gpointer)argument); + g_hash_table_insert (first_map, + GINT_TO_POINTER (T_TEST_NUMBERZ_THREE), + (gpointer)argument); + + /* Increment argument's ref count by two because first_map now holds + two references to it and the caller is not aware we have made any + additional references to argument. (That is, caller owns argument + and will unref it explicitly in addition to unref-ing *_return.) + + We do this instead of creating a copy of argument in order to mimic + the C++ implementation (and since, frankly, the world needs less + argument, not more). */ + g_object_ref ((gpointer)argument); + g_object_ref ((gpointer)argument); + + looney = g_object_new (T_TEST_TYPE_INSANITY, NULL); + g_hash_table_insert (second_map, + GINT_TO_POINTER (T_TEST_NUMBERZ_SIX), + looney); + + user_id = g_malloc (sizeof *user_id); + *user_id = 1; + g_hash_table_insert (*_return, user_id, first_map); + + user_id = g_malloc (sizeof *user_id); + *user_id = 2; + g_hash_table_insert (*_return, user_id, second_map); + + printf ("return"); + printf (" = {"); + g_hash_table_iter_init (&hash_table_iter, *_return); + while (g_hash_table_iter_next (&hash_table_iter, + &key, + &value)) { + printf ("%" PRId64 " => {", *(TTestUserId *)key); + + g_hash_table_iter_init (&inner_hash_table_iter, + (GHashTable *)value); + while (g_hash_table_iter_next (&inner_hash_table_iter, + &key, + &value)) { + printf ("%d => {", (TTestNumberz)key); + + g_object_get ((TTestInsanity *)value, + "userMap", &user_map, + "xtructs", &xtructs, + NULL); + + printf ("{"); + g_hash_table_iter_init (&user_map_iter, user_map); + while (g_hash_table_iter_next (&user_map_iter, + &key, + &value)) { + printf ("%d => %" PRId64 ", ", + (TTestNumberz)key, + *(TTestUserId *)value); + } + printf ("}, "); + g_hash_table_unref (user_map); + + printf ("{"); + for (i = 0; i < xtructs->len; ++i) { + xtruct_in = g_ptr_array_index (xtructs, i); + g_object_get (xtruct_in, + "string_thing", &string_thing, + "byte_thing", &byte_thing, + "i32_thing", &i32_thing, + "i64_thing", &i64_thing, + NULL); + + printf ("{\"%s\", %d, %d, %" PRId64 "}, ", + string_thing, + byte_thing, + i32_thing, + i64_thing); + } + printf ("}"); + g_ptr_array_unref (xtructs); + + printf ("}, "); + } + printf ("}, "); + } + printf ("}\n"); + + return TRUE; +} + +gboolean +thrift_test_handler_test_multi (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gint8 arg0, + const gint32 arg1, + const gint64 arg2, + const GHashTable *arg3, + const TTestNumberz arg4, + const TTestUserId arg5, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + THRIFT_UNUSED_VAR (arg3); + THRIFT_UNUSED_VAR (arg4); + THRIFT_UNUSED_VAR (arg5); + + printf ("testMulti()\n"); + + g_object_set (*_return, + "string_thing", g_strdup ("Hello2"), + "byte_thing", arg0, + "i32_thing", arg1, + "i64_thing", arg2, + NULL); + + return TRUE; +} + +gboolean +thrift_test_handler_test_exception (TTestThriftTestIf *iface, + const gchar *arg, + TTestXception **err1, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + + TTestXtruct *xtruct; + gboolean result; + + printf ("testException(%s)\n", arg); + + /* Unlike argument objects, exception objects are not pre-created */ + g_assert (*err1 == NULL); + + if (strncmp (arg, "Xception", 9) == 0) { + /* "Throw" a custom exception: Set the corresponding exception + argument, set *error to NULL and return FALSE */ + *err1 = g_object_new (T_TEST_TYPE_XCEPTION, + "errorCode", 1001, + "message", g_strdup (arg), + NULL); + *error = NULL; + result = FALSE; + } + else if (strncmp (arg, "TException", 11) == 0) { + /* "Throw" a generic TException (ThriftApplicationException): Set + all exception arguments to NULL, set *error and return FALSE */ + *err1 = NULL; + g_set_error (error, + thrift_application_exception_error_quark (), + THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN, + "Default TException."); + result = FALSE; + } + else { + *err1 = NULL; + *error = NULL; + + /* 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), + NULL); + g_object_unref (xtruct); + + result = TRUE; + } + + return result; +} + +gboolean +thrift_test_handler_test_multi_exception (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gchar *arg0, + const gchar *arg1, + TTestXception **err1, + TTestXception2 **err2, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + TTestXtruct *struct_thing; + gboolean result; + + printf ("testMultiException(%s, %s)\n", arg0, arg1); + + g_assert (*err1 == NULL); + g_assert (*err2 == NULL); + + 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"), + NULL); + result = FALSE; + } + else if (strncmp (arg0, "Xception2", 9) == 0) { + *err2 = g_object_new (T_TEST_TYPE_XCEPTION2, + "errorCode", 2002, + NULL); + + g_object_get (*err2, + "struct_thing", &struct_thing, + NULL); + g_object_set (struct_thing, + "string_thing", g_strdup ("This is an Xception2"), + NULL); + g_object_set (*err2, + "struct_thing", struct_thing, + NULL); + g_object_unref (struct_thing); + + result = FALSE; + } + else { + g_object_set (*_return, + "string_thing", g_strdup (arg1), + NULL); + result = TRUE; + } + + return result; +} + +gboolean +thrift_test_handler_test_oneway (TTestThriftTestIf *iface, + const gint32 secondsToSleep, + GError **error) +{ + THRIFT_UNUSED_VAR (iface); + THRIFT_UNUSED_VAR (error); + + printf ("testOneway(%d): Sleeping...\n", secondsToSleep); + sleep (secondsToSleep); + printf ("testOneway(%d): done sleeping!\n", secondsToSleep); + + return TRUE; +} + +static void +thrift_test_handler_init (ThriftTestHandler *self) +{ + THRIFT_UNUSED_VAR (self); +} + +static void +thrift_test_handler_class_init (ThriftTestHandlerClass *klass) +{ + TTestThriftTestHandlerClass *base_class = + T_TEST_THRIFT_TEST_HANDLER_CLASS (klass); + + base_class->test_void = + klass->test_void = + thrift_test_handler_test_void; + base_class->test_string = + klass->test_string = + thrift_test_handler_test_string; + base_class->test_bool = + klass->test_bool = + thrift_test_handler_test_bool; + base_class->test_byte = + klass->test_byte = + thrift_test_handler_test_byte; + base_class->test_i32 = + klass->test_i32 = + thrift_test_handler_test_i32; + base_class->test_i64 = + klass->test_i64 = + thrift_test_handler_test_i64; + base_class->test_double = + klass->test_double = + thrift_test_handler_test_double; + base_class->test_binary = + klass->test_binary = + thrift_test_handler_test_binary; + base_class->test_struct = + klass->test_struct = + thrift_test_handler_test_struct; + base_class->test_nest = + klass->test_nest = + thrift_test_handler_test_nest; + base_class->test_map = + klass->test_map = + thrift_test_handler_test_map; + base_class->test_string_map = + klass->test_string_map = + thrift_test_handler_test_string_map; + base_class->test_set = + klass->test_set = + thrift_test_handler_test_set; + base_class->test_list = + klass->test_list = + thrift_test_handler_test_list; + base_class->test_enum = + klass->test_enum = + thrift_test_handler_test_enum; + base_class->test_typedef = + klass->test_typedef = + thrift_test_handler_test_typedef; + base_class->test_map_map = + klass->test_map_map = + thrift_test_handler_test_map_map; + base_class->test_insanity = + klass->test_insanity = + thrift_test_handler_test_insanity; + base_class->test_multi = + klass->test_multi = + thrift_test_handler_test_multi; + base_class->test_exception = + klass->test_exception = + thrift_test_handler_test_exception; + base_class->test_multi_exception = + klass->test_multi_exception = + thrift_test_handler_test_multi_exception; + base_class->test_oneway = + klass->test_oneway = + thrift_test_handler_test_oneway; +} diff --git a/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.h b/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.h new file mode 100644 index 00000000..b64cb16f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/c_glib/src/thrift_test_handler.h @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef _THRIFT_TEST_HANDLER_H +#define _THRIFT_TEST_HANDLER_H + +#include +#include + +#include "../gen-c_glib/t_test_thrift_test.h" + +G_BEGIN_DECLS + +/* A handler that implements the TTestThriftTestIf interface */ + +#define TYPE_THRIFT_TEST_HANDLER (thrift_test_handler_get_type ()) + +#define THRIFT_TEST_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TYPE_THRIFT_TEST_HANDLER, \ + ThriftTestHandler)) +#define IS_THRIFT_TEST_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TYPE_THRIFT_TEST_HANDLER)) +#define THRIFT_TEST_HANDLER_CLASS(c) \ + (G_TYPE_CHECK_CLASS_CAST ((c), \ + TYPE_THRIFT_TEST_HANDLER, \ + ThriftTestHandlerClass)) +#define IS_THRIFT_TEST_HANDLER_CLASS(c) \ + (G_TYPE_CHECK_CLASS_TYPE ((c), \ + TYPE_THRIFT_TEST_HANDLER)) +#define THRIFT_TEST_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TYPE_THRIFT_TEST_HANDLER, \ + ThriftTestHandlerClass)) + +typedef struct _ThriftTestHandler ThriftTestHandler; +typedef struct _ThriftTestHandlerClass ThriftTestHandlerClass; + +struct _ThriftTestHandler { + TTestThriftTestHandler parent; +}; + +struct _ThriftTestHandlerClass { + TTestThriftTestHandlerClass parent; + + gboolean (*test_void) (TTestThriftTestIf *iface, + GError **error); + gboolean (*test_string) (TTestThriftTestIf *iface, + gchar **_return, + const gchar *thing, + GError **error); + gboolean (*test_bool) (TTestThriftTestIf *iface, + gboolean*_return, + const gboolean thing, + GError **error); + gboolean (*test_byte) (TTestThriftTestIf *iface, + gint8*_return, + const gint8 thing, + GError **error); + gboolean (*test_i32) (TTestThriftTestIf *iface, + gint32*_return, + const gint32 thing, + GError **error); + gboolean (*test_i64) (TTestThriftTestIf *iface, + gint64*_return, + const gint64 thing, + GError **error); + gboolean (*test_double) (TTestThriftTestIf *iface, + gdouble*_return, + const gdouble thing, + GError **error); + gboolean (*test_binary) (TTestThriftTestIf *iface, + GByteArray **_return, + const GByteArray *thing, + GError **error); + gboolean (*test_struct) (TTestThriftTestIf *iface, + TTestXtruct **_return, + const TTestXtruct *thing, + GError **error); + gboolean (*test_nest) (TTestThriftTestIf *iface, + TTestXtruct2 **_return, + const TTestXtruct2 *thing, + GError **error); + gboolean (*test_map) (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); + gboolean (*test_string_map) (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); + gboolean (*test_set) (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); + gboolean (*test_list) (TTestThriftTestIf *iface, + GArray **_return, + const GArray *thing, + GError **error); + gboolean (*test_enum) (TTestThriftTestIf *iface, + TTestNumberz*_return, + const TTestNumberz thing, + GError **error); + gboolean (*test_typedef) (TTestThriftTestIf *iface, + TTestUserId*_return, + const TTestUserId thing, + GError **error); + gboolean (*test_map_map) (TTestThriftTestIf *iface, + GHashTable **_return, + const gint32 hello, + GError **error); + gboolean (*test_insanity) (TTestThriftTestIf *iface, + GHashTable **_return, + const TTestInsanity *argument, + GError **error); + gboolean (*test_multi) (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gint8 arg0, + const gint32 arg1, + const gint64 arg2, + const GHashTable *arg3, + const TTestNumberz arg4, + const TTestUserId arg5, + GError **error); + gboolean (*test_exception) (TTestThriftTestIf *iface, + const gchar *arg, + TTestXception **err1, + GError **error); + gboolean (*test_multi_exception) (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gchar *arg0, + const gchar *arg1, + TTestXception **err1, + TTestXception2 **err2, + GError **error); + gboolean (*test_oneway) (TTestThriftTestIf *iface, + const gint32 secondsToSleep, + GError **error); +}; + +/* Used by THRIFT_TEST_HANDLER_GET_TYPE */ +GType thrift_test_handler_get_type (void); + +gboolean thrift_test_handler_test_void (TTestThriftTestIf *iface, + GError **error); +gboolean thrift_test_handler_test_string (TTestThriftTestIf *iface, + gchar **_return, + const gchar *thing, + GError **error); +gboolean thrift_test_handler_test_byte (TTestThriftTestIf *iface, + gint8*_return, + const gint8 thing, + GError **error); +gboolean t_test_thrift_test_if_test_i32 (TTestThriftTestIf *iface, + gint32*_return, + const gint32 thing, + GError **error); +gboolean thrift_test_handler_test_i64 (TTestThriftTestIf *iface, + gint64*_return, + const gint64 thing, + GError **error); +gboolean thrift_test_handler_test_double (TTestThriftTestIf *iface, + gdouble*_return, + const gdouble thing, + GError **error); +gboolean thrift_test_handler_test_struct (TTestThriftTestIf *iface, + TTestXtruct **_return, + const TTestXtruct *thing, + GError **error); +gboolean thrift_test_handler_test_nest (TTestThriftTestIf *iface, + TTestXtruct2 **_return, + const TTestXtruct2 *thing, + GError **error); +gboolean thrift_test_handler_test_map (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); +gboolean thrift_test_handler_test_string_map (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); +gboolean thrift_test_handler_test_set (TTestThriftTestIf *iface, + GHashTable **_return, + const GHashTable *thing, + GError **error); +gboolean thrift_test_handler_test_list (TTestThriftTestIf *iface, + GArray **_return, + const GArray *thing, + GError **error); +gboolean thrift_test_handler_test_typedef (TTestThriftTestIf *iface, + TTestUserId*_return, + const TTestUserId thing, + GError **error); +gboolean thrift_test_handler_test_map_map (TTestThriftTestIf *iface, + GHashTable **_return, + const gint32 hello, + GError **error); +gboolean thrift_test_handler_test_insanity (TTestThriftTestIf *iface, + GHashTable **_return, + const TTestInsanity *argument, + GError **error); +gboolean thrift_test_handler_test_multi (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gint8 arg0, + const gint32 arg1, + const gint64 arg2, + const GHashTable *arg3, + const TTestNumberz arg4, + const TTestUserId arg5, + GError **error); +gboolean thrift_test_handler_test_exception (TTestThriftTestIf *iface, + const gchar *arg, + TTestXception **err1, + GError **error); +gboolean thrift_test_handler_test_multi_exception (TTestThriftTestIf *iface, + TTestXtruct **_return, + const gchar *arg0, + const gchar *arg1, + TTestXception **err1, + TTestXception2 **err2, + GError **error); +gboolean thrift_test_handler_test_oneway (TTestThriftTestIf *iface, + const gint32 secondsToSleep, + GError **error); + +G_END_DECLS + +#endif /* _THRIFT_TEST_HANDLER_H */ diff --git a/vendor/src/github.com/apache/thrift/test/cpp/CMakeLists.txt b/vendor/src/github.com/apache/thrift/test/cpp/CMakeLists.txt new file mode 100644 index 00000000..09850a81 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/CMakeLists.txt @@ -0,0 +1,89 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# 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}") + +find_package(Libevent REQUIRED) # Libevent comes with CMake support from upstream +include_directories(SYSTEM ${LIBEVENT_INCLUDE_DIRS}) + +#Make sure gen-cpp files can be included +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-cpp") +include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src") + + +set(crosstestgencpp_SOURCES + gen-cpp/ThriftTest.cpp + gen-cpp/ThriftTest_types.cpp + gen-cpp/ThriftTest_constants.cpp + src/ThriftTest_extras.cpp +) +add_library(crosstestgencpp STATIC ${crosstestgencpp_SOURCES}) +LINK_AGAINST_THRIFT_LIBRARY(crosstestgencpp thrift) + +set(crossstressgencpp_SOURCES + gen-cpp/Service.cpp + #gen-cpp/StressTest_types.cpp #basically empty, so omitting + gen-cpp/StressTest_constants.cpp +) +add_library(crossstressgencpp STATIC ${crossstressgencpp_SOURCES}) +LINK_AGAINST_THRIFT_LIBRARY(crossstressgencpp thrift) + +add_executable(TestServer src/TestServer.cpp) +target_link_libraries(TestServer crosstestgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) +LINK_AGAINST_THRIFT_LIBRARY(TestServer thrift) +LINK_AGAINST_THRIFT_LIBRARY(TestServer thriftnb) +LINK_AGAINST_THRIFT_LIBRARY(TestServer thriftz) + +add_executable(TestClient src/TestClient.cpp) +target_link_libraries(TestClient crosstestgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) +LINK_AGAINST_THRIFT_LIBRARY(TestClient thrift) +LINK_AGAINST_THRIFT_LIBRARY(TestClient thriftnb) +LINK_AGAINST_THRIFT_LIBRARY(TestClient thriftz) + +add_executable(StressTest src/StressTest.cpp) +target_link_libraries(StressTest crossstressgencpp ${Boost_LIBRARIES} ${LIBEVENT_LIB}) +LINK_AGAINST_THRIFT_LIBRARY(StressTest thrift) +LINK_AGAINST_THRIFT_LIBRARY(StressTest thriftnb) +add_test(NAME StressTest COMMAND StressTest) + +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) + +# +# Common thrift code generation rules +# + +add_custom_command(OUTPUT gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_constants.cpp + 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 + COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/StressTest.thrift +) diff --git a/vendor/src/github.com/apache/thrift/test/cpp/Makefile.am b/vendor/src/github.com/apache/thrift/test/cpp/Makefile.am new file mode 100644 index 00000000..9c4ac073 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/Makefile.am @@ -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. +# +AUTOMAKE_OPTIONS = subdir-objects serial-tests + +BUILT_SOURCES = gen-cpp/ThriftTest.cpp \ + gen-cpp/ThriftTest_types.cpp \ + gen-cpp/ThriftTest_constants.cpp \ + gen-cpp/StressTest_types.cpp \ + gen-cpp/StressTest_constants.cpp \ + gen-cpp/Service.cpp + +noinst_LTLIBRARIES = libtestgencpp.la libstresstestgencpp.la +nodist_libtestgencpp_la_SOURCES = \ + gen-cpp/ThriftTest_constants.cpp \ + gen-cpp/ThriftTest_constants.h \ + gen-cpp/ThriftTest_types.cpp \ + gen-cpp/ThriftTest_types.h \ + gen-cpp/ThriftTest_types.tcc \ + gen-cpp/ThriftTest.cpp \ + gen-cpp/ThriftTest.h \ + gen-cpp/ThriftTest.tcc \ + src/ThriftTest_extras.cpp + +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 + +libstresstestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la + +precross: TestServer TestClient + +check_PROGRAMS = \ + TestServer \ + TestClient \ + StressTest \ + StressTestNonBlocking + +# we currently do not run the testsuite, stop c++ server issue +# TESTS = \ +# $(check_PROGRAMS) + +TestServer_SOURCES = \ + src/TestServer.cpp + +TestServer_LDADD = \ + libtestgencpp.la \ + $(top_builddir)/lib/cpp/libthrift.la \ + $(top_builddir)/lib/cpp/libthriftz.la \ + $(top_builddir)/lib/cpp/libthriftnb.la \ + -levent -lboost_program_options -lboost_system -lboost_filesystem $(ZLIB_LIBS) + +TestClient_SOURCES = \ + src/TestClient.cpp + +TestClient_LDADD = \ + libtestgencpp.la \ + $(top_builddir)/lib/cpp/libthrift.la \ + $(top_builddir)/lib/cpp/libthriftz.la \ + $(top_builddir)/lib/cpp/libthriftnb.la \ + -levent -lboost_program_options -lboost_system -lboost_filesystem $(ZLIB_LIBS) + +StressTest_SOURCES = \ + src/StressTest.cpp + +StressTest_LDADD = \ + libstresstestgencpp.la \ + $(top_builddir)/lib/cpp/libthrift.la + +StressTestNonBlocking_SOURCES = \ + src/StressTestNonBlocking.cpp + +StressTestNonBlocking_LDADD = \ + libstresstestgencpp.la \ + $(top_builddir)/lib/cpp/libthriftnb.la \ + -levent +# +# Common thrift code generation rules +# +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_constants.cpp: $(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) + $(THRIFT) --gen cpp $< + +AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp +AM_CXXFLAGS = -Wall -Wextra -pedantic +AM_LDFLAGS = $(BOOST_LDFLAGS) $(LIBEVENT_LDFLAGS) $(ZLIB_LIBS) + +clean-local: + $(RM) gen-cpp/* + +style-local: + $(CPPSTYLE_CMD) + +EXTRA_DIST = \ + src/TestClient.cpp \ + src/TestServer.cpp \ + src/StressTest.cpp \ + src/StressTestNonBlocking.cpp \ + realloc/realloc_test.c \ + realloc/Makefile diff --git a/vendor/src/github.com/apache/thrift/test/cpp/realloc/Makefile b/vendor/src/github.com/apache/thrift/test/cpp/realloc/Makefile new file mode 100644 index 00000000..f89bbb3c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/realloc/Makefile @@ -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. +# + +# This probably should not go into "make check", because it is an experiment, +# not a test. Specifically, it is meant to determine how likely realloc is +# to avoid a copy. This is poorly documented. + +run: realloc_test + for it in 1 4 64 ; do \ + for nb in 1 8 64 512 ; do \ + for mins in 64 512 ; do \ + for maxs in 2048 262144 ; do \ + for db in 8 64 ; do \ + ./realloc_test $$nb $$mins $$maxs $$db $$it \ + ; done \ + ; done \ + ; done \ + ; done \ + ; done \ + > raw_stats + +CFLAGS = -Wall -g -std=c99 +LDLIBS = -ldl +realloc_test: realloc_test.c diff --git a/vendor/src/github.com/apache/thrift/test/cpp/realloc/realloc_test.c b/vendor/src/github.com/apache/thrift/test/cpp/realloc/realloc_test.c new file mode 100644 index 00000000..f9763adf --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/realloc/realloc_test.c @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +int copies; +int non_copies; + +void *realloc(void *ptr, size_t size) { + static void *(*real_realloc)(void*, size_t) = NULL; + if (real_realloc == NULL) { + real_realloc = (void* (*) (void*, size_t)) dlsym(RTLD_NEXT, "realloc"); + } + + void *ret_ptr = (*real_realloc)(ptr, size); + + if (ret_ptr == ptr) { + non_copies++; + } else { + copies++; + } + + return ret_ptr; +} + + +struct TMemoryBuffer { + void* ptr; + int size; +}; + +int main(int argc, char *argv[]) { + int num_buffers; + int init_size; + int max_size; + int doublings; + int iterations; + + if (argc < 6 || + argc > 7 || + (num_buffers = atoi(argv[1])) == 0 || + (init_size = atoi(argv[2])) == 0 || + (max_size = atoi(argv[3])) == 0 || + init_size > max_size || + (iterations = atoi(argv[4])) == 0 || + (doublings = atoi(argv[5])) == 0 || + (argc == 7 && atoi(argv[6]) == 0)) { + fprintf(stderr, "usage: realloc_test [seed]\n"); + exit(EXIT_FAILURE); + } + + for ( int i = 0 ; i < argc ; i++ ) { + printf("%s ", argv[i]); + } + printf("\n"); + + if (argc == 7) { + srand(atoi(argv[6])); + } else { + srand(time(NULL)); + } + + struct TMemoryBuffer* buffers = calloc(num_buffers, sizeof(*buffers)); + if (buffers == NULL) abort(); + + for ( int i = 0 ; i < num_buffers ; i++ ) { + buffers[i].size = max_size; + } + + while (iterations --> 0) { + for ( int i = 0 ; i < doublings * num_buffers ; i++ ) { + struct TMemoryBuffer* buf = &buffers[rand() % num_buffers]; + buf->size *= 2; + if (buf->size <= max_size) { + buf->ptr = realloc(buf->ptr, buf->size); + } else { + free(buf->ptr); + buf->size = init_size; + buf->ptr = malloc(buf->size); + } + if (buf->ptr == NULL) abort(); + } + } + + printf("Non-copied %d/%d (%.2f%%)\n", non_copies, copies + non_copies, 100.0 * non_copies / (copies + non_copies)); + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/test/cpp/src/StressTest.cpp b/vendor/src/github.com/apache/thrift/test/cpp/src/StressTest.cpp new file mode 100644 index 00000000..9371bce7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/src/StressTest.cpp @@ -0,0 +1,605 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include +#include +#include +#include + +#include "Service.h" +#include +#include +#include +#include +#include +#if _WIN32 +#include +#endif + +using namespace std; + +using namespace apache::thrift; +using namespace apache::thrift::protocol; +using namespace apache::thrift::transport; +using namespace apache::thrift::server; +using namespace apache::thrift::concurrency; + +using namespace test::stress; + +struct eqstr { + bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) == 0; } +}; + +struct ltstr { + bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } +}; + +// typedef hash_map, eqstr> count_map; +typedef map count_map; + +class Server : public ServiceIf { +public: + Server() {} + + void count(const char* method) { + Guard m(lock_); + int ct = counts_[method]; + counts_[method] = ++ct; + } + + void echoVoid() { + count("echoVoid"); + return; + } + + count_map getCount() { + Guard m(lock_); + 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) { + 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; } + +private: + count_map counts_; + Mutex lock_; +}; + +enum TransportOpenCloseBehavior { + OpenAndCloseTransportInThread, + DontOpenAndCloseTransportInThread +}; +class ClientThread : public Runnable { +public: + ClientThread(boost::shared_ptr transport, + boost::shared_ptr client, + Monitor& monitor, + size_t& workerCount, + size_t loopCount, + TType loopType, + TransportOpenCloseBehavior behavior) + : _transport(transport), + _client(client), + _monitor(monitor), + _workerCount(workerCount), + _loopCount(loopCount), + _loopType(loopType), + _behavior(behavior) {} + + void run() { + + // Wait for all worker threads to start + + { + Synchronized s(_monitor); + while (_workerCount == 0) { + _monitor.wait(); + } + } + + _startTime = Util::currentTime(); + if(_behavior == OpenAndCloseTransportInThread) { + _transport->open(); + } + + switch (_loopType) { + case T_VOID: + loopEchoVoid(); + break; + case T_BYTE: + loopEchoByte(); + break; + case T_I32: + loopEchoI32(); + break; + case T_I64: + loopEchoI64(); + break; + case T_STRING: + loopEchoString(); + break; + default: + cerr << "Unexpected loop type" << _loopType << endl; + break; + } + + _endTime = Util::currentTime(); + + if(_behavior == OpenAndCloseTransportInThread) { + _transport->close(); + } + + _done = true; + + { + Synchronized s(_monitor); + + _workerCount--; + + if (_workerCount == 0) { + + _monitor.notify(); + } + } + } + + void loopEchoVoid() { + for (size_t ix = 0; ix < _loopCount; ix++) { + _client->echoVoid(); + } + } + + void loopEchoByte() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int8_t arg = 1; + int8_t result; + result = _client->echoByte(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoI32() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int32_t arg = 1; + int32_t result; + result = _client->echoI32(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoI64() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int64_t arg = 1; + int64_t result; + result = _client->echoI64(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoString() { + for (size_t ix = 0; ix < _loopCount; ix++) { + string arg = "hello"; + string result; + _client->echoString(result, arg); + assert(result == arg); + } + } + + boost::shared_ptr _transport; + boost::shared_ptr _client; + Monitor& _monitor; + size_t& _workerCount; + size_t _loopCount; + TType _loopType; + int64_t _startTime; + int64_t _endTime; + bool _done; + Monitor _sleep; + TransportOpenCloseBehavior _behavior; +}; + +class TStartObserver : public apache::thrift::server::TServerEventHandler { +public: + TStartObserver() : awake_(false) {} + virtual void preServe() { + apache::thrift::concurrency::Synchronized s(m_); + awake_ = true; + m_.notifyAll(); + } + void waitForService() { + apache::thrift::concurrency::Synchronized s(m_); + while (!awake_) + m_.waitForever(); + } + +private: + apache::thrift::concurrency::Monitor m_; + bool awake_; +}; + +int main(int argc, char** argv) { +#if _WIN32 + transport::TWinsockSingleton::create(); +#endif + + int port = 9091; + string clientType = "regular"; + string serverType = "thread-pool"; + string protocolType = "binary"; + size_t workerCount = 4; + size_t clientCount = 20; + size_t loopCount = 50000; + TType loopType = T_VOID; + string callName = "echoVoid"; + bool runServer = true; + bool logRequests = false; + string requestLogPath = "./requestlog.tlog"; + bool replayRequests = false; + + ostringstream usage; + + usage << argv[0] << " [--port=] [--server] [--server-type=] " + "[--protocol-type=] [--workers=] " + "[--clients=] [--loop=] " + "[--client-type=]" << endl + << "\tclients Number of client threads to create - 0 implies no clients, i.e. " + "server only. Default is " << clientCount << endl + << "\thelp Prints this help text." << endl + << "\tcall Service method to call. Default is " << callName << endl + << "\tloop The number of remote thrift calls each client makes. Default is " << loopCount << endl + << "\tport The port the server and clients should bind to " + "for thrift network connections. Default is " << port << endl + << "\tserver Run the Thrift server in this process. Default is " << runServer << endl + << "\tserver-type Type of server, \"simple\" or \"thread-pool\". Default is " << serverType << endl + << "\tprotocol-type Type of protocol, \"binary\", \"ascii\", or \"xml\". Default is " << protocolType << endl + << "\tlog-request Log all request to ./requestlog.tlog. Default is " << logRequests << endl + << "\treplay-request Replay requests from log file (./requestlog.tlog) Default is " << replayRequests << endl + << "\tworkers Number of thread pools workers. Only valid " + "for thread-pool server type. Default is " << workerCount << endl + << "\tclient-type Type of client, \"regular\" or \"concurrent\". Default is " << clientType << endl + << endl; + + map args; + + for (int ix = 1; ix < argc; ix++) { + + string arg(argv[ix]); + + if (arg.compare(0, 2, "--") == 0) { + + size_t end = arg.find_first_of("=", 2); + + string key = string(arg, 2, end - 2); + + if (end != string::npos) { + args[key] = string(arg, end + 1); + } else { + args[key] = "true"; + } + } else { + throw invalid_argument("Unexcepted command line token: " + arg); + } + } + + try { + + if (!args["clients"].empty()) { + clientCount = atoi(args["clients"].c_str()); + } + + if (!args["help"].empty()) { + cerr << usage.str(); + return 0; + } + + if (!args["loop"].empty()) { + loopCount = atoi(args["loop"].c_str()); + } + + if (!args["call"].empty()) { + callName = args["call"]; + } + + if (!args["port"].empty()) { + port = atoi(args["port"].c_str()); + } + + if (!args["server"].empty()) { + runServer = args["server"] == "true"; + } + + if (!args["log-request"].empty()) { + logRequests = args["log-request"] == "true"; + } + + if (!args["replay-request"].empty()) { + replayRequests = args["replay-request"] == "true"; + } + + if (!args["server-type"].empty()) { + serverType = args["server-type"]; + + if (serverType == "simple") { + + } else if (serverType == "thread-pool") { + + } else if (serverType == "threaded") { + + } else { + + throw invalid_argument("Unknown server type " + serverType); + } + } + if (!args["client-type"].empty()) { + clientType = args["client-type"]; + + if (clientType == "regular") { + + } else if (clientType == "concurrent") { + + } else { + + throw invalid_argument("Unknown client type " + clientType); + } + } + if (!args["workers"].empty()) { + workerCount = atoi(args["workers"].c_str()); + } + + } catch (std::exception& e) { + cerr << e.what() << endl; + cerr << usage.str(); + } + + boost::shared_ptr threadFactory + = boost::shared_ptr(new PlatformThreadFactory()); + + // Dispatcher + boost::shared_ptr serviceHandler(new Server()); + + if (replayRequests) { + boost::shared_ptr serviceHandler(new Server()); + boost::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + + // Transports + boost::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + fileTransport->setChunkSize(2 * 1024 * 1024); + fileTransport->setMaxEventSize(1024 * 16); + fileTransport->seekToEnd(); + + // Protocol Factory + boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + + TFileProcessor fileProcessor(serviceProcessor, protocolFactory, fileTransport); + + fileProcessor.process(0, true); + exit(0); + } + + if (runServer) { + + boost::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + + // Transport + boost::shared_ptr serverSocket(new TServerSocket(port)); + + // Transport Factory + boost::shared_ptr transportFactory(new TBufferedTransportFactory()); + + // Protocol Factory + boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + + if (logRequests) { + // initialize the log file + boost::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + fileTransport->setChunkSize(2 * 1024 * 1024); + fileTransport->setMaxEventSize(1024 * 16); + + transportFactory + = boost::shared_ptr(new TPipedTransportFactory(fileTransport)); + } + + boost::shared_ptr server; + + if (serverType == "simple") { + + server.reset( + new TSimpleServer(serviceProcessor, serverSocket, transportFactory, protocolFactory)); + + } else if (serverType == "threaded") { + + server.reset( + new TThreadedServer(serviceProcessor, serverSocket, transportFactory, protocolFactory)); + + } else if (serverType == "thread-pool") { + + boost::shared_ptr threadManager + = ThreadManager::newSimpleThreadManager(workerCount); + + threadManager->threadFactory(threadFactory); + threadManager->start(); + server.reset(new TThreadPoolServer(serviceProcessor, + serverSocket, + transportFactory, + protocolFactory, + threadManager)); + } + + boost::shared_ptr observer(new TStartObserver); + server->setServerEventHandler(observer); + boost::shared_ptr serverThread = threadFactory->newThread(server); + + cerr << "Starting the server on port " << port << endl; + + serverThread->start(); + observer->waitForService(); + + // If we aren't running clients, just wait forever for external clients + if (clientCount == 0) { + serverThread->join(); + } + } + + if (clientCount > 0) { //FIXME: start here for client type? + + Monitor monitor; + + size_t threadCount = 0; + + set > clientThreads; + + if (callName == "echoVoid") { + loopType = T_VOID; + } else if (callName == "echoByte") { + loopType = T_BYTE; + } else if (callName == "echoI32") { + loopType = T_I32; + } else if (callName == "echoI64") { + loopType = T_I64; + } else if (callName == "echoString") { + loopType = T_STRING; + } else { + throw invalid_argument("Unknown service call " + callName); + } + + if(clientType == "regular") { + for (size_t ix = 0; ix < clientCount; ix++) { + + boost::shared_ptr socket(new TSocket("127.0.0.1", port)); + boost::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); + boost::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); + boost::shared_ptr serviceClient(new ServiceClient(protocol)); + + clientThreads.insert(threadFactory->newThread(boost::shared_ptr( + new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType, OpenAndCloseTransportInThread)))); + } + } else if(clientType == "concurrent") { + boost::shared_ptr socket(new TSocket("127.0.0.1", port)); + boost::shared_ptr bufferedSocket(new TBufferedTransport(socket, 2048)); + boost::shared_ptr protocol(new TBinaryProtocol(bufferedSocket)); + //boost::shared_ptr serviceClient(new ServiceClient(protocol)); + boost::shared_ptr serviceClient(new ServiceConcurrentClient(protocol)); + socket->open(); + for (size_t ix = 0; ix < clientCount; ix++) { + clientThreads.insert(threadFactory->newThread(boost::shared_ptr( + new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType, DontOpenAndCloseTransportInThread)))); + } + } + + for (std::set >::const_iterator thread = clientThreads.begin(); + thread != clientThreads.end(); + thread++) { + (*thread)->start(); + } + + int64_t time00; + int64_t time01; + + { + Synchronized s(monitor); + threadCount = clientCount; + + cerr << "Launch " << clientCount << " " << clientType << " client threads" << endl; + + time00 = Util::currentTime(); + + monitor.notifyAll(); + + while (threadCount > 0) { + monitor.wait(); + } + + time01 = Util::currentTime(); + } + + int64_t firstTime = 9223372036854775807LL; + int64_t lastTime = 0; + + double averageTime = 0; + int64_t minTime = 9223372036854775807LL; + int64_t maxTime = 0; + + for (set >::iterator ix = clientThreads.begin(); + ix != clientThreads.end(); + ix++) { + + boost::shared_ptr client + = boost::dynamic_pointer_cast((*ix)->runnable()); + + int64_t delta = client->_endTime - client->_startTime; + + assert(delta > 0); + + if (client->_startTime < firstTime) { + firstTime = client->_startTime; + } + + if (client->_endTime > lastTime) { + lastTime = client->_endTime; + } + + if (delta < minTime) { + minTime = delta; + } + + if (delta > maxTime) { + maxTime = delta; + } + + averageTime += delta; + } + + averageTime /= clientCount; + + cout << "workers :" << workerCount << ", client : " << clientCount << ", loops : " << loopCount + << ", rate : " << (clientCount * loopCount * 1000) / ((double)(time01 - time00)) << endl; + + count_map count = serviceHandler->getCount(); + count_map::iterator iter; + for (iter = count.begin(); iter != count.end(); ++iter) { + printf("%s => %d\n", iter->first, iter->second); + } + cerr << "done." << endl; + } + + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/test/cpp/src/StressTestNonBlocking.cpp b/vendor/src/github.com/apache/thrift/test/cpp/src/StressTestNonBlocking.cpp new file mode 100644 index 00000000..8f161c05 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/src/StressTestNonBlocking.cpp @@ -0,0 +1,538 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 +#include +#include +#include +#include +#include + +#include "Service.h" + +#include + +#include +#include +#include +#include +#include +#if _WIN32 +#include +#endif + +using namespace std; + +using namespace apache::thrift; +using namespace apache::thrift::protocol; +using namespace apache::thrift::transport; +using namespace apache::thrift::server; +using namespace apache::thrift::concurrency; + +using namespace test::stress; + +struct eqstr { + bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) == 0; } +}; + +struct ltstr { + bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } +}; + +// typedef hash_map, eqstr> count_map; +typedef map count_map; + +class Server : public ServiceIf { +public: + Server() {} + + void count(const char* method) { + Guard m(lock_); + int ct = counts_[method]; + counts_[method] = ++ct; + } + + void echoVoid() { + count("echoVoid"); + // Sleep to simulate work + THRIFT_SLEEP_USEC(1); + return; + } + + count_map getCount() { + Guard m(lock_); + 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) { + 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; } + +private: + count_map counts_; + Mutex lock_; +}; + +class ClientThread : public Runnable { +public: + ClientThread(boost::shared_ptr transport, + boost::shared_ptr client, + Monitor& monitor, + size_t& workerCount, + size_t loopCount, + TType loopType) + : _transport(transport), + _client(client), + _monitor(monitor), + _workerCount(workerCount), + _loopCount(loopCount), + _loopType(loopType) {} + + void run() { + + // Wait for all worker threads to start + + { + Synchronized s(_monitor); + while (_workerCount == 0) { + _monitor.wait(); + } + } + + _startTime = Util::currentTime(); + + _transport->open(); + + switch (_loopType) { + case T_VOID: + loopEchoVoid(); + break; + case T_BYTE: + loopEchoByte(); + break; + case T_I32: + loopEchoI32(); + break; + case T_I64: + loopEchoI64(); + break; + case T_STRING: + loopEchoString(); + break; + default: + cerr << "Unexpected loop type" << _loopType << endl; + break; + } + + _endTime = Util::currentTime(); + + _transport->close(); + + _done = true; + + { + Synchronized s(_monitor); + + _workerCount--; + + if (_workerCount == 0) { + + _monitor.notify(); + } + } + } + + void loopEchoVoid() { + for (size_t ix = 0; ix < _loopCount; ix++) { + _client->echoVoid(); + } + } + + void loopEchoByte() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int8_t arg = 1; + int8_t result; + result = _client->echoByte(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoI32() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int32_t arg = 1; + int32_t result; + result = _client->echoI32(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoI64() { + for (size_t ix = 0; ix < _loopCount; ix++) { + int64_t arg = 1; + int64_t result; + result = _client->echoI64(arg); + (void)result; + assert(result == arg); + } + } + + void loopEchoString() { + for (size_t ix = 0; ix < _loopCount; ix++) { + string arg = "hello"; + string result; + _client->echoString(result, arg); + assert(result == arg); + } + } + + boost::shared_ptr _transport; + boost::shared_ptr _client; + Monitor& _monitor; + size_t& _workerCount; + size_t _loopCount; + TType _loopType; + int64_t _startTime; + int64_t _endTime; + bool _done; + Monitor _sleep; +}; + +int main(int argc, char** argv) { +#if _WIN32 + transport::TWinsockSingleton::create(); +#endif + + int port = 9091; + string serverType = "simple"; + string protocolType = "binary"; + uint32_t workerCount = 4; + uint32_t clientCount = 20; + uint32_t loopCount = 1000; + TType loopType = T_VOID; + string callName = "echoVoid"; + bool runServer = true; + bool logRequests = false; + string requestLogPath = "./requestlog.tlog"; + bool replayRequests = false; + + ostringstream usage; + + usage << argv[0] << " [--port=] [--server] [--server-type=] " + "[--protocol-type=] [--workers=] " + "[--clients=] [--loop=]" << endl + << "\tclients Number of client threads to create - 0 implies no clients, i.e. " + "server only. Default is " << clientCount << endl + << "\thelp Prints this help text." << endl + << "\tcall Service method to call. Default is " << callName << endl + << "\tloop The number of remote thrift calls each client makes. Default is " + << loopCount << endl << "\tport The port the server and clients should bind to " + "for thrift network connections. Default is " << port << endl + << "\tserver Run the Thrift server in this process. Default is " << runServer + << endl << "\tserver-type Type of server, \"simple\" or \"thread-pool\". Default is " + << serverType << endl + << "\tprotocol-type Type of protocol, \"binary\", \"ascii\", or \"xml\". Default is " + << protocolType << endl + << "\tlog-request Log all request to ./requestlog.tlog. Default is " << logRequests + << endl << "\treplay-request Replay requests from log file (./requestlog.tlog) Default is " + << replayRequests << endl << "\tworkers Number of thread pools workers. Only valid " + "for thread-pool server type. Default is " << workerCount + << endl; + + map args; + + for (int ix = 1; ix < argc; ix++) { + + string arg(argv[ix]); + + if (arg.compare(0, 2, "--") == 0) { + + size_t end = arg.find_first_of("=", 2); + + string key = string(arg, 2, end - 2); + + if (end != string::npos) { + args[key] = string(arg, end + 1); + } else { + args[key] = "true"; + } + } else { + throw invalid_argument("Unexcepted command line token: " + arg); + } + } + + try { + + if (!args["clients"].empty()) { + clientCount = atoi(args["clients"].c_str()); + } + + if (!args["help"].empty()) { + cerr << usage.str(); + return 0; + } + + if (!args["loop"].empty()) { + loopCount = atoi(args["loop"].c_str()); + } + + if (!args["call"].empty()) { + callName = args["call"]; + } + + if (!args["port"].empty()) { + port = atoi(args["port"].c_str()); + } + + if (!args["server"].empty()) { + runServer = args["server"] == "true"; + } + + if (!args["log-request"].empty()) { + logRequests = args["log-request"] == "true"; + } + + if (!args["replay-request"].empty()) { + replayRequests = args["replay-request"] == "true"; + } + + if (!args["server-type"].empty()) { + serverType = args["server-type"]; + } + + if (!args["workers"].empty()) { + workerCount = atoi(args["workers"].c_str()); + } + + } catch (std::exception& e) { + cerr << e.what() << endl; + cerr << usage.str(); + } + + boost::shared_ptr threadFactory + = boost::shared_ptr(new PlatformThreadFactory()); + + // Dispatcher + boost::shared_ptr serviceHandler(new Server()); + + if (replayRequests) { + boost::shared_ptr serviceHandler(new Server()); + boost::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + + // Transports + boost::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + fileTransport->setChunkSize(2 * 1024 * 1024); + fileTransport->setMaxEventSize(1024 * 16); + fileTransport->seekToEnd(); + + // Protocol Factory + boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + + TFileProcessor fileProcessor(serviceProcessor, protocolFactory, fileTransport); + + fileProcessor.process(0, true); + exit(0); + } + + if (runServer) { + + boost::shared_ptr serviceProcessor(new ServiceProcessor(serviceHandler)); + + // Protocol Factory + boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + + // Transport Factory + boost::shared_ptr transportFactory; + + if (logRequests) { + // initialize the log file + boost::shared_ptr fileTransport(new TFileTransport(requestLogPath)); + fileTransport->setChunkSize(2 * 1024 * 1024); + fileTransport->setMaxEventSize(1024 * 16); + + transportFactory + = boost::shared_ptr(new TPipedTransportFactory(fileTransport)); + } + + boost::shared_ptr serverThread; + boost::shared_ptr serverThread2; + + if (serverType == "simple") { + + serverThread = threadFactory->newThread(boost::shared_ptr( + new TNonblockingServer(serviceProcessor, protocolFactory, port))); + serverThread2 = threadFactory->newThread(boost::shared_ptr( + new TNonblockingServer(serviceProcessor, protocolFactory, port + 1))); + + } else if (serverType == "thread-pool") { + + boost::shared_ptr threadManager + = ThreadManager::newSimpleThreadManager(workerCount); + + threadManager->threadFactory(threadFactory); + threadManager->start(); + serverThread = threadFactory->newThread(boost::shared_ptr( + new TNonblockingServer(serviceProcessor, protocolFactory, port, threadManager))); + serverThread2 = threadFactory->newThread(boost::shared_ptr( + new TNonblockingServer(serviceProcessor, protocolFactory, port + 1, threadManager))); + } + + cerr << "Starting the server on port " << port << " and " << (port + 1) << endl; + serverThread->start(); + serverThread2->start(); + + // If we aren't running clients, just wait forever for external clients + + if (clientCount == 0) { + serverThread->join(); + serverThread2->join(); + } + } + THRIFT_SLEEP_SEC(1); + + if (clientCount > 0) { + + Monitor monitor; + + size_t threadCount = 0; + + set > clientThreads; + + if (callName == "echoVoid") { + loopType = T_VOID; + } else if (callName == "echoByte") { + loopType = T_BYTE; + } else if (callName == "echoI32") { + loopType = T_I32; + } else if (callName == "echoI64") { + loopType = T_I64; + } else if (callName == "echoString") { + loopType = T_STRING; + } else { + throw invalid_argument("Unknown service call " + callName); + } + + for (uint32_t ix = 0; ix < clientCount; ix++) { + + boost::shared_ptr socket(new TSocket("127.0.0.1", port + (ix % 2))); + boost::shared_ptr framedSocket(new TFramedTransport(socket)); + boost::shared_ptr protocol(new TBinaryProtocol(framedSocket)); + boost::shared_ptr serviceClient(new ServiceClient(protocol)); + + clientThreads.insert(threadFactory->newThread(boost::shared_ptr( + new ClientThread(socket, serviceClient, monitor, threadCount, loopCount, loopType)))); + } + + for (std::set >::const_iterator thread = clientThreads.begin(); + thread != clientThreads.end(); + thread++) { + (*thread)->start(); + } + + int64_t time00; + int64_t time01; + + { + Synchronized s(monitor); + threadCount = clientCount; + + cerr << "Launch " << clientCount << " client threads" << endl; + + time00 = Util::currentTime(); + + monitor.notifyAll(); + + while (threadCount > 0) { + monitor.wait(); + } + + time01 = Util::currentTime(); + } + + int64_t firstTime = 9223372036854775807LL; + int64_t lastTime = 0; + + double averageTime = 0; + int64_t minTime = 9223372036854775807LL; + int64_t maxTime = 0; + + for (set >::iterator ix = clientThreads.begin(); + ix != clientThreads.end(); + ix++) { + + boost::shared_ptr client + = boost::dynamic_pointer_cast((*ix)->runnable()); + + int64_t delta = client->_endTime - client->_startTime; + + assert(delta > 0); + + if (client->_startTime < firstTime) { + firstTime = client->_startTime; + } + + if (client->_endTime > lastTime) { + lastTime = client->_endTime; + } + + if (delta < minTime) { + minTime = delta; + } + + if (delta > maxTime) { + maxTime = delta; + } + + averageTime += delta; + } + + averageTime /= clientCount; + + cout << "workers :" << workerCount << ", client : " << clientCount << ", loops : " << loopCount + << ", rate : " << (clientCount * loopCount * 1000) / ((double)(time01 - time00)) << endl; + + count_map count = serviceHandler->getCount(); + count_map::iterator iter; + for (iter = count.begin(); iter != count.end(); ++iter) { + printf("%s => %d\n", iter->first, iter->second); + } + cerr << "done." << endl; + } + + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/test/cpp/src/TestClient.cpp b/vendor/src/github.com/apache/thrift/test/cpp/src/TestClient.cpp new file mode 100644 index 00000000..c16d0454 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/src/TestClient.cpp @@ -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. + */ + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // + +#include +#include +#include +#include +#if _WIN32 +#include +#endif + +#include "ThriftTest.h" + +using namespace std; +using namespace apache::thrift; +using namespace apache::thrift::protocol; +using namespace apache::thrift::transport; +using namespace thrift::test; +using namespace apache::thrift::async; + +// Current time, microseconds since the epoch +uint64_t now() { + int64_t ret; + struct timeval tv; + + THRIFT_GETTIMEOFDAY(&tv, NULL); + ret = tv.tv_sec; + ret = ret * 1000 * 1000 + tv.tv_usec; + return ret; +} + +static void testString_clientReturn(event_base* base, + int testNr, + ThriftTestCobClient* client) { + try { + string s; + client->recv_testString(s); + std::ostringstream os; + os << "test" << testNr; + const bool ok = (s == os.str()); + cout << "testString: " << s << " " << ((ok) ? "ok" : "failed") << endl; + } catch (TException& exn) { + cout << "Error: " << exn.what() << endl; + } + + if (testNr == 9) + event_base_loopbreak(base); // end test +} + +static void testVoid_clientReturn(event_base* base, ThriftTestCobClient* client) { + try { + client->recv_testVoid(); + cout << "testVoid" << endl; + + for (int testNr = 0; testNr < 10; ++testNr) { + std::ostringstream os; + os << "test" << testNr; + client->testString(tcxx::bind(testString_clientReturn, + base, + testNr, + tcxx::placeholders::_1), + os.str()); + } + } catch (TException& exn) { + cout << "Error: " << exn.what() << endl; + } +} + +// Workaround for absense of C++11 "auto" keyword. +template +bool print_eq(T expected, T actual) { + cout << "(" << actual << ")" << endl; + if (expected != actual) { + cout << "*** FAILED ***" << endl << "Expected: " << expected << " but got: " << actual << endl; + return false; + } + return true; +} + +#define BASETYPE_IDENTITY_TEST(func, value) \ + cout << #func "(" << value << ") = "; \ + try { \ + if (!print_eq(value, testClient.func(value))) \ + return_code |= ERR_BASETYPES; \ + } catch (TTransportException&) { \ + throw; \ + } catch (exception & ex) { \ + cout << "*** FAILED ***" << endl << ex.what() << endl; \ + return_code |= ERR_BASETYPES; \ + } + +int main(int argc, char** argv) { + cout.precision(19); + int ERR_BASETYPES = 1; + int ERR_STRUCTS = 2; + int ERR_CONTAINERS = 4; + int ERR_EXCEPTIONS = 8; + int ERR_UNKNOWN = 64; + + string testDir = boost::filesystem::system_complete(argv[0]).parent_path().parent_path().parent_path().string(); + string pemPath = testDir + "/keys/CA.pem"; +#if _WIN32 + transport::TWinsockSingleton::create(); +#endif + string host = "localhost"; + int port = 9090; + int numTests = 1; + bool ssl = false; + string transport_type = "buffered"; + string protocol_type = "binary"; + string domain_socket = ""; + bool abstract_namespace = false; + bool noinsane = false; + + int return_code = 0; + + boost::program_options::options_description desc("Allowed options"); + desc.add_options()("help,h", + "produce help message")("host", + boost::program_options::value(&host) + ->default_value(host), + "Host to connect")("port", + boost::program_options::value( + &port)->default_value(port), + "Port number to connect")( + "domain-socket", + boost::program_options::value(&domain_socket)->default_value(domain_socket), + "Domain Socket (e.g. /tmp/ThriftTest.thrift), instead of host and port")( + "abstract-namespace", + "Look for the domain socket in the Abstract Namespace (no connection with filesystem pathnames)")( + "transport", + boost::program_options::value(&transport_type)->default_value(transport_type), + "Transport: buffered, framed, http, evhttp")( + "protocol", + boost::program_options::value(&protocol_type)->default_value(protocol_type), + "Protocol: binary, header, compact, json")("ssl", "Encrypted Transport using SSL")( + "testloops,n", + boost::program_options::value(&numTests)->default_value(numTests), + "Number of Tests")("noinsane", "Do not run insanity test"); + + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + + if (vm.count("help")) { + cout << desc << endl; + return ERR_UNKNOWN; + } + + try { + if (!protocol_type.empty()) { + if (protocol_type == "binary") { + } else if (protocol_type == "compact") { + } else if (protocol_type == "header") { + } else if (protocol_type == "json") { + } else { + throw invalid_argument("Unknown protocol type " + protocol_type); + } + } + + if (!transport_type.empty()) { + if (transport_type == "buffered") { + } else if (transport_type == "framed") { + } else if (transport_type == "http") { + } else if (transport_type == "evhttp") { + } else { + throw invalid_argument("Unknown transport type " + transport_type); + } + } + + } catch (exception& e) { + cerr << e.what() << endl; + cout << desc << endl; + return ERR_UNKNOWN; + } + + if (vm.count("ssl")) { + ssl = true; + } + + if (vm.count("abstract-namespace")) { + abstract_namespace = true; + } + + if (vm.count("noinsane")) { + noinsane = true; + } + + boost::shared_ptr transport; + boost::shared_ptr protocol; + + boost::shared_ptr socket; + boost::shared_ptr factory; + + if (ssl) { + factory = boost::shared_ptr(new TSSLSocketFactory()); + factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + factory->loadTrustedCertificates(pemPath.c_str()); + factory->authenticate(true); + socket = factory->createSocket(host, port); + } else { + if (domain_socket != "") { + if (abstract_namespace) { + std::string abstract_socket("\0", 1); + abstract_socket += domain_socket; + socket = boost::shared_ptr(new TSocket(abstract_socket)); + } else { + socket = boost::shared_ptr(new TSocket(domain_socket)); + } + port = 0; + } else { + socket = boost::shared_ptr(new TSocket(host, port)); + } + } + + if (transport_type.compare("http") == 0) { + boost::shared_ptr httpSocket(new THttpClient(socket, host, "/service")); + transport = httpSocket; + } else if (transport_type.compare("framed") == 0) { + boost::shared_ptr framedSocket(new TFramedTransport(socket)); + transport = framedSocket; + } else { + boost::shared_ptr bufferedSocket(new TBufferedTransport(socket)); + transport = bufferedSocket; + } + + if (protocol_type.compare("json") == 0) { + boost::shared_ptr jsonProtocol(new TJSONProtocol(transport)); + protocol = jsonProtocol; + } else if (protocol_type.compare("compact") == 0) { + boost::shared_ptr compactProtocol(new TCompactProtocol(transport)); + protocol = compactProtocol; + } else if (protocol_type == "header") { + boost::shared_ptr headerProtocol(new THeaderProtocol(transport)); + protocol = headerProtocol; + } else { + boost::shared_ptr binaryProtocol(new TBinaryProtocol(transport)); + protocol = binaryProtocol; + } + + // Connection info + cout << "Connecting (" << transport_type << "/" << protocol_type << ") to: "; + if (abstract_namespace) { + cout << '@'; + } + cout << domain_socket; + if (port != 0) { + cout << host << ":" << port; + } + cout << endl; + + if (transport_type.compare("evhttp") == 0) { + event_base* base = event_base_new(); + cout << "Libevent Version: " << event_get_version() << endl; + cout << "Libevent Method: " << event_base_get_method(base) << endl; +#if LIBEVENT_VERSION_NUMBER >= 0x02000000 + cout << "Libevent Features: 0x" << hex << event_base_get_features(base) << endl; +#endif + + boost::shared_ptr protocolFactory(new TBinaryProtocolFactory()); + + boost::shared_ptr channel( + new TEvhttpClientChannel(host.c_str(), "/", host.c_str(), port, base)); + ThriftTestCobClient* client = new ThriftTestCobClient(channel, protocolFactory.get()); + client->testVoid(tcxx::bind(testVoid_clientReturn, + base, + tcxx::placeholders::_1)); + + event_base_loop(base, 0); + return 0; + } + + ThriftTestClient testClient(protocol); + + uint64_t time_min = 0; + uint64_t time_max = 0; + uint64_t time_tot = 0; + + int test = 0; + for (test = 0; test < numTests; ++test) { + + try { + transport->open(); + } catch (TTransportException& ex) { + cout << "Connect failed: " << ex.what() << endl; + return ERR_UNKNOWN; + } + + /** + * CONNECT TEST + */ + printf("Test #%d, connect %s:%d\n", test + 1, host.c_str(), port); + + uint64_t start = now(); + + /** + * VOID TEST + */ + try { + cout << "testVoid()" << flush; + testClient.testVoid(); + cout << " = void" << endl; + } catch (TTransportException&) { + // Stop here if transport got broken + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + } + + /** + * STRING TEST + */ + cout << "testString(\"Test\")" << flush; + string s; + testClient.testString(s, "Test"); + cout << " = " << s << endl; + if (s != "Test") { + cout << "*** FAILED ***" << endl; + return_code |= ERR_BASETYPES; + } + + try { + string str( + "}{Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " + "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " + "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " + "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, " + "Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, " + "Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, " + "Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, " + "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " + "Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, " + "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " + "Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, " + "ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, " + "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " + "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " + "Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa " + "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪" + "Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad, " + "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " + "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " + "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " + "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, " + "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " + "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " + "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " + "Bân-lâm-gú, 粵語"); + cout << "testString(" << str << ") = " << flush; + testClient.testString(s, str); + cout << s << endl; + if (s != str) { + cout.imbue(locale("en_US.UTF8")); + cout << "*** FAILED ***" << endl << "Expected string: " << str << " but got: " << s << endl << "CLEAR"; + return_code |= ERR_BASETYPES; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + return return_code; + } + try { + string str( + "quote: \" backslash:" + " forwardslash-escaped: \\/ " + " backspace: \b formfeed: \f newline: \n return: \r tab: " + " now-all-of-them-together: \"\\\\/\b\n\r\t" + " now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><" + " char-to-test-json-parsing: ]] \"]] \\\" }}}{ [[[ "); + cout << "testString(" << str << ") = " << flush; + testClient.testString(s, str); + cout << s << endl; + if (s != str) { + cout.imbue(locale("en_US.UTF8")); + cout << "*** FAILED ***" << endl + << "Expected string: " << str << " but got: " << s << endl + << "CLEAR"; + ; + return_code |= ERR_BASETYPES; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + return return_code; + } + + /** + * BOOL TEST + */ + cout << boolalpha; + BASETYPE_IDENTITY_TEST(testBool, true); + BASETYPE_IDENTITY_TEST(testBool, false); + + /** + * BYTE TEST + */ + BASETYPE_IDENTITY_TEST(testByte, (int8_t)0); + BASETYPE_IDENTITY_TEST(testByte, (int8_t)-1); + BASETYPE_IDENTITY_TEST(testByte, (int8_t)42); + BASETYPE_IDENTITY_TEST(testByte, (int8_t)-42); + BASETYPE_IDENTITY_TEST(testByte, (int8_t)127); + BASETYPE_IDENTITY_TEST(testByte, (int8_t)-128); + + /** + * I32 TEST + */ + BASETYPE_IDENTITY_TEST(testI32, 0); + BASETYPE_IDENTITY_TEST(testI32, -1); + BASETYPE_IDENTITY_TEST(testI32, 190000013); + BASETYPE_IDENTITY_TEST(testI32, -190000013); + BASETYPE_IDENTITY_TEST(testI32, numeric_limits::max()); + BASETYPE_IDENTITY_TEST(testI32, numeric_limits::min()); + + /** + * I64 TEST + */ + BASETYPE_IDENTITY_TEST(testI64, (int64_t)0); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)-1); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)7000000000000000123LL); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)-7000000000000000123LL); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)pow(2LL, 32)); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)-pow(2LL, 32)); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)pow(2LL, 32) + 1); + BASETYPE_IDENTITY_TEST(testI64, (int64_t)-pow(2LL, 32) - 1); + BASETYPE_IDENTITY_TEST(testI64, numeric_limits::max()); + BASETYPE_IDENTITY_TEST(testI64, numeric_limits::min()); + + /** + * DOUBLE TEST + */ + // Comparing double values with plain equality because Thrift handles full precision of double + BASETYPE_IDENTITY_TEST(testDouble, 0.0); + BASETYPE_IDENTITY_TEST(testDouble, -1.0); + BASETYPE_IDENTITY_TEST(testDouble, -5.2098523); + BASETYPE_IDENTITY_TEST(testDouble, -0.000341012439638598279); + BASETYPE_IDENTITY_TEST(testDouble, pow(2, 32)); + BASETYPE_IDENTITY_TEST(testDouble, pow(2, 32) + 1); + BASETYPE_IDENTITY_TEST(testDouble, pow(2, 53) - 1); + BASETYPE_IDENTITY_TEST(testDouble, -pow(2, 32)); + BASETYPE_IDENTITY_TEST(testDouble, -pow(2, 32) - 1); + BASETYPE_IDENTITY_TEST(testDouble, -pow(2, 53) + 1); + + try { + double expected = pow(10, 307); + cout << "testDouble(" << expected << ") = " << flush; + double actual = testClient.testDouble(expected); + cout << "(" << actual << ")" << endl; + if (expected - actual > pow(10, 292)) { + cout << "*** FAILED ***" << endl + << "Expected: " << expected << " but got: " << actual << endl; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + } + + try { + double expected = pow(10, -292); + cout << "testDouble(" << expected << ") = " << flush; + double actual = testClient.testDouble(expected); + cout << "(" << actual << ")" << endl; + if (expected - actual > pow(10, -307)) { + cout << "*** FAILED ***" << endl + << "Expected: " << expected << " but got: " << actual << endl; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + } + + /** + * BINARY TEST + */ + cout << "testBinary(empty)" << endl; + try { + string bin_result; + testClient.testBinary(bin_result, string()); + if (!bin_result.empty()) { + cout << endl << "*** FAILED ***" << endl; + cout << "invalid length: " << bin_result.size() << endl; + return_code |= ERR_BASETYPES; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + } + cout << "testBinary([-128..127]) = {" << flush; + const signed char bin_data[256] + = {-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, + -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, + -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, + -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, + -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, + -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, + -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, + -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127}; + try { + string bin_result; + testClient.testBinary(bin_result, string(reinterpret_cast(bin_data), 256)); + if (bin_result.size() != 256) { + cout << endl << "*** FAILED ***" << endl; + cout << "invalid length: " << bin_result.size() << endl; + return_code |= ERR_BASETYPES; + } else { + bool first = true; + bool failed = false; + for (int i = 0; i < 256; ++i) { + if (!first) + cout << ","; + else + first = false; + cout << static_cast(bin_result[i]); + if (!failed && bin_result[i] != i - 128) { + failed = true; + } + } + cout << "}" << endl; + if (failed) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_BASETYPES; + } + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_BASETYPES; + } + + + /** + * STRUCT TEST + */ + cout << "testStruct({\"Zero\", 1, -3, -5})" << flush; + Xtruct out; + out.string_thing = "Zero"; + out.byte_thing = 1; + out.i32_thing = -3; + out.i64_thing = -5; + Xtruct in; + testClient.testStruct(in, out); + printf(" = {\"%s\", %d, %d, %" PRId64 "}\n", + in.string_thing.c_str(), + (int)in.byte_thing, + in.i32_thing, + in.i64_thing); + if (in != out) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + /** + * NESTED STRUCT TEST + */ + cout << "testNest({1, {\"Zero\", 1, -3, -5}), 5}" << flush; + Xtruct2 out2; + out2.byte_thing = 1; + out2.struct_thing = out; + out2.i32_thing = 5; + Xtruct2 in2; + testClient.testNest(in2, out2); + in = in2.struct_thing; + printf(" = {%d, {\"%s\", %d, %d, %" PRId64 "}, %d}\n", + in2.byte_thing, + in.string_thing.c_str(), + (int)in.byte_thing, + in.i32_thing, + in.i64_thing, + in2.i32_thing); + if (in2 != out2) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + /** + * MAP TEST + */ + map mapout; + for (int32_t i = 0; i < 5; ++i) { + mapout.insert(make_pair(i, i - 10)); + } + cout << "testMap({" << flush; + map::const_iterator m_iter; + bool first = true; + for (m_iter = mapout.begin(); m_iter != mapout.end(); ++m_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << m_iter->first << " => " << m_iter->second; + } + cout << "})"; + map mapin; + testClient.testMap(mapin, mapout); + cout << " = {"; + first = true; + for (m_iter = mapin.begin(); m_iter != mapin.end(); ++m_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << m_iter->first << " => " << m_iter->second; + } + cout << "}" << endl; + if (mapin != mapout) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_CONTAINERS; + } + + /** + * STRING MAP TEST + */ + cout << "testStringMap({a => 2, b => blah, some => thing}) = {" << flush; + map smapin; + map smapout; + smapin["a"] = "2"; + smapin["b"] = "blah"; + smapin["some"] = "thing"; + try { + testClient.testStringMap(smapout, smapin); + first = true; + for (map::const_iterator it = smapout.begin(); it != smapout.end(); ++it) { + if (first) + cout << ","; + else + first = false; + cout << it->first << " => " << it->second; + } + cout << "}" << endl; + if (smapin != smapout) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_CONTAINERS; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_CONTAINERS; + } + + /** + * SET TEST + */ + set setout; + for (int32_t i = -2; i < 3; ++i) { + setout.insert(i); + } + cout << "testSet({" << flush; + set::const_iterator s_iter; + first = true; + for (s_iter = setout.begin(); s_iter != setout.end(); ++s_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << *s_iter; + } + cout << "})"; + set setin; + testClient.testSet(setin, setout); + cout << " = {"; + first = true; + for (s_iter = setin.begin(); s_iter != setin.end(); ++s_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << *s_iter; + } + cout << "}" << endl; + if (setin != setout) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_CONTAINERS; + } + + /** + * LIST TEST + */ + cout << "testList(empty)" << flush; + try { + vector listout; + testClient.testList(listout, vector()); + if (!listout.empty()) { + cout << "*** FAILED ***" << endl; + cout << "invalid length: " << listout.size() << endl; + return_code |= ERR_CONTAINERS; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_CONTAINERS; + } + try { + vector listout; + for (int32_t i = -2; i < 3; ++i) { + listout.push_back(i); + } + cout << "testList({" << flush; + vector::const_iterator l_iter; + first = true; + for (l_iter = listout.begin(); l_iter != listout.end(); ++l_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << *l_iter; + } + cout << "})"; + vector listin; + testClient.testList(listin, listout); + cout << " = {"; + first = true; + for (l_iter = listin.begin(); l_iter != listin.end(); ++l_iter) { + if (first) { + first = false; + } else { + cout << ","; + } + cout << *l_iter; + } + cout << "}" << endl; + if (listin != listout) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_CONTAINERS; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_CONTAINERS; + } + + /** + * ENUM TEST + */ + cout << "testEnum(ONE)" << flush; + Numberz::type ret = testClient.testEnum(Numberz::ONE); + cout << " = " << ret << endl; + if (ret != Numberz::ONE) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + cout << "testEnum(TWO)" << flush; + ret = testClient.testEnum(Numberz::TWO); + cout << " = " << ret << endl; + if (ret != Numberz::TWO) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + cout << "testEnum(THREE)" << flush; + ret = testClient.testEnum(Numberz::THREE); + cout << " = " << ret << endl; + if (ret != Numberz::THREE) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + cout << "testEnum(FIVE)" << flush; + ret = testClient.testEnum(Numberz::FIVE); + cout << " = " << ret << endl; + if (ret != Numberz::FIVE) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + cout << "testEnum(EIGHT)" << flush; + ret = testClient.testEnum(Numberz::EIGHT); + cout << " = " << ret << endl; + if (ret != Numberz::EIGHT) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + /** + * TYPEDEF TEST + */ + cout << "testTypedef(309858235082523)" << flush; + UserId uid = testClient.testTypedef(309858235082523LL); + cout << " = " << uid << endl; + if (uid != 309858235082523LL) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + + /** + * NESTED MAP TEST + */ + cout << "testMapMap(1)" << flush; + map > mm; + testClient.testMapMap(mm, 1); + cout << " = {"; + map >::const_iterator mi; + for (mi = mm.begin(); mi != mm.end(); ++mi) { + printf("%d => {", mi->first); + map::const_iterator mi2; + for (mi2 = mi->second.begin(); mi2 != mi->second.end(); ++mi2) { + cout << mi2->first << " => " << mi2->second; + } + cout << "}, "; + } + cout << "}" << endl; + if (mm.size() != 2 || + mm[-4][-4] != -4 || + mm[-4][-3] != -3 || + mm[-4][-2] != -2 || + mm[-4][-1] != -1 || + mm[4][4] != 4 || + mm[4][3] != 3 || + mm[4][2] != 2 || + mm[4][1] != 1) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_CONTAINERS; + } + + /** + * INSANITY TEST + */ + if (!noinsane) { + Insanity insane; + insane.userMap.insert(make_pair(Numberz::FIVE, 5)); + insane.userMap.insert(make_pair(Numberz::EIGHT, 8)); + Xtruct truck; + truck.string_thing = "Goodbye4"; + truck.byte_thing = 4; + truck.i32_thing = 4; + truck.i64_thing = 4; + Xtruct truck2; + truck2.string_thing = "Hello2"; + truck2.byte_thing = 2; + truck2.i32_thing = 2; + truck2.i64_thing = 2; + insane.xtructs.push_back(truck); + insane.xtructs.push_back(truck2); + cout << "testInsanity()" << flush; + map > whoa; + testClient.testInsanity(whoa, insane); + cout << " = {"; + map >::const_iterator i_iter; + for (i_iter = whoa.begin(); i_iter != whoa.end(); ++i_iter) { + printf("%" PRId64 " => {", i_iter->first); + map::const_iterator i2_iter; + for (i2_iter = i_iter->second.begin(); i2_iter != i_iter->second.end(); ++i2_iter) { + printf("%d => {", i2_iter->first); + map userMap = i2_iter->second.userMap; + map::const_iterator um; + cout << "{"; + for (um = userMap.begin(); um != userMap.end(); ++um) { + cout << um->first << " => " << um->second; + } + cout << "}, "; + + vector xtructs = i2_iter->second.xtructs; + vector::const_iterator x; + cout << "{"; + for (x = xtructs.begin(); x != xtructs.end(); ++x) { + printf("{\"%s\", %d, %d, %" PRId64 "}, ", + x->string_thing.c_str(), + (int)x->byte_thing, + x->i32_thing, + x->i64_thing); + } + cout << "}"; + + cout << "}, "; + } + cout << "}, "; + } + cout << "}" << endl; + bool failed = false; + map >::const_iterator it1 = whoa.find(UserId(1)); + if (whoa.size() != 2) { + failed = true; + } + if (it1 == whoa.end()) { + failed = true; + } else { + map::const_iterator 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); + if (it13 == it1->second.end() || it13->second != insane) { + failed = true; + } + } + map >::const_iterator it2 = whoa.find(UserId(2)); + if (it2 == whoa.end()) { + failed = true; + } else { + map::const_iterator it26 = it2->second.find(Numberz::SIX); + if (it26 == it1->second.end() || it26->second != Insanity()) { + failed = true; + } + } + if (failed) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + } + + /** + * MULTI TEST + */ + cout << "testMulti()" << endl; + try { + map mul_map; + Xtruct mul_result; + mul_map[1] = "blah"; + mul_map[2] = "thing"; + testClient.testMulti(mul_result, 42, 4242, 424242, mul_map, Numberz::EIGHT, UserId(24)); + Xtruct xxs; + xxs.string_thing = "Hello2"; + xxs.byte_thing = 42; + xxs.i32_thing = 4242; + xxs.i64_thing = 424242; + if (mul_result != xxs) { + cout << "*** FAILED ***" << endl; + return_code |= ERR_STRUCTS; + } + } catch (TTransportException&) { + throw; + } catch (exception& ex) { + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_STRUCTS; + } + + /* test exception */ + + try { + cout << "testClient.testException(\"Xception\") =>" << flush; + testClient.testException("Xception"); + cout << " void\n*** FAILED ***" << endl; + return_code |= ERR_EXCEPTIONS; + + } catch (Xception& e) { + printf(" {%u, \"%s\"}\n", e.errorCode, e.message.c_str()); + } + + try { + cout << "testClient.testException(\"TException\") =>" << flush; + testClient.testException("TException"); + cout << " void\n*** FAILED ***" << endl; + return_code |= ERR_EXCEPTIONS; + + } catch (const TException&) { + cout << " Caught TException" << endl; + } + + try { + cout << "testClient.testException(\"success\") =>" << flush; + testClient.testException("success"); + cout << " void" << endl; + } catch (exception & ex) { \ + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_EXCEPTIONS; + } + + /* test multi exception */ + + try { + cout << "testClient.testMultiException(\"Xception\", \"test 1\") =>" << flush; + Xtruct result; + testClient.testMultiException(result, "Xception", "test 1"); + cout << " result\n*** FAILED ***" << endl; + return_code |= ERR_EXCEPTIONS; + } catch (Xception& e) { + printf(" {%u, \"%s\"}\n", e.errorCode, e.message.c_str()); + } + + try { + cout << "testClient.testMultiException(\"Xception2\", \"test 2\") =>" << flush; + Xtruct result; + testClient.testMultiException(result, "Xception2", "test 2"); + cout << " result\n*** FAILED ***" << endl; + return_code |= ERR_EXCEPTIONS; + + } catch (Xception2& e) { + printf(" {%u, {\"%s\"}}\n", e.errorCode, e.struct_thing.string_thing.c_str()); + } + + try { + cout << "testClient.testMultiException(\"success\", \"test 3\") =>" << flush; + Xtruct result; + testClient.testMultiException(result, "success", "test 3"); + printf(" {{\"%s\"}}\n", result.string_thing.c_str()); + } catch (exception & ex) { \ + cout << "*** FAILED ***" << endl << ex.what() << endl; + return_code |= ERR_EXCEPTIONS; + } + + /* test oneway void */ + { + cout << "testClient.testOneway(1) =>" << flush; + uint64_t startOneway = now(); + testClient.testOneway(1); + uint64_t elapsed = now() - startOneway; + if (elapsed > 200 * 1000) { // 0.2 seconds + printf("*** FAILED *** - took %.2f ms\n", (double)elapsed / 1000.0); + return_code |= ERR_BASETYPES; + } else { + printf(" success - took %.2f ms\n", (double)elapsed / 1000.0); + } + } + + /** + * redo a simple test after the oneway to make sure we aren't "off by one" -- + * if the server treated oneway void like normal void, this next test will + * fail since it will get the void confirmation rather than the correct + * result. In this circumstance, the client will throw the exception: + * + * TApplicationException: Wrong method namea + */ + /** + * I32 TEST + */ + cout << "re-test testI32(-1)"; + int i32 = testClient.testI32(-1); + cout << " = " << i32 << endl; + if (i32 != -1) + return_code |= ERR_BASETYPES; + + uint64_t stop = now(); + uint64_t tot = stop - start; + + cout << "Total time: " << stop - start << " us" << endl; + + time_tot += tot; + if (time_min == 0 || tot < time_min) { + time_min = tot; + } + if (tot > time_max) { + time_max = tot; + } + + transport->close(); + } + + cout << endl << "All tests done." << endl; + + uint64_t time_avg = time_tot / numTests; + + cout << "Min time: " << time_min << " us" << endl; + cout << "Max time: " << time_max << " us" << endl; + cout << "Avg time: " << time_avg << " us" << endl; + + return return_code; +} diff --git a/vendor/src/github.com/apache/thrift/test/cpp/src/TestServer.cpp b/vendor/src/github.com/apache/thrift/test/cpp/src/TestServer.cpp new file mode 100644 index 00000000..b4378608 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/src/TestServer.cpp @@ -0,0 +1,775 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 __STDC_FORMAT_MACROS +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ThriftTest.h" + +#include +#include +#include + +#include +#include +#include + +#include +#if _WIN32 +#include +#endif + +using namespace std; + +using namespace apache::thrift; +using namespace apache::thrift::concurrency; +using namespace apache::thrift::protocol; +using namespace apache::thrift::transport; +using namespace apache::thrift::server; +using namespace apache::thrift::async; + +using namespace thrift::test; + +class TestHandler : public ThriftTestIf { +public: + TestHandler() {} + + void testVoid() { printf("testVoid()\n"); } + + void testString(string& out, const string& thing) { + printf("testString(\"%s\")\n", thing.c_str()); + out = thing; + } + + bool testBool(const bool thing) { + printf("testBool(%s)\n", thing ? "true" : "false"); + return thing; + } + + int8_t testByte(const int8_t thing) { + printf("testByte(%d)\n", (int)thing); + return thing; + } + + int32_t testI32(const int32_t thing) { + printf("testI32(%d)\n", thing); + return thing; + } + + int64_t testI64(const int64_t thing) { + printf("testI64(%" PRId64 ")\n", thing); + return thing; + } + + double testDouble(const double thing) { + printf("testDouble(%f)\n", thing); + return thing; + } + + void testBinary(std::string& _return, const std::string& thing) { + std::ostringstream hexstr; + hexstr << std::hex << thing; + printf("testBinary(%s)\n", hexstr.str().c_str()); + _return = thing; + } + + void testStruct(Xtruct& out, const Xtruct& thing) { + printf("testStruct({\"%s\", %d, %d, %" PRId64 "})\n", + thing.string_thing.c_str(), + (int)thing.byte_thing, + thing.i32_thing, + thing.i64_thing); + out = thing; + } + + void testNest(Xtruct2& out, const Xtruct2& nest) { + const Xtruct& thing = nest.struct_thing; + printf("testNest({%d, {\"%s\", %d, %d, %" PRId64 "}, %d})\n", + (int)nest.byte_thing, + thing.string_thing.c_str(), + (int)thing.byte_thing, + thing.i32_thing, + thing.i64_thing, + nest.i32_thing); + out = nest; + } + + void testMap(map& out, const map& thing) { + printf("testMap({"); + map::const_iterator m_iter; + bool first = true; + for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d => %d", m_iter->first, m_iter->second); + } + printf("})\n"); + out = thing; + } + + void testStringMap(map& out, + const map& thing) { + printf("testMap({"); + map::const_iterator m_iter; + bool first = true; + for (m_iter = thing.begin(); m_iter != thing.end(); ++m_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%s => %s", (m_iter->first).c_str(), (m_iter->second).c_str()); + } + printf("})\n"); + out = thing; + } + + void testSet(set& out, const set& thing) { + printf("testSet({"); + set::const_iterator s_iter; + bool first = true; + for (s_iter = thing.begin(); s_iter != thing.end(); ++s_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *s_iter); + } + printf("})\n"); + out = thing; + } + + void testList(vector& out, const vector& thing) { + printf("testList({"); + vector::const_iterator l_iter; + bool first = true; + for (l_iter = thing.begin(); l_iter != thing.end(); ++l_iter) { + if (first) { + first = false; + } else { + printf(", "); + } + printf("%d", *l_iter); + } + printf("})\n"); + out = thing; + } + + Numberz::type testEnum(const Numberz::type thing) { + printf("testEnum(%d)\n", thing); + return thing; + } + + UserId testTypedef(const UserId thing) { + printf("testTypedef(%" PRId64 ")\n", thing); + return thing; + } + + void testMapMap(map >& mapmap, const int32_t hello) { + printf("testMapMap(%d)\n", hello); + + map pos; + map neg; + for (int i = 1; i < 5; i++) { + pos.insert(make_pair(i, i)); + neg.insert(make_pair(-i, -i)); + } + + mapmap.insert(make_pair(4, pos)); + mapmap.insert(make_pair(-4, neg)); + } + + void testInsanity(map >& insane, const Insanity& argument) { + printf("testInsanity()\n"); + + Insanity looney; + map first_map; + map second_map; + + first_map.insert(make_pair(Numberz::TWO, argument)); + first_map.insert(make_pair(Numberz::THREE, argument)); + + second_map.insert(make_pair(Numberz::SIX, looney)); + + insane.insert(make_pair(1, first_map)); + insane.insert(make_pair(2, second_map)); + + printf("return"); + printf(" = {"); + map >::const_iterator i_iter; + for (i_iter = insane.begin(); i_iter != insane.end(); ++i_iter) { + printf("%" PRId64 " => {", i_iter->first); + map::const_iterator i2_iter; + for (i2_iter = i_iter->second.begin(); i2_iter != i_iter->second.end(); ++i2_iter) { + printf("%d => {", i2_iter->first); + map userMap = i2_iter->second.userMap; + map::const_iterator um; + printf("{"); + for (um = userMap.begin(); um != userMap.end(); ++um) { + printf("%d => %" PRId64 ", ", um->first, um->second); + } + printf("}, "); + + vector xtructs = i2_iter->second.xtructs; + vector::const_iterator x; + printf("{"); + for (x = xtructs.begin(); x != xtructs.end(); ++x) { + printf("{\"%s\", %d, %d, %" PRId64 "}, ", + x->string_thing.c_str(), + (int)x->byte_thing, + x->i32_thing, + x->i64_thing); + } + printf("}"); + + printf("}, "); + } + printf("}, "); + } + printf("}\n"); + } + + 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)arg3; + (void)arg4; + (void)arg5; + + printf("testMulti()\n"); + + hello.string_thing = "Hello2"; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = (int64_t)arg2; + } + + void testException(const std::string& arg) { + printf("testException(%s)\n", arg.c_str()); + if (arg.compare("Xception") == 0) { + Xception e; + e.errorCode = 1001; + e.message = arg; + throw e; + } else if (arg.compare("TException") == 0) { + apache::thrift::TException e; + throw e; + } else { + Xtruct result; + result.string_thing = arg; + return; + } + } + + void testMultiException(Xtruct& result, + const std::string& arg0, + const std::string& arg1) { + + printf("testMultiException(%s, %s)\n", arg0.c_str(), arg1.c_str()); + + if (arg0.compare("Xception") == 0) { + Xception e; + e.errorCode = 1001; + e.message = "This is an Xception"; + throw e; + } else if (arg0.compare("Xception2") == 0) { + Xception2 e; + e.errorCode = 2002; + e.struct_thing.string_thing = "This is an Xception2"; + throw e; + } else { + result.string_thing = arg1; + return; + } + } + + void testOneway(const int32_t sleepFor) { + printf("testOneway(%d): Sleeping...\n", sleepFor); + THRIFT_SLEEP_SEC(sleepFor); + printf("testOneway(%d): done sleeping!\n", sleepFor); + } +}; + +class TestProcessorEventHandler : public TProcessorEventHandler { + virtual void* getContext(const char* fn_name, void* serverContext) { + (void)serverContext; + return new std::string(fn_name); + } + virtual void freeContext(void* ctx, const char* fn_name) { + (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)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)bytes; + communicate("postWrite", ctx, fn_name); + } + virtual void asyncComplete(void* ctx, const char* fn_name) { + communicate("asyncComplete", ctx, fn_name); + } + virtual void handlerError(void* ctx, const char* fn_name) { + communicate("handlerError", ctx, fn_name); + } + + void communicate(const char* event, void* ctx, const char* fn_name) { + std::cout << event << ": " << *static_cast(ctx) << " = " << fn_name << std::endl; + } +}; + +class TestHandlerAsync : public ThriftTestCobSvIf { +public: + TestHandlerAsync(boost::shared_ptr& handler) : _delegate(handler) {} + virtual ~TestHandlerAsync() {} + + virtual void testVoid(tcxx::function cob) { + _delegate->testVoid(); + cob(); + } + + virtual void testString(tcxx::function cob, + const std::string& thing) { + std::string res; + _delegate->testString(res, thing); + cob(res); + } + + virtual void testBool(tcxx::function cob, const bool thing) { + bool res = _delegate->testBool(thing); + cob(res); + } + + virtual void testByte(tcxx::function cob, const int8_t thing) { + int8_t res = _delegate->testByte(thing); + cob(res); + } + + virtual void testI32(tcxx::function cob, const int32_t thing) { + int32_t res = _delegate->testI32(thing); + cob(res); + } + + virtual void testI64(tcxx::function cob, const int64_t thing) { + int64_t res = _delegate->testI64(thing); + cob(res); + } + + virtual void testDouble(tcxx::function cob, const double thing) { + double res = _delegate->testDouble(thing); + cob(res); + } + + virtual void testBinary(tcxx::function cob, + const std::string& thing) { + std::string res; + _delegate->testBinary(res, thing); + cob(res); + } + + virtual void testStruct(tcxx::function cob, const Xtruct& thing) { + Xtruct res; + _delegate->testStruct(res, thing); + cob(res); + } + + virtual void testNest(tcxx::function cob, const Xtruct2& thing) { + Xtruct2 res; + _delegate->testNest(res, thing); + cob(res); + } + + virtual void testMap(tcxx::function const& _return)> cob, + const std::map& thing) { + std::map res; + _delegate->testMap(res, thing); + cob(res); + } + + virtual void testStringMap( + tcxx::function const& _return)> cob, + const std::map& thing) { + std::map res; + _delegate->testStringMap(res, thing); + cob(res); + } + + virtual void testSet(tcxx::function const& _return)> cob, + const std::set& thing) { + std::set res; + _delegate->testSet(res, thing); + cob(res); + } + + virtual void testList(tcxx::function const& _return)> cob, + const std::vector& thing) { + std::vector res; + _delegate->testList(res, thing); + cob(res); + } + + virtual void testEnum(tcxx::function cob, + const Numberz::type thing) { + Numberz::type res = _delegate->testEnum(thing); + cob(res); + } + + virtual void testTypedef(tcxx::function cob, const UserId thing) { + UserId res = _delegate->testTypedef(thing); + cob(res); + } + + virtual void testMapMap( + tcxx::function > const& _return)> cob, + const int32_t hello) { + std::map > res; + _delegate->testMapMap(res, hello); + cob(res); + } + + virtual void testInsanity( + tcxx::function > const& _return)> cob, + const Insanity& argument) { + std::map > res; + _delegate->testInsanity(res, argument); + cob(res); + } + + virtual void testMulti(tcxx::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) { + Xtruct res; + _delegate->testMulti(res, arg0, arg1, arg2, arg3, arg4, arg5); + cob(res); + } + + virtual void testException( + tcxx::function cob, + tcxx::function exn_cob, + const std::string& arg) { + try { + _delegate->testException(arg); + } catch (const apache::thrift::TException& e) { + exn_cob(apache::thrift::TDelayedException::delayException(e)); + return; + } + cob(); + } + + virtual void testMultiException( + tcxx::function cob, + tcxx::function exn_cob, + const std::string& arg0, + const std::string& arg1) { + Xtruct res; + try { + _delegate->testMultiException(res, arg0, arg1); + } catch (const apache::thrift::TException& e) { + exn_cob(apache::thrift::TDelayedException::delayException(e)); + return; + } + cob(res); + } + + virtual void testOneway(tcxx::function cob, const int32_t secondsToSleep) { + _delegate->testOneway(secondsToSleep); + cob(); + } + +protected: + boost::shared_ptr _delegate; +}; + +namespace po = boost::program_options; + +int main(int argc, char** argv) { + + string testDir = boost::filesystem::system_complete(argv[0]).parent_path().parent_path().parent_path().string(); + string certPath = testDir + "/keys/server.crt"; + string keyPath = testDir + "/keys/server.key"; + +#if _WIN32 + transport::TWinsockSingleton::create(); +#endif + int port = 9090; + bool ssl = false; + string transport_type = "buffered"; + string protocol_type = "binary"; + string server_type = "simple"; + string domain_socket = ""; + bool abstract_namespace = false; + size_t workers = 4; + int string_limit = 0; + int container_limit = 0; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "produce help message") + ("port", po::value(&port)->default_value(port), "Port number to listen") + ("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") + ("protocol", po::value(&protocol_type)->default_value(protocol_type), "protocol: binary, compact, header, json") + ("ssl", "Encrypted Transport using SSL") + ("processor-events", "processor-events") + ("workers,n", po::value(&workers)->default_value(workers), "Number of thread pools workers. Only valid for thread-pool server type") + ("string-limit", po::value(&string_limit)) + ("container-limit", po::value(&container_limit)); + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << desc << "\n"; + return 1; + } + + try { + if (!server_type.empty()) { + if (server_type == "simple") { + } else if (server_type == "thread-pool") { + } else if (server_type == "threaded") { + } else if (server_type == "nonblocking") { + } else { + throw invalid_argument("Unknown server type " + server_type); + } + } + + if (!protocol_type.empty()) { + if (protocol_type == "binary") { + } else if (protocol_type == "compact") { + } else if (protocol_type == "json") { + } else if (protocol_type == "header") { + } else { + throw invalid_argument("Unknown protocol type " + protocol_type); + } + } + + if (!transport_type.empty()) { + if (transport_type == "buffered") { + } else if (transport_type == "framed") { + } else if (transport_type == "http") { + } else { + throw invalid_argument("Unknown transport type " + transport_type); + } + } + + } catch (std::exception& e) { + cerr << e.what() << endl; + cout << desc << "\n"; + return 1; + } + + if (vm.count("ssl")) { + ssl = true; + } + + if (vm.count("abstract-namespace")) { + abstract_namespace = true; + } + + // Dispatcher + boost::shared_ptr protocolFactory; + if (protocol_type == "json") { + boost::shared_ptr jsonProtocolFactory(new TJSONProtocolFactory()); + protocolFactory = jsonProtocolFactory; + } else if (protocol_type == "compact") { + TCompactProtocolFactoryT *compactProtocolFactory = new TCompactProtocolFactoryT(); + compactProtocolFactory->setContainerSizeLimit(container_limit); + compactProtocolFactory->setStringSizeLimit(string_limit); + protocolFactory.reset(compactProtocolFactory); + } else if (protocol_type == "header") { + boost::shared_ptr headerProtocolFactory(new THeaderProtocolFactory()); + protocolFactory = headerProtocolFactory; + } else { + TBinaryProtocolFactoryT* binaryProtocolFactory = new TBinaryProtocolFactoryT(); + binaryProtocolFactory->setContainerSizeLimit(container_limit); + binaryProtocolFactory->setStringSizeLimit(string_limit); + protocolFactory.reset(binaryProtocolFactory); + } + + // Processor + boost::shared_ptr testHandler(new TestHandler()); + boost::shared_ptr testProcessor(new ThriftTestProcessor(testHandler)); + + if (vm.count("processor-events")) { + testProcessor->setEventHandler( + boost::shared_ptr(new TestProcessorEventHandler())); + } + + // Transport + boost::shared_ptr sslSocketFactory; + boost::shared_ptr serverSocket; + + if (ssl) { + sslSocketFactory = boost::shared_ptr(new TSSLSocketFactory()); + sslSocketFactory->loadCertificate(certPath.c_str()); + sslSocketFactory->loadPrivateKey(keyPath.c_str()); + sslSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + serverSocket = boost::shared_ptr(new TSSLServerSocket(port, sslSocketFactory)); + } else { + if (domain_socket != "") { + if (abstract_namespace) { + std::string abstract_socket("\0", 1); + abstract_socket += domain_socket; + serverSocket = boost::shared_ptr(new TServerSocket(abstract_socket)); + } else { + unlink(domain_socket.c_str()); + serverSocket = boost::shared_ptr(new TServerSocket(domain_socket)); + } + port = 0; + } else { + serverSocket = boost::shared_ptr(new TServerSocket(port)); + } + } + + // Factory + boost::shared_ptr transportFactory; + + if (transport_type == "http" && server_type != "nonblocking") { + boost::shared_ptr httpTransportFactory(new THttpServerTransportFactory()); + transportFactory = httpTransportFactory; + } else if (transport_type == "framed") { + boost::shared_ptr framedTransportFactory(new TFramedTransportFactory()); + transportFactory = framedTransportFactory; + } else { + boost::shared_ptr bufferedTransportFactory(new TBufferedTransportFactory()); + transportFactory = bufferedTransportFactory; + } + + // Server Info + cout << "Starting \"" << server_type << "\" server (" << transport_type << "/" << protocol_type + << ") listen on: "; + if (abstract_namespace) { + cout << '@'; + } + cout << domain_socket; + if (port != 0) { + cout << port; + } + cout << endl; + + // Server + boost::shared_ptr server; + + if (server_type == "simple") { + server.reset(new TSimpleServer(testProcessor, serverSocket, transportFactory, protocolFactory)); + } else if (server_type == "thread-pool") { + + boost::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(workers); + + boost::shared_ptr threadFactory + = boost::shared_ptr(new PlatformThreadFactory()); + + threadManager->threadFactory(threadFactory); + + threadManager->start(); + + server.reset(new TThreadPoolServer(testProcessor, + serverSocket, + transportFactory, + protocolFactory, + threadManager)); + } else if (server_type == "threaded") { + + server.reset( + new TThreadedServer(testProcessor, serverSocket, transportFactory, protocolFactory)); + } else if (server_type == "nonblocking") { + if (transport_type == "http") { + boost::shared_ptr testHandlerAsync(new TestHandlerAsync(testHandler)); + boost::shared_ptr testProcessorAsync( + new ThriftTestAsyncProcessor(testHandlerAsync)); + boost::shared_ptr testBufferProcessor( + new TAsyncProtocolProcessor(testProcessorAsync, protocolFactory)); + + // not loading nonblockingServer into "server" because + // TEvhttpServer doesn't inherit from TServer, and doesn't + // provide a stop method. + TEvhttpServer nonblockingServer(testBufferProcessor, port); + nonblockingServer.serve(); + } else { + server.reset(new TNonblockingServer(testProcessor, protocolFactory, port)); + } + } + + if (server.get() != NULL) { + if (protocol_type == "header") { + // Tell the server to use the same protocol for input / output + // if using header + server->setOutputProtocolFactory(boost::shared_ptr()); + } + apache::thrift::concurrency::PlatformThreadFactory factory; + factory.setDetached(false); + boost::shared_ptr serverThreadRunner(server); + boost::shared_ptr thread + = factory.newThread(serverThreadRunner); + thread->start(); + + // HACK: cross language test suite is unable to handle cin properly + // that's why we stay in a endless loop here + while (1) { + } + // FIXME: find another way to stop the server (e.g. a signal) + // cout<<"Press enter to stop the server."<stop(); + thread->join(); + server.reset(); + } + + cout << "done." << endl; + return 0; +} diff --git a/vendor/src/github.com/apache/thrift/test/cpp/src/ThriftTest_extras.cpp b/vendor/src/github.com/apache/thrift/test/cpp/src/ThriftTest_extras.cpp new file mode 100644 index 00000000..af5606ef --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/cpp/src/ThriftTest_extras.cpp @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Extra functions required for ThriftTest_types to work + +#include +#include "gen-cpp/ThriftTest_types.h" + +namespace thrift { +namespace test { + +bool Insanity::operator<(thrift::test::Insanity const& other) const { + using apache::thrift::ThriftDebugString; + return ThriftDebugString(*this) < ThriftDebugString(other); +} +} +} diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/__init__.py b/vendor/src/github.com/apache/thrift/test/crossrunner/__init__.py new file mode 100644 index 00000000..9d0b83ac --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/__init__.py @@ -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. +# + +from .test import test_name # noqa +from .collect import collect_cross_tests, collect_feature_tests # noqa +from .run import TestDispatcher # noqa +from .report import generate_known_failures, load_known_failures # noqa diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/collect.py b/vendor/src/github.com/apache/thrift/test/crossrunner/collect.py new file mode 100644 index 00000000..03b0c36c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/collect.py @@ -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. +# + +import platform +import re +from itertools import product + +from .util import merge_dict +from .test import TestEntry + +# Those keys are passed to execution as is. +# Note that there are keys other than these, namely: +# delay: After server is started, client start is delayed for the value +# (seconds). +# timeout: Test timeout after client is started (seconds). +# platforms: Supported platforms. Should match platform.system() value. +# protocols: list of supported protocols +# transports: list of supported transports +# sockets: list of supported sockets +# +# protocols and transports entries can be colon separated "spec:impl" pair +# (e.g. binary:accel) where test is run for any matching "spec" while actual +# argument passed to test executable is "impl". +# Otherwise "spec" is equivalent to "spec:spec" pair. +# (e.g. "binary" is equivalent to "binary:binary" in tests.json) +# +VALID_JSON_KEYS = [ + 'name', # name of the library, typically a language name + 'workdir', # work directory where command is executed + 'command', # test command + 'extra_args', # args appended to command after other args are appended + 'remote_args', # args added to the other side of the program + 'join_args', # whether args should be passed as single concatenated string + 'env', # additional environmental variable +] + +DEFAULT_MAX_DELAY = 5 +DEFAULT_TIMEOUT = 5 + + +def _collect_testlibs(config, server_match, client_match=[None]): + """Collects server/client configurations from library configurations""" + def expand_libs(config): + for lib in config: + sv = lib.pop('server', None) + cl = lib.pop('client', None) + yield lib, sv, cl + + def yield_testlibs(base_configs, configs, match): + for base, conf in zip(base_configs, configs): + if conf: + if not match or base['name'] in match: + platforms = conf.get('platforms') or base.get('platforms') + if not platforms or platform.system() in platforms: + yield merge_dict(base, conf) + + libs, svs, cls = zip(*expand_libs(config)) + servers = list(yield_testlibs(libs, svs, server_match)) + clients = list(yield_testlibs(libs, cls, client_match)) + return servers, clients + + +def collect_features(config, match): + res = list(map(re.compile, match)) + return list(filter(lambda c: any(map(lambda r: r.search(c['name']), res)), config)) + + +def _do_collect_tests(servers, clients): + def intersection(key, o1, o2): + """intersection of two collections. + collections are replaced with sets the first time""" + def cached_set(o, key): + v = o[key] + if not isinstance(v, set): + v = set(v) + o[key] = v + return v + return cached_set(o1, key) & cached_set(o2, key) + + def intersect_with_spec(key, o1, o2): + # store as set of (spec, impl) tuple + def cached_set(o): + def to_spec_impl_tuples(values): + for v in values: + spec, _, impl = v.partition(':') + yield spec, impl or spec + v = o[key] + if not isinstance(v, set): + v = set(to_spec_impl_tuples(set(v))) + o[key] = v + return v + for spec1, impl1 in cached_set(o1): + for spec2, impl2 in cached_set(o2): + if spec1 == spec2: + name = impl1 if impl1 == impl2 else '%s-%s' % (impl1, impl2) + yield name, impl1, impl2 + + def maybe_max(key, o1, o2, default): + """maximum of two if present, otherwise defult value""" + v1 = o1.get(key) + v2 = o2.get(key) + return max(v1, v2) if v1 and v2 else v1 or v2 or default + + def filter_with_validkeys(o): + ret = {} + for key in VALID_JSON_KEYS: + if key in o: + ret[key] = o[key] + return ret + + def merge_metadata(o, **ret): + for key in VALID_JSON_KEYS: + if key in o: + ret[key] = o[key] + return ret + + for sv, cl in product(servers, clients): + for proto, proto1, proto2 in intersect_with_spec('protocols', sv, cl): + for trans, trans1, trans2 in intersect_with_spec('transports', sv, cl): + for sock in intersection('sockets', sv, cl): + yield { + 'server': merge_metadata(sv, **{'protocol': proto1, 'transport': trans1}), + 'client': merge_metadata(cl, **{'protocol': proto2, 'transport': trans2}), + 'delay': maybe_max('delay', sv, cl, DEFAULT_MAX_DELAY), + 'timeout': maybe_max('timeout', sv, cl, DEFAULT_TIMEOUT), + 'protocol': proto, + 'transport': trans, + 'socket': sock + } + + +def _filter_entries(tests, regex): + if regex: + return filter(lambda t: re.search(regex, TestEntry.get_name(**t)), tests) + return tests + + +def collect_cross_tests(tests_dict, server_match, client_match, regex): + sv, cl = _collect_testlibs(tests_dict, server_match, client_match) + return list(_filter_entries(_do_collect_tests(sv, cl), regex)) + + +def collect_feature_tests(tests_dict, features_dict, server_match, feature_match, regex): + sv, _ = _collect_testlibs(tests_dict, server_match) + ft = collect_features(features_dict, feature_match) + return list(_filter_entries(_do_collect_tests(sv, ft), regex)) diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/compat.py b/vendor/src/github.com/apache/thrift/test/crossrunner/compat.py new file mode 100644 index 00000000..f1ca91bb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/compat.py @@ -0,0 +1,24 @@ +import os +import sys + +if sys.version_info[0] == 2: + _ENCODE = sys.getfilesystemencoding() + + 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) + return b.join(bin_args).encode(_ENCODE) + + logfile_open = open + +else: + + path_join = os.path.join + str_join = str.join + + def logfile_open(*args): + return open(*args, errors='replace') diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/report.py b/vendor/src/github.com/apache/thrift/test/crossrunner/report.py new file mode 100644 index 00000000..cc5f26fe --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/report.py @@ -0,0 +1,434 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from __future__ import print_function +import datetime +import json +import multiprocessing +import os +import platform +import re +import subprocess +import sys +import time +import traceback + +from .compat import logfile_open, path_join, str_join +from .test import TestEntry + +LOG_DIR = 'log' +RESULT_HTML = 'index.html' +RESULT_JSON = 'results.json' +FAIL_JSON = 'known_failures_%s.json' + + +def generate_known_failures(testdir, overwrite, save, out): + def collect_failures(results): + success_index = 5 + for r in results: + if not r[success_index]: + yield TestEntry.get_name(*r) + try: + with logfile_open(path_join(testdir, RESULT_JSON), 'r') as fp: + results = json.load(fp) + except IOError: + sys.stderr.write('Unable to load last result. Did you run tests ?\n') + return False + fails = collect_failures(results['results']) + if not overwrite: + known = load_known_failures(testdir) + known.extend(fails) + fails = known + fails_json = json.dumps(sorted(set(fails)), indent=2, separators=(',', ': ')) + if save: + with logfile_open(os.path.join(testdir, FAIL_JSON % platform.system()), 'w+') as fp: + fp.write(fails_json) + sys.stdout.write('Successfully updated known failures.\n') + if out: + sys.stdout.write(fails_json) + sys.stdout.write('\n') + return True + + +def load_known_failures(testdir): + try: + with logfile_open(path_join(testdir, FAIL_JSON % platform.system()), 'r') as fp: + return json.load(fp) + except IOError: + return [] + + +class TestReporter(object): + # Unfortunately, standard library doesn't handle timezone well + # DATETIME_FORMAT = '%a %b %d %H:%M:%S %Z %Y' + DATETIME_FORMAT = '%a %b %d %H:%M:%S %Y' + + def __init__(self): + self._log = multiprocessing.get_logger() + self._lock = multiprocessing.Lock() + + @classmethod + def test_logfile(cls, test_name, prog_kind, dir=None): + relpath = path_join('log', '%s_%s.log' % (test_name, prog_kind)) + return relpath if not dir else os.path.realpath(path_join(dir, relpath)) + + def _start(self): + self._start_time = time.time() + + @property + def _elapsed(self): + return time.time() - self._start_time + + @classmethod + def _format_date(cls): + return '%s' % datetime.datetime.now().strftime(cls.DATETIME_FORMAT) + + def _print_date(self): + print(self._format_date(), file=self.out) + + def _print_bar(self, out=None): + print( + '==========================================================================', + file=(out or self.out)) + + def _print_exec_time(self): + print('Test execution took {:.1f} seconds.'.format(self._elapsed), file=self.out) + + +class ExecReporter(TestReporter): + def __init__(self, testdir, test, prog): + super(ExecReporter, self).__init__() + self._test = test + self._prog = prog + self.logpath = self.test_logfile(test.name, prog.kind, testdir) + self.out = None + + def begin(self): + self._start() + self._open() + if self.out and not self.out.closed: + self._print_header() + else: + self._log.debug('Output stream is not available.') + + def end(self, returncode): + self._lock.acquire() + try: + if self.out and not self.out.closed: + self._print_footer(returncode) + self._close() + self.out = None + else: + self._log.debug('Output stream is not available.') + finally: + self._lock.release() + + def killed(self): + print(file=self.out) + print('Server process is successfully killed.', file=self.out) + self.end(None) + + def died(self): + print(file=self.out) + print('*** Server process has died unexpectedly ***', file=self.out) + self.end(None) + + _init_failure_exprs = { + 'server': list(map(re.compile, [ + '[Aa]ddress already in use', + 'Could not bind', + 'EADDRINUSE', + ])), + 'client': list(map(re.compile, [ + '[Cc]onnection refused', + 'Could not connect to localhost', + 'ECONNREFUSED', + 'No such file or directory', # domain socket + ])), + } + + def maybe_false_positive(self): + """Searches through log file for socket bind error. + Returns True if suspicious expression is found, otherwise False""" + try: + if self.out and not self.out.closed: + self.out.flush() + exprs = self._init_failure_exprs[self._prog.kind] + + def match(line): + for expr in exprs: + if expr.search(line): + return True + + with logfile_open(self.logpath, 'r') as fp: + if any(map(match, fp)): + return True + except (KeyboardInterrupt, SystemExit): + raise + except Exception as ex: + self._log.warn('[%s]: Error while detecting false positive: %s' % (self._test.name, str(ex))) + self._log.info(traceback.print_exc()) + return False + + def _open(self): + self.out = logfile_open(self.logpath, 'w+') + + def _close(self): + self.out.close() + + def _print_header(self): + self._print_date() + print('Executing: %s' % str_join(' ', self._prog.command), file=self.out) + print('Directory: %s' % self._prog.workdir, file=self.out) + print('config:delay: %s' % self._test.delay, file=self.out) + print('config:timeout: %s' % self._test.timeout, file=self.out) + self._print_bar() + self.out.flush() + + def _print_footer(self, returncode=None): + self._print_bar() + if returncode is not None: + print('Return code: %d' % returncode, file=self.out) + else: + print('Process is killed.', file=self.out) + self._print_exec_time() + self._print_date() + + +class SummaryReporter(TestReporter): + def __init__(self, basedir, testdir_relative, concurrent=True): + super(SummaryReporter, self).__init__() + self._basedir = basedir + self._testdir_rel = testdir_relative + self.logdir = path_join(self.testdir, LOG_DIR) + self.out_path = path_join(self.testdir, RESULT_JSON) + self.concurrent = concurrent + self.out = sys.stdout + self._platform = platform.system() + self._revision = self._get_revision() + self._tests = [] + if not os.path.exists(self.logdir): + os.mkdir(self.logdir) + self._known_failures = load_known_failures(self.testdir) + self._unexpected_success = [] + self._flaky_success = [] + self._unexpected_failure = [] + self._expected_failure = [] + self._print_header() + + @property + def testdir(self): + return path_join(self._basedir, self._testdir_rel) + + def _result_string(self, test): + if test.success: + if test.retry_count == 0: + return 'success' + elif test.retry_count == 1: + return 'flaky(1 retry)' + else: + return 'flaky(%d retries)' % test.retry_count + elif test.expired: + return 'failure(timeout)' + else: + return 'failure(%d)' % test.returncode + + def _get_revision(self): + p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], + cwd=self.testdir, stdout=subprocess.PIPE) + out, _ = p.communicate() + return out.strip() + + def _format_test(self, test, with_result=True): + name = '%s-%s' % (test.server.name, test.client.name) + trans = '%s-%s' % (test.transport, test.socket) + if not with_result: + return '{:24s}{:13s}{:25s}'.format(name[:23], test.protocol[:12], trans[:24]) + else: + return '{:24s}{:13s}{:25s}{:s}\n'.format(name[:23], test.protocol[:12], trans[:24], self._result_string(test)) + + def _print_test_header(self): + self._print_bar() + print( + '{:24s}{:13s}{:25s}{:s}'.format('server-client:', 'protocol:', 'transport:', 'result:'), + file=self.out) + + def _print_header(self): + self._start() + print('Apache Thrift - Integration Test Suite', file=self.out) + self._print_date() + self._print_test_header() + + def _print_unexpected_failure(self): + if len(self._unexpected_failure) > 0: + self.out.writelines([ + '*** Following %d failures were unexpected ***:\n' % len(self._unexpected_failure), + 'If it is introduced by you, please fix it before submitting the code.\n', + # 'If not, please report at https://issues.apache.org/jira/browse/THRIFT\n', + ]) + self._print_test_header() + for i in self._unexpected_failure: + self.out.write(self._format_test(self._tests[i])) + self._print_bar() + else: + print('No unexpected failures.', file=self.out) + + def _print_flaky_success(self): + if len(self._flaky_success) > 0: + print( + 'Following %d tests were expected to cleanly succeed but needed retry:' % len(self._flaky_success), + file=self.out) + self._print_test_header() + for i in self._flaky_success: + self.out.write(self._format_test(self._tests[i])) + self._print_bar() + + def _print_unexpected_success(self): + if len(self._unexpected_success) > 0: + print( + 'Following %d tests were known to fail but succeeded (maybe flaky):' % len(self._unexpected_success), + file=self.out) + self._print_test_header() + for i in self._unexpected_success: + self.out.write(self._format_test(self._tests[i])) + self._print_bar() + + def _http_server_command(self, port): + if sys.version_info[0] < 3: + return 'python -m SimpleHTTPServer %d' % port + else: + return 'python -m http.server %d' % port + + def _print_footer(self): + fail_count = len(self._expected_failure) + len(self._unexpected_failure) + self._print_bar() + self._print_unexpected_success() + self._print_flaky_success() + self._print_unexpected_failure() + self._write_html_data() + self._assemble_log('unexpected failures', self._unexpected_failure) + self._assemble_log('known failures', self._expected_failure) + self.out.writelines([ + 'You can browse results at:\n', + '\tfile://%s/%s\n' % (self.testdir, RESULT_HTML), + '# If you use Chrome, run:\n', + '# \tcd %s\n#\t%s\n' % (self._basedir, self._http_server_command(8001)), + '# then browse:\n', + '# \thttp://localhost:%d/%s/\n' % (8001, self._testdir_rel), + 'Full log for each test is here:\n', + '\ttest/log/client_server_protocol_transport_client.log\n', + '\ttest/log/client_server_protocol_transport_server.log\n', + '%d failed of %d tests in total.\n' % (fail_count, len(self._tests)), + ]) + self._print_exec_time() + self._print_date() + + def _render_result(self, test): + return [ + test.server.name, + test.client.name, + test.protocol, + test.transport, + test.socket, + test.success, + test.as_expected, + test.returncode, + { + 'server': self.test_logfile(test.name, test.server.kind), + 'client': self.test_logfile(test.name, test.client.kind), + }, + ] + + def _write_html_data(self): + """Writes JSON data to be read by result html""" + results = [self._render_result(r) for r in self._tests] + with logfile_open(self.out_path, 'w+') as fp: + fp.write(json.dumps({ + 'date': self._format_date(), + 'revision': str(self._revision), + 'platform': self._platform, + 'duration': '{:.1f}'.format(self._elapsed), + 'results': results, + }, indent=2)) + + def _assemble_log(self, title, indexes): + if len(indexes) > 0: + def add_prog_log(fp, test, prog_kind): + print('*************************** %s message ***************************' % prog_kind, + file=fp) + path = self.test_logfile(test.name, prog_kind, self.testdir) + if os.path.exists(path): + with logfile_open(path, 'r') as prog_fp: + print(prog_fp.read(), file=fp) + filename = title.replace(' ', '_') + '.log' + with logfile_open(os.path.join(self.logdir, filename), 'w+') as fp: + for test in map(self._tests.__getitem__, indexes): + fp.write('TEST: [%s]\n' % test.name) + add_prog_log(fp, test, test.server.kind) + add_prog_log(fp, test, test.client.kind) + fp.write('**********************************************************************\n\n') + print('%s are logged to %s/%s/%s' % (title.capitalize(), self._testdir_rel, LOG_DIR, filename)) + + def end(self): + self._print_footer() + return len(self._unexpected_failure) == 0 + + def add_test(self, test_dict): + test = TestEntry(self.testdir, **test_dict) + self._lock.acquire() + try: + if not self.concurrent: + self.out.write(self._format_test(test, False)) + self.out.flush() + self._tests.append(test) + return len(self._tests) - 1 + finally: + self._lock.release() + + def add_result(self, index, returncode, expired, retry_count): + self._lock.acquire() + try: + failed = returncode is None or returncode != 0 + flaky = not failed and retry_count != 0 + test = self._tests[index] + known = test.name in self._known_failures + if failed: + if known: + self._log.debug('%s failed as expected' % test.name) + self._expected_failure.append(index) + else: + self._log.info('unexpected failure: %s' % test.name) + self._unexpected_failure.append(index) + elif flaky and not known: + self._log.info('unexpected flaky success: %s' % test.name) + self._flaky_success.append(index) + elif not flaky and known: + self._log.info('unexpected success: %s' % test.name) + self._unexpected_success.append(index) + test.success = not failed + test.returncode = returncode + test.retry_count = retry_count + test.expired = expired + test.as_expected = known == failed + if not self.concurrent: + self.out.write(self._result_string(test) + '\n') + else: + self.out.write(self._format_test(test)) + finally: + self._lock.release() diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/run.py b/vendor/src/github.com/apache/thrift/test/crossrunner/run.py new file mode 100644 index 00000000..f522bb19 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/run.py @@ -0,0 +1,389 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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 contextlib +import multiprocessing +import multiprocessing.managers +import os +import platform +import random +import signal +import socket +import subprocess +import sys +import threading +import time + +from .compat import str_join +from .test import TestEntry, domain_socket_path +from .report import ExecReporter, SummaryReporter + +RESULT_TIMEOUT = 128 +RESULT_ERROR = 64 + +# globals +ports = None +stop = None + + +class ExecutionContext(object): + def __init__(self, cmd, cwd, env, report): + self._log = multiprocessing.get_logger() + self.report = report + self.cmd = cmd + self.cwd = cwd + self.env = env + self.timer = None + self.expired = False + self.killed = False + self.proc = None + + def _expire(self): + self._log.info('Timeout') + self.expired = True + self.kill() + + def kill(self): + self._log.debug('Killing process : %d' % self.proc.pid) + self.killed = True + if platform.system() != 'Windows': + try: + os.killpg(self.proc.pid, signal.SIGKILL) + except Exception: + self._log.info('Failed to kill process group', exc_info=sys.exc_info()) + try: + self.proc.kill() + except Exception: + self._log.info('Failed to kill process', exc_info=sys.exc_info()) + + def _popen_args(self): + args = { + 'cwd': self.cwd, + 'env': self.env, + 'stdout': self.report.out, + 'stderr': subprocess.STDOUT, + } + # make sure child processes doesn't remain after killing + if platform.system() == 'Windows': + DETACHED_PROCESS = 0x00000008 + args.update(creationflags=DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP) + else: + args.update(preexec_fn=os.setsid) + return args + + def start(self, timeout=0): + joined = str_join(' ', self.cmd) + self._log.debug('COMMAND: %s', joined) + self._log.debug('WORKDIR: %s', self.cwd) + self._log.debug('LOGFILE: %s', self.report.logpath) + self.report.begin() + self.proc = subprocess.Popen(self.cmd, **self._popen_args()) + if timeout > 0: + self.timer = threading.Timer(timeout, self._expire) + self.timer.start() + return self._scoped() + + @contextlib.contextmanager + def _scoped(self): + yield self + self._log.debug('Killing scoped process') + if self.proc.poll() is None: + self.kill() + self.report.killed() + else: + self._log.debug('Process died unexpectedly') + self.report.died() + + def wait(self): + self.proc.communicate() + if self.timer: + self.timer.cancel() + self.report.end(self.returncode) + + @property + def returncode(self): + return self.proc.returncode if self.proc else None + + +def exec_context(port, logdir, test, prog): + report = ExecReporter(logdir, test, prog) + prog.build_command(port) + return ExecutionContext(prog.command, prog.workdir, prog.env, report) + + +def run_test(testdir, logdir, test_dict, max_retry, async=True): + logger = multiprocessing.get_logger() + + def ensure_socket_open(proc, port, max_delay): + sleeped = 0.1 + time.sleep(sleeped) + sleep_step = 0.2 + while True: + # Create sockets every iteration because refused sockets cannot be + # reused on some systems. + sock4 = socket.socket() + sock6 = socket.socket(family=socket.AF_INET6) + try: + if sock4.connect_ex(('127.0.0.1', port)) == 0 \ + or sock6.connect_ex(('::1', port)) == 0: + return True + if proc.poll() is not None: + logger.warn('server process is exited') + return False + if sleeped > max_delay: + logger.warn('sleeped for %f seconds but server port is not open' % sleeped) + return False + time.sleep(sleep_step) + sleeped += sleep_step + finally: + sock4.close() + sock6.close() + logger.debug('waited %f sec for server port open' % sleeped) + return True + + try: + max_bind_retry = 3 + retry_count = 0 + bind_retry_count = 0 + test = TestEntry(testdir, **test_dict) + while True: + if stop.is_set(): + logger.debug('Skipping because shutting down') + return (retry_count, None) + logger.debug('Start') + with PortAllocator.alloc_port_scoped(ports, test.socket) as port: + logger.debug('Start with port %d' % port) + sv = exec_context(port, logdir, test, test.server) + cl = exec_context(port, logdir, test, test.client) + + logger.debug('Starting server') + with sv.start(): + if test.socket in ('domain', 'abstract'): + time.sleep(0.1) + port_ok = True + else: + port_ok = ensure_socket_open(sv.proc, port, test.delay) + if port_ok: + connect_retry_count = 0 + max_connect_retry = 3 + connect_retry_wait = 0.5 + while True: + if sv.proc.poll() is not None: + logger.info('not starting client because server process is absent') + break + logger.debug('Starting client') + cl.start(test.timeout) + logger.debug('Waiting client') + cl.wait() + if not cl.report.maybe_false_positive() or connect_retry_count >= max_connect_retry: + if connect_retry_count > 0 and connect_retry_count < max_connect_retry: + logger.warn('[%s]: Connected after %d retry (%.2f sec each)' % (test.server.name, connect_retry_count, connect_retry_wait)) + # Wait for 50ms to see if server does not die at the end. + time.sleep(0.05) + break + logger.debug('Server may not be ready, waiting %.2f second...' % connect_retry_wait) + time.sleep(connect_retry_wait) + connect_retry_count += 1 + + if sv.report.maybe_false_positive() and bind_retry_count < max_bind_retry: + logger.warn('[%s]: Detected socket bind failure, retrying...', test.server.name) + bind_retry_count += 1 + else: + if cl.expired: + result = RESULT_TIMEOUT + else: + result = cl.proc.returncode if cl.proc else RESULT_ERROR + if not sv.killed: + # Server died without being killed. + result |= RESULT_ERROR + + if result == 0 or retry_count >= max_retry: + return (retry_count, result) + else: + logger.info('[%s-%s]: test failed, retrying...', test.server.name, test.client.name) + retry_count += 1 + except Exception: + if not async: + raise + logger.warn('Error executing [%s]', test.name, exc_info=True) + return (retry_count, RESULT_ERROR) + except: + logger.info('Interrupted execution', exc_info=True) + if not async: + raise + stop.set() + return (retry_count, RESULT_ERROR) + + +class PortAllocator(object): + def __init__(self): + self._log = multiprocessing.get_logger() + self._lock = multiprocessing.Lock() + self._ports = set() + self._dom_ports = set() + self._last_alloc = 0 + + def _get_tcp_port(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', 0)) + port = sock.getsockname()[1] + self._lock.acquire() + try: + ok = port not in self._ports + if ok: + self._ports.add(port) + self._last_alloc = time.time() + finally: + self._lock.release() + sock.close() + return port if ok else self._get_tcp_port() + + def _get_domain_port(self): + port = random.randint(1024, 65536) + self._lock.acquire() + try: + ok = port not in self._dom_ports + if ok: + self._dom_ports.add(port) + finally: + self._lock.release() + return port if ok else self._get_domain_port() + + def alloc_port(self, socket_type): + if socket_type in ('domain', 'abstract'): + return self._get_domain_port() + else: + return self._get_tcp_port() + + # static method for inter-process invokation + @staticmethod + @contextlib.contextmanager + def alloc_port_scoped(allocator, socket_type): + port = allocator.alloc_port(socket_type) + yield port + allocator.free_port(socket_type, port) + + def free_port(self, socket_type, port): + self._log.debug('free_port') + self._lock.acquire() + try: + if socket_type == 'domain': + self._dom_ports.remove(port) + path = domain_socket_path(port) + if os.path.exists(path): + os.remove(path) + elif socket_type == 'abstract': + self._dom_ports.remove(port) + else: + self._ports.remove(port) + except IOError: + self._log.info('Error while freeing port', exc_info=sys.exc_info()) + finally: + self._lock.release() + + +class NonAsyncResult(object): + def __init__(self, value): + self._value = value + + def get(self, timeout=None): + return self._value + + def wait(self, timeout=None): + pass + + def ready(self): + return True + + def successful(self): + return self._value == 0 + + +class TestDispatcher(object): + def __init__(self, testdir, basedir, logdir_rel, concurrency): + self._log = multiprocessing.get_logger() + self.testdir = testdir + self._report = SummaryReporter(basedir, logdir_rel, concurrency > 1) + self.logdir = self._report.testdir + # seems needed for python 2.x to handle keyboard interrupt + self._stop = multiprocessing.Event() + self._async = concurrency > 1 + if not self._async: + self._pool = None + global stop + global ports + stop = self._stop + ports = PortAllocator() + else: + self._m = multiprocessing.managers.BaseManager() + self._m.register('ports', PortAllocator) + self._m.start() + self._pool = multiprocessing.Pool(concurrency, self._pool_init, (self._m.address,)) + self._log.debug( + 'TestDispatcher started with %d concurrent jobs' % concurrency) + + def _pool_init(self, address): + global stop + global m + global ports + stop = self._stop + m = multiprocessing.managers.BaseManager(address) + m.connect() + ports = m.ports() + + def _dispatch_sync(self, test, cont, max_retry): + r = run_test(self.testdir, self.logdir, test, max_retry, False) + cont(r) + return NonAsyncResult(r) + + def _dispatch_async(self, test, cont, max_retry): + self._log.debug('_dispatch_async') + return self._pool.apply_async(func=run_test, args=(self.testdir, self.logdir, test, max_retry), callback=cont) + + def dispatch(self, test, max_retry): + index = self._report.add_test(test) + + def cont(result): + if not self._stop.is_set(): + if result and len(result) == 2: + retry_count, returncode = result + else: + retry_count = 0 + returncode = RESULT_ERROR + self._log.debug('freeing port') + self._log.debug('adding result') + self._report.add_result(index, returncode, returncode == RESULT_TIMEOUT, retry_count) + self._log.debug('finish continuation') + fn = self._dispatch_async if self._async else self._dispatch_sync + return fn(test, cont, max_retry) + + def wait(self): + if self._async: + self._pool.close() + self._pool.join() + self._m.shutdown() + return self._report.end() + + def terminate(self): + self._stop.set() + if self._async: + self._pool.terminate() + self._pool.join() + self._m.shutdown() diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/setup.cfg b/vendor/src/github.com/apache/thrift/test/crossrunner/setup.cfg new file mode 100644 index 00000000..7da1f960 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 100 diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/test.py b/vendor/src/github.com/apache/thrift/test/crossrunner/test.py new file mode 100644 index 00000000..74fd916e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/test.py @@ -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. +# + +import copy +import multiprocessing +import os +import sys +from .compat import path_join +from .util import merge_dict + + +def domain_socket_path(port): + return '/tmp/ThriftTest.thrift.%d' % port + + +class TestProgram(object): + def __init__(self, kind, name, protocol, transport, socket, workdir, command, env=None, + extra_args=[], extra_args2=[], join_args=False, **kwargs): + self.kind = kind + self.name = name + self.protocol = protocol + self.transport = transport + self.socket = socket + self.workdir = workdir + self.command = None + self._base_command = self._fix_cmd_path(command) + if env: + self.env = copy.copy(os.environ) + self.env.update(env) + else: + self.env = os.environ + self._extra_args = extra_args + self._extra_args2 = extra_args2 + self._join_args = join_args + + def _fix_cmd_path(self, cmd): + # if the arg is a file in the current directory, make it path + def abs_if_exists(arg): + p = path_join(self.workdir, arg) + return p if os.path.exists(p) else arg + + if cmd[0] == 'python': + cmd[0] = sys.executable + else: + cmd[0] = abs_if_exists(cmd[0]) + return cmd + + def _socket_args(self, socket, port): + return { + 'ip-ssl': ['--ssl'], + 'domain': ['--domain-socket=%s' % domain_socket_path(port)], + 'abstract': ['--abstract-namespace', '--domain-socket=%s' % domain_socket_path(port)], + }.get(socket, None) + + def build_command(self, port): + cmd = copy.copy(self._base_command) + args = copy.copy(self._extra_args2) + args.append('--protocol=' + self.protocol) + args.append('--transport=' + self.transport) + socket_args = self._socket_args(self.socket, port) + if socket_args: + args += socket_args + args.append('--port=%d' % port) + if self._join_args: + cmd.append('%s' % " ".join(args)) + else: + cmd.extend(args) + if self._extra_args: + cmd.extend(self._extra_args) + self.command = cmd + return self.command + + +class TestEntry(object): + def __init__(self, testdir, server, client, delay, timeout, **kwargs): + self.testdir = testdir + self._log = multiprocessing.get_logger() + self._config = kwargs + self.protocol = kwargs['protocol'] + self.transport = kwargs['transport'] + self.socket = kwargs['socket'] + srv_dict = self._fix_workdir(merge_dict(self._config, server)) + cli_dict = self._fix_workdir(merge_dict(self._config, client)) + cli_dict['extra_args2'] = srv_dict.pop('remote_args', []) + srv_dict['extra_args2'] = cli_dict.pop('remote_args', []) + self.server = TestProgram('server', **srv_dict) + self.client = TestProgram('client', **cli_dict) + self.delay = delay + self.timeout = timeout + self._name = None + # results + self.success = None + self.as_expected = None + self.returncode = None + self.expired = False + self.retry_count = 0 + + def _fix_workdir(self, config): + key = 'workdir' + path = config.get(key, None) + if not path: + path = self.testdir + if os.path.isabs(path): + path = os.path.realpath(path) + else: + path = os.path.realpath(path_join(self.testdir, path)) + config.update({key: path}) + return config + + @classmethod + def get_name(cls, server, client, protocol, transport, socket, *args, **kwargs): + return '%s-%s_%s_%s-%s' % (server, client, protocol, transport, socket) + + @property + def name(self): + if not self._name: + self._name = self.get_name( + self.server.name, self.client.name, self.protocol, self.transport, self.socket) + return self._name + + @property + def transport_name(self): + return '%s-%s' % (self.transport, self.socket) + + +def test_name(server, client, protocol, transport, socket, **kwargs): + return TestEntry.get_name(server['name'], client['name'], protocol, transport, socket) diff --git a/vendor/src/github.com/apache/thrift/test/crossrunner/util.py b/vendor/src/github.com/apache/thrift/test/crossrunner/util.py new file mode 100644 index 00000000..e2d195a2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/crossrunner/util.py @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import copy + + +def merge_dict(base, update): + """Update dict concatenating list values""" + res = copy.deepcopy(base) + for k, v in list(update.items()): + if k in list(res.keys()) and isinstance(v, list): + res[k].extend(v) + else: + res[k] = v + return res diff --git a/vendor/src/github.com/apache/thrift/test/csharp/Makefile.am b/vendor/src/github.com/apache/thrift/test/csharp/Makefile.am new file mode 100644 index 00000000..7aa332cc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/Makefile.am @@ -0,0 +1,87 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +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) diff --git a/vendor/src/github.com/apache/thrift/test/csharp/Program.cs b/vendor/src/github.com/apache/thrift/test/csharp/Program.cs new file mode 100644 index 00000000..8ec00e30 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/Program.cs @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// 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/vendor/src/github.com/apache/thrift/test/csharp/Properties/AssemblyInfo.cs b/vendor/src/github.com/apache/thrift/test/csharp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..504ca8de --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ThriftTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ThriftTest")] +[assembly: AssemblyCopyright("Copyright © 2009 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/vendor/src/github.com/apache/thrift/test/csharp/TestClient.cs b/vendor/src/github.com/apache/thrift/test/csharp/TestClient.cs new file mode 100644 index 00000000..67673ec3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/TestClient.cs @@ -0,0 +1,836 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 + { + private 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; + 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 ErrorUnknown = 64; + + private class ClientTest + { + private readonly TTransport transport; + private readonly ThriftTest.Client client; + private readonly int numIterations; + private bool done; + + public int ReturnCode { get; set; } + + public ClientTest(TestParams param) + { + transport = param.CreateTransport(); + client = new ThriftTest.Client(param.CreateProtocol(transport)); + 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); + } + 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") + { + param.protocol = "compact"; + Console.WriteLine("Using compact protocol"); + } + else if (args[i] == "--json" || args[i] == "--protocol=json") + { + param.protocol = "json"; + Console.WriteLine("Using JSON protocol"); + } + else if (args[i] == "--ssl") + { + param.encrypted = true; + Console.WriteLine("Using encrypted transport"); + } + } + } + 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) + { + byte[] retval = new byte[0x100]; + int initLen = Math.Min(0x100,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) + { + 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; + } + + 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; + } + + byte[] binOut = PrepareTestData(true); + 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/vendor/src/github.com/apache/thrift/test/csharp/TestServer.cs b/vendor/src/github.com/apache/thrift/test/csharp/TestServer.cs new file mode 100644 index 00000000..a9af715a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/TestServer.cs @@ -0,0 +1,547 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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; + } + + public Dictionary> testInsanity(Insanity argument) + { + testLogDelegate.Invoke("testInsanity()"); + + Xtruct hello = new Xtruct(); + hello.String_thing = "Hello2"; + hello.Byte_thing = 2; + hello.I32_thing = 2; + hello.I64_thing = 2; + + Xtruct goodbye = new Xtruct(); + goodbye.String_thing = "Goodbye4"; + goodbye.Byte_thing = (sbyte)4; + goodbye.I32_thing = 4; + goodbye.I64_thing = (long)4; + + Insanity crazy = new Insanity(); + crazy.UserMap = new Dictionary(); + crazy.UserMap[Numberz.EIGHT] = (long)8; + crazy.Xtructs = new List(); + crazy.Xtructs.Add(goodbye); + + Insanity looney = new Insanity(); + crazy.UserMap[Numberz.FIVE] = (long)5; + crazy.Xtructs.Add(hello); + + Dictionary first_map = new Dictionary(); + Dictionary second_map = new Dictionary(); ; + + first_map[Numberz.TWO] = crazy; + first_map[Numberz.THREE] = crazy; + + second_map[Numberz.SIX] = looney; + + 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/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.csproj b/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.csproj new file mode 100644 index 00000000..65c0daf7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.csproj @@ -0,0 +1,141 @@ + + + + + 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 + 1.0.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" + + \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.sln b/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.sln new file mode 100644 index 00000000..1765a03a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/csharp/ThriftTest.sln @@ -0,0 +1,17 @@ + +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/vendor/src/github.com/apache/thrift/test/dart/Makefile.am b/vendor/src/github.com/apache/thrift/test/dart/Makefile.am new file mode 100644 index 00000000..6f97de93 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/dart/Makefile.am @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-dart/thrift_test/lib/thrift_test.dart: ../ThriftTest.thrift + $(THRIFT) --gen dart ../ThriftTest.thrift + +pub-get-gen: gen-dart/thrift_test/lib/thrift_test.dart + cd gen-dart/thrift_test; ${DARTPUB} get + +pub-get: pub-get-gen + cd test_client; ${DARTPUB} get + +stubs: gen-dart/thrift_test/lib/thrift_test.dart pub-get + +precross: stubs + +check: stubs + +clean-local: + $(RM) -r gen-dart test_client/.pub + find . -type d -name "packages" | xargs $(RM) -r + find . -type f -name ".packages" | xargs $(RM) + +client: stubs + ${DART} test_client/bin/main.dart diff --git a/vendor/src/github.com/apache/thrift/test/dart/test_client/bin/main.dart b/vendor/src/github.com/apache/thrift/test/dart/test_client/bin/main.dart new file mode 100644 index 00000000..996844b5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/dart/test_client/bin/main.dart @@ -0,0 +1,337 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// 'License'); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:collection/collection.dart'; +import 'package:http/http.dart' as http; +import 'package:thrift/thrift.dart'; +import 'package:thrift/thrift_console.dart'; +import 'package:thrift_test/thrift_test.dart'; + +const TEST_BASETYPES = 1; // 0000 0001 +const TEST_STRUCTS = 2; // 0000 0010 +const TEST_CONTAINERS = 4; // 0000 0100 +const TEST_EXCEPTIONS = 8; // 0000 1000 +const TEST_UNKNOWN = 64; // 0100 0000 (Failed to prepare environemt etc.) +const TEST_TIMEOUT = 128; // 1000 0000 +const TEST_NOTUSED = 48; // 0011 0000 (reserved bits) + +typedef Future FutureFunction(); + +class TTest { + final int errorCode; + final String name; + final FutureFunction func; + + TTest(this.errorCode, this.name, this.func); +} + +class TTestError extends Error { + final actual; + final expected; + + TTestError(this.actual, this.expected); + + String toString() => '$actual != $expected'; +} + +List _tests; +ThriftTestClient client; +bool verbose; + +/// Adapted from TestClient.php +main(List args) async { + ArgResults results = _parseArgs(args); + + if (results == null) { + exit(TEST_UNKNOWN); + } + + verbose = results['verbose'] == true; + + await _initTestClient( + host: results['host'], + port: int.parse(results['port']), + transportType: results['transport'], + protocolType: results['protocol']).catchError((e) { + stdout.writeln('Error:'); + stdout.writeln('$e'); + if (e is Error) { + stdout.writeln('${e.stackTrace}'); + } + exit(TEST_UNKNOWN); + }); + + // run tests + _tests = _createTests(); + + int result = 0; + + for (TTest test in _tests) { + if (verbose) stdout.write('${test.name}... '); + try { + await test.func(); + if (verbose) stdout.writeln('success!'); + } catch (e) { + if (verbose) stdout.writeln('$e'); + result = result | test.errorCode; + } + } + + exit(result); +} + +ArgResults _parseArgs(List args) { + var parser = new ArgParser(); + parser.addOption('host', defaultsTo: 'localhost', help: 'The server host'); + parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to'); + parser.addOption('transport', + defaultsTo: 'buffered', + allowed: ['buffered', 'framed', 'http'], + help: 'The transport name', + allowedHelp: { + 'buffered': 'TBufferedTransport', + 'framed': 'TFramedTransport' + }); + parser.addOption('protocol', + defaultsTo: 'binary', + allowed: ['binary', 'compact', 'json'], + help: 'The protocol name', + allowedHelp: { + 'binary': 'TBinaryProtocol', + 'compact': 'TCompactProtocol', + 'json': 'TJsonProtocol' + }); + parser.addFlag('verbose', defaultsTo: false); + + ArgResults results; + try { + results = parser.parse(args); + } catch (e) { + stdout.writeln('$e\n'); + } + + if (results == null) stdout.write(parser.usage); + + return results; +} + +TProtocolFactory getProtocolFactory(String protocolType) { + if (protocolType == 'binary') { + return new TBinaryProtocolFactory(); + } else if (protocolType == 'compact') { + return new TCompactProtocolFactory(); + } else if (protocolType == 'json') { + return new TJsonProtocolFactory(); + } + + throw new ArgumentError.value(protocolType); +} + +Future _initTestClient( + {String host, int port, String transportType, String protocolType}) async { + TTransport transport; + var protocolFactory = getProtocolFactory(protocolType); + + if (transportType == 'http') { + var httpClient = new http.IOClient(); + var uri = Uri.parse('http://$host:$port'); + var config = new THttpConfig(uri, {}); + transport = new THttpClientTransport(httpClient, config); + } else { + var socket = await Socket.connect(host, port); + transport = new TClientSocketTransport(new TTcpSocket(socket)); + if (transportType == 'framed') { + transport = new TFramedTransport(transport); + } + } + + var protocol = protocolFactory.getProtocol(transport); + client = new ThriftTestClient(protocol); + + await transport.open(); +} + +List _createTests() { + List tests = []; + + var xtruct = new Xtruct() + ..string_thing = 'Zero' + ..byte_thing = 1 + ..i32_thing = -3 + ..i64_thing = -5; + + tests.add(new TTest(TEST_BASETYPES, 'testVoid', () async { + await client.testVoid(); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testString', () async { + var input = 'Test'; + var result = await client.testString(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testBool', () async { + var input = true; + var result = await client.testBool(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testByte', () async { + var input = 64; + var result = await client.testByte(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testI32', () async { + var input = 2147483647; + var result = await client.testI32(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testI64', () async { + var input = 9223372036854775807; + var result = await client.testI64(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testDouble', () async { + var input = 3.1415926; + var result = await client.testDouble(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testBinary', () async { + var utf8Codec = const Utf8Codec(); + var input = utf8Codec.encode('foo'); + var result = await client.testBinary(input); + var equality = const ListEquality(); + if (!equality.equals(result, input)) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testStruct', () async { + var result = await client.testStruct(xtruct); + if ('$result' != '$xtruct') throw new TTestError(result, xtruct); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testNest', () async { + var input = new Xtruct2() + ..byte_thing = 1 + ..struct_thing = xtruct + ..i32_thing = -3; + + var result = await client.testNest(input); + if ('$result' != '$input') throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testMap', () async { + Map input = {1: -10, 2: -9, 3: -8, 4: -7, 5: -6}; + + var result = await client.testMap(input); + var equality = const MapEquality(); + if (!equality.equals(result, input)) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testSet', () async { + var input = new Set.from([-2, -1, 0, 1, 2]); + var result = await client.testSet(input); + var equality = const SetEquality(); + if (!equality.equals(result, input)) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testList', () async { + var input = [-2, -1, 0, 1, 2]; + var result = await client.testList(input); + var equality = const ListEquality(); + if (!equality.equals(result, input)) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testEnum', () async { + await _testEnum(Numberz.ONE); + await _testEnum(Numberz.TWO); + await _testEnum(Numberz.THREE); + await _testEnum(Numberz.FIVE); + await _testEnum(Numberz.EIGHT); + })); + + tests.add(new TTest(TEST_BASETYPES, 'testTypedef', () async { + var input = 309858235082523; + var result = await client.testTypedef(input); + if (result != input) throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testMapMap', () async { + Map> result = await client.testMapMap(1); + if (result.isEmpty || result[result.keys.first].isEmpty) { + throw new TTestError(result, 'Map>'); + } + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testInsanity', () async { + var input = new Insanity(); + input.userMap = {Numberz.FIVE: 5000}; + input.xtructs = [xtruct]; + + Map> result = await client.testInsanity(input); + if (result.isEmpty || result[result.keys.first].isEmpty) { + throw new TTestError(result, 'Map>'); + } + })); + + tests.add(new TTest(TEST_CONTAINERS, 'testMulti', () async { + var input = new Xtruct() + ..string_thing = 'Hello2' + ..byte_thing = 123 + ..i32_thing = 456 + ..i64_thing = 789; + + var result = await client.testMulti(input.byte_thing, input.i32_thing, + input.i64_thing, {1: 'one'}, Numberz.EIGHT, 5678); + if ('$result' != '$input') throw new TTestError(result, input); + })); + + tests.add(new TTest(TEST_EXCEPTIONS, 'testException', () async { + try { + await client.testException('Xception'); + } on Xception catch (_) { + return; + } + + throw new TTestError(null, 'Xception'); + })); + + tests.add(new TTest(TEST_EXCEPTIONS, 'testMultiException', () async { + try { + await client.testMultiException('Xception2', 'foo'); + } on Xception2 catch (_) { + return; + } + + throw new TTestError(null, 'Xception2'); + })); + + return tests; +} + +Future _testEnum(int input) async { + var result = await client.testEnum(input); + if (result != input) throw new TTestError(result, input); +} diff --git a/vendor/src/github.com/apache/thrift/test/dart/test_client/pubspec.yaml b/vendor/src/github.com/apache/thrift/test/dart/test_client/pubspec.yaml new file mode 100644 index 00000000..0b6bda21 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/dart/test_client/pubspec.yaml @@ -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. + +name: thrift_test_client +version: 0.10.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" + +dependencies: + args: ^0.13.0 + http: ^0.11.0 + thrift: + path: ../../../lib/dart + thrift_test: + path: ../gen-dart/thrift_test + +dev_dependencies: + test: "^0.12.0" diff --git a/vendor/src/github.com/apache/thrift/test/erl/Makefile.am b/vendor/src/github.com/apache/thrift/test/erl/Makefile.am new file mode 100644 index 00000000..be8b4f54 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/erl/Makefile.am @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +THRIFT_FILES = $(wildcard ../*.thrift) + +if ERLANG_OTP16 +ERL_FLAG = erl:otp16 +else +ERL_FLAG = erl +endif +# make sure ThriftTest.thrift is generated last to prevent conflicts with other *.thrift files +.generated: $(THRIFT_FILES) + for f in $(THRIFT_FILES) ; do \ + $(THRIFT) --gen $(ERL_FLAG) -o src $$f ; \ + done ; \ + $(THRIFT) --gen $(ERL_FLAG) -o src ../ThriftTest.thrift + touch .generated + +precross: .generated + $(REBAR) compile + +clean: + rm -f .generated + rm -rf src/gen-erl + $(REBAR) clean diff --git a/vendor/src/github.com/apache/thrift/test/erl/rebar.config b/vendor/src/github.com/apache/thrift/test/erl/rebar.config new file mode 100644 index 00000000..59a0788d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/erl/rebar.config @@ -0,0 +1,6 @@ +{sub_dirs, ["../../lib/erl"]}. + +{erl_opts, [ + debug_info, + {i, "../../lib/erl/include"} +]}. diff --git a/vendor/src/github.com/apache/thrift/test/erl/src/test_client.erl b/vendor/src/github.com/apache/thrift/test/erl/src/test_client.erl new file mode 100644 index 00000000..50ffc64f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/erl/src/test_client.erl @@ -0,0 +1,174 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_client). + +-export([start/0, start/1]). + +-include("gen-erl/thrift_test_types.hrl"). + +-record(options, {port = 9090, + client_opts = []}). + +parse_args(Args) -> parse_args(Args, #options{}). +parse_args([], Opts) -> + Opts; +parse_args([Head | Rest], Opts) -> + NewOpts = + case Head of + "--port=" ++ Port -> + case string:to_integer(Port) of + {IntPort,_} when is_integer(IntPort) -> + Opts#options{port = IntPort}; + _Else -> + erlang:error({bad_arg, Head}) + end; + "--transport=" ++ Trans -> + % TODO: Enable Buffered and HTTP transport + case Trans of + "framed" -> + Opts#options{client_opts = [{framed, true} | Opts#options.client_opts]}; + _Else -> + Opts + end; + "--ssl" -> + ssl:start(), + SslOptions = + {ssloptions, [ + {certfile, "../keys/client.crt"} + ,{keyfile, "../keys/server.key"} + ]}, + Opts#options{client_opts = [{ssltransport, true} | [SslOptions | Opts#options.client_opts]]}; + "--protocol=" ++ Proto -> + Opts#options{client_opts = [{protocol, list_to_atom(Proto)}]}; + _Else -> + erlang:error({bad_arg, Head}) + end, + parse_args(Rest, NewOpts). + +start() -> start(init:get_plain_arguments()). +start(Args) -> + #options{port = Port, client_opts = ClientOpts} = parse_args(Args), + {ok, Client0} = thrift_client_util:new( + "127.0.0.1", Port, thrift_test_thrift, ClientOpts), + + DemoXtruct = #'thrift.test.Xtruct'{ + string_thing = <<"Zero">>, + byte_thing = 1, + i32_thing = 9128361, + i64_thing = 9223372036854775807}, + + DemoNest = #'thrift.test.Xtruct2'{ + byte_thing = 7, + struct_thing = DemoXtruct, + % Note that we don't set i32_thing, it will come back as undefined + % from the Python server, but 0 from the C++ server, since it is not + % optional + i32_thing = 2}, + + % Is it safe to match these things? + DemoDict = dict:from_list([ {Key, Key-10} || Key <- lists:seq(0,10) ]), + DemoSet = sets:from_list([ Key || Key <- lists:seq(-3,3) ]), + + DemoInsane = #'thrift.test.Insanity'{ + userMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_FIVE, 5000}]), + xtructs = [#'thrift.test.Xtruct'{ string_thing = <<"Truck">>, byte_thing = 8, i32_thing = 8, i64_thing = 8}]}, + + error_logger:info_msg("testVoid"), + {Client01, {ok, ok}} = thrift_client:call(Client0, testVoid, []), + + error_logger:info_msg("testString"), + {Client02, {ok, <<"Test">>}} = thrift_client:call(Client01, testString, ["Test"]), + error_logger:info_msg("testString"), + {Client03, {ok, <<"Test">>}} = thrift_client:call(Client02, testString, [<<"Test">>]), + error_logger:info_msg("testByte"), + {Client04, {ok, 63}} = thrift_client:call(Client03, testByte, [63]), + error_logger:info_msg("testI32"), + {Client05, {ok, -1}} = thrift_client:call(Client04, testI32, [-1]), + error_logger:info_msg("testI32"), + {Client06, {ok, 0}} = thrift_client:call(Client05, testI32, [0]), + error_logger:info_msg("testI64"), + {Client07, {ok, -34359738368}} = thrift_client:call(Client06, testI64, [-34359738368]), + error_logger:info_msg("testDouble"), + {Client08, {ok, -5.2098523}} = thrift_client:call(Client07, testDouble, [-5.2098523]), + %% TODO: add testBinary() call + error_logger:info_msg("testStruct"), + {Client09, {ok, DemoXtruct}} = thrift_client:call(Client08, testStruct, [DemoXtruct]), + error_logger:info_msg("testNest"), + {Client10, {ok, DemoNest}} = thrift_client:call(Client09, testNest, [DemoNest]), + error_logger:info_msg("testMap"), + {Client11, {ok, DemoDict}} = thrift_client:call(Client10, testMap, [DemoDict]), + error_logger:info_msg("testSet"), + {Client12, {ok, DemoSet}} = thrift_client:call(Client11, testSet, [DemoSet]), + error_logger:info_msg("testList"), + {Client13, {ok, [-1,2,3]}} = thrift_client:call(Client12, testList, [[-1,2,3]]), + error_logger:info_msg("testEnum"), + {Client14, {ok, 1}} = thrift_client:call(Client13, testEnum, [?THRIFT_TEST_NUMBERZ_ONE]), + error_logger:info_msg("testTypedef"), + {Client15, {ok, 309858235082523}} = thrift_client:call(Client14, testTypedef, [309858235082523]), + error_logger:info_msg("testInsanity"), + {Client16, {ok, InsaneResult}} = thrift_client:call(Client15, testInsanity, [DemoInsane]), + io:format("~p~n", [InsaneResult]), + + {Client17, {ok, #'thrift.test.Xtruct'{string_thing = <<"Message">>}}} = + thrift_client:call(Client16, testMultiException, ["Safe", "Message"]), + + Client18 = + try + {ClientS1, Result1} = thrift_client:call(Client17, testMultiException, ["Xception", "Message"]), + io:format("Unexpected return! ~p~n", [Result1]), + ClientS1 + catch + throw:{ClientS2, {exception, ExnS1 = #'thrift.test.Xception'{}}} -> + #'thrift.test.Xception'{errorCode = 1001, message = <<"This is an Xception">>} = ExnS1, + ClientS2; + throw:{ClientS2, {exception, _ExnS1 = #'thrift.test.Xception2'{}}} -> + io:format("Wrong exception type!~n", []), + ClientS2 + end, + + Client19 = + try + {ClientS3, Result2} = thrift_client:call(Client18, testMultiException, ["Xception2", "Message"]), + io:format("Unexpected return! ~p~n", [Result2]), + ClientS3 + catch + throw:{ClientS4, {exception, _ExnS2 = #'thrift.test.Xception'{}}} -> + io:format("Wrong exception type!~n", []), + ClientS4; + throw:{ClientS4, {exception, ExnS2 = #'thrift.test.Xception2'{}}} -> + #'thrift.test.Xception2'{errorCode = 2002, + struct_thing = #'thrift.test.Xtruct'{ + string_thing = <<"This is an Xception2">>}} = ExnS2, + ClientS4 + end, + + %% Use deprecated erlang:now until we start requiring OTP18 + %% Started = erlang:monotonic_time(milli_seconds), + {_, StartSec, StartUSec} = erlang:now(), + error_logger:info_msg("testOneway"), + {Client20, {ok, ok}} = thrift_client:call(Client19, testOneway, [1]), + {_, EndSec, EndUSec} = erlang:now(), + Elapsed = (EndSec - StartSec) * 1000 + (EndUSec - StartUSec) / 1000, + if + Elapsed > 1000 -> exit(1); + true -> true + end, + + thrift_client:close(Client20). diff --git a/vendor/src/github.com/apache/thrift/test/erl/src/test_thrift_server.erl b/vendor/src/github.com/apache/thrift/test/erl/src/test_thrift_server.erl new file mode 100644 index 00000000..f3ed2f7b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/erl/src/test_thrift_server.erl @@ -0,0 +1,233 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% + +-module(test_thrift_server). + +-export([start/0, start/1, start_link/2, handle_function/2]). + +-include("thrift_constants.hrl"). +-include("gen-erl/thrift_test_types.hrl"). + +-record(options, {port = 9090, + server_opts = []}). + +parse_args(Args) -> parse_args(Args, #options{}). +parse_args([], Opts) -> + Opts; +parse_args([Head | Rest], Opts) -> + NewOpts = + case Head of + "--port=" ++ Port -> + case string:to_integer(Port) of + {IntPort,_} when is_integer(IntPort) -> + Opts#options{port = IntPort}; + _Else -> + erlang:error({bad_arg, Head}) + end; + "--transport=" ++ Trans -> + case Trans of + "framed" -> + Opts#options{server_opts = [{framed, true} | Opts#options.server_opts]}; + _Else -> + Opts + end; + "--ssl" -> + ssl:start(), + SslOptions = + {ssloptions, [ + {certfile, "../keys/server.crt"} + ,{keyfile, "../keys/server.key"} + ]}, + Opts#options{server_opts = [{ssltransport, true} | [SslOptions | Opts#options.server_opts]]}; + "--protocol=" ++ Proto -> + Opts#options{server_opts = [{protocol, list_to_atom(Proto)} | Opts#options.server_opts]}; + _Else -> + erlang:error({bad_arg, Head}) + end, + parse_args(Rest, NewOpts). + +start() -> start(init:get_plain_arguments()). +start(Args) -> + #options{port = Port, server_opts = ServerOpts} = parse_args(Args), + spawn(fun() -> start_link(Port, ServerOpts), receive after infinity -> ok end end). + +start_link(Port, ServerOpts) -> + thrift_socket_server:start([{handler, ?MODULE}, + {service, thrift_test_thrift}, + {port, Port}] ++ + ServerOpts). + + +handle_function(testVoid, {}) -> + io:format("testVoid~n"), + ok; + +handle_function(testString, {S}) when is_binary(S) -> + io:format("testString: ~p~n", [S]), + {reply, S}; + +handle_function(testBool, {B}) when is_boolean(B) -> + io:format("testBool: ~p~n", [B]), + {reply, B}; + +handle_function(testByte, {I8}) when is_integer(I8) -> + io:format("testByte: ~p~n", [I8]), + {reply, I8}; + +handle_function(testI32, {I32}) when is_integer(I32) -> + io:format("testI32: ~p~n", [I32]), + {reply, I32}; + +handle_function(testI64, {I64}) when is_integer(I64) -> + io:format("testI64: ~p~n", [I64]), + {reply, I64}; + +handle_function(testDouble, {Double}) when is_float(Double) -> + io:format("testDouble: ~p~n", [Double]), + {reply, Double}; + +handle_function(testBinary, {S}) when is_binary(S) -> + io:format("testBinary: ~p~n", [S]), + {reply, S}; + +handle_function(testStruct, + {Struct = #'thrift.test.Xtruct'{string_thing = String, + byte_thing = Byte, + i32_thing = I32, + i64_thing = I64}}) +when is_binary(String), + is_integer(Byte), + is_integer(I32), + is_integer(I64) -> + io:format("testStruct: ~p~n", [Struct]), + {reply, Struct}; + +handle_function(testNest, + {Nest}) when is_record(Nest, 'thrift.test.Xtruct2'), + is_record(Nest#'thrift.test.Xtruct2'.struct_thing, 'thrift.test.Xtruct') -> + io:format("testNest: ~p~n", [Nest]), + {reply, Nest}; + +handle_function(testMap, {Map}) -> + io:format("testMap: ~p~n", [dict:to_list(Map)]), + {reply, Map}; + +handle_function(testStringMap, {Map}) -> + io:format("testStringMap: ~p~n", [dict:to_list(Map)]), + {reply, Map}; + +handle_function(testSet, {Set}) -> + true = sets:is_set(Set), + io:format("testSet: ~p~n", [sets:to_list(Set)]), + {reply, Set}; + +handle_function(testList, {List}) when is_list(List) -> + io:format("testList: ~p~n", [List]), + {reply, List}; + +handle_function(testEnum, {Enum}) when is_integer(Enum) -> + io:format("testEnum: ~p~n", [Enum]), + {reply, Enum}; + +handle_function(testTypedef, {UserID}) when is_integer(UserID) -> + io:format("testTypedef: ~p~n", [UserID]), + {reply, UserID}; + +handle_function(testMapMap, {Hello}) -> + io:format("testMapMap: ~p~n", [Hello]), + + PosList = [{I, I} || I <- lists:seq(1, 4)], + NegList = [{-I, -I} || I <- lists:seq(1, 4)], + + MapMap = dict:from_list([{4, dict:from_list(PosList)}, + {-4, dict:from_list(NegList)}]), + {reply, MapMap}; + +handle_function(testInsanity, {Insanity}) when is_record(Insanity, 'thrift.test.Insanity') -> + Hello = #'thrift.test.Xtruct'{string_thing = <<"Hello2">>, + byte_thing = 2, + i32_thing = 2, + i64_thing = 2}, + + Goodbye = #'thrift.test.Xtruct'{string_thing = <<"Goodbye4">>, + byte_thing = 4, + i32_thing = 4, + i64_thing = 4}, + Crazy = #'thrift.test.Insanity'{ + userMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_EIGHT, 8}]), + xtructs = [Goodbye] + }, + + Looney = #'thrift.test.Insanity'{}, + + FirstMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_TWO, Insanity}, + {?THRIFT_TEST_NUMBERZ_THREE, Insanity}]), + + SecondMap = dict:from_list([{?THRIFT_TEST_NUMBERZ_SIX, Looney}]), + + Insane = dict:from_list([{1, FirstMap}, + {2, SecondMap}]), + + io:format("Return = ~p~n", [Insane]), + + {reply, Insane}; + +handle_function(testMulti, Args = {Arg0, Arg1, Arg2, _Arg3, Arg4, Arg5}) + when is_integer(Arg0), + is_integer(Arg1), + is_integer(Arg2), + is_integer(Arg4), + is_integer(Arg5) -> + + io:format("testMulti(~p)~n", [Args]), + {reply, #'thrift.test.Xtruct'{string_thing = <<"Hello2">>, + byte_thing = Arg0, + i32_thing = Arg1, + i64_thing = Arg2}}; + +handle_function(testException, {String}) when is_binary(String) -> + io:format("testException(~p)~n", [String]), + case String of + <<"Xception">> -> + throw(#'thrift.test.Xception'{errorCode = 1001, + message = String}); + <<"TException">> -> + throw({?TApplicationException_Structure}); + _ -> + ok + end; + +handle_function(testMultiException, {Arg0, Arg1}) -> + io:format("testMultiException(~p, ~p)~n", [Arg0, Arg1]), + case Arg0 of + <<"Xception">> -> + throw(#'thrift.test.Xception'{errorCode = 1001, + message = <<"This is an Xception">>}); + <<"Xception2">> -> + throw(#'thrift.test.Xception2'{errorCode = 2002, + struct_thing = + #'thrift.test.Xtruct'{string_thing = <<"This is an Xception2">>}}); + _ -> + {reply, #'thrift.test.Xtruct'{string_thing = Arg1}} + end; + +handle_function(testOneway, {Seconds}) -> + io:format("testOneway: ~p~n", [Seconds]), + timer:sleep(1000 * Seconds), + ok. diff --git a/vendor/src/github.com/apache/thrift/test/erl/src/thrift_test.app.src b/vendor/src/github.com/apache/thrift/test/erl/src/thrift_test.app.src new file mode 100644 index 00000000..66536396 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/erl/src/thrift_test.app.src @@ -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. +%% +%%% -*- mode:erlang -*- +{application, thrift_test, [ + % A quick description of the application. + {description, "Thrift cross language test"}, + + % The version of the applicaton + {vsn, "0.10.0"}, + + % All modules used by the application. + {modules, [ + test_client, + test_thrift_server + ]}, + + % All of the registered names the application uses. This can be ignored. + {registered, []}, + + % Applications that are to be started prior to this one. This can be ignored + % leave it alone unless you understand it well and let the .rel files in + % your release handle this. + {applications, [kernel, stdlib]}, + + % OTP application loader will load, but not start, included apps. Again + % this can be ignored as well. To load but not start an application it + % is easier to include it in the .rel file followed by the atom 'none' + {included_applications, []}, + + % configuration parameters similar to those in the config file specified + % on the command line. can be fetched with gas:get_env + {env, [ + % If an error/crash occurs during processing of a function, + % should the TApplicationException serialized back to the client + % include the erlang backtrace? + {exceptions_include_traces, true} + ]} +]}. diff --git a/vendor/src/github.com/apache/thrift/test/features/Makefile.am b/vendor/src/github.com/apache/thrift/test/features/Makefile.am new file mode 100644 index 00000000..f8d65387 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/features/Makefile.am @@ -0,0 +1,28 @@ +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +EXTRA_DIST = \ + local_thrift \ + index.html \ + container_limit.py \ + index.html \ + known_failures_Linux.json \ + Makefile.am \ + string_limit.py \ + tests.json \ + theader_binary.py \ + setup.cfg \ + util.py diff --git a/vendor/src/github.com/apache/thrift/test/features/container_limit.py b/vendor/src/github.com/apache/thrift/test/features/container_limit.py new file mode 100644 index 00000000..a16e3641 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/features/container_limit.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +import argparse +import sys + +from util import add_common_args, init_protocol +from local_thrift import thrift # noqa +from thrift.Thrift import TMessageType, TType + + +# TODO: generate from ThriftTest.thrift +def test_list(proto, value): + method_name = 'testList' + ttype = TType.LIST + etype = TType.I32 + proto.writeMessageBegin(method_name, TMessageType.CALL, 3) + proto.writeStructBegin(method_name + '_args') + proto.writeFieldBegin('thing', ttype, 1) + proto.writeListBegin(etype, len(value)) + for e in value: + proto.writeI32(e) + proto.writeListEnd() + proto.writeFieldEnd() + proto.writeFieldStop() + proto.writeStructEnd() + proto.writeMessageEnd() + proto.trans.flush() + + _, mtype, _ = proto.readMessageBegin() + assert mtype == TMessageType.REPLY + proto.readStructBegin() + _, ftype, fid = proto.readFieldBegin() + assert fid == 0 + assert ftype == ttype + etype2, len2 = proto.readListBegin() + assert etype == etype2 + assert len2 == len(value) + for i in range(len2): + v = proto.readI32() + assert v == value[i] + proto.readListEnd() + proto.readFieldEnd() + _, ftype, _ = proto.readFieldBegin() + assert ftype == TType.STOP + proto.readStructEnd() + proto.readMessageEnd() + + +def main(argv): + p = argparse.ArgumentParser() + add_common_args(p) + p.add_argument('--limit', type=int) + args = p.parse_args() + proto = init_protocol(args) + # TODO: test set and map + test_list(proto, list(range(args.limit - 1))) + test_list(proto, list(range(args.limit - 1))) + print('[OK]: limit - 1') + test_list(proto, list(range(args.limit))) + test_list(proto, list(range(args.limit))) + print('[OK]: just limit') + try: + test_list(proto, list(range(args.limit + 1))) + except: + print('[OK]: limit + 1') + else: + print('[ERROR]: limit + 1') + assert False + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/vendor/src/github.com/apache/thrift/test/features/index.html b/vendor/src/github.com/apache/thrift/test/features/index.html new file mode 100644 index 00000000..34a00102 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/test/features/index.html @@ -0,0 +1,51 @@ + + + + + +Apache Thrift - integration test suite + + + + + + +

Apache Thrift - integration test suite: Results

+ + + + + + + + + + + +
ServerClientProtocolTransportResult (log)Expected
+

Test Information

+

+
+browse raw log files
+
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/features/known_failures_Linux.json b/vendor/src/github.com/apache/thrift/test/features/known_failures_Linux.json
new file mode 100644
index 00000000..257095d4
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/known_failures_Linux.json
@@ -0,0 +1,36 @@
+[
+  "c_glib-limit_container_length_binary_buffered-ip",
+  "c_glib-limit_string_length_binary_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",
+  "d-limit_container_length_binary_buffered-ip",
+  "d-limit_container_length_compact_buffered-ip",
+  "d-limit_string_length_binary_buffered-ip",
+  "d-limit_string_length_compact_buffered-ip",
+  "erl-limit_container_length_binary_buffered-ip",
+  "erl-limit_container_length_compact_buffered-ip",
+  "erl-limit_string_length_binary_buffered-ip",
+  "erl-limit_string_length_compact_buffered-ip",
+  "go-limit_container_length_binary_buffered-ip",
+  "go-limit_container_length_compact_buffered-ip",
+  "go-limit_string_length_binary_buffered-ip",
+  "go-limit_string_length_compact_buffered-ip",
+  "hs-limit_container_length_binary_buffered-ip",
+  "hs-limit_container_length_compact_buffered-ip",
+  "hs-limit_string_length_binary_buffered-ip",
+  "hs-limit_string_length_compact_buffered-ip",
+  "nodejs-limit_container_length_binary_buffered-ip",
+  "nodejs-limit_container_length_compact_buffered-ip",
+  "nodejs-limit_string_length_binary_buffered-ip",
+  "nodejs-limit_string_length_compact_buffered-ip",
+  "perl-limit_container_length_binary_buffered-ip",
+  "perl-limit_string_length_binary_buffered-ip",
+  "rb-limit_container_length_accel-binary_buffered-ip",
+  "rb-limit_container_length_binary_buffered-ip",
+  "rb-limit_container_length_compact_buffered-ip",
+  "rb-limit_string_length_accel-binary_buffered-ip",
+  "rb-limit_string_length_binary_buffered-ip",
+  "rb-limit_string_length_compact_buffered-ip"
+]
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/features/local_thrift/__init__.py b/vendor/src/github.com/apache/thrift/test/features/local_thrift/__init__.py
new file mode 100644
index 00000000..c85cebe5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/local_thrift/__init__.py
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import glob
+import os
+import sys
+
+_SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))
+_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(_SCRIPT_DIR)))
+_LIBDIR = os.path.join(_ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
+
+for libpath in glob.glob(_LIBDIR):
+    if libpath.endswith('-%d.%d' % (sys.version_info[0], sys.version_info[1])):
+        sys.path.insert(0, libpath)
+        thrift = __import__('thrift')
+        break
diff --git a/vendor/src/github.com/apache/thrift/test/features/setup.cfg b/vendor/src/github.com/apache/thrift/test/features/setup.cfg
new file mode 100644
index 00000000..7da1f960
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/vendor/src/github.com/apache/thrift/test/features/string_limit.py b/vendor/src/github.com/apache/thrift/test/features/string_limit.py
new file mode 100644
index 00000000..695d9652
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/string_limit.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+import argparse
+import sys
+
+from util import add_common_args, init_protocol
+from local_thrift import thrift  # noqa
+from thrift.Thrift import TMessageType, TType
+
+
+# TODO: generate from ThriftTest.thrift
+def test_string(proto, value):
+    method_name = 'testString'
+    ttype = TType.STRING
+    proto.writeMessageBegin(method_name, TMessageType.CALL, 3)
+    proto.writeStructBegin(method_name + '_args')
+    proto.writeFieldBegin('thing', ttype, 1)
+    proto.writeString(value)
+    proto.writeFieldEnd()
+    proto.writeFieldStop()
+    proto.writeStructEnd()
+    proto.writeMessageEnd()
+    proto.trans.flush()
+
+    _, mtype, _ = proto.readMessageBegin()
+    assert mtype == TMessageType.REPLY
+    proto.readStructBegin()
+    _, ftype, fid = proto.readFieldBegin()
+    assert fid == 0
+    assert ftype == ttype
+    result = proto.readString()
+    proto.readFieldEnd()
+    _, ftype, _ = proto.readFieldBegin()
+    assert ftype == TType.STOP
+    proto.readStructEnd()
+    proto.readMessageEnd()
+    assert value == result
+
+
+def main(argv):
+    p = argparse.ArgumentParser()
+    add_common_args(p)
+    p.add_argument('--limit', type=int)
+    args = p.parse_args()
+    proto = init_protocol(args)
+    test_string(proto, 'a' * (args.limit - 1))
+    test_string(proto, 'a' * (args.limit - 1))
+    print('[OK]: limit - 1')
+    test_string(proto, 'a' * args.limit)
+    test_string(proto, 'a' * args.limit)
+    print('[OK]: just limit')
+    try:
+        test_string(proto, 'a' * (args.limit + 1))
+    except:
+        print('[OK]: limit + 1')
+    else:
+        print('[ERROR]: limit + 1')
+        assert False
+
+if __name__ == '__main__':
+    main(sys.argv[1:])
diff --git a/vendor/src/github.com/apache/thrift/test/features/tests.json b/vendor/src/github.com/apache/thrift/test/features/tests.json
new file mode 100644
index 00000000..cfcb4b6a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/tests.json
@@ -0,0 +1,94 @@
+[
+  {
+    "description": "THeader detects unframed binary wire format",
+    "name": "theader_unframed_binary",
+    "command": [
+      "python",
+      "theader_binary.py",
+      "--override-protocol=binary",
+      "--override-transport=buffered"
+    ],
+    "protocols": ["header"],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  },
+  {
+    "description": "THeader detects framed binary wire format",
+    "name": "theader_framed_binary",
+    "command": [
+      "python",
+      "theader_binary.py",
+      "--override-protocol=binary",
+      "--override-transport=framed"
+    ],
+    "protocols": ["header"],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  },
+  {
+    "description": "THeader detects unframed compact wire format",
+    "name": "theader_unframed_compact",
+    "command": [
+      "python",
+      "theader_binary.py",
+      "--override-protocol=compact",
+      "--override-transport=buffered"
+    ],
+    "protocols": ["header"],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  },
+  {
+    "description": "THeader detects framed compact wire format",
+    "name": "theader_framed_compact",
+    "command": [
+      "python",
+      "theader_binary.py",
+      "--override-protocol=compact",
+      "--override-transport=framed"
+    ],
+    "protocols": ["header"],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  },
+  {
+    "name": "limit_string_length",
+    "command": [
+      "python",
+      "string_limit.py",
+      "--limit=50"
+    ],
+    "remote_args": [
+      "--string-limit=50"
+    ],
+    "protocols": [
+      "binary",
+      "compact"
+    ],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  },
+  {
+    "name": "limit_container_length",
+    "command": [
+      "python",
+      "container_limit.py",
+      "--limit=50"
+    ],
+    "remote_args": [
+      "--container-limit=50"
+    ],
+    "protocols": [
+      "binary",
+      "compact"
+    ],
+    "transports": ["buffered"],
+    "sockets": ["ip"],
+    "workdir": "features"
+  }
+]
diff --git a/vendor/src/github.com/apache/thrift/test/features/theader_binary.py b/vendor/src/github.com/apache/thrift/test/features/theader_binary.py
new file mode 100644
index 00000000..451399aa
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/theader_binary.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+import argparse
+import socket
+import sys
+
+from util import add_common_args
+from local_thrift import thrift  # noqa
+from thrift.Thrift import TMessageType, TType
+from thrift.transport.TSocket import TSocket
+from thrift.transport.TTransport import TBufferedTransport, TFramedTransport
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol
+from thrift.protocol.TCompactProtocol import TCompactProtocol
+
+
+def test_void(proto):
+    proto.writeMessageBegin('testVoid', TMessageType.CALL, 3)
+    proto.writeStructBegin('testVoid_args')
+    proto.writeFieldStop()
+    proto.writeStructEnd()
+    proto.writeMessageEnd()
+    proto.trans.flush()
+
+    _, mtype, _ = proto.readMessageBegin()
+    assert mtype == TMessageType.REPLY
+    proto.readStructBegin()
+    _, ftype, _ = proto.readFieldBegin()
+    assert ftype == TType.STOP
+    proto.readStructEnd()
+    proto.readMessageEnd()
+
+
+# THeader stack should accept binary protocol with optionally framed transport
+def main(argv):
+    p = argparse.ArgumentParser()
+    add_common_args(p)
+    # Since THeaderTransport acts as framed transport when detected frame, we
+    # cannot use --transport=framed as it would result in 2 layered frames.
+    p.add_argument('--override-transport')
+    p.add_argument('--override-protocol')
+    args = p.parse_args()
+    assert args.protocol == 'header'
+    assert args.transport == 'buffered'
+    assert not args.ssl
+
+    sock = TSocket(args.host, args.port, socket_family=socket.AF_INET)
+    if not args.override_transport or args.override_transport == 'buffered':
+        trans = TBufferedTransport(sock)
+    elif args.override_transport == 'framed':
+        print('TFRAMED')
+        trans = TFramedTransport(sock)
+    else:
+        raise ValueError('invalid transport')
+    trans.open()
+
+    if not args.override_protocol or args.override_protocol == 'binary':
+        proto = TBinaryProtocol(trans)
+    elif args.override_protocol == 'compact':
+        proto = TCompactProtocol(trans)
+    else:
+        raise ValueError('invalid transport')
+
+    test_void(proto)
+    test_void(proto)
+
+    trans.close()
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/vendor/src/github.com/apache/thrift/test/features/util.py b/vendor/src/github.com/apache/thrift/test/features/util.py
new file mode 100644
index 00000000..3abbbbd9
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/features/util.py
@@ -0,0 +1,40 @@
+import argparse
+import socket
+
+from local_thrift import thrift  # noqa
+from thrift.transport.TSocket import TSocket
+from thrift.transport.TTransport import TBufferedTransport, TFramedTransport
+from thrift.transport.THttpClient import THttpClient
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol
+from thrift.protocol.TCompactProtocol import TCompactProtocol
+from thrift.protocol.TJSONProtocol import TJSONProtocol
+
+
+def add_common_args(p):
+    p.add_argument('--host', default='localhost')
+    p.add_argument('--port', type=int, default=9090)
+    p.add_argument('--protocol', default='binary')
+    p.add_argument('--transport', default='buffered')
+    p.add_argument('--ssl', action='store_true')
+
+
+def parse_common_args(argv):
+    p = argparse.ArgumentParser()
+    add_common_args(p)
+    return p.parse_args(argv)
+
+
+def init_protocol(args):
+    sock = TSocket(args.host, args.port, socket_family=socket.AF_INET)
+    sock.setTimeout(500)
+    trans = {
+        'buffered': TBufferedTransport,
+        'framed': TFramedTransport,
+        'http': THttpClient,
+    }[args.transport](sock)
+    trans.open()
+    return {
+        'binary': TBinaryProtocol,
+        'compact': TCompactProtocol,
+        'json': TJSONProtocol,
+    }[args.protocol](trans)
diff --git a/vendor/src/github.com/apache/thrift/test/go/Makefile.am b/vendor/src/github.com/apache/thrift/test/go/Makefile.am
new file mode 100644
index 00000000..2b2dbce6
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/Makefile.am
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+BUILT_SOURCES = gopath
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+THRIFTCMD = $(THRIFT) -out src/gen --gen go:thrift_import=thrift
+THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift
+
+precross: bin/testclient bin/testserver
+
+ThriftTest.thrift: $(THRIFTTEST)
+	grep -v list.*map.*list.*map $(THRIFTTEST) > ThriftTest.thrift
+
+# Thrift for GO has problems with complex map keys: THRIFT-2063
+gopath: $(THRIFT) ThriftTest.thrift
+	mkdir -p src/gen
+	$(THRIFTCMD) ThriftTest.thrift
+	$(THRIFTCMD) ../StressTest.thrift
+	ln -nfs ../../../lib/go/thrift src/thrift
+	GOPATH=`pwd` $(GO) get github.com/golang/mock/gomock
+	touch gopath
+
+bin/testclient: gopath
+	GOPATH=`pwd` $(GO) install bin/testclient
+
+bin/testserver: gopath
+	GOPATH=`pwd` $(GO) install bin/testserver
+
+bin/stress: gopath
+	GOPATH=`pwd` $(GO) install bin/stress
+
+clean-local:
+	$(RM) -r src/gen src/github.com/golang src/thrift bin pkg gopath ThriftTest.thrift
+
+check_PROGRAMS: bin/testclient bin/testserver bin/stress
+
+check: gopath
+	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
+
+EXTRA_DIST = \
+	src/bin \
+	src/common
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/bin/stress/main.go b/vendor/src/github.com/apache/thrift/test/go/src/bin/stress/main.go
new file mode 100644
index 00000000..1f713bbd
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/bin/stress/main.go
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"gen/stress"
+	"log"
+	_ "net/http/pprof"
+	"os"
+	"runtime"
+	"runtime/pprof"
+	"sync"
+	"sync/atomic"
+	"thrift"
+	"time"
+)
+
+var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
+var memprofile = flag.String("memprofile", "", "write memory profile to this file")
+
+var (
+	host      = flag.String("host", "localhost", "Host to connect")
+	port      = flag.Int64("port", 9091, "Port number to connect")
+	loop      = flag.Int("loops", 50000, "The number of remote thrift calls each client makes")
+	runserver = flag.Int("server", 1, "Run the Thrift server in this process")
+	clients   = flag.Int("clients", 20, "Number of client threads to create - 0 implies no clients, i.e. server only")
+	callName  = flag.String("call", "echoVoid", "Service method to call, one of echoVoid, echoByte, echoI32, echoI64, echoString, echiList, echoSet, echoMap")
+	compact   = flag.Bool("compact", false, "Use compact protocol instead of binary.")
+	framed    = flag.Bool("framed", false, "Use framed transport instead of buffered.")
+)
+var hostPort string
+
+type callT int64
+
+const (
+	echoVoid callT = iota
+	echoByte
+	echoI32
+	echoI64
+	echoString
+	echiList
+	echoSet
+	echoMap
+)
+
+var callTMap = map[string]callT{
+	"echoVoid":   echoVoid,
+	"echoByte":   echoByte,
+	"echoI32":    echoI32,
+	"echoI64":    echoI64,
+	"echoString": echoString,
+	"echiList":   echiList,
+	"echoSet":    echoSet,
+	"echoMap":    echoMap,
+}
+var callType callT
+
+var ready, done sync.WaitGroup
+
+var clicounter int64 = 0
+var counter int64 = 0
+
+func main() {
+	flag.Parse()
+	if *memprofile != "" {
+		runtime.MemProfileRate = 100
+	}
+	var ok bool
+	if callType, ok = callTMap[*callName]; !ok {
+		log.Fatal("Unknown service call", *callName)
+	}
+	if *cpuprofile != "" {
+		f, err := os.Create(*cpuprofile)
+		if err != nil {
+			log.Fatal(err)
+		}
+		pprof.StartCPUProfile(f)
+		defer pprof.StopCPUProfile()
+	}
+	hostPort = fmt.Sprintf("%s:%d", *host, *port)
+	var protocolFactory thrift.TProtocolFactory
+	var transportFactory thrift.TTransportFactory
+
+	if *compact {
+		protocolFactory = thrift.NewTCompactProtocolFactory()
+	} else {
+		protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
+	}
+
+	if *framed {
+		transportFactory = thrift.NewTTransportFactory()
+		transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
+	} else {
+		transportFactory = thrift.NewTBufferedTransportFactory(8192)
+	}
+
+	if *runserver > 0 {
+		serverTransport, err := thrift.NewTServerSocket(hostPort)
+		if err != nil {
+			log.Fatalf("Unable to create server socket: %s", err)
+		}
+
+		processor := stress.NewServiceProcessor(&handler{})
+		server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+		if *clients == 0 {
+			server.Serve()
+		} else {
+			go server.Serve()
+		}
+	}
+	//start clients
+	if *clients != 0 {
+		ready.Add(*clients + 1)
+		done.Add(*clients)
+		for i := 0; i < *clients; i++ {
+			go client(protocolFactory)
+		}
+		ready.Done()
+		ready.Wait()
+		//run!
+		startTime := time.Now()
+		//wait for completion
+		done.Wait()
+		endTime := time.Now()
+		duration := endTime.Sub(startTime)
+		log.Printf("%d calls in %v (%f calls per second)\n", clicounter, duration, float64(clicounter)/duration.Seconds())
+	}
+	if *memprofile != "" {
+		f, err := os.Create(*memprofile)
+		if err != nil {
+			log.Fatal(err)
+		}
+		pprof.WriteHeapProfile(f)
+		f.Close()
+		return
+	}
+}
+
+func client(protocolFactory thrift.TProtocolFactory) {
+	trans, err := thrift.NewTSocket(hostPort)
+	if err != nil {
+		log.Fatalf("Unable to create server socket: %s", err)
+	}
+	btrans := thrift.NewTBufferedTransport(trans, 2048)
+	client := stress.NewServiceClientFactory(btrans, protocolFactory)
+	err = trans.Open()
+	if err != nil {
+		log.Fatalf("Unable to open connection: %s", err)
+	}
+	ready.Done()
+	ready.Wait()
+	switch callType {
+	case echoVoid:
+		for i := 0; i < *loop; i++ {
+			client.EchoVoid()
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoByte:
+		for i := 0; i < *loop; i++ {
+			client.EchoByte(42)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoI32:
+		for i := 0; i < *loop; i++ {
+			client.EchoI32(4242)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoI64:
+		for i := 0; i < *loop; i++ {
+			client.EchoI64(424242)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoString:
+		for i := 0; i < *loop; i++ {
+			client.EchoString("TestString")
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echiList:
+		l := []int8{-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8}
+		for i := 0; i < *loop; i++ {
+			client.EchoList(l)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoSet:
+		s := map[int8]struct{}{-10: {}, -9: {}, -8: {}, -7: {}, -6: {}, -5: {}, -4: {}, -3: {}, -2: {}, -1: {}, 0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {}, 8: {}}
+		for i := 0; i < *loop; i++ {
+			client.EchoSet(s)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	case echoMap:
+		m := map[int8]int8{-10: 10, -9: 9, -8: 8, -7: 7, -6: 6, -5: 5, -4: 4, -3: 3, -2: 2, -1: 1, 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8}
+		for i := 0; i < *loop; i++ {
+			client.EchoMap(m)
+			atomic.AddInt64(&clicounter, 1)
+		}
+	}
+
+	done.Done()
+}
+
+type handler struct{}
+
+func (h *handler) EchoVoid() (err error) {
+	atomic.AddInt64(&counter, 1)
+	return nil
+}
+func (h *handler) EchoByte(arg int8) (r int8, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoI32(arg int32) (r int32, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoI64(arg int64) (r int64, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoString(arg string) (r string, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoList(arg []int8) (r []int8, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoSet(arg map[int8]struct{}) (r map[int8]struct{}, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
+func (h *handler) EchoMap(arg map[int8]int8) (r map[int8]int8, err error) {
+	atomic.AddInt64(&counter, 1)
+	return arg, nil
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/bin/testclient/main.go b/vendor/src/github.com/apache/thrift/test/go/src/bin/testclient/main.go
new file mode 100644
index 00000000..f4a19dde
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/bin/testclient/main.go
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"common"
+	"flag"
+	"gen/thrifttest"
+	t "log"
+	"reflect"
+	"thrift"
+)
+
+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 ssl = flag.Bool("ssl", false, "Encrypted Transport using SSL")
+var testloops = flag.Int("testloops", 1, "Number of Tests")
+
+func main() {
+	flag.Parse()
+	client, err := common.StartClient(*host, *port, *domain_socket, *transport, *protocol, *ssl)
+	if err != nil {
+		t.Fatalf("Unable to start client: ", err)
+	}
+	for i := 0; i < *testloops; i++ {
+		callEverything(client)
+	}
+}
+
+var rmapmap = map[int32]map[int32]int32{
+	-4: map[int32]int32{-4: -4, -3: -3, -2: -2, -1: -1},
+	4:  map[int32]int32{4: 4, 3: 3, 2: 2, 1: 1},
+}
+
+var xxs = &thrifttest.Xtruct{
+	StringThing: "Hello2",
+	ByteThing:   42,
+	I32Thing:    4242,
+	I64Thing:    424242,
+}
+
+var xcept = &thrifttest.Xception{ErrorCode: 1001, Message: "Xception"}
+
+func callEverything(client *thrifttest.ThriftTestClient) {
+	var err error
+	if err = client.TestVoid(); err != nil {
+		t.Fatalf("Unexpected error in TestVoid() call: ", err)
+	}
+
+	thing, err := client.TestString("thing")
+	if err != nil {
+		t.Fatalf("Unexpected error in TestString() call: ", err)
+	}
+	if thing != "thing" {
+		t.Fatalf("Unexpected TestString() result, expected 'thing' got '%s' ", thing)
+	}
+
+	bl, err := client.TestBool(true)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestBool() call: ", err)
+	}
+	if !bl {
+		t.Fatalf("Unexpected TestBool() result expected true, got %f ", bl)
+	}
+	bl, err = client.TestBool(false)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestBool() call: ", err)
+	}
+	if bl {
+		t.Fatalf("Unexpected TestBool() result expected false, got %f ", bl)
+	}
+
+	b, err := client.TestByte(42)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestByte() call: ", err)
+	}
+	if b != 42 {
+		t.Fatalf("Unexpected TestByte() result expected 42, got %d ", b)
+	}
+
+	i32, err := client.TestI32(4242)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestI32() call: ", err)
+	}
+	if i32 != 4242 {
+		t.Fatalf("Unexpected TestI32() result expected 4242, got %d ", i32)
+	}
+
+	i64, err := client.TestI64(424242)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestI64() call: ", err)
+	}
+	if i64 != 424242 {
+		t.Fatalf("Unexpected TestI64() result expected 424242, got %d ", i64)
+	}
+
+	d, err := client.TestDouble(42.42)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestDouble() call: ", err)
+	}
+	if d != 42.42 {
+		t.Fatalf("Unexpected TestDouble() result expected 42.42, got %f ", d)
+	}
+
+	binout := make([]byte, 256)
+	for i := 0; i < 256; i++ {
+		binout[i] = byte(i)
+	}
+	bin, err := client.TestBinary(binout)
+	for i := 0; i < 256; i++ {
+		if (binout[i] != bin[i]) {
+			t.Fatalf("Unexpected TestBinary() result expected %d, got %d ", binout[i], bin[i])
+		}
+	}
+	
+	xs := thrifttest.NewXtruct()
+	xs.StringThing = "thing"
+	xs.ByteThing = 42
+	xs.I32Thing = 4242
+	xs.I64Thing = 424242
+	xsret, err := client.TestStruct(xs)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestStruct() call: ", err)
+	}
+	if *xs != *xsret {
+		t.Fatalf("Unexpected TestStruct() result expected %#v, got %#v ", xs, xsret)
+	}
+
+	x2 := thrifttest.NewXtruct2()
+	x2.StructThing = xs
+	x2ret, err := client.TestNest(x2)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestNest() call: ", err)
+	}
+	if !reflect.DeepEqual(x2, x2ret) {
+		t.Fatalf("Unexpected TestNest() result expected %#v, got %#v ", x2, x2ret)
+	}
+
+	m := map[int32]int32{1: 2, 3: 4, 5: 42}
+	mret, err := client.TestMap(m)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestMap() call: ", err)
+	}
+	if !reflect.DeepEqual(m, mret) {
+		t.Fatalf("Unexpected TestMap() result expected %#v, got %#v ", m, mret)
+	}
+
+	sm := map[string]string{"a": "2", "b": "blah", "some": "thing"}
+	smret, err := client.TestStringMap(sm)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestStringMap() call: ", err)
+	}
+	if !reflect.DeepEqual(sm, smret) {
+		t.Fatalf("Unexpected TestStringMap() result expected %#v, got %#v ", sm, smret)
+	}
+
+	s := map[int32]struct{}{1: struct{}{}, 2: struct{}{}, 42: struct{}{}}
+	sret, err := client.TestSet(s)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestSet() call: ", err)
+	}
+	if !reflect.DeepEqual(s, sret) {
+		t.Fatalf("Unexpected TestSet() result expected %#v, got %#v ", s, sret)
+	}
+
+	l := []int32{1, 2, 42}
+	lret, err := client.TestList(l)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestList() call: ", err)
+	}
+	if !reflect.DeepEqual(l, lret) {
+		t.Fatalf("Unexpected TestSet() result expected %#v, got %#v ", l, lret)
+	}
+
+	eret, err := client.TestEnum(thrifttest.Numberz_TWO)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestEnum() call: ", err)
+	}
+	if eret != thrifttest.Numberz_TWO {
+		t.Fatalf("Unexpected TestEnum() result expected %#v, got %#v ", thrifttest.Numberz_TWO, eret)
+	}
+
+	tret, err := client.TestTypedef(thrifttest.UserId(42))
+	if err != nil {
+		t.Fatalf("Unexpected error in TestTypedef() call: ", err)
+	}
+	if tret != thrifttest.UserId(42) {
+		t.Fatalf("Unexpected TestTypedef() result expected %#v, got %#v ", thrifttest.UserId(42), tret)
+	}
+
+	mapmap, err := client.TestMapMap(42)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestMapMap() call: ", err)
+	}
+	if !reflect.DeepEqual(mapmap, rmapmap) {
+		t.Fatalf("Unexpected TestMapMap() result expected %#v, got %#v ", rmapmap, mapmap)
+	}
+
+	crazy := thrifttest.NewInsanity()
+	crazy.UserMap = map[thrifttest.Numberz]thrifttest.UserId {
+		thrifttest.Numberz_FIVE: 5,
+		thrifttest.Numberz_EIGHT: 8,
+	}
+	truck1 := thrifttest.NewXtruct()
+	truck1.StringThing = "Goodbye4"
+	truck1.ByteThing = 4;
+	truck1.I32Thing = 4;
+	truck1.I64Thing = 4;
+	truck2 := thrifttest.NewXtruct()
+	truck2.StringThing = "Hello2"
+	truck2.ByteThing = 2;
+	truck2.I32Thing = 2;
+	truck2.I64Thing = 2;
+	crazy.Xtructs = []*thrifttest.Xtruct {
+		truck1,
+		truck2,
+	}
+	insanity, err := client.TestInsanity(crazy)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestInsanity() call: ", err)
+	}
+	if !reflect.DeepEqual(crazy, insanity[1][2]) {
+		t.Fatalf("Unexpected TestInsanity() first result expected %#v, got %#v ",
+		crazy,
+		insanity[1][2])
+	}
+	if !reflect.DeepEqual(crazy, insanity[1][3]) {
+		t.Fatalf("Unexpected TestInsanity() second result expected %#v, got %#v ",
+		crazy,
+		insanity[1][3])
+	}
+	if len(insanity[2][6].UserMap) > 0 || len(insanity[2][6].Xtructs) > 0 {
+		t.Fatalf("Unexpected TestInsanity() non-empty result got %#v ",
+		insanity[2][6])
+	}
+
+	xxsret, err := client.TestMulti(42, 4242, 424242, map[int16]string{1: "blah", 2: "thing"}, thrifttest.Numberz_EIGHT, thrifttest.UserId(24))
+	if err != nil {
+		t.Fatalf("Unexpected error in TestMulti() call: ", err)
+	}
+	if !reflect.DeepEqual(xxs, xxsret) {
+		t.Fatalf("Unexpected TestMulti() result expected %#v, got %#v ", xxs, xxsret)
+	}
+
+	err = client.TestException("Xception")
+	if err == nil {
+		t.Fatalf("Expecting exception in TestException() call")
+	}
+	if !reflect.DeepEqual(err, xcept) {
+		t.Fatalf("Unexpected TestException() result expected %#v, got %#v ", xcept, err)
+	}
+
+	err = client.TestException("TException")
+	_, ok := err.(thrift.TApplicationException)
+	if err == nil || !ok {
+		t.Fatalf("Unexpected TestException() result expected ApplicationError, got %#v ", err)
+	}
+
+	ign, err := client.TestMultiException("Xception", "ignoreme")
+	if ign != nil || err == nil {
+		t.Fatalf("Expecting exception in TestMultiException() call")
+	}
+	if !reflect.DeepEqual(err, &thrifttest.Xception{ErrorCode: 1001, Message: "This is an Xception"}) {
+		t.Fatalf("Unexpected TestMultiException() %#v ", err)
+	}
+
+	ign, err = client.TestMultiException("Xception2", "ignoreme")
+	if ign != nil || err == nil {
+		t.Fatalf("Expecting exception in TestMultiException() call")
+	}
+	expecting := &thrifttest.Xception2{ErrorCode: 2002, StructThing: &thrifttest.Xtruct{StringThing: "This is an Xception2"}}
+
+	if !reflect.DeepEqual(err, expecting) {
+		t.Fatalf("Unexpected TestMultiException() %#v ", err)
+	}
+
+	err = client.TestOneway(2)
+	if err != nil {
+		t.Fatalf("Unexpected error in TestOneway() call: ", err)
+	}
+
+	//Make sure the connection still alive
+	if err = client.TestVoid(); err != nil {
+		t.Fatalf("Unexpected error in TestVoid() call: ", err)
+	}
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/bin/testserver/main.go b/vendor/src/github.com/apache/thrift/test/go/src/bin/testserver/main.go
new file mode 100644
index 00000000..0bf833d1
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/bin/testserver/main.go
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"common"
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+	"thrift"
+)
+
+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 ssl = flag.Bool("ssl", false, "Encrypted Transport using SSL")
+var certPath = flag.String("certPath", "keys", "Directory that contains SSL certificates")
+
+func main() {
+	flag.Parse()
+
+	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)
+	}
+
+	if *transport == "http" {
+		http.HandleFunc("/", thrift.NewThriftHandlerFunc(processor, protocolFactory, protocolFactory))
+
+		if *ssl {
+			err := http.ListenAndServeTLS(fmt.Sprintf(":%d", *port),
+				*certPath+"/server.pem", *certPath+"/server.key", nil)
+
+			if err != nil {
+				fmt.Println(err)
+				return
+			}
+		} else {
+			http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
+		}
+	} else {
+		server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+		if err = server.Listen(); err != nil {
+			return
+		}
+		go server.AcceptLoop()
+		server.Serve()
+	}
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/client.go b/vendor/src/github.com/apache/thrift/test/go/src/common/client.go
new file mode 100644
index 00000000..4251d910
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/client.go
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"compress/zlib"
+	"crypto/tls"
+	"flag"
+	"fmt"
+	"gen/thrifttest"
+	"net/http"
+	"thrift"
+)
+
+var debugClientProtocol bool
+
+func init() {
+	flag.BoolVar(&debugClientProtocol, "debug_client_protocol", false, "turn client protocol trace on")
+}
+
+func StartClient(
+	host string,
+	port int64,
+	domain_socket string,
+	transport string,
+	protocol string,
+	ssl bool) (client *thrifttest.ThriftTestClient, err error) {
+
+	hostPort := fmt.Sprintf("%s:%d", host, port)
+
+	var protocolFactory thrift.TProtocolFactory
+	switch protocol {
+	case "compact":
+		protocolFactory = thrift.NewTCompactProtocolFactory()
+	case "simplejson":
+		protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
+	case "json":
+		protocolFactory = thrift.NewTJSONProtocolFactory()
+	case "binary":
+		protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
+	default:
+		return nil, fmt.Errorf("Invalid protocol specified %s", protocol)
+	}
+	if debugClientProtocol {
+		protocolFactory = thrift.NewTDebugProtocolFactory(protocolFactory, "client:")
+	}
+	var trans thrift.TTransport
+	if ssl {
+		trans, err = thrift.NewTSSLSocket(hostPort, &tls.Config{InsecureSkipVerify: true})
+	} else {
+		if domain_socket != "" {
+			trans, err = thrift.NewTSocket(domain_socket)
+		} else {
+			trans, err = thrift.NewTSocket(hostPort)
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+	switch transport {
+	case "http":
+		if ssl {
+			tr := &http.Transport{
+				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+			}
+			client := &http.Client{Transport: tr}
+			trans, err = thrift.NewTHttpPostClientWithOptions(fmt.Sprintf("https://%s/", hostPort), thrift.THttpClientOptions{Client: client})
+			fmt.Println(hostPort)
+		} else {
+			trans, err = thrift.NewTHttpPostClient(fmt.Sprintf("http://%s/", hostPort))
+		}
+
+		if err != nil {
+			return nil, err
+		}
+
+	case "framed":
+		trans = thrift.NewTFramedTransport(trans)
+	case "buffered":
+		trans = thrift.NewTBufferedTransport(trans, 8192)
+	case "zlib":
+		trans, err = thrift.NewTZlibTransport(trans, zlib.BestCompression)
+		if err != nil {
+			return nil, err
+		}
+	case "":
+		trans = trans
+	default:
+		return nil, fmt.Errorf("Invalid transport specified %s", transport)
+	}
+
+	if err = trans.Open(); err != nil {
+		return nil, err
+	}
+	client = thrifttest.NewThriftTestClientFactory(trans, protocolFactory)
+	return
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/clientserver_test.go b/vendor/src/github.com/apache/thrift/test/go/src/common/clientserver_test.go
new file mode 100644
index 00000000..549e8d1e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/clientserver_test.go
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"errors"
+	"gen/thrifttest"
+	"reflect"
+	"testing"
+	"thrift"
+
+	"github.com/golang/mock/gomock"
+)
+
+type test_unit struct {
+	host          string
+	port          int64
+	domain_socket string
+	transport     string
+	protocol      string
+	ssl           bool
+}
+
+var units = []test_unit{
+	{"127.0.0.1", 9095, "", "", "binary", false},
+	{"127.0.0.1", 9091, "", "", "compact", false},
+	{"127.0.0.1", 9092, "", "", "binary", true},
+	{"127.0.0.1", 9093, "", "", "compact", true},
+}
+
+func TestAllConnection(t *testing.T) {
+	certPath = "../../../keys"
+	for _, unit := range units {
+		t.Logf("%#v", unit)
+		doUnit(t, &unit)
+	}
+}
+
+func doUnit(t *testing.T, unit *test_unit) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	handler := NewMockThriftTest(ctrl)
+
+	processor, serverTransport, transportFactory, protocolFactory, err := GetServerParams(unit.host, unit.port, unit.domain_socket, unit.transport, unit.protocol, unit.ssl, "../../../keys", handler)
+
+	server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
+	if err = server.Listen(); err != nil {
+		t.Errorf("Unable to start server", err)
+		t.FailNow()
+	}
+	go server.AcceptLoop()
+	defer server.Stop()
+	client, err := StartClient(unit.host, unit.port, unit.domain_socket, unit.transport, unit.protocol, unit.ssl)
+	if err != nil {
+		t.Errorf("Unable to start client", err)
+		t.FailNow()
+	}
+	defer client.Transport.Close()
+	callEverythingWithMock(t, client, handler)
+}
+
+var rmapmap = map[int32]map[int32]int32{
+	-4: map[int32]int32{-4: -4, -3: -3, -2: -2, -1: -1},
+	4:  map[int32]int32{4: 4, 3: 3, 2: 2, 1: 1},
+}
+
+var xxs = &thrifttest.Xtruct{
+	StringThing: "Hello2",
+	ByteThing:   42,
+	I32Thing:    4242,
+	I64Thing:    424242,
+}
+
+var xcept = &thrifttest.Xception{ErrorCode: 1001, Message: "some"}
+
+func callEverythingWithMock(t *testing.T, client *thrifttest.ThriftTestClient, handler *MockThriftTest) {
+	gomock.InOrder(
+		handler.EXPECT().TestVoid(),
+		handler.EXPECT().TestString("thing").Return("thing", nil),
+		handler.EXPECT().TestBool(true).Return(true, nil),
+		handler.EXPECT().TestBool(false).Return(false, nil),
+		handler.EXPECT().TestByte(int8(42)).Return(int8(42), nil),
+		handler.EXPECT().TestI32(int32(4242)).Return(int32(4242), nil),
+		handler.EXPECT().TestI64(int64(424242)).Return(int64(424242), nil),
+		// TODO: add TestBinary()
+		handler.EXPECT().TestDouble(float64(42.42)).Return(float64(42.42), nil),
+		handler.EXPECT().TestStruct(&thrifttest.Xtruct{StringThing: "thing", ByteThing: 42, I32Thing: 4242, I64Thing: 424242}).Return(&thrifttest.Xtruct{StringThing: "thing", ByteThing: 42, I32Thing: 4242, I64Thing: 424242}, nil),
+		handler.EXPECT().TestNest(&thrifttest.Xtruct2{StructThing: &thrifttest.Xtruct{StringThing: "thing", ByteThing: 42, I32Thing: 4242, I64Thing: 424242}}).Return(&thrifttest.Xtruct2{StructThing: &thrifttest.Xtruct{StringThing: "thing", ByteThing: 42, I32Thing: 4242, I64Thing: 424242}}, nil),
+		handler.EXPECT().TestMap(map[int32]int32{1: 2, 3: 4, 5: 42}).Return(map[int32]int32{1: 2, 3: 4, 5: 42}, nil),
+		handler.EXPECT().TestStringMap(map[string]string{"a": "2", "b": "blah", "some": "thing"}).Return(map[string]string{"a": "2", "b": "blah", "some": "thing"}, nil),
+		handler.EXPECT().TestSet(map[int32]struct{}{1: struct{}{}, 2: struct{}{}, 42: struct{}{}}).Return(map[int32]struct{}{1: struct{}{}, 2: struct{}{}, 42: struct{}{}}, nil),
+		handler.EXPECT().TestList([]int32{1, 2, 42}).Return([]int32{1, 2, 42}, nil),
+		handler.EXPECT().TestEnum(thrifttest.Numberz_TWO).Return(thrifttest.Numberz_TWO, nil),
+		handler.EXPECT().TestTypedef(thrifttest.UserId(42)).Return(thrifttest.UserId(42), nil),
+		handler.EXPECT().TestMapMap(int32(42)).Return(rmapmap, nil),
+		// TODO: not testing insanity
+		handler.EXPECT().TestMulti(int8(42), int32(4242), int64(424242), map[int16]string{1: "blah", 2: "thing"}, thrifttest.Numberz_EIGHT, thrifttest.UserId(24)).Return(xxs, nil),
+		handler.EXPECT().TestException("some").Return(xcept),
+		handler.EXPECT().TestException("TException").Return(errors.New("Just random exception")),
+		handler.EXPECT().TestMultiException("Xception", "ignoreme").Return(nil, &thrifttest.Xception{ErrorCode: 1001, Message: "This is an Xception"}),
+		handler.EXPECT().TestMultiException("Xception2", "ignoreme").Return(nil, &thrifttest.Xception2{ErrorCode: 2002, StructThing: &thrifttest.Xtruct{StringThing: "This is an Xception2"}}),
+		handler.EXPECT().TestOneway(int32(2)).Return(nil),
+		handler.EXPECT().TestVoid(),
+	)
+	var err error
+	if err = client.TestVoid(); err != nil {
+		t.Errorf("Unexpected error in TestVoid() call: ", err)
+	}
+
+	thing, err := client.TestString("thing")
+	if err != nil {
+		t.Errorf("Unexpected error in TestString() call: ", err)
+	}
+	if thing != "thing" {
+		t.Errorf("Unexpected TestString() result, expected 'thing' got '%s' ", thing)
+	}
+
+	bl, err := client.TestBool(true)
+	if err != nil {
+		t.Errorf("Unexpected error in TestBool() call: ", err)
+	}
+	if !bl {
+		t.Errorf("Unexpected TestBool() result expected true, got %f ", bl)
+	}
+	bl, err = client.TestBool(false)
+	if err != nil {
+		t.Errorf("Unexpected error in TestBool() call: ", err)
+	}
+	if bl {
+		t.Errorf("Unexpected TestBool() result expected false, got %f ", bl)
+	}
+
+	b, err := client.TestByte(42)
+	if err != nil {
+		t.Errorf("Unexpected error in TestByte() call: ", err)
+	}
+	if b != 42 {
+		t.Errorf("Unexpected TestByte() result expected 42, got %d ", b)
+	}
+
+	i32, err := client.TestI32(4242)
+	if err != nil {
+		t.Errorf("Unexpected error in TestI32() call: ", err)
+	}
+	if i32 != 4242 {
+		t.Errorf("Unexpected TestI32() result expected 4242, got %d ", i32)
+	}
+
+	i64, err := client.TestI64(424242)
+	if err != nil {
+		t.Errorf("Unexpected error in TestI64() call: ", err)
+	}
+	if i64 != 424242 {
+		t.Errorf("Unexpected TestI64() result expected 424242, got %d ", i64)
+	}
+
+	d, err := client.TestDouble(42.42)
+	if err != nil {
+		t.Errorf("Unexpected error in TestDouble() call: ", err)
+	}
+	if d != 42.42 {
+		t.Errorf("Unexpected TestDouble() result expected 42.42, got %f ", d)
+	}
+
+	// TODO: add TestBinary() call
+
+	xs := thrifttest.NewXtruct()
+	xs.StringThing = "thing"
+	xs.ByteThing = 42
+	xs.I32Thing = 4242
+	xs.I64Thing = 424242
+	xsret, err := client.TestStruct(xs)
+	if err != nil {
+		t.Errorf("Unexpected error in TestStruct() call: ", err)
+	}
+	if *xs != *xsret {
+		t.Errorf("Unexpected TestStruct() result expected %#v, got %#v ", xs, xsret)
+	}
+
+	x2 := thrifttest.NewXtruct2()
+	x2.StructThing = xs
+	x2ret, err := client.TestNest(x2)
+	if err != nil {
+		t.Errorf("Unexpected error in TestNest() call: ", err)
+	}
+	if !reflect.DeepEqual(x2, x2ret) {
+		t.Errorf("Unexpected TestNest() result expected %#v, got %#v ", x2, x2ret)
+	}
+
+	m := map[int32]int32{1: 2, 3: 4, 5: 42}
+	mret, err := client.TestMap(m)
+	if err != nil {
+		t.Errorf("Unexpected error in TestMap() call: ", err)
+	}
+	if !reflect.DeepEqual(m, mret) {
+		t.Errorf("Unexpected TestMap() result expected %#v, got %#v ", m, mret)
+	}
+
+	sm := map[string]string{"a": "2", "b": "blah", "some": "thing"}
+	smret, err := client.TestStringMap(sm)
+	if err != nil {
+		t.Errorf("Unexpected error in TestStringMap() call: ", err)
+	}
+	if !reflect.DeepEqual(sm, smret) {
+		t.Errorf("Unexpected TestStringMap() result expected %#v, got %#v ", sm, smret)
+	}
+
+	s := map[int32]struct{}{1: struct{}{}, 2: struct{}{}, 42: struct{}{}}
+	sret, err := client.TestSet(s)
+	if err != nil {
+		t.Errorf("Unexpected error in TestSet() call: ", err)
+	}
+	if !reflect.DeepEqual(s, sret) {
+		t.Errorf("Unexpected TestSet() result expected %#v, got %#v ", s, sret)
+	}
+
+	l := []int32{1, 2, 42}
+	lret, err := client.TestList(l)
+	if err != nil {
+		t.Errorf("Unexpected error in TestList() call: ", err)
+	}
+	if !reflect.DeepEqual(l, lret) {
+		t.Errorf("Unexpected TestSet() result expected %#v, got %#v ", l, lret)
+	}
+
+	eret, err := client.TestEnum(thrifttest.Numberz_TWO)
+	if err != nil {
+		t.Errorf("Unexpected error in TestEnum() call: ", err)
+	}
+	if eret != thrifttest.Numberz_TWO {
+		t.Errorf("Unexpected TestEnum() result expected %#v, got %#v ", thrifttest.Numberz_TWO, eret)
+	}
+
+	tret, err := client.TestTypedef(thrifttest.UserId(42))
+	if err != nil {
+		t.Errorf("Unexpected error in TestTypedef() call: ", err)
+	}
+	if tret != thrifttest.UserId(42) {
+		t.Errorf("Unexpected TestTypedef() result expected %#v, got %#v ", thrifttest.UserId(42), tret)
+	}
+
+	mapmap, err := client.TestMapMap(42)
+	if err != nil {
+		t.Errorf("Unexpected error in TestMapmap() call: ", err)
+	}
+	if !reflect.DeepEqual(mapmap, rmapmap) {
+		t.Errorf("Unexpected TestMapmap() result expected %#v, got %#v ", rmapmap, mapmap)
+	}
+
+	xxsret, err := client.TestMulti(42, 4242, 424242, map[int16]string{1: "blah", 2: "thing"}, thrifttest.Numberz_EIGHT, thrifttest.UserId(24))
+	if err != nil {
+		t.Errorf("Unexpected error in TestMulti() call: ", err)
+	}
+	if !reflect.DeepEqual(xxs, xxsret) {
+		t.Errorf("Unexpected TestMulti() result expected %#v, got %#v ", xxs, xxsret)
+	}
+
+	err = client.TestException("some")
+	if err == nil {
+		t.Errorf("Expecting exception in TestException() call")
+	}
+	if !reflect.DeepEqual(err, xcept) {
+		t.Errorf("Unexpected TestException() result expected %#v, got %#v ", xcept, err)
+	}
+
+	// TODO: connection is being closed on this
+	err = client.TestException("TException")
+	tex, ok := err.(thrift.TApplicationException)
+	if err == nil || !ok || tex.TypeId() != thrift.INTERNAL_ERROR {
+		t.Errorf("Unexpected TestException() result expected ApplicationError, got %#v ", err)
+	}
+
+	ign, err := client.TestMultiException("Xception", "ignoreme")
+	if ign != nil || err == nil {
+		t.Errorf("Expecting exception in TestMultiException() call")
+	}
+	if !reflect.DeepEqual(err, &thrifttest.Xception{ErrorCode: 1001, Message: "This is an Xception"}) {
+		t.Errorf("Unexpected TestMultiException() %#v ", err)
+	}
+
+	ign, err = client.TestMultiException("Xception2", "ignoreme")
+	if ign != nil || err == nil {
+		t.Errorf("Expecting exception in TestMultiException() call")
+	}
+	expecting := &thrifttest.Xception2{ErrorCode: 2002, StructThing: &thrifttest.Xtruct{StringThing: "This is an Xception2"}}
+
+	if !reflect.DeepEqual(err, expecting) {
+		t.Errorf("Unexpected TestMultiException() %#v ", err)
+	}
+
+	err = client.TestOneway(2)
+	if err != nil {
+		t.Errorf("Unexpected error in TestOneway() call: ", err)
+	}
+
+	//Make sure the connection still alive
+	if err = client.TestVoid(); err != nil {
+		t.Errorf("Unexpected error in TestVoid() call: ", err)
+	}
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/mock_handler.go b/vendor/src/github.com/apache/thrift/test/go/src/common/mock_handler.go
new file mode 100644
index 00000000..6ae8130a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/mock_handler.go
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Automatically generated by MockGen. DO NOT EDIT!
+// Source: gen/thrifttest (interfaces: ThriftTest)
+
+package common
+
+import (
+	gomock "github.com/golang/mock/gomock"
+	thrifttest "gen/thrifttest"
+)
+
+// Mock of ThriftTest interface
+type MockThriftTest struct {
+	ctrl     *gomock.Controller
+	recorder *_MockThriftTestRecorder
+}
+
+// Recorder for MockThriftTest (not exported)
+type _MockThriftTestRecorder struct {
+	mock *MockThriftTest
+}
+
+func NewMockThriftTest(ctrl *gomock.Controller) *MockThriftTest {
+	mock := &MockThriftTest{ctrl: ctrl}
+	mock.recorder = &_MockThriftTestRecorder{mock}
+	return mock
+}
+
+func (_m *MockThriftTest) EXPECT() *_MockThriftTestRecorder {
+	return _m.recorder
+}
+
+func (_m *MockThriftTest) TestBool(_param0 bool) (bool, error) {
+	ret := _m.ctrl.Call(_m, "TestBool", _param0)
+	ret0, _ := ret[0].(bool)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestBool(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestBool", arg0)
+}
+
+
+func (_m *MockThriftTest) TestByte(_param0 int8) (int8, error) {
+	ret := _m.ctrl.Call(_m, "TestByte", _param0)
+	ret0, _ := ret[0].(int8)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestByte(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestByte", arg0)
+}
+
+func (_m *MockThriftTest) TestDouble(_param0 float64) (float64, error) {
+	ret := _m.ctrl.Call(_m, "TestDouble", _param0)
+	ret0, _ := ret[0].(float64)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestDouble(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestDouble", arg0)
+}
+
+func (_m *MockThriftTest) TestBinary(_param0 []byte) ([]byte, error) {
+	ret := _m.ctrl.Call(_m, "TestBinary", _param0)
+	ret0, _ := ret[0].([]byte)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestBinary(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestBinary", arg0)
+}
+
+func (_m *MockThriftTest) TestEnum(_param0 thrifttest.Numberz) (thrifttest.Numberz, error) {
+	ret := _m.ctrl.Call(_m, "TestEnum", _param0)
+	ret0, _ := ret[0].(thrifttest.Numberz)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestEnum(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestEnum", arg0)
+}
+
+func (_m *MockThriftTest) TestException(_param0 string) error {
+	ret := _m.ctrl.Call(_m, "TestException", _param0)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+func (_mr *_MockThriftTestRecorder) TestException(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestException", arg0)
+}
+
+func (_m *MockThriftTest) TestI32(_param0 int32) (int32, error) {
+	ret := _m.ctrl.Call(_m, "TestI32", _param0)
+	ret0, _ := ret[0].(int32)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestI32(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestI32", arg0)
+}
+
+func (_m *MockThriftTest) TestI64(_param0 int64) (int64, error) {
+	ret := _m.ctrl.Call(_m, "TestI64", _param0)
+	ret0, _ := ret[0].(int64)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestI64(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestI64", arg0)
+}
+
+func (_m *MockThriftTest) TestInsanity(_param0 *thrifttest.Insanity) (map[thrifttest.UserId]map[thrifttest.Numberz]*thrifttest.Insanity, error) {
+	ret := _m.ctrl.Call(_m, "TestInsanity", _param0)
+	ret0, _ := ret[0].(map[thrifttest.UserId]map[thrifttest.Numberz]*thrifttest.Insanity)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestInsanity(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestInsanity", arg0)
+}
+
+func (_m *MockThriftTest) TestList(_param0 []int32) ([]int32, error) {
+	ret := _m.ctrl.Call(_m, "TestList", _param0)
+	ret0, _ := ret[0].([]int32)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestList(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestList", arg0)
+}
+
+func (_m *MockThriftTest) TestMap(_param0 map[int32]int32) (map[int32]int32, error) {
+	ret := _m.ctrl.Call(_m, "TestMap", _param0)
+	ret0, _ := ret[0].(map[int32]int32)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestMap(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestMap", arg0)
+}
+
+func (_m *MockThriftTest) TestMapMap(_param0 int32) (map[int32]map[int32]int32, error) {
+	ret := _m.ctrl.Call(_m, "TestMapMap", _param0)
+	ret0, _ := ret[0].(map[int32]map[int32]int32)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestMapMap(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestMapMap", arg0)
+}
+
+func (_m *MockThriftTest) TestMulti(_param0 int8, _param1 int32, _param2 int64, _param3 map[int16]string, _param4 thrifttest.Numberz, _param5 thrifttest.UserId) (*thrifttest.Xtruct, error) {
+	ret := _m.ctrl.Call(_m, "TestMulti", _param0, _param1, _param2, _param3, _param4, _param5)
+	ret0, _ := ret[0].(*thrifttest.Xtruct)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestMulti(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestMulti", arg0, arg1, arg2, arg3, arg4, arg5)
+}
+
+func (_m *MockThriftTest) TestMultiException(_param0 string, _param1 string) (*thrifttest.Xtruct, error) {
+	ret := _m.ctrl.Call(_m, "TestMultiException", _param0, _param1)
+	ret0, _ := ret[0].(*thrifttest.Xtruct)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestMultiException(arg0, arg1 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestMultiException", arg0, arg1)
+}
+
+func (_m *MockThriftTest) TestNest(_param0 *thrifttest.Xtruct2) (*thrifttest.Xtruct2, error) {
+	ret := _m.ctrl.Call(_m, "TestNest", _param0)
+	ret0, _ := ret[0].(*thrifttest.Xtruct2)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestNest(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestNest", arg0)
+}
+
+func (_m *MockThriftTest) TestOneway(_param0 int32) error {
+	ret := _m.ctrl.Call(_m, "TestOneway", _param0)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+func (_mr *_MockThriftTestRecorder) TestOneway(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestOneway", arg0)
+}
+
+func (_m *MockThriftTest) TestSet(_param0 map[int32]struct{}) (map[int32]struct{}, error) {
+	ret := _m.ctrl.Call(_m, "TestSet", _param0)
+	ret0, _ := ret[0].(map[int32]struct{})
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestSet(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestSet", arg0)
+}
+
+func (_m *MockThriftTest) TestString(_param0 string) (string, error) {
+	ret := _m.ctrl.Call(_m, "TestString", _param0)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestString(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestString", arg0)
+}
+
+func (_m *MockThriftTest) TestStringMap(_param0 map[string]string) (map[string]string, error) {
+	ret := _m.ctrl.Call(_m, "TestStringMap", _param0)
+	ret0, _ := ret[0].(map[string]string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestStringMap(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestStringMap", arg0)
+}
+
+func (_m *MockThriftTest) TestStruct(_param0 *thrifttest.Xtruct) (*thrifttest.Xtruct, error) {
+	ret := _m.ctrl.Call(_m, "TestStruct", _param0)
+	ret0, _ := ret[0].(*thrifttest.Xtruct)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestStruct(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestStruct", arg0)
+}
+
+func (_m *MockThriftTest) TestTypedef(_param0 thrifttest.UserId) (thrifttest.UserId, error) {
+	ret := _m.ctrl.Call(_m, "TestTypedef", _param0)
+	ret0, _ := ret[0].(thrifttest.UserId)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+func (_mr *_MockThriftTestRecorder) TestTypedef(arg0 interface{}) *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestTypedef", arg0)
+}
+
+func (_m *MockThriftTest) TestVoid() error {
+	ret := _m.ctrl.Call(_m, "TestVoid")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+func (_mr *_MockThriftTestRecorder) TestVoid() *gomock.Call {
+	return _mr.mock.ctrl.RecordCall(_mr.mock, "TestVoid")
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/printing_handler.go b/vendor/src/github.com/apache/thrift/test/go/src/common/printing_handler.go
new file mode 100644
index 00000000..afee8da7
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/printing_handler.go
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"errors"
+	"fmt"
+	"encoding/hex"
+	. "gen/thrifttest"
+	"time"
+)
+
+var PrintingHandler = &printingHandler{}
+
+type printingHandler struct{}
+
+// Prints "testVoid()" and returns nothing.
+func (p *printingHandler) TestVoid() (err error) {
+	fmt.Println("testVoid()")
+	return nil
+}
+
+// Prints 'testString("%s")' with thing as '%s'
+// @param string thing - the string to print
+// @return string - returns the string 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestString(thing string) (r string, err error) {
+	fmt.Printf("testString(\"%s\")\n", thing)
+	return thing, nil
+}
+
+// Prints 'testBool("%t")' with thing as 'true' or 'false'
+// @param bool thing - the bool to print
+// @return bool - returns the bool 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestBool(thing bool) (r bool, err error) {
+	fmt.Printf("testBool(%t)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testByte("%d")' with thing as '%d'
+// @param byte thing - the byte to print
+// @return byte - returns the byte 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestByte(thing int8) (r int8, err error) {
+	fmt.Printf("testByte(%d)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testI32("%d")' with thing as '%d'
+// @param i32 thing - the i32 to print
+// @return i32 - returns the i32 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestI32(thing int32) (r int32, err error) {
+	fmt.Printf("testI32(%d)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testI64("%d")' with thing as '%d'
+// @param i64 thing - the i64 to print
+// @return i64 - returns the i64 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestI64(thing int64) (r int64, err error) {
+	fmt.Printf("testI64(%d)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testDouble("%f")' with thing as '%f'
+// @param double thing - the double to print
+// @return double - returns the double 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestDouble(thing float64) (r float64, err error) {
+	fmt.Printf("testDouble(%f)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data
+// @param []byte thing - the binary to print
+// @return []byte - returns the binary 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestBinary(thing []byte) (r []byte, err error) {
+	fmt.Printf("testBinary(%s)\n", hex.EncodeToString(thing))
+	return thing, nil
+}
+
+// Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values
+// @param Xtruct thing - the Xtruct to print
+// @return Xtruct - returns the Xtruct 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestStruct(thing *Xtruct) (r *Xtruct, err error) {
+	fmt.Printf("testStruct({\"%s\", %d, %d, %d})\n", thing.StringThing, thing.ByteThing, thing.I32Thing, thing.I64Thing)
+	return thing, err
+}
+
+// Prints 'testNest("{%s}")' where thing has been formatted into a string of the nested struct
+// @param Xtruct2 thing - the Xtruct2 to print
+// @return Xtruct2 - returns the Xtruct2 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestNest(nest *Xtruct2) (r *Xtruct2, err error) {
+	thing := nest.StructThing
+	fmt.Printf("testNest({%d, {\"%s\", %d, %d, %d}, %d})\n", nest.ByteThing, thing.StringThing, thing.ByteThing, thing.I32Thing, thing.I64Thing, nest.I32Thing)
+	return nest, nil
+}
+
+// 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'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestMap(thing map[int32]int32) (r map[int32]int32, err error) {
+	fmt.Printf("testMap({")
+	first := true
+	for k, v := range thing {
+		if first {
+			first = false
+		} else {
+			fmt.Printf(", ")
+		}
+		fmt.Printf("%d => %d", k, v)
+	}
+	fmt.Printf("})\n")
+	return thing, nil
+}
+
+// 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'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestStringMap(thing map[string]string) (r map[string]string, err error) {
+	fmt.Printf("testStringMap({")
+	first := true
+	for k, v := range thing {
+		if first {
+			first = false
+		} else {
+			fmt.Printf(", ")
+		}
+		fmt.Printf("%s => %s", k, v)
+	}
+	fmt.Printf("})\n")
+	return thing, nil
+}
+
+// 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'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestSet(thing map[int32]struct{}) (r map[int32]struct{}, err error) {
+	fmt.Printf("testSet({")
+	first := true
+	for k, _ := range thing {
+		if first {
+			first = false
+		} else {
+			fmt.Printf(", ")
+		}
+		fmt.Printf("%d", k)
+	}
+	fmt.Printf("})\n")
+	return thing, nil
+}
+
+// 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'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestList(thing []int32) (r []int32, err error) {
+	fmt.Printf("testList({")
+	for i, v := range thing {
+		if i != 0 {
+			fmt.Printf(", ")
+		}
+		fmt.Printf("%d", v)
+	}
+	fmt.Printf("})\n")
+	return thing, nil
+}
+
+// Prints 'testEnum("%d")' where thing has been formatted into it's numeric value
+// @param Numberz thing - the Numberz to print
+// @return Numberz - returns the Numberz 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestEnum(thing Numberz) (r Numberz, err error) {
+	fmt.Printf("testEnum(%d)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testTypedef("%d")' with thing as '%d'
+// @param UserId thing - the UserId to print
+// @return UserId - returns the UserId 'thing'
+//
+// Parameters:
+//  - Thing
+func (p *printingHandler) TestTypedef(thing UserId) (r UserId, err error) {
+	fmt.Printf("testTypedef(%d)\n", thing)
+	return thing, nil
+}
+
+// Prints 'testMapMap("%d")' with hello as '%d'
+// @param i32 hello - the i32 to print
+// @return map> - returns a dictionary with these values:
+//   {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
+//
+// Parameters:
+//  - Hello
+func (p *printingHandler) TestMapMap(hello int32) (r map[int32]map[int32]int32, err error) {
+	fmt.Printf("testMapMap(%d)\n", hello)
+
+	r = map[int32]map[int32]int32{
+		-4: map[int32]int32{-4: -4, -3: -3, -2: -2, -1: -1},
+		4:  map[int32]int32{4: 4, 3: 3, 2: 2, 1: 1},
+	}
+	return
+}
+
+// So you think you've got this all worked, out eh?
+//
+// Creates a the returned map with these values and prints it out:
+//   { 1 => { 2 => argument,
+//            3 => argument,
+//          },
+//     2 => { 6 => , },
+//   }
+// @return map> - a map with the above values
+//
+// Parameters:
+//  - Argument
+func (p *printingHandler) TestInsanity(argument *Insanity) (r map[UserId]map[Numberz]*Insanity, err error) {
+	fmt.Printf("testInsanity()\n")
+	r = make(map[UserId]map[Numberz]*Insanity)
+	r[1] = map[Numberz]*Insanity {
+		2: argument,
+		3: argument,
+	}
+	r[2] = map[Numberz]*Insanity {
+		6: NewInsanity(),
+	}
+	return
+}
+
+// Prints 'testMulti()'
+// @param byte arg0 -
+// @param i32 arg1 -
+// @param i64 arg2 -
+// @param map arg3 -
+// @param Numberz arg4 -
+// @param UserId arg5 -
+// @return Xtruct - returns an Xtruct with StringThing = "Hello2, ByteThing = arg0, I32Thing = arg1
+//    and I64Thing = arg2
+//
+// Parameters:
+//  - Arg0
+//  - Arg1
+//  - Arg2
+//  - Arg3
+//  - Arg4
+//  - Arg5
+func (p *printingHandler) TestMulti(arg0 int8, arg1 int32, arg2 int64, arg3 map[int16]string, arg4 Numberz, arg5 UserId) (r *Xtruct, err error) {
+	fmt.Printf("testMulti()\n")
+	r = NewXtruct()
+
+	r.StringThing = "Hello2"
+	r.ByteThing = arg0
+	r.I32Thing = arg1
+	r.I64Thing = arg2
+	return
+}
+
+// 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
+//
+// Parameters:
+//  - Arg
+func (p *printingHandler) TestException(arg string) (err error) {
+	fmt.Printf("testException(%s)\n", arg)
+	switch arg {
+	case "Xception":
+		e := NewXception()
+		e.ErrorCode = 1001
+		e.Message = arg
+		return e
+	case "TException":
+		return errors.New("Just TException")
+	}
+	return
+}
+
+// Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s'
+// @param string arg - a string indication 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 message = "This is an Xception2"
+// else do not throw anything
+// @return Xtruct - an Xtruct with StringThing = arg1
+//
+// Parameters:
+//  - Arg0
+//  - Arg1
+func (p *printingHandler) TestMultiException(arg0 string, arg1 string) (r *Xtruct, err error) {
+	fmt.Printf("testMultiException(%s, %s)\n", arg0, arg1)
+	switch arg0 {
+
+	case "Xception":
+		e := NewXception()
+		e.ErrorCode = 1001
+		e.Message = "This is an Xception"
+		return nil, e
+	case "Xception2":
+		e := NewXception2()
+		e.ErrorCode = 2002
+		e.StructThing = NewXtruct()
+		e.StructThing.StringThing = "This is an Xception2"
+		return nil, e
+	default:
+		r = NewXtruct()
+		r.StringThing = arg1
+		return
+	}
+}
+
+// Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'
+// sleep 'secondsToSleep'
+// Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d'
+// @param i32 secondsToSleep - the number of seconds to sleep
+//
+// Parameters:
+//  - SecondsToSleep
+func (p *printingHandler) TestOneway(secondsToSleep int32) (err error) {
+	fmt.Printf("testOneway(%d): Sleeping...\n", secondsToSleep)
+	time.Sleep(time.Second * time.Duration(secondsToSleep))
+	fmt.Printf("testOneway(%d): done sleeping!\n", secondsToSleep)
+	return
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/server.go b/vendor/src/github.com/apache/thrift/test/go/src/common/server.go
new file mode 100644
index 00000000..5ac44000
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/server.go
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"compress/zlib"
+	"crypto/tls"
+	"flag"
+	"fmt"
+	"gen/thrifttest"
+	"thrift"
+)
+
+var (
+	debugServerProtocol bool
+	certPath            string
+)
+
+func init() {
+	flag.BoolVar(&debugServerProtocol, "debug_server_protocol", false, "turn server protocol trace on")
+}
+
+func GetServerParams(
+	host string,
+	port int64,
+	domain_socket string,
+	transport string,
+	protocol string,
+	ssl bool,
+	certPath string,
+	handler thrifttest.ThriftTest) (thrift.TProcessor, thrift.TServerTransport, thrift.TTransportFactory, thrift.TProtocolFactory, error) {
+
+	var err error
+	hostPort := fmt.Sprintf("%s:%d", host, port)
+
+	var protocolFactory thrift.TProtocolFactory
+	switch protocol {
+	case "compact":
+		protocolFactory = thrift.NewTCompactProtocolFactory()
+	case "simplejson":
+		protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
+	case "json":
+		protocolFactory = thrift.NewTJSONProtocolFactory()
+	case "binary":
+		protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
+	default:
+		return nil, nil, nil, nil, fmt.Errorf("Invalid protocol specified %s", protocol)
+	}
+	if debugServerProtocol {
+		protocolFactory = thrift.NewTDebugProtocolFactory(protocolFactory, "server:")
+	}
+
+	var serverTransport thrift.TServerTransport
+	if ssl {
+		cfg := new(tls.Config)
+		if cert, err := tls.LoadX509KeyPair(certPath+"/server.crt", certPath+"/server.key"); err != nil {
+			return nil, nil, nil, nil, err
+		} else {
+			cfg.Certificates = append(cfg.Certificates, cert)
+		}
+		serverTransport, err = thrift.NewTSSLServerSocket(hostPort, cfg)
+	} else {
+		if domain_socket != "" {
+			serverTransport, err = thrift.NewTServerSocket(domain_socket)
+		} else {
+			serverTransport, err = thrift.NewTServerSocket(hostPort)
+		}
+	}
+	if err != nil {
+		return nil, nil, nil, nil, err
+	}
+
+	var transportFactory thrift.TTransportFactory
+
+	switch transport {
+	case "http":
+		// there is no such factory, and we don't need any
+		transportFactory = nil
+	case "framed":
+		transportFactory = thrift.NewTTransportFactory()
+		transportFactory = thrift.NewTFramedTransportFactory(transportFactory)
+	case "buffered":
+		transportFactory = thrift.NewTBufferedTransportFactory(8192)
+	case "zlib":
+		transportFactory = thrift.NewTZlibTransportFactory(zlib.BestCompression)
+	case "":
+		transportFactory = thrift.NewTTransportFactory()
+	default:
+		return nil, nil, nil, nil, fmt.Errorf("Invalid transport specified %s", transport)
+	}
+	processor := thrifttest.NewThriftTestProcessor(handler)
+
+	return processor, serverTransport, transportFactory, protocolFactory, nil
+}
diff --git a/vendor/src/github.com/apache/thrift/test/go/src/common/simple_handler.go b/vendor/src/github.com/apache/thrift/test/go/src/common/simple_handler.go
new file mode 100644
index 00000000..7bd3a30f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/go/src/common/simple_handler.go
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"errors"
+	. "gen/thrifttest"
+	"time"
+)
+
+var SimpleHandler = &simpleHandler{}
+
+type simpleHandler struct{}
+
+func (p *simpleHandler) TestVoid() (err error) {
+	return nil
+}
+
+func (p *simpleHandler) TestString(thing string) (r string, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestBool(thing []byte) (r []byte, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestByte(thing int8) (r int8, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestI32(thing int32) (r int32, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestI64(thing int64) (r int64, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestDouble(thing float64) (r float64, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestBinary(thing []byte) (r []byte, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestStruct(thing *Xtruct) (r *Xtruct, err error) {
+	return r, err
+}
+
+func (p *simpleHandler) TestNest(nest *Xtruct2) (r *Xtruct2, err error) {
+	return nest, nil
+}
+
+func (p *simpleHandler) TestMap(thing map[int32]int32) (r map[int32]int32, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestStringMap(thing map[string]string) (r map[string]string, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestSet(thing map[int32]struct{}) (r map[int32]struct{}, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestList(thing []int32) (r []int32, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestEnum(thing Numberz) (r Numberz, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestTypedef(thing UserId) (r UserId, err error) {
+	return thing, nil
+}
+
+func (p *simpleHandler) TestMapMap(hello int32) (r map[int32]map[int32]int32, err error) {
+
+	r = map[int32]map[int32]int32{
+		-4: map[int32]int32{-4: -4, -3: -3, -2: -2, -1: -1},
+		4:  map[int32]int32{4: 4, 3: 3, 2: 2, 1: 1},
+	}
+	return
+}
+
+func (p *simpleHandler) TestInsanity(argument *Insanity) (r map[UserId]map[Numberz]*Insanity, err error) {
+	return nil, errors.New("No Insanity")
+}
+
+func (p *simpleHandler) TestMulti(arg0 int8, arg1 int32, arg2 int64, arg3 map[int16]string, arg4 Numberz, arg5 UserId) (r *Xtruct, err error) {
+	r = NewXtruct()
+
+	r.StringThing = "Hello2"
+	r.ByteThing = arg0
+	r.I32Thing = arg1
+	r.I64Thing = arg2
+	return
+}
+
+func (p *simpleHandler) TestException(arg string) (err error) {
+	switch arg {
+	case "Xception":
+		e := NewXception()
+		e.ErrorCode = 1001
+		e.Message = arg
+		return e
+	case "TException":
+		return errors.New("Just TException")
+	}
+	return
+}
+
+func (p *simpleHandler) TestMultiException(arg0 string, arg1 string) (r *Xtruct, err error) {
+	switch arg0 {
+
+	case "Xception":
+		e := NewXception()
+		e.ErrorCode = 1001
+		e.Message = "This is an Xception"
+		return nil, e
+	case "Xception2":
+		e := NewXception2()
+		e.ErrorCode = 2002
+		e.StructThing.StringThing = "This is an Xception2"
+		return nil, e
+	default:
+		r = NewXtruct()
+		r.StringThing = arg1
+		return
+	}
+}
+
+func (p *simpleHandler) TestOneway(secondsToSleep int32) (err error) {
+	time.Sleep(time.Second * time.Duration(secondsToSleep))
+	return
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/Makefile.am b/vendor/src/github.com/apache/thrift/test/haxe/Makefile.am
new file mode 100644
index 00000000..1a32185e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/Makefile.am
@@ -0,0 +1,103 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+THRIFTCMD = $(THRIFT) --gen haxe -r
+THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift
+
+BIN_CPP = bin/Main-debug
+BIN_PHP = bin/php/Main-debug.php
+BIN_PHP_WEB = bin/php-web-server/Main-debug.php
+
+gen-haxe/thrift/test/ThriftTest.hx: $(THRIFTTEST)
+	$(THRIFTCMD) $(THRIFTTEST)
+
+all-local: $(BIN_CPP) $(BIN_PHP) $(BIN_PHP_WEB)
+
+$(BIN_CPP): \
+		src/*.hx \
+		../../lib/haxe/src/org/apache/thrift/**/*.hx \
+		gen-haxe/thrift/test/ThriftTest.hx
+	$(HAXE) --cwd .  cpp.hxml
+
+$(BIN_PHP): \
+		src/*.hx \
+		../../lib/haxe/src/org/apache/thrift/**/*.hx \
+		gen-haxe/thrift/test/ThriftTest.hx
+	$(HAXE) --cwd .  php.hxml
+
+$(BIN_PHP_WEB): \
+		src/*.hx \
+		../../lib/haxe/src/org/apache/thrift/**/*.hx \
+		gen-haxe/thrift/test/ThriftTest.hx
+	$(HAXE) --cwd .  php-web-server.hxml
+
+
+
+#TODO: other haxe targets
+#    $(HAXE)  --cwd .  csharp
+#    $(HAXE)  --cwd .  flash
+#    $(HAXE)  --cwd .  java
+#    $(HAXE)  --cwd .  javascript
+#    $(HAXE)  --cwd .  neko
+#    $(HAXE)  --cwd .  python  # needs Haxe 3.2.0
+
+
+clean-local:
+	$(RM) -r gen-haxe bin
+
+.NOTPARALLEL:
+
+check: check_cpp \
+	check_php \
+	check_php_web 
+
+check_cpp: $(BIN_CPP) 
+	timeout 20 $(BIN_CPP) server &
+	sleep 1
+	$(BIN_CPP) client
+	sleep 10
+
+check_php: $(BIN_PHP) 
+	timeout 20 php -f $(BIN_PHP) server &
+	sleep 1
+	php -f $(BIN_PHP) client
+	sleep 10
+
+check_php_web: $(BIN_PHP_WEB) $(BIN_CPP)
+	timeout 20 php -S 127.0.0.1:9090 router.php &
+	sleep 1
+	$(BIN_CPP) client --transport http
+	sleep 10
+
+
+EXTRA_DIST = \
+             src \
+             cpp.hxml \
+             csharp.hxml \
+             flash.hxml \
+             java.hxml \
+             javascript.hxml \
+             neko.hxml \
+             php.hxml \
+             python.hxml \
+             project.hide \
+             TestClientServer.hxproj \
+             make_all.bat \
+             make_all.sh
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/TestClientServer.hxproj b/vendor/src/github.com/apache/thrift/test/haxe/TestClientServer.hxproj
new file mode 100644
index 00000000..6696d80c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/TestClientServer.hxproj
@@ -0,0 +1,67 @@
+
+
+  
+  
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  
+    
+    
+    
+  
+  
+  
+    
+  
+  
+    
+  
+  
+  
+    
+  
+  
+  
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+  thrift -r -gen haxe  ../ThriftTest.thrift
+  
+  
+  
+  
+    
+  
+  
+
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/cpp.hxml b/vendor/src/github.com/apache/thrift/test/haxe/cpp.hxml
new file mode 100644
index 00000000..6adb52d7
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/cpp.hxml
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#CPP target
+-cpp bin
+
+#To produce 64 bit binaries the file should define the HXCPP_M64 compile variable:
+#-D HXCPP_M64
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/csharp.hxml b/vendor/src/github.com/apache/thrift/test/haxe/csharp.hxml
new file mode 100644
index 00000000..295c017e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/csharp.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#CSHARP target
+-cs bin/Tutorial.exe
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/flash.hxml b/vendor/src/github.com/apache/thrift/test/haxe/flash.hxml
new file mode 100644
index 00000000..a1f0568a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/flash.hxml
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Flash target
+-swf bin/Tutorial.swf
+
+#Add debug information
+-debug
+
+# we need some goodies from sys.net
+# --macro allowPackage("sys")
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/java.hxml b/vendor/src/github.com/apache/thrift/test/haxe/java.hxml
new file mode 100644
index 00000000..c615565a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/java.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src 
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Java target
+-java bin/Tutorial.jar
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/javascript.hxml b/vendor/src/github.com/apache/thrift/test/haxe/javascript.hxml
new file mode 100644
index 00000000..b2b3876c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/javascript.hxml
@@ -0,0 +1,44 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#JavaScript target
+-js bin/Tutorial.js
+
+#You can use -D source-map-content (requires Haxe 3.1+) to have the .hx 
+#files directly embedded into the map file, this way you only have to 
+#upload it, and it will be always in sync with the compiled .js even if 
+#you modify your .hx files.
+-D source-map-content
+
+#Generate source map and add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/make_all.bat b/vendor/src/github.com/apache/thrift/test/haxe/make_all.bat
new file mode 100644
index 00000000..eaeba890
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/make_all.bat
@@ -0,0 +1,68 @@
+@echo off
+rem /*
+rem  * Licensed to the Apache Software Foundation (ASF) under one
+rem  * or more contributor license agreements. See the NOTICE file
+rem  * distributed with this work for additional information
+rem  * regarding copyright ownership. The ASF licenses this file
+rem  * to you under the Apache License, Version 2.0 (the
+rem  * "License"); you may not use this file except in compliance
+rem  * with the License. You may obtain a copy of the License at
+rem  *
+rem  *   http://www.apache.org/licenses/LICENSE-2.0
+rem  *
+rem  * Unless required by applicable law or agreed to in writing,
+rem  * software distributed under the License is distributed on an
+rem  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem  * KIND, either express or implied. See the License for the
+rem  * specific language governing permissions and limitations
+rem  * under the License.
+rem  */
+
+setlocal
+if "%HOMEDRIVE%"=="" goto MISSINGVARS
+if "%HOMEPATH%"=="" goto MISSINGVARS
+if "%HAXEPATH%"=="" goto NOTINSTALLED
+
+set path=%HAXEPATH%;%HAXEPATH%\..\neko;%path%
+
+rem # invoke Thrift comnpiler
+thrift -r -gen haxe   ..\ThriftTest.thrift
+if errorlevel 1 goto STOP
+
+rem # invoke Haxe compiler for all targets
+for %%a in (*.hxml) do (
+	rem * filter Python, as it is not supported by Haxe 3.1.3 (but will be in 3.1.4)
+	if not "%%a"=="python.hxml" (
+		echo --------------------------
+		echo Building %%a ...
+		echo --------------------------
+		haxe  --cwd .  %%a
+	)
+)
+
+
+echo.
+echo done.
+pause
+goto eof
+
+:NOTINSTALLED
+echo FATAL: Either Haxe is not installed, or the HAXEPATH variable is not set.
+pause
+goto eof
+
+:MISSINGVARS
+echo FATAL: Unable to locate home folder.
+echo.
+echo Both HOMEDRIVE and HOMEPATH need to be set to point to your Home folder.
+echo The current values are:
+echo HOMEDRIVE=%HOMEDRIVE%
+echo HOMEPATH=%HOMEPATH%
+pause
+goto eof
+
+:STOP
+pause
+goto eof
+
+:eof
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/make_all.sh b/vendor/src/github.com/apache/thrift/test/haxe/make_all.sh
new file mode 100644
index 00000000..26212587
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/make_all.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# invoke Thrift comnpiler
+thrift -r -gen haxe  ../ThriftTest.thrift
+
+# output folder
+if [ ! -d bin ]; then
+  mkdir  bin
+fi
+
+# invoke Haxe compiler
+for target in *.hxml; do 
+  echo --------------------------
+  echo Building ${target} ...
+  echo --------------------------
+  if [ ! -d bin/${target} ]; then
+    mkdir  bin/${target}
+  fi
+  haxe  --cwd .  ${target} 
+done
+
+
+#eof
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/neko.hxml b/vendor/src/github.com/apache/thrift/test/haxe/neko.hxml
new file mode 100644
index 00000000..6161f697
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/neko.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#neko target
+-neko bin/Tutorial.n
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/php-web-server.hxml b/vendor/src/github.com/apache/thrift/test/haxe/php-web-server.hxml
new file mode 100644
index 00000000..395a8521
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/php-web-server.hxml
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#PHP target
+-php bin/php-web-server/
+--php-front Main-debug.php
+
+#defines
+-D phpwebserver
+
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/php.hxml b/vendor/src/github.com/apache/thrift/test/haxe/php.hxml
new file mode 100644
index 00000000..96518984
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/php.hxml
@@ -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.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#PHP target
+-php bin/php/
+--php-front Main-debug.php
+
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/project.hide b/vendor/src/github.com/apache/thrift/test/haxe/project.hide
new file mode 100644
index 00000000..a1c09bac
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/project.hide
@@ -0,0 +1,76 @@
+{
+     "type" : 0
+    ,"target" : 4
+    ,"name" : "Apache Thrift cross-platform test client/server"
+    ,"main" : null
+    ,"projectPackage" : ""
+    ,"company" : "Apache Software Foundation (ASF)"
+    ,"license" : "Apache License, Version 2.0"
+    ,"url" : "http://www.apache.org/licenses/LICENSE-2.0"
+    ,"targetData" : [
+         {
+             "pathToHxml" : "flash.hxml"
+            ,"runActionType" : 1
+            ,"runActionText" : "bin/Tutorial.swf"
+        }
+        ,{
+             "pathToHxml" : "javascript.hxml"
+            ,"runActionType" : 1
+            ,"runActionText" : "bin\\index.html"
+        }
+        ,{
+             "pathToHxml" : "neko.hxml"
+            ,"runActionType" : 2
+            ,"runActionText" : "neko bin/Tutorial.n"
+        }
+        ,{
+             "pathToHxml" : "php.hxml"
+        }
+        ,{
+             "pathToHxml" : "cpp.hxml"
+            ,"runActionType" : 2
+            ,"runActionText" : "bin/Main-debug.exe  client --protocol json"
+        }
+        ,{
+             "pathToHxml" : "java.hxml"
+        }
+        ,{
+             "pathToHxml" : "csharp.hxml"
+        }
+        ,{
+             "pathToHxml" : "python.hxml"
+            ,"runActionType" : 2
+            ,"runActionText" : "python bin/Tutorial.py"
+        }
+    ]
+    ,"files" : [
+         {
+             "path" : "src\\TestClient.hx"
+            ,"useTabs" : true
+            ,"indentSize" : 4
+            ,"foldedRegions" : [
+
+            ]
+            ,"activeLine" : 188
+        }
+        ,{
+             "path" : "src\\TestServer.hx"
+            ,"useTabs" : true
+            ,"indentSize" : 4
+            ,"foldedRegions" : [
+
+            ]
+            ,"activeLine" : 88
+        }
+    ]
+    ,"activeFile" : "src\\TestClient.hx"
+    ,"openFLTarget" : null
+    ,"openFLBuildMode" : "Debug"
+    ,"runActionType" : null
+    ,"runActionText" : null
+    ,"buildActionCommand" : null
+    ,"hiddenItems" : [
+
+    ]
+    ,"showHiddenItems" : false
+}
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/python.hxml b/vendor/src/github.com/apache/thrift/test/haxe/python.hxml
new file mode 100644
index 00000000..f2c19fa9
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/python.hxml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+ 
+#integrate files to classpath
+-cp src
+-cp gen-haxe
+-cp ../../lib/haxe/src
+
+#this class wil be used as entry point for your app.
+-main Main
+
+#Python target
+-python bin/Tutorial.py
+
+#Add debug information
+-debug
+
+#dead code elimination : remove unused code
+#"-dce no" : do not remove unused code
+#"-dce std" : remove unused code in the std lib (default)
+#"-dce full" : remove all unused code
+-dce full
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/router.php b/vendor/src/github.com/apache/thrift/test/haxe/router.php
new file mode 100644
index 00000000..e34135cc
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/router.php
@@ -0,0 +1,31 @@
+ 0) {
+            arg = args.shift();
+
+            if ( (arg == "-h") || (arg == "--help")) {
+                // -h [ --help ]               produce help message
+                Sys.println( GetHelp());
+                printHelpOnly = true;
+                return;
+            }
+            else if (arg == "--port") {
+                // --port arg (=9090)          Port number to listen
+                arg = args.shift();
+                var tmp = Std.parseInt(arg);
+                if( tmp != null) {
+                    port = tmp;
+                } else {
+                    throw "Invalid port number "+arg;
+                }
+            }
+            else if (arg == "--domain-socket") {
+                //   --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)
+                throw "named pipes not supported yet";
+            }
+            else if (arg == "--protocol") {
+                // --protocol arg (=binary)    protocol: binary, compact, json
+                arg = args.shift();
+                if( arg == "binary") {
+                    protocol = binary;
+                } else if( arg == "compact") {
+                    protocol = compact;
+                } else if( arg == "json") {
+                    protocol = json;
+                } else {
+                    InvalidArg(arg);
+                }
+            }
+            else if (arg == "--ssl") {
+                // --ssl                       Encrypted Transport using SSL
+                throw "SSL not supported yet";
+            }
+            else {
+                //Server only options:
+                if( server) {
+                    ParseServerArgument( arg, args);
+                } else {
+                    ParseClientArgument( arg, args);
+                }
+            }
+        }
+    }
+
+
+    private function ParseServerArgument( arg : String, args : Array) : Void {
+        if (arg == "--transport") {
+            //  --transport arg (=sockets)  Transport: buffered, framed, http, anonpipe
+            arg = args.shift();
+            if( arg == "buffered") {
+                buffered = true;
+            } else if( arg == "framed") {
+                framed = true;
+            } else if( arg == "http") {
+                transport = http;
+            } else if( arg == "anonpipe") {
+                throw "Anon pipes transport not supported yet";
+            } else {
+                InvalidArg(arg);
+            }
+        }
+        else if (arg == "--processor-events") {
+            throw "Processor events not supported yet";
+        }
+        else if (arg == "--server-type") {
+            //  --server-type arg (=simple) type of server,
+            // one of "simple", "thread-pool", "threaded", "nonblocking"
+            arg = args.shift();
+            if( arg == "simple") {
+                servertype = simple;
+            } else if( arg == "thread-pool") {
+                throw arg+" server not supported yet";
+            } else if( arg == "threaded") {
+                throw arg+" server not supported yet";
+            } else if( arg == "nonblocking") {
+                throw arg+" server not supported yet";
+            } else {
+                InvalidArg(arg);
+            }
+        }
+        else if ((arg == "-n") || (arg == "--workers")) {
+            //  -n [ --workers ] arg (=4)   Number of thread pools workers. Only valid for
+            //                              thread-pool server type
+            arg = args.shift();
+            var tmp = Std.parseInt(arg);
+            if( tmp != null) {
+                numThreads = tmp;
+            } else{
+                throw "Invalid number "+arg;
+            }
+        }
+        else {
+            InvalidArg(arg);
+        }
+    }
+
+
+    private function ParseClientArgument( arg : String, args : Array) : Void {
+        if (arg == "--host") {
+            //  --host arg (=localhost)     Host to connect
+            host = args.shift();
+        }
+        else if (arg == "--transport") {
+            //  --transport arg (=sockets)  Transport: buffered, framed, http, evhttp
+            arg = args.shift();
+            if( arg == "buffered") {
+                buffered = true;
+            } else if( arg == "framed") {
+                framed = true;
+            } else if( arg == "http") {
+                transport = http;
+            } else if( arg == "evhttp") {
+                throw "evhttp transport not supported yet";
+            } else {
+                InvalidArg(arg);
+            }
+        }
+        else if (arg == "--anon-pipes") {
+            //  --anon-pipes hRead hWrite   Windows Anonymous Pipes pair (handles)
+            throw "Anon pipes transport not supported yet";
+        }
+        else if ((arg == "-n") || (arg == "--testloops")) {
+            //  -n [ --testloops ] arg (=1) Number of Tests
+            arg = args.shift();
+            var tmp = Std.parseInt(arg);
+            if( tmp != null) {
+                numIterations = tmp;
+            } else {
+                throw "Invalid number "+arg;
+            }
+        }
+        else if ((arg == "-t") || (arg == "--threads")) {
+            //  -t [ --threads ] arg (=1)   Number of Test threads
+            arg = args.shift();
+            var tmp = Std.parseInt(arg);
+            if( tmp != null) {
+                numThreads = tmp;
+            } else {
+                throw "Invalid number "+arg;
+            }
+        }
+        else if (arg == "--skip-speed-test") {
+            //  --skip-speed-test              Skip the speed test
+            skipSpeedTest = true;
+        }
+        else {
+            InvalidArg(arg);
+        }
+    }
+
+
+    #end
+
+
+    private function InvalidArg( arg : String) : Void {
+        throw 'Invalid argument $arg';
+    }
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/Main.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/Main.hx
new file mode 100644
index 00000000..9eb828f1
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/Main.hx
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+import thrift.test.*;  // generated code
+
+class Main
+{
+    static function main() {
+        #if phpwebserver
+        initPhpWebServer();
+        //check method
+        if(php.Web.getMethod() != 'POST') {
+          Sys.println('http endpoint for thrift test server');
+          return;
+        }
+        #end
+
+        try {
+            var args = new Arguments();
+
+            if( args.printHelpOnly)
+                return;
+
+            if (args.server)
+                TestServer.Execute(args);
+            else
+                TestClient.Execute(args);
+
+            trace("Completed.");
+        } catch (e : String) {
+            trace(e);
+        }
+    }
+
+    #if phpwebserver
+    private static function initPhpWebServer()
+    {
+        //remap trace to error log
+        haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos)
+        {
+          // handle trace
+          var newValue : Dynamic;
+          if (infos != null && infos.customParams!=null) {
+            var extra:String = "";
+            for( v in infos.customParams )
+              extra += "," + v;
+            newValue = v + extra;
+          }
+          else {
+            newValue = v;
+          }
+          var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : '';
+          Sys.stderr().writeString('${msg}${newValue}\n');
+        }
+    }
+    #end
+
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/TestClient.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/TestClient.hx
new file mode 100644
index 00000000..aa496dc8
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/TestClient.hx
@@ -0,0 +1,932 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import haxe.Int32;
+import haxe.Int64;
+import haxe.io.Bytes;
+import haxe.Timer;
+import haxe.ds.IntMap;
+import haxe.ds.StringMap;
+import haxe.ds.ObjectMap;
+
+import org.apache.thrift.*;
+import org.apache.thrift.helper.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+#if cpp
+import cpp.vm.Thread;
+#else
+// no thread support (yet)
+#end
+
+import thrift.test.*;  // generated code
+
+
+using StringTools;
+
+class TestResults {
+    private var successCnt : Int = 0;
+    private var errorCnt : Int = 0;
+    private var failedTests : String = "";
+    private var print_direct : Bool = false;
+
+    public static var EXITCODE_SUCCESS            = 0x00;  // no errors bits set
+    //
+    public static var EXITCODE_FAILBIT_BASETYPES  = 0x01;
+    public static var EXITCODE_FAILBIT_STRUCTS    = 0x02;
+    public static var EXITCODE_FAILBIT_CONTAINERS = 0x04;
+    public static var EXITCODE_FAILBIT_EXCEPTIONS = 0x08;
+    //
+    public static var EXITCODE_ALL_FAILBITS       = 0x0F;
+    //
+    private var testsExecuted : Int = 0;
+    private var testsFailed : Int = 0;
+    private var currentTest : Int = 0;
+
+
+    public function new(direct : Bool) {
+        print_direct = direct;
+    }
+
+    public function StartTestGroup( groupBit : Int) : Void {
+        currentTest = groupBit;
+        testsExecuted |= groupBit;
+    }
+
+    public function Expect( expr : Bool, msg : String) : Void {
+        if ( expr) {
+            ++successCnt;
+        } else {
+            ++errorCnt;
+            testsFailed |= currentTest;
+            failedTests += "\n  " + msg;
+            if( print_direct) {
+                trace('FAIL: $msg');
+            }
+        }
+    }
+
+    public function CalculateExitCode() : Int {
+        var notExecuted : Int = EXITCODE_ALL_FAILBITS & (~testsExecuted);
+        return testsFailed | notExecuted;
+    }
+
+    public function PrintSummary() : Void {
+        var total = successCnt + errorCnt;
+        var sp = Math.round((1000 * successCnt) / total) / 10;
+        var ep = Math.round((1000 * errorCnt) / total) / 10;
+
+        trace('===========================');
+        trace('Tests executed    $total');
+        trace('Tests succeeded   $successCnt ($sp%)');
+        trace('Tests failed      $errorCnt ($ep%)');
+        if ( errorCnt > 0)
+        {
+            trace('===========================');
+            trace('FAILED TESTS: $failedTests');
+        }
+        trace('===========================');
+    }
+}
+
+
+class TestClient {
+
+    public static function Execute(args : Arguments) :  Void
+    {
+        var exitCode = 0xFF;
+        try
+        {
+            var difft = Timer.stamp();
+
+            if ( args.numThreads > 1) {
+                #if cpp
+                exitCode = MultiThreadClient(args);
+                #else
+                trace('Threads not supported/implemented for this platform.');
+                exitCode = SingleThreadClient(args);
+                #end
+            } else {
+                exitCode = SingleThreadClient(args);
+            }
+
+            difft = Math.round( 1000 * (Timer.stamp() - difft)) / 1000;
+            trace('total test time: $difft seconds');
+        }
+        catch (e : TException)
+        {
+            trace('TException: $e');
+            exitCode = 0xFF;
+        }
+        catch (e : Dynamic)
+        {
+            trace('Exception: $e');
+            exitCode = 0xFF;
+        }
+
+        #if sys
+        Sys.exit( exitCode);
+        #end
+    }
+
+
+    public static function SingleThreadClient(args : Arguments) :  Int
+    {
+        var rslt = new TestResults(true);
+        RunClient(args,rslt);
+        rslt.PrintSummary();
+        return rslt.CalculateExitCode();
+    }
+
+
+    #if cpp
+    public static function MultiThreadClient(args : Arguments) :  Int
+    {
+        var threads = new List();
+        for( test in 0 ... args.numThreads) {
+            threads.add( StartThread( args));
+        }
+        var exitCode : Int = 0;
+        for( thread in threads) {
+            exitCode |= Thread.readMessage(true);
+        }
+        return exitCode;
+    }
+    #end
+
+    #if cpp
+    private static function StartThread(args : Arguments) : Thread {
+        var thread = Thread.create(
+            function() : Void {
+                var rslt = new TestResults(false);
+                var main : Thread = Thread.readMessage(true);
+                try
+                {
+                    RunClient(args,rslt);
+                }
+                catch (e : TException)
+                {
+                    rslt.Expect( false, '$e');
+                    trace('$e');
+                }
+                catch (e : Dynamic)
+                {
+                    rslt.Expect( false, '$e');
+                    trace('$e');
+                }
+                main.sendMessage( rslt.CalculateExitCode());
+            });
+
+        thread.sendMessage(Thread.current());
+        return thread;
+    }
+    #end
+
+
+    public static function RunClient(args : Arguments, rslt : TestResults)
+    {
+        var transport : TTransport = null;
+        switch (args.transport)
+        {
+            case socket:
+                transport = new TSocket(args.host, args.port);
+            case http:
+                var uri = 'http://${args.host}:${args.port}';
+                trace('- http client : ${uri}');
+                transport = new THttpClient(uri);
+            default:
+                throw "Unhandled transport";
+        }
+
+        // optional: layered transport
+        if ( args.framed) {
+            trace("- framed transport");
+            transport = new TFramedTransport(transport);
+        }
+        if ( args.buffered) {
+            trace("- buffered transport");
+            transport = new TBufferedTransport(transport);
+        }
+
+        // protocol
+        var protocol : TProtocol = null;
+        switch( args.protocol)
+        {
+        case binary:
+            trace("- binary protocol");
+            protocol = new TBinaryProtocol(transport);
+        case json:
+            trace("- json protocol");
+            protocol = new TJSONProtocol(transport);
+        case compact:
+            trace("- compact protocol");
+            protocol = new TCompactProtocol(transport);
+        }
+
+        // some quick and basic unit tests
+        HaxeBasicsTest( args, rslt);
+        ModuleUnitTests( args, rslt);
+
+        // now run the test code
+        trace('- ${args.numIterations} iterations');
+        for( i in 0 ... args.numIterations) {
+            ClientTest( transport, protocol, args, rslt);
+        }
+    }
+
+
+    public static function HaxeBasicsTest( args : Arguments, rslt : TestResults) : Void
+    {
+        // We need to test a few basic things used in the ClientTest
+        // Anything else beyond this scope should go into /lib/haxe/ instead
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_BASETYPES);
+
+        var map32 = new IntMap();
+        var map64 = new Int64Map();
+
+        rslt.Expect( map32.keys().hasNext() == map64.keys().hasNext(), "Int64Map Test #1");
+        rslt.Expect( map32.exists( 4711) == map64.exists( Int64.make(47,11)), "Int64Map Test #2");
+        rslt.Expect( map32.remove( 4711) == map64.remove( Int64.make(47,11)), "Int64Map Test #3");
+        rslt.Expect( map32.get( 4711) == map64.get( Int64.make(47,11)), "Int64Map Test #4");
+
+        map32.set( 42, 815);
+        map64.set( Int64.make(0,42), 815);
+        map32.set( -517, 23);
+        map64.set( Int64.neg(Int64.make(0,517)), 23);
+        map32.set( 0, -123);
+        map64.set( Int64.make(0,0), -123);
+
+        //trace('map32 = $map32');
+        //trace('map64 = $map64');
+
+        rslt.Expect( map32.keys().hasNext() == map64.keys().hasNext(), "Int64Map Test #10");
+        rslt.Expect( map32.exists( 4711) == map64.exists( Int64.make(47,11)), "Int64Map Test #11");
+        rslt.Expect( map32.exists( -517) == map64.exists( Int64.neg(Int64.make(0,517))), "Int64Map Test #12");
+        rslt.Expect( map32.exists( 42) == map64.exists( Int64.make(0,42)), "Int64Map Test #13");
+        rslt.Expect( map32.exists( 0) == map64.exists( Int64.make(0,0)), "Int64Map Test #14");
+        rslt.Expect( map32.get( 4711) == map64.get( Int64.make(47,11)), "Int64Map Test #15");
+        rslt.Expect( map32.get( -517) == map64.get( Int64.neg(Int64.make(0,517))), "Int64Map Test #16");
+        rslt.Expect( map32.get( 42) == map64.get( Int64.make(0,42)), "Int64Map Test #Int64.make(-5,17)");
+        rslt.Expect( map32.get( 0) == map64.get( Int64.make(0,0)), "Int64Map Test #18");
+        rslt.Expect( map32.remove( 4711) == map64.remove( Int64.make(47,11)), "Int64Map Test #19");
+        rslt.Expect( map32.remove( -517) == map64.remove( Int64.neg(Int64.make(0,517))), "Int64Map Test #20");
+        rslt.Expect( map32.exists( 4711) == map64.exists( Int64.make(47,11)), "Int64Map Test #21");
+        rslt.Expect( map32.exists( -517) == map64.exists( Int64.neg(Int64.make(0,517))), "Int64Map Test #22");
+        rslt.Expect( map32.exists( 42) == map64.exists( Int64.make(0,42)), "Int64Map Test #23");
+        rslt.Expect( map32.exists( 0) == map64.exists( Int64.make(0,0)), "Int64Map Test #24");
+        rslt.Expect( map32.get( 4711) == map64.get( Int64.make(47,11)), "Int64Map Test #25");
+        rslt.Expect( map32.get( -517) == map64.get( Int64.neg(Int64.make(0,517))), "Int64Map Test #26");
+        rslt.Expect( map32.get( 42) == map64.get( Int64.make(0,42)), "Int64Map Test #27");
+        rslt.Expect( map32.get( 0) == map64.get( Int64.make(0,0)), "Int64Map Test #28");
+
+        map32.set( 42, 1);
+        map64.set( Int64.make(0,42), 1);
+        map32.set( -517, -2);
+        map64.set( Int64.neg(Int64.make(0,517)), -2);
+        map32.set( 0, 3);
+        map64.set( Int64.make(0,0), 3);
+
+        var c32 = 0;
+        var ksum32 = 0;
+        for (key in map32.keys()) {
+            ++c32;
+            ksum32 += key;
+        }
+        var c64 = 0;
+        var ksum64 = Int64.make(0,0);
+        for (key in map64.keys()) {
+            ++c64;
+            ksum64 = Int64.add( ksum64, key);
+        }
+        rslt.Expect( c32 == c64, "Int64Map Test #30");
+        rslt.Expect( '$ksum64' == '$ksum32', '$ksum64 == $ksum32   Test #31');
+
+        //compare without spaces because differ in php and cpp
+        var s32 = map32.toString().replace(' ', '');
+        var s64 = map64.toString().replace(' ', '');
+        rslt.Expect( s32 == s64, "Int64Map.toString(): " + ' ("$s32" == "$s64") Test #32');
+
+        map32.remove( 42);
+        map64.remove( Int64.make(0,42));
+        map32.remove( -517);
+        map64.remove( Int64.neg(Int64.make(0,517)));
+        map32.remove( 0);
+        map64.remove( Int64.make(0,0));
+
+        rslt.Expect( map32.keys().hasNext() == map64.keys().hasNext(), "Int64Map Test #90");
+        rslt.Expect( map32.exists( 4711) == map64.exists( Int64.make(47,11)), "Int64Map Test #91");
+        rslt.Expect( map32.exists( -517) == map64.exists( Int64.neg(Int64.make(0,517))), "Int64Map Test #92");
+        rslt.Expect( map32.exists( 42) == map64.exists( Int64.make(0,42)), "Int64Map Test #93");
+        rslt.Expect( map32.exists( 0) == map64.exists( Int64.make(0,0)), "Int64Map Test #94");
+        rslt.Expect( map32.get( 4711) == map64.get( Int64.make(47,11)), "Int64Map Test #95");
+        rslt.Expect( map32.get( -517) == map64.get( Int64.make(-5,17)), "Int64Map Test #96");
+        rslt.Expect( map32.get( 42) == map64.get( Int64.make(0,42)), "Int64Map Test #97");
+        rslt.Expect( map32.get( 0) == map64.get( Int64.make(0, 0)), "Int64Map Test #98");
+    }
+
+
+    // core module unit tests
+    public static function ModuleUnitTests( args : Arguments, rslt : TestResults) : Void {
+        #if debug
+
+        try {
+            BitConverter.UnitTest();
+            rslt.Expect( true, 'BitConverter.UnitTest  Test #100');
+        }
+        catch( e : Dynamic) {
+            rslt.Expect( false, 'BitConverter.UnitTest: $e  Test #100');
+        }
+
+        try {
+            ZigZag.UnitTest();
+            rslt.Expect( true, 'ZigZag.UnitTest  Test #101');
+        }
+        catch( e : Dynamic) {
+            rslt.Expect( false, 'ZigZag.UnitTest: $e  Test #101');
+        }
+
+        #end
+    }
+
+
+    public static function BytesToHex(data : Bytes) : String {
+        var hex = "";
+        for ( i in 0 ... data.length) {
+            hex += StringTools.hex( data.get(i), 2);
+        }
+        return hex;
+    }
+
+    public static function PrepareTestData(randomDist : Bool) : Bytes    {
+        var retval = Bytes.alloc(0x100);
+        var initLen : Int = (retval.length > 0x100 ? 0x100 : retval.length);
+
+        // linear distribution, unless random is requested
+        if (!randomDist) {
+            for (i in 0 ... initLen) {
+                retval.set(i, i % 0x100);
+            }
+            return retval;
+        }
+
+        // random distribution
+        for (i in 0 ... initLen) {
+            retval.set(i, 0);
+        }
+        for (i in 1 ... initLen) {
+            while( true) {
+                var nextPos = Std.random(initLen);
+                if (retval.get(nextPos) == 0) {
+                    retval.set( nextPos, i % 0x100);
+                    break;
+                }
+            }
+        }
+        return retval;
+    }
+
+
+    public static function ClientTest( transport : TTransport, protocol : TProtocol,
+                                       args : Arguments, rslt : TestResults) : Void
+    {
+        var client = new ThriftTestImpl(protocol,protocol);
+        try
+        {
+            if (!transport.isOpen())
+            {
+                transport.open();
+            }
+        }
+        catch (e : TException)
+        {
+            rslt.Expect( false, 'unable to open transport: $e');
+            return;
+        }
+        catch (e : Dynamic)
+        {
+            rslt.Expect( false, 'unable to open transport: $e');
+            return;
+        }
+
+        var start = Date.now();
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_EXCEPTIONS);
+
+        // if arg == "Xception" throw Xception with errorCode = 1001 and message = arg
+        trace('testException("Xception")');
+        try {
+            client.testException("Xception");
+            rslt.Expect( false, 'testException("Xception") should throw');
+        }
+        catch (e : Xception)
+        {
+            rslt.Expect( e.message == "Xception", 'testException("Xception")  -  e.message == "Xception"');
+            rslt.Expect( e.errorCode == 1001, 'testException("Xception")  -  e.errorCode == 1001');
+        }
+        catch (e : Dynamic)
+        {
+            rslt.Expect( false, 'testException("Xception")  -  $e');
+        }
+
+        // if arg == "TException" throw TException
+        trace('testException("TException")');
+        try {
+            client.testException("TException");
+            rslt.Expect( false, 'testException("TException") should throw');
+        }
+        catch (e : TException)
+        {
+            rslt.Expect( true, 'testException("TException")  -  $e');
+        }
+        catch (e : Dynamic)
+        {
+            rslt.Expect( false, 'testException("TException")  -  $e');
+        }
+
+        // reopen the transport, just in case the server closed his end
+        if (transport.isOpen())
+            transport.close();
+        transport.open();
+
+        // else do not throw anything
+        trace('testException("bla")');
+        try {
+            client.testException("bla");
+            rslt.Expect( true, 'testException("bla") should not throw');
+        }
+        catch (e : Dynamic)
+        {
+            rslt.Expect( false, 'testException("bla")  -  $e');
+        }
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_BASETYPES);
+
+        trace('testVoid()');
+        client.testVoid();
+        trace(' = void');
+        rslt.Expect(true,"testVoid()");  // bump counter
+
+        trace('testBool(${true})');
+        var b = client.testBool(true);
+        trace(' = $b');
+        rslt.Expect(b, '$b == "${true}"');
+        trace('testBool(${false})');
+        b = client.testBool(false);
+        trace(' = $b');
+        rslt.Expect( ! b, '$b == "${false}"');
+
+        trace('testString("Test")');
+        var s = client.testString("Test");
+        trace(' = "$s"');
+        rslt.Expect(s == "Test", '$s == "Test"');
+
+        trace('testByte(1)');
+        var i8 = client.testByte(1);
+        trace(' = $i8');
+        rslt.Expect(i8 == 1, '$i8 == 1');
+
+        trace('testI32(-1)');
+        var i32 = client.testI32(-1);
+        trace(' = $i32');
+        rslt.Expect(i32 == -1, '$i32 == -1');
+
+        trace('testI64(-34359738368)');
+        var i64 = client.testI64( Int64.make( 0xFFFFFFF8, 0x00000000)); // -34359738368
+        trace(' = $i64');
+        rslt.Expect( Int64.compare( i64, Int64.make( 0xFFFFFFF8, 0x00000000)) == 0,
+                     Int64.toStr(i64) +" == "+Int64.toStr(Int64.make( 0xFFFFFFF8, 0x00000000)));
+
+        // edge case: the largest negative Int64 has no positive Int64 equivalent
+        trace('testI64(-9223372036854775808)');
+        i64 = client.testI64( Int64.make( 0x80000000, 0x00000000)); // -9223372036854775808
+        trace(' = $i64');
+        rslt.Expect( Int64.compare( i64, Int64.make( 0x80000000, 0x00000000)) == 0,
+                     Int64.toStr(i64) +" == "+Int64.toStr(Int64.make( 0x80000000, 0x00000000)));
+
+        trace('testDouble(5.325098235)');
+        var dub = client.testDouble(5.325098235);
+        trace(' = $dub');
+        rslt.Expect(dub == 5.325098235, '$dub == 5.325098235');
+
+        var binOut = PrepareTestData(true);
+        trace('testBinary('+BytesToHex(binOut)+')');
+        try {
+            var binIn = client.testBinary(binOut);
+            trace('testBinary() = '+BytesToHex(binIn));
+            rslt.Expect( binIn.length == binOut.length, '${binIn.length} == ${binOut.length}');
+            var len = ((binIn.length < binOut.length)  ?  binIn.length  : binOut.length);
+            for (ofs in 0 ... len) {
+                if (binIn.get(ofs) != binOut.get(ofs)) {
+                    rslt.Expect( false, 'testBinary('+BytesToHex(binOut)+'): content mismatch at offset $ofs');
+                }
+            }
+        }
+        catch (e : TApplicationException) {
+            trace('testBinary('+BytesToHex(binOut)+'): '+e.errorMsg);  // may not be supported by the server
+        }
+
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_STRUCTS);
+
+        trace('testStruct({"Zero", 1, -3, -5})');
+        var o = new Xtruct();
+        o.string_thing = "Zero";
+        o.byte_thing = 1;
+        o.i32_thing = -3;
+        o.i64_thing = Int64.make(0,-5);
+        var i = client.testStruct(o);
+        trace(' = {"' + i.string_thing + '", ' + i.byte_thing +', '
+                      + i.i32_thing +', '+ Int64.toStr(i.i64_thing) + '}');
+        rslt.Expect( i.string_thing == o.string_thing, "i.string_thing == o.string_thing");
+        rslt.Expect( i.byte_thing == o.byte_thing, "i.byte_thing == o.byte_thing");
+        rslt.Expect( i.i32_thing == o.i32_thing, "i.i64_thing == o.i64_thing");
+        rslt.Expect( i.i32_thing == o.i32_thing, "i.i64_thing == o.i64_thing");
+
+        trace('testNest({1, {\"Zero\", 1, -3, -5}, 5})');
+        var o2 = new Xtruct2();
+        o2.byte_thing = 1;
+        o2.struct_thing = o;
+        o2.i32_thing = 5;
+        var i2 = client.testNest(o2);
+        i = i2.struct_thing;
+        trace(" = {" + i2.byte_thing + ", {\"" + i.string_thing + "\", "
+              + i.byte_thing + ", " + i.i32_thing + ", " + Int64.toStr(i.i64_thing) + "}, "
+              + i2.i32_thing + "}");
+        rslt.Expect( i2.byte_thing == o2.byte_thing, "i2.byte_thing == o2.byte_thing");
+        rslt.Expect( i2.i32_thing == o2.i32_thing, "i2.i32_thing == o2.i32_thing");
+        rslt.Expect( i.string_thing == o.string_thing, "i.string_thing == o.string_thing");
+        rslt.Expect( i.byte_thing == o.byte_thing, "i.byte_thing == o.byte_thing");
+        rslt.Expect( i.i32_thing == o.i32_thing, "i.i32_thing == o.i32_thing");
+        rslt.Expect( Int64.compare( i.i64_thing, o.i64_thing) == 0, "i.i64_thing == o.i64_thing");
+
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_CONTAINERS);
+
+        var mapout = new IntMap< haxe.Int32>();
+        for ( j in 0 ... 5)
+        {
+            mapout.set(j, j - 10);
+        }
+        trace("testMap({");
+        var first : Bool = true;
+        for( key in mapout.keys())
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(key + " => " + mapout.get(key));
+        }
+        trace("})");
+
+        var mapin = client.testMap(mapout);
+
+        trace(" = {");
+        first = true;
+        for( key in mapin.keys())
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(key + " => " + mapin.get(key));
+            rslt.Expect( mapin.get(key) == mapout.get(key), ' mapin.get($key) == mapout.get($key)');
+        }
+        trace("}");
+        for( key in mapout.keys())
+        {
+            rslt.Expect(mapin.exists(key), 'mapin.exists($key)');
+        }
+
+        var listout = new List();
+        for (j in -2 ... 3)
+        {
+            listout.add(j);
+        }
+        trace("testList({");
+        first = true;
+        for( j in listout)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(j);
+        }
+        trace("})");
+
+        var listin = client.testList(listout);
+
+        trace(" = {");
+        first = true;
+        for( j in listin)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(j);
+        }
+        trace("}");
+
+        rslt.Expect(listin.length == listout.length, "listin.length == listout.length");
+        var literout = listout.iterator();
+        var literin = listin.iterator();
+        while( literin.hasNext()) {
+            rslt.Expect(literin.next() == literout.next(), "literin[i] == literout[i]");
+        }
+
+        //set
+        var setout = new IntSet();
+        for (j in -2 ... 3)
+        {
+            setout.add(j);
+        }
+        trace("testSet({");
+        first = true;
+        for( j in setout)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(j);
+        }
+        trace("})");
+
+        var setin = client.testSet(setout);
+
+        trace(" = {");
+        first = true;
+        for( j in setin)
+        {
+            if (first)
+            {
+                first = false;
+            }
+            else
+            {
+                trace(", ");
+            }
+            trace(j);
+            rslt.Expect(setout.contains(j), 'setout.contains($j)');
+        }
+        trace("}");
+        rslt.Expect(setin.size == setout.size, "setin.length == setout.length");
+
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_BASETYPES);
+
+        trace("testEnum(ONE)");
+        var ret = client.testEnum(Numberz.ONE);
+        trace(" = " + ret);
+        rslt.Expect(ret == Numberz.ONE, '$ret == Numberz.ONE');
+
+        trace("testEnum(TWO)");
+        ret = client.testEnum(Numberz.TWO);
+        trace(" = " + ret);
+        rslt.Expect(ret == Numberz.TWO, '$ret == Numberz.TWO');
+
+        trace("testEnum(THREE)");
+        ret = client.testEnum(Numberz.THREE);
+        trace(" = " + ret);
+        rslt.Expect(ret == Numberz.THREE, '$ret == Numberz.THREE');
+
+        trace("testEnum(FIVE)");
+        ret = client.testEnum(Numberz.FIVE);
+        trace(" = " + ret);
+        rslt.Expect(ret == Numberz.FIVE, '$ret == Numberz.FIVE');
+
+        trace("testEnum(EIGHT)");
+        ret = client.testEnum(Numberz.EIGHT);
+        trace(" = " + ret);
+        rslt.Expect(ret == Numberz.EIGHT, '$ret == Numberz.EIGHT');
+
+        trace("testTypedef(309858235082523)");
+        var uid = client.testTypedef( Int64.make( 0x119D0, 0x7E08671B));  // 309858235082523
+        trace(" = " + uid);
+        rslt.Expect( Int64.compare( uid, Int64.make( 0x119D0, 0x7E08671B)) == 0,
+                     Int64.toStr(uid)+" == "+Int64.toStr(Int64.make( 0x119D0, 0x7E08671B)));
+
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_CONTAINERS);
+
+        trace("testMapMap(1)");
+        var mm = client.testMapMap(1);
+        trace(" = {");
+        for( key in mm.keys())
+        {
+            trace(key + " => {");
+            var m2 = mm.get(key);
+            for( k2 in m2.keys())
+            {
+                trace(k2 + " => " + m2.get(k2) + ", ");
+            }
+            trace("}, ");
+        }
+        trace("}");
+
+        var pos = mm.get(4);
+        var neg = mm.get(-4);
+        rslt.Expect( (pos != null) && (neg != null), "(pos != null) && (neg != null)");
+        for (i in 1 ... 5) {
+            rslt.Expect( pos.get(i) == i, 'pos.get($i) == $i');
+            rslt.Expect( neg.get(-i) == -i, 'neg.get(-$i) == -$i');
+        }
+        rslt.Expect( ! pos.exists(0), '!pos.exists(0)');
+        rslt.Expect( ! neg.exists(-0), '!neg.exists(-0)');
+        rslt.Expect( ! pos.exists(42), '!pos.exists(42)');
+        rslt.Expect( ! neg.exists(-42), '!neg.exists(-42)');
+
+
+        rslt.StartTestGroup( TestResults.EXITCODE_FAILBIT_STRUCTS);
+
+        var insane = new Insanity();
+        insane.userMap = new IntMap< Int64>();
+        insane.userMap.set( Numberz.FIVE, Int64.make(0,5000));
+        var truck = new Xtruct();
+        truck.string_thing = "Truck";
+        truck.byte_thing = 8;
+        truck.i32_thing = 8;
+        truck.i64_thing = Int64.make(0,8);
+        insane.xtructs = new List();
+        insane.xtructs.add(truck);
+        trace("testInsanity()");
+        var whoa = client.testInsanity(insane);
+        trace(" = {");
+        for( key in whoa.keys())
+        {
+            var val = whoa.get(key);
+            trace(key + " => {");
+
+            for( k2 in val.keys())
+            {
+                var v2 = val.get(k2);
+
+                trace(k2 + " => {");
+                var userMap = v2.userMap;
+
+                trace("{");
+                if (userMap != null)
+                {
+                    for( k3 in userMap.keys())
+                    {
+                        trace(k3 + " => " + userMap.get(k3) + ", ");
+                    }
+                }
+                else
+                {
+                    trace("null");
+                }
+                trace("}, ");
+
+                var xtructs = v2.xtructs;
+
+                trace("{");
+                if (xtructs != null)
+                {
+                    for( x in xtructs)
+                    {
+                        trace("{\"" + x.string_thing + "\", "
+                              + x.byte_thing + ", " + x.i32_thing + ", "
+                              + x.i32_thing + "}, ");
+                    }
+                }
+                else
+                {
+                    trace("null");
+                }
+                trace("}");
+
+                trace("}, ");
+            }
+            trace("}, ");
+        }
+        trace("}");
+
+
+        var first_map = whoa.get(Int64.make(0,1));
+        var second_map = whoa.get(Int64.make(0,2));
+        rslt.Expect( (first_map != null) && (second_map != null), "(first_map != null) && (second_map != null)");
+        if ((first_map != null) && (second_map != null))
+        {
+            var crazy2 = first_map.get(Numberz.TWO);
+            var crazy3 = first_map.get(Numberz.THREE);
+            var looney = second_map.get(Numberz.SIX);
+            rslt.Expect( (crazy2 != null) && (crazy3 != null) && (looney != null),
+                        "(crazy2 != null) && (crazy3 != null) && (looney != null)");
+
+            rslt.Expect( Int64.compare( crazy2.userMap.get(Numberz.EIGHT), Int64.make(0,8)) == 0,
+                        "crazy2.UserMap.get(Numberz.EIGHT) == 8");
+            rslt.Expect( Int64.compare( crazy3.userMap.get(Numberz.EIGHT), Int64.make(0,8)) == 0,
+                        "crazy3.UserMap.get(Numberz.EIGHT) == 8");
+            rslt.Expect( Int64.compare( crazy2.userMap.get(Numberz.FIVE), Int64.make(0,5)) == 0,
+                        "crazy2.UserMap.get(Numberz.FIVE) == 5");
+            rslt.Expect( Int64.compare( crazy3.userMap.get(Numberz.FIVE), Int64.make(0,5)) == 0,
+                        "crazy3.UserMap.get(Numberz.FIVE) == 5");
+
+            var crz2iter = crazy2.xtructs.iterator();
+            var crz3iter = crazy3.xtructs.iterator();
+            rslt.Expect( crz2iter.hasNext() && crz3iter.hasNext(), "crz2iter.hasNext() && crz3iter.hasNext()");
+            var goodbye2 = crz2iter.next();
+            var goodbye3 = crz3iter.next();
+            rslt.Expect( crz2iter.hasNext() && crz3iter.hasNext(), "crz2iter.hasNext() && crz3iter.hasNext()");
+            var hello2 = crz2iter.next();
+            var hello3 = crz3iter.next();
+            rslt.Expect( ! (crz2iter.hasNext() || crz3iter.hasNext()), "! (crz2iter.hasNext() || crz3iter.hasNext())");
+
+            rslt.Expect( hello2.string_thing == "Hello2", 'hello2.String_thing == "Hello2"');
+            rslt.Expect( hello2.byte_thing == 2, 'hello2.Byte_thing == 2');
+            rslt.Expect( hello2.i32_thing == 2, 'hello2.I32_thing == 2');
+            rslt.Expect( Int64.compare( hello2.i64_thing, Int64.make(0,2)) == 0, 'hello2.I64_thing == 2');
+            rslt.Expect( hello3.string_thing == "Hello2", 'hello3.String_thing == "Hello2"');
+            rslt.Expect( hello3.byte_thing == 2, 'hello3.Byte_thing == 2');
+            rslt.Expect( hello3.i32_thing == 2, 'hello3.I32_thing == 2');
+            rslt.Expect( Int64.compare( hello3.i64_thing, Int64.make(0,2)) == 0, 'hello3.I64_thing == 2');
+
+            rslt.Expect( goodbye2.string_thing == "Goodbye4", 'goodbye2.String_thing == "Goodbye4"');
+            rslt.Expect( goodbye2.byte_thing == 4, 'goodbye2.Byte_thing == 4');
+            rslt.Expect( goodbye2.i32_thing == 4, 'goodbye2.I32_thing == 4');
+            rslt.Expect( Int64.compare( goodbye2.i64_thing, Int64.make(0,4)) == 0, 'goodbye2.I64_thing == 4');
+            rslt.Expect( goodbye3.string_thing == "Goodbye4", 'goodbye3.String_thing == "Goodbye4"');
+            rslt.Expect( goodbye3.byte_thing == 4, 'goodbye3.Byte_thing == 4');
+            rslt.Expect( goodbye3.i32_thing == 4, 'goodbye3.I32_thing == 4');
+            rslt.Expect( Int64.compare( goodbye3.i64_thing, Int64.make(0,4)) == 0, 'goodbye3.I64_thing == 4');
+        }
+
+        var arg0 = 1;
+        var arg1 = 2;
+        var arg2 = Int64.make( 0x7FFFFFFF,0xFFFFFFFF);
+        var multiDict = new IntMap< String>();
+        multiDict.set(1, "one");
+        var arg4 = Numberz.FIVE;
+        var arg5 = Int64.make(0,5000000);
+        trace("Test Multi(" + arg0 + "," + arg1 + "," + arg2 + "," + multiDict + "," + arg4 + "," + arg5 + ")");
+        var multiResponse = client.testMulti(arg0, arg1, arg2, multiDict, arg4, arg5);
+        trace(" = Xtruct(byte_thing:" + multiResponse.byte_thing + ",string_thing:" + multiResponse.string_thing
+                    + ",i32_thing:" + multiResponse.i32_thing
+                    + ",i64_thing:" + Int64.toStr(multiResponse.i64_thing) + ")");
+
+        rslt.Expect( multiResponse.string_thing == "Hello2", 'multiResponse.String_thing == "Hello2"');
+        rslt.Expect( multiResponse.byte_thing == arg0, 'multiResponse.Byte_thing == arg0');
+        rslt.Expect( multiResponse.i32_thing == arg1, 'multiResponse.I32_thing == arg1');
+        rslt.Expect( Int64.compare( multiResponse.i64_thing, arg2) == 0, 'multiResponse.I64_thing == arg2');
+
+
+        rslt.StartTestGroup( 0);
+
+        trace("Test Oneway(1)");
+        client.testOneway(1);
+
+        if( ! args.skipSpeedTest) {
+            trace("Test Calltime()");
+            var difft = Timer.stamp();
+            for ( k in 0 ... 1000) {
+                client.testVoid();
+            }
+            difft = Math.round( 1000 * (Timer.stamp() - difft)) / 1000;
+            trace('$difft ms per testVoid() call');
+        }
+    }
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/TestMacro.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/TestMacro.hx
new file mode 100644
index 00000000..a6207606
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/TestMacro.hx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package ;
+
+import haxe.macro.Context;
+import haxe.macro.Expr;
+
+/****
+ * If you call the Thrift compiler this way (e.g. by changing the prebuild command)
+ *
+ *     thrift -r -gen haxe:buildmacro=TestMacro.handle()   ../ThriftTest.thrift
+ *
+ * the TestMacro.handle() function implemented below is called for each generated class
+ * and interface. Use "thrift --help" to get more info about other available options.
+ */
+class TestMacro
+{
+  public static function handle( ) : Array< Field> {
+    trace('TestMacro called for ' + Context.getLocalType());
+    return Context.getBuildFields();
+  }
+
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/TestServer.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServer.hx
new file mode 100644
index 00000000..450c8f28
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServer.hx
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+import thrift.test.*;  // generated code
+
+
+class TestServer
+{
+    public static function Execute(args : Arguments) :  Void
+    {
+        try
+        {
+            // Transport
+            var transport : TServerTransport = null;
+            switch( args.transport) {
+            case socket:
+                trace("- socket port "+args.port);
+                transport = new TServerSocket( args.port);
+            case http:
+                trace("- http");
+                #if !phpwebserver
+                  throw "HTTP server not implemented yet";
+                 //transport = new THttpServer( targetHost);
+                #else
+                transport =    new TWrappingServerTransport(
+                        new TStreamTransport(
+                          new TFileStream("php://input", Read),
+                          new TFileStream("php://output", Append)
+                          )
+                        );
+
+                #end
+            default:
+                throw "Unhandled transport";
+            }
+
+            // optional: layered transport
+            var transfactory : TTransportFactory = null;
+            if ( args.framed) {
+                trace("- framed transport");
+                transfactory = new TFramedTransportFactory();
+            }
+            if ( args.buffered) {
+                trace("- buffered transport");
+                transfactory = new TBufferedTransportFactory();
+            }
+
+            // protocol
+            var protfactory : TProtocolFactory = null;
+            switch( args.protocol)
+            {
+            case binary:
+                trace("- binary protocol");
+                protfactory = new TBinaryProtocolFactory();
+            case json:
+                trace("- json protocol");
+                protfactory = new TJSONProtocolFactory();
+            case compact:
+                trace("- compact protocol");
+                protfactory = new TCompactProtocolFactory();
+            }
+
+
+            // Processor
+            var handler = new TestServerHandler();
+            var processor = new ThriftTestProcessor(handler);
+
+            // Simple Server
+            var server : TServer = null;
+            switch( args.servertype)
+            {
+            case simple:
+                var simpleServer = new TSimpleServer( processor, transport, transfactory, protfactory);
+                #if phpwebserver
+                simpleServer.runOnce = true;
+                #end
+                server = simpleServer;
+
+            default:
+                throw "Unhandled server type";
+            }
+
+
+            /*
+            // Server event handler
+            if( args.serverEvents) {
+                var events = new TestServerEventHandler();
+                server.setEventHandler(serverEvents);
+                handler.server = serverEngine;
+            }
+            */
+
+            // Run it
+            server.Serve();
+            trace("done.");
+
+        }
+        catch (x : TException)
+        {
+            trace('$x ${x.errorID} ${x.errorMsg}');
+        }
+        catch (x : Dynamic)
+        {
+            trace('$x');
+        }
+    }
+}
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerEventHandler.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerEventHandler.hx
new file mode 100644
index 00000000..d17567c2
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerEventHandler.hx
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+
+import thrift.test.*;  // generated code
+
+
+class TestServerEventHandler : 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++;
+    }
+}
+
+    
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerHandler.hx b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerHandler.hx
new file mode 100644
index 00000000..9fba1360
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/haxe/src/TestServerHandler.hx
@@ -0,0 +1,500 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package;
+
+import org.apache.thrift.*;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.*;
+import org.apache.thrift.server.*;
+import org.apache.thrift.meta_data.*;
+import org.apache.thrift.helper.*;
+
+import haxe.Int32;
+import haxe.Int64;
+import haxe.io.Bytes;
+import haxe.ds.IntMap;
+import haxe.ds.StringMap;
+import haxe.ds.ObjectMap;
+
+import thrift.test.*;  // generated code
+
+
+class TestServerHandler implements ThriftTest {
+
+    public var server:TServer;
+
+    public function new() {
+    }
+
+    /**
+    * Prints "testVoid()" and returns nothing.
+    */
+    public function testVoid():Void
+    {
+        trace("testVoid()");
+    }
+
+    /**
+    * Prints 'testBool("%s")' where '%s' with thing as 'true' or 'false'
+    * @param bool  thing - the bool data to print
+    * @return bool  - returns the bool 'thing'
+    *
+    * @param thing
+    */
+    public function testBool(thing : Bool) : Bool
+    {
+        trace('testBool($thing)');
+        return thing;
+    }
+
+    /**
+    * Prints 'testString("%s")' with thing as '%s'
+    * @param string thing - the string to print
+    * @return string - returns the string 'thing'
+    *
+    * @param thing
+    */
+    public function testString(thing:String):String
+    {
+        trace("teststring(\"" + thing + "\")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testByte("%d")' with thing as '%d'
+    * @param byte thing - the byte to print
+    * @return byte - returns the byte 'thing'
+    *
+    * @param thing
+    */
+    public function testByte(thing:haxe.Int32):haxe.Int32
+    {
+        trace("testByte(" + thing + ")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testI32("%d")' with thing as '%d'
+    * @param i32 thing - the i32 to print
+    * @return i32 - returns the i32 'thing'
+    *
+    * @param thing
+    */
+    public function testI32(thing:haxe.Int32):haxe.Int32
+    {
+        trace("testI32(" + thing + ")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testI64("%d")' with thing as '%d'
+    * @param i64 thing - the i64 to print
+    * @return i64 - returns the i64 'thing'
+    *
+    * @param thing
+    */
+    public function testI64(thing:haxe.Int64):haxe.Int64
+    {
+        trace("testI64(" + thing + ")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testDouble("%f")' with thing as '%f'
+    * @param double thing - the double to print
+    * @return double - returns the double 'thing'
+    *
+    * @param thing
+    */
+    public function testDouble(thing:Float):Float
+    {
+        trace("testDouble(" + thing + ")");
+        return thing;
+    }
+
+    /**
+     * Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data
+     * @param binary  thing - the binary data to print
+     * @return binary  - returns the binary 'thing'
+     *
+     * @param thing
+     */
+    public function testBinary(thing : haxe.io.Bytes) : haxe.io.Bytes
+    {
+        var hex = "";
+        for ( i in 0 ... thing.length) {
+            hex += StringTools.hex( thing.get(i), 2);
+        }
+        trace('testBinary($hex)');
+        return thing;
+    }
+
+    /**
+    * Prints 'testStruct("{%s}")' where thing has been formatted
+    *  into a string of comma separated values
+    * @param Xtruct thing - the Xtruct to print
+    * @return Xtruct - returns the Xtruct 'thing'
+    *
+    * @param thing
+    */
+    public function testStruct(thing:Xtruct):Xtruct
+    {
+        trace("testStruct({" +
+                          "\"" + thing.string_thing + "\", " +
+                          thing.byte_thing + ", " +
+                          thing.i32_thing + ", " +
+                          Int64.toStr(thing.i64_thing) + "})");
+        return thing;
+    }
+
+    /**
+    * Prints 'testNest("{%s}")' where thing has been formatted
+    *  into a string of the nested struct
+    * @param Xtruct2 thing - the Xtruct2 to print
+    * @return Xtruct2 - returns the Xtruct2 'thing'
+    *
+    * @param thing
+    */
+    public function testNest(nest:Xtruct2):Xtruct2
+    {
+        var thing:Xtruct = nest.struct_thing;
+        trace("testNest({" +
+                          nest.byte_thing + ", {" +
+                          "\"" + thing.string_thing + "\", " +
+                          thing.byte_thing + ", " +
+                          thing.i32_thing + ", " +
+                          Int64.toStr(thing.i64_thing) + "}, " +
+                          nest.i32_thing + "})");
+        return nest;
+    }
+
+    /**
+    * 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'
+    *
+    * @param thing
+    */
+    public function testMap(thing:IntMap):IntMap
+    {
+        trace("testMap({");
+        var first:Bool = true;
+        for (key in thing.keys()) {
+            if (first) {
+                first = false;
+            } else {
+                trace(", ");
+            };
+            trace(key + " => " + thing.get(key));
+        };
+        trace("})");
+        return thing;
+    }
+
+    /**
+    * 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'
+    *
+    * @param thing
+    */
+    public function testStringMap(thing:StringMap):StringMap
+    {
+        trace("testStringMap({");
+        var first:Bool = true;
+        for (key in thing.keys()) {
+            if (first) {
+                first = false;
+            } else {
+                trace(", ");
+            };
+            trace(key + " => " + thing.get(key));
+        };
+        trace("})");
+        return thing;
+    }
+
+    /**
+    * 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'
+    *
+    * @param thing
+    */
+    public function testSet(thing:IntSet):IntSet
+    {
+        trace("testSet({");
+        var first:Bool = true;
+        for (elem in thing) {
+            if (first) {
+                first = false;
+            } else {
+                trace(", ");
+            };
+            trace(elem);
+        };
+        trace("})");
+        return thing;
+    }
+
+    /**
+    * 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'
+    *
+    * @param thing
+    */
+    public function testList(thing:List):List
+    {
+        trace("testList({");
+        var first:Bool = true;
+        for (elem in thing) {
+            if (first) {
+                first = false;
+            } else {
+                trace(", ");
+            };
+            trace(elem);
+        };
+        trace("})");
+        return thing;
+    }
+
+    /**
+    * Prints 'testEnum("%d")' where thing has been formatted into it's numeric value
+    * @param Numberz thing - the Numberz to print
+    * @return Numberz - returns the Numberz 'thing'
+    *
+    * @param thing
+    */
+    public function testEnum(thing:Int):Int
+    {
+        trace("testEnum(" + thing + ")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testTypedef("%d")' with thing as '%d'
+    * @param UserId thing - the UserId to print
+    * @return UserId - returns the UserId 'thing'
+    *
+    * @param thing
+    */
+    public function testTypedef(thing:haxe.Int64):haxe.Int64
+    {
+        trace("testTypedef(" + thing + ")");
+        return thing;
+    }
+
+    /**
+    * Prints 'testMapMap("%d")' with hello as '%d'
+    * @param i32 hello - the i32 to print
+    * @return map> - returns a dictionary with these values:
+    *   {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, },
+    *     4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
+    *
+    * @param hello
+    */
+    public function testMapMap(hello:haxe.Int32):IntMap>
+    {
+        trace("testMapMap(" + hello + ")");
+        var mapmap = new IntMap>();
+        var pos = new IntMap();
+        var neg = new IntMap();
+        for (i in 1 ... 5) {
+            pos.set(i, i);
+            neg.set(-i, -i);
+        };
+        mapmap.set(4, pos);
+        mapmap.set(-4, neg);
+        return mapmap;
+    }
+
+    /**
+    * So you think you've got this all worked, out eh?
+    *
+    * Creates a the returned map with these values and prints it out:
+    *   { 1 => { 2 => argument,
+    *            3 => argument,
+    *          },
+    *     2 => { 6 => , },
+    *   }
+    * @return map> - a map with the above values
+    *
+    * @param argument
+    */
+    public function testInsanity(argument : Insanity) : Int64Map< IntMap< Insanity>>
+    {
+        trace("testInsanity()");
+
+        var hello = new Xtruct();
+        hello.string_thing = "Hello2";
+        hello.byte_thing = 2;
+        hello.i32_thing = 2;
+        hello.i64_thing = Int64.make(0, 2);
+
+        var goodbye = new Xtruct();
+        goodbye.string_thing = "Goodbye4";
+        goodbye.byte_thing = 4;
+        goodbye.i32_thing = 4;
+        goodbye.i64_thing = Int64.make(0, 4);
+
+        var crazy = new Insanity();
+        crazy.userMap = new IntMap< haxe.Int64>();
+        crazy.userMap.set(Numberz.EIGHT, Int64.make(0,8));
+        crazy.xtructs = new List();
+        crazy.xtructs.add(goodbye);
+
+        var looney = new Insanity();
+        crazy.userMap.set(Numberz.FIVE, Int64.make(0,5));
+        crazy.xtructs.add(hello);
+
+        var first_map = new IntMap< Insanity>();
+        first_map.set(Numberz.TWO, crazy);
+        first_map.set(Numberz.THREE, crazy);
+
+        var second_map = new IntMap< Insanity>();
+        second_map.set(Numberz.SIX, looney);
+
+        var insane = new Int64Map< IntMap< Insanity>>();
+        insane.set( Int64.make(0,1), first_map);
+        insane.set( Int64.make(0,2), second_map);
+
+        return insane;
+    }
+
+    /**
+    * Prints 'testMulti()'
+    * @param byte arg0 -
+    * @param i32 arg1 -
+    * @param i64 arg2 -
+    * @param map arg3 -
+    * @param Numberz arg4 -
+    * @param UserId arg5 -
+    * @return Xtruct - returns an Xtruct
+    *    with string_thing = "Hello2, byte_thing = arg0, i32_thing = arg1
+    *    and i64_thing = arg2
+    *
+    * @param arg0
+    * @param arg1
+    * @param arg2
+    * @param arg3
+    * @param arg4
+    * @param arg5
+    */
+    public function testMulti(arg0:haxe.Int32, arg1:haxe.Int32, arg2:haxe.Int64,
+        arg3:IntMap, arg4:Int, arg5:haxe.Int64):Xtruct
+    {
+        trace("testMulti()");
+        var 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
+    *
+    * @param arg
+    */
+    public function testException(arg:String):Void
+    {
+        trace("testException(" + arg + ")");
+        if (arg == "Xception") {
+            var x = new Xception();
+            x.errorCode = 1001;
+            x.message = arg;
+            throw x;
+        };
+        if (arg == "TException") {
+            throw new TException();
+        };
+        return;
+    }
+
+    /**
+    * Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s'
+    * @param string arg - a string indication what type of exception to throw
+    * if arg0 == "Xception"
+    * throw Xception with errorCode = 1001 and message = "This is an Xception"
+    * else if arg0 == "Xception2"
+    * throw Xception2 with errorCode = 2002 and message = "This is an Xception2"
+    * else do not throw anything
+    * @return Xtruct - an Xtruct with string_thing = arg1
+    *
+    * @param arg0
+    * @param arg1
+    */
+    public function testMultiException(arg0:String, arg1:String):Xtruct
+    {
+        trace("testMultiException(" + arg0 + ", " + arg1 + ")");
+        if (arg0 == "Xception") {
+            var x = new Xception();
+            x.errorCode = 1001;
+            x.message = "This is an Xception";
+            throw x;
+        } else if (arg0 == "Xception2") {
+            var x = new Xception2();
+            x.errorCode = 2002;
+            x.struct_thing = new Xtruct();
+            x.struct_thing.string_thing = "This is an Xception2";
+            throw x;
+        };
+        var result = new Xtruct();
+        result.string_thing = arg1;
+        return result;
+    }
+
+    /**
+    * Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'
+    * sleep 'secondsToSleep'
+    * Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d'
+    * @param i32 secondsToSleep - the number of seconds to sleep
+    *
+    * @param secondsToSleep
+    */
+    public function testOneway(secondsToSleep:haxe.Int32):Void
+    {
+        trace("testOneway(" + secondsToSleep + "), sleeping...");
+        Sys.sleep(secondsToSleep);
+        trace("testOneway finished");
+    }
+
+    public function testStop():Void
+    {
+        if (server != null) {
+            server.Stop();
+        };
+    }
+}
diff --git a/vendor/src/github.com/apache/thrift/test/hs/CMakeLists.txt b/vendor/src/github.com/apache/thrift/test/hs/CMakeLists.txt
new file mode 100644
index 00000000..eaca3fa0
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/CMakeLists.txt
@@ -0,0 +1,114 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set(hs_test_gen
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ConstantsDemo_Consts.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ConstantsDemo_Types.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/DebugProtoTest_Consts.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/DebugProtoTest_Types.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/EmptyService_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/EmptyService.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/EmptyService_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Include_Consts.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Include_Types.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Inherited_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Inherited.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Inherited_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ReverseOrderService_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ReverseOrderService.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ReverseOrderService_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/SecondService_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/SecondService.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/SecondService_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ServiceForExceptionWithAMap_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ServiceForExceptionWithAMap.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ServiceForExceptionWithAMap_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Srv_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Srv.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Srv_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ThriftTest_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ThriftTest_Consts.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ThriftTest.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ThriftTest_Iface.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/ThriftTest_Types.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Yowza_Client.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Yowza.hs
+    ${CMAKE_CURRENT_BINARY_DIR}/gen-hs/Yowza_Iface.hs
+)
+
+set(hs_crosstest_apps
+    ${CMAKE_CURRENT_BINARY_DIR}/TestServer
+    ${CMAKE_CURRENT_BINARY_DIR}/TestClient
+)
+set(hs_crosstest_args
+    -igen-hs
+    -odir=${CMAKE_CURRENT_BINARY_DIR}
+    -hidir=${CMAKE_CURRENT_BINARY_DIR}
+)
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+  set(hs_optimize -O0)
+else()
+  set(hs_optimize -O1)
+endif()
+
+add_custom_command(
+    OUTPUT ${hs_crosstest_apps}
+    COMMAND ${GHC} ${hs_optimize} ${hs_crosstest_args} ${CMAKE_CURRENT_SOURCE_DIR}/TestServer.hs -o TestServer
+    COMMAND ${GHC} ${hs_optimize} ${hs_crosstest_args} ${CMAKE_CURRENT_SOURCE_DIR}/TestClient.hs -o TestClient
+    DEPENDS ${hs_test_gen} haskell_library TestServer.hs TestClient.hs
+)
+add_custom_target(haskell_crosstest ALL
+    COMMENT "Building Haskell cross test executables"
+    DEPENDS ${hs_crosstest_apps}
+)
+
+set(hs_test_sources
+    ConstantsDemo_Main.hs
+    DebugProtoTest_Main.hs
+    Include_Main.hs
+    ThriftTest_Main.hs
+)
+set(hs_test_args
+    -Wall
+    -XScopedTypeVariables
+    -i${PROJECT_SOURCE_DIR}/lib/hs/src
+    -i${CMAKE_CURRENT_BINARY_DIR}/gen-hs
+)
+add_custom_target(haskell_tests ALL DEPENDS ${hs_test_gen})
+foreach(SRC ${hs_test_sources})
+    get_filename_component(BASE ${SRC} NAME_WE)
+    add_test(NAME HaskellTests-${BASE}
+        COMMAND ${RUN_HASKELL} ${hs_test_args} ${SRC}
+        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+endforeach()
+
+set(hs_test_gen_sources
+    ${PROJECT_SOURCE_DIR}/test/ConstantsDemo.thrift
+    ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
+    ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+    ${PROJECT_SOURCE_DIR}/test/Include.thrift
+)
+add_custom_command(OUTPUT ${hs_test_gen}
+    COMMAND ${THRIFT_COMPILER} --gen hs ${PROJECT_SOURCE_DIR}/test/ConstantsDemo.thrift
+    COMMAND ${THRIFT_COMPILER} --gen hs ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
+    COMMAND ${THRIFT_COMPILER} --gen hs ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+    COMMAND ${THRIFT_COMPILER} --gen hs ${PROJECT_SOURCE_DIR}/test/Include.thrift
+    DEPENDS ${hs_test_gen_sources}
+)
diff --git a/vendor/src/github.com/apache/thrift/test/hs/ConstantsDemo_Main.hs b/vendor/src/github.com/apache/thrift/test/hs/ConstantsDemo_Main.hs
new file mode 100644
index 00000000..28de4f7e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/ConstantsDemo_Main.hs
@@ -0,0 +1,68 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+module Main where
+
+
+import qualified Control.Exception
+import qualified Network
+
+import Thrift.Protocol.Binary
+import Thrift.Server
+import Thrift.Transport.Handle
+
+import qualified ThriftTestUtils
+
+import qualified Yowza
+import qualified Yowza_Client as Client
+import qualified Yowza_Iface as Iface
+
+
+data YowzaHandler = YowzaHandler
+instance Iface.Yowza_Iface YowzaHandler where
+    blingity _ = do
+        ThriftTestUtils.serverLog "SERVER: Got blingity"
+        return ()
+
+    blangity _ = do
+        ThriftTestUtils.serverLog "SERVER: Got blangity"
+        return $ 31
+
+
+client :: (String, Network.PortID) -> IO ()
+client addr = do
+    to <- hOpen addr
+    let ps = (BinaryProtocol to, BinaryProtocol to)
+
+    Client.blingity ps
+
+    rv <- Client.blangity ps
+    ThriftTestUtils.clientLog $ show rv
+
+    tClose to
+
+server :: Network.PortNumber -> IO ()
+server port = do 
+    ThriftTestUtils.serverLog "Ready..."
+    (runBasicServer YowzaHandler Yowza.process port)
+    `Control.Exception.catch`
+    (\(TransportExn s _) -> error $ "FAILURE: " ++ show s)
+
+main :: IO ()
+main = ThriftTestUtils.runTest server client
diff --git a/vendor/src/github.com/apache/thrift/test/hs/DebugProtoTest_Main.hs b/vendor/src/github.com/apache/thrift/test/hs/DebugProtoTest_Main.hs
new file mode 100644
index 00000000..fb28963f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/DebugProtoTest_Main.hs
@@ -0,0 +1,168 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+
+import qualified Control.Exception
+import qualified Data.ByteString.Lazy as DBL
+import qualified Data.HashMap.Strict as Map
+import qualified Data.HashSet as Set
+import qualified Data.Vector as Vector
+import qualified Network
+
+import Thrift.Protocol.Binary
+import Thrift.Server
+import Thrift.Transport.Handle
+
+import qualified ThriftTestUtils
+
+import qualified DebugProtoTest_Types as Types
+import qualified Inherited
+import qualified Inherited_Client as IClient
+import qualified Inherited_Iface as IIface
+import qualified Srv_Client as SClient
+import qualified Srv_Iface as SIface
+
+-- we don't actually need this import, but force it to check the code generator exports proper Haskell syntax
+import qualified Srv()
+
+
+data InheritedHandler = InheritedHandler
+instance SIface.Srv_Iface InheritedHandler where
+    janky _ arg = do
+        ThriftTestUtils.serverLog $ "Got janky method call: " ++ show arg
+        return $ 31
+
+    voidMethod _ = do
+        ThriftTestUtils.serverLog "Got voidMethod method call"
+        return ()
+
+    primitiveMethod _ = do
+        ThriftTestUtils.serverLog "Got primitiveMethod call"
+        return $ 42
+
+    structMethod _ = do
+        ThriftTestUtils.serverLog "Got structMethod call"
+        return $ Types.CompactProtoTestStruct {
+            Types.compactProtoTestStruct_a_byte = 0x01,
+            Types.compactProtoTestStruct_a_i16 = 0x02,
+            Types.compactProtoTestStruct_a_i32 = 0x03,
+            Types.compactProtoTestStruct_a_i64 = 0x04,
+            Types.compactProtoTestStruct_a_double = 0.1,
+            Types.compactProtoTestStruct_a_string = "abcdef",
+            Types.compactProtoTestStruct_a_binary = DBL.empty,
+            Types.compactProtoTestStruct_true_field = True,
+            Types.compactProtoTestStruct_false_field = False,
+            Types.compactProtoTestStruct_empty_struct_field = Types.Empty,
+            
+            Types.compactProtoTestStruct_byte_list = Vector.empty,
+            Types.compactProtoTestStruct_i16_list = Vector.empty,
+            Types.compactProtoTestStruct_i32_list = Vector.empty,
+            Types.compactProtoTestStruct_i64_list = Vector.empty,
+            Types.compactProtoTestStruct_double_list = Vector.empty,
+            Types.compactProtoTestStruct_string_list = Vector.empty,
+            Types.compactProtoTestStruct_binary_list = Vector.empty,
+            Types.compactProtoTestStruct_boolean_list = Vector.empty,
+            Types.compactProtoTestStruct_struct_list = Vector.empty,
+
+            Types.compactProtoTestStruct_byte_set = Set.empty,
+            Types.compactProtoTestStruct_i16_set = Set.empty,
+            Types.compactProtoTestStruct_i32_set = Set.empty,
+            Types.compactProtoTestStruct_i64_set = Set.empty,
+            Types.compactProtoTestStruct_double_set = Set.empty,
+            Types.compactProtoTestStruct_string_set = Set.empty,
+            Types.compactProtoTestStruct_binary_set = Set.empty,
+            Types.compactProtoTestStruct_boolean_set = Set.empty,
+            Types.compactProtoTestStruct_struct_set = Set.empty,
+
+            Types.compactProtoTestStruct_byte_byte_map = Map.empty,
+            Types.compactProtoTestStruct_i16_byte_map = Map.empty,
+            Types.compactProtoTestStruct_i32_byte_map = Map.empty,
+            Types.compactProtoTestStruct_i64_byte_map = Map.empty,
+            Types.compactProtoTestStruct_double_byte_map = Map.empty,
+            Types.compactProtoTestStruct_string_byte_map = Map.empty,
+            Types.compactProtoTestStruct_binary_byte_map = Map.empty,
+            Types.compactProtoTestStruct_boolean_byte_map = Map.empty,
+
+            Types.compactProtoTestStruct_byte_i16_map = Map.empty,
+            Types.compactProtoTestStruct_byte_i32_map = Map.empty,
+            Types.compactProtoTestStruct_byte_i64_map = Map.empty,
+            Types.compactProtoTestStruct_byte_double_map = Map.empty,
+            Types.compactProtoTestStruct_byte_string_map = Map.empty,
+            Types.compactProtoTestStruct_byte_binary_map = Map.empty,
+            Types.compactProtoTestStruct_byte_boolean_map = Map.empty,
+
+            Types.compactProtoTestStruct_list_byte_map = Map.empty,
+            Types.compactProtoTestStruct_set_byte_map = Map.empty,
+            Types.compactProtoTestStruct_map_byte_map = Map.empty,
+
+            Types.compactProtoTestStruct_byte_map_map = Map.empty,
+            Types.compactProtoTestStruct_byte_set_map = Map.empty,
+            Types.compactProtoTestStruct_byte_list_map = Map.empty }
+
+    methodWithDefaultArgs _ arg = do
+        ThriftTestUtils.serverLog $ "Got methodWithDefaultArgs: " ++ show arg
+        return ()
+
+    onewayMethod _ = do
+        ThriftTestUtils.serverLog "Got onewayMethod"
+
+instance IIface.Inherited_Iface InheritedHandler where
+    identity _ arg = do
+        ThriftTestUtils.serverLog $ "Got identity method: " ++ show arg
+        return arg
+
+client :: (String, Network.PortID) -> IO ()
+client addr = do
+    to <- hOpen addr
+    let p =  BinaryProtocol to
+    let ps = (p,p)
+
+    v1 <- SClient.janky ps 42
+    ThriftTestUtils.clientLog $ show v1
+
+    SClient.voidMethod ps
+
+    v2 <- SClient.primitiveMethod ps
+    ThriftTestUtils.clientLog $ show v2
+
+    v3 <- SClient.structMethod ps
+    ThriftTestUtils.clientLog $ show v3
+
+    SClient.methodWithDefaultArgs ps 42
+
+    SClient.onewayMethod ps
+
+    v4 <- IClient.identity ps 42
+    ThriftTestUtils.clientLog $ show v4
+
+    return ()
+
+server :: Network.PortNumber -> IO ()
+server port = do 
+    ThriftTestUtils.serverLog "Ready..."
+    (runBasicServer InheritedHandler Inherited.process port)
+    `Control.Exception.catch`
+    (\(TransportExn s _) -> error $ "FAILURE: " ++ show s)
+
+main :: IO ()
+main = ThriftTestUtils.runTest server client
diff --git a/vendor/src/github.com/apache/thrift/test/hs/Include_Main.hs b/vendor/src/github.com/apache/thrift/test/hs/Include_Main.hs
new file mode 100644
index 00000000..d3977a15
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/Include_Main.hs
@@ -0,0 +1,7 @@
+module Main where
+
+import Include_Types
+import ThriftTest_Types
+
+main :: IO ()
+main = putStrLn ("Includes work: " ++ (show (IncludeTest $ Bools True False)))
diff --git a/vendor/src/github.com/apache/thrift/test/hs/Makefile.am b/vendor/src/github.com/apache/thrift/test/hs/Makefile.am
new file mode 100644
index 00000000..3f353966
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/Makefile.am
@@ -0,0 +1,43 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: $(THRIFT) ../ConstantsDemo.thrift ../DebugProtoTest.thrift ../ThriftTest.thrift ../Include.thrift
+	$(THRIFT) --gen hs ../ConstantsDemo.thrift
+	$(THRIFT) --gen hs ../DebugProtoTest.thrift
+	$(THRIFT) --gen hs ../ThriftTest.thrift
+	$(THRIFT) --gen hs ../Include.thrift
+
+check: stubs
+	sh run-test.sh ConstantsDemo
+	sh run-test.sh DebugProtoTest
+	sh run-test.sh ThriftTest
+	sh run-test.sh Include
+
+clean-local:
+	$(RM) -r gen-hs
+	$(RM) *.hi
+	$(RM) *.o
+
+all-local: stubs
+	ghc -igen-hs TestServer.hs
+	ghc -igen-hs TestClient.hs
+
+precross: all-local
diff --git a/vendor/src/github.com/apache/thrift/test/hs/TestClient.hs b/vendor/src/github.com/apache/thrift/test/hs/TestClient.hs
new file mode 100644
index 00000000..d1ebb3cd
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/TestClient.hs
@@ -0,0 +1,302 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE OverloadedStrings, RecordWildCards, ScopedTypeVariables #-}
+module Main where
+
+import Control.Exception
+import Control.Monad
+import Data.Functor
+import Data.List.Split
+import Data.String
+import Network
+import Network.URI
+import System.Environment
+import System.Exit
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.HashMap.Strict as Map
+import qualified Data.HashSet as Set
+import qualified Data.Vector as Vector
+import qualified System.IO as IO
+
+import ThriftTest_Iface
+import ThriftTest_Types
+import qualified ThriftTest_Client as Client
+
+import Thrift.Transport
+import Thrift.Transport.Framed
+import Thrift.Transport.Handle
+import Thrift.Transport.HttpClient
+import Thrift.Protocol
+import Thrift.Protocol.Binary
+import Thrift.Protocol.Compact
+import Thrift.Protocol.JSON
+
+data Options = Options
+  { host         :: String
+  , port         :: Int
+  , domainSocket :: String
+  , transport    :: String
+  , protocol     :: ProtocolType
+  -- TODO: Haskell lib does not have SSL support
+  , ssl          :: Bool
+  , testLoops    :: Int
+  }
+  deriving (Show, Eq)
+
+data TransportType = Buffered IO.Handle
+                   | Framed (FramedTransport IO.Handle)
+                   | Http HttpClient
+                   | NoTransport String
+
+getTransport :: String -> String -> Int -> (IO TransportType)
+getTransport "buffered" host port = do
+  h <- hOpen (host, PortNumber $ fromIntegral port)
+  IO.hSetBuffering h $ IO.BlockBuffering Nothing
+  return $ Buffered h
+getTransport "framed" host port = do
+  h <- hOpen (host, PortNumber $ fromIntegral port)
+  t <- openFramedTransport h
+  return $ Framed t
+getTransport "http" host port = let uriStr = "http://" ++ host ++ ":" ++ show port in
+                                case parseURI uriStr of
+                                  Nothing -> do return (NoTransport $ "Failed to parse URI: " ++ uriStr)
+                                  Just(uri) -> do
+                                    t <- openHttpClient uri
+                                    return $ Http t
+getTransport t host port = do return (NoTransport $ "Unsupported transport: " ++ t)
+
+data ProtocolType = Binary
+                  | Compact
+                  | JSON
+                  deriving (Show, Eq)
+
+getProtocol :: String -> ProtocolType
+getProtocol "binary"  = Binary
+getProtocol "compact" = Compact
+getProtocol "json"    = JSON
+getProtocol p = error $ "Unsupported Protocol: " ++ p
+
+defaultOptions :: Options
+defaultOptions = Options
+  { port         = 9090
+  , domainSocket = ""
+  , host         = "localhost"
+  , transport    = "buffered"
+  , protocol     = Binary
+  , ssl          = False
+  , testLoops    = 1
+  }
+
+runClient :: (Protocol p, Transport t) => p t -> IO ()
+runClient p = do
+  let prot = (p,p)
+  putStrLn "Starting Tests"
+
+  -- VOID Test
+  putStrLn "testVoid"
+  Client.testVoid prot
+
+  -- String Test
+  putStrLn "testString"
+  s <- Client.testString prot "Test"
+  when (s /= "Test") exitFailure
+
+  -- Bool Test
+  putStrLn "testBool"
+  bool <- Client.testBool prot True
+  when (not bool) exitFailure
+  putStrLn "testBool"
+  bool <- Client.testBool prot False
+  when (bool) exitFailure
+
+  -- Byte Test
+  putStrLn "testByte"
+  byte <- Client.testByte prot 1
+  when (byte /= 1) exitFailure
+
+  -- I32 Test
+  putStrLn "testI32"
+  i32 <- Client.testI32 prot (-1)
+  when (i32 /= -1) exitFailure
+
+  -- I64 Test
+  putStrLn "testI64"
+  i64 <- Client.testI64 prot (-34359738368)
+  when (i64 /= -34359738368) exitFailure
+
+  -- Double Test
+  putStrLn "testDouble"
+  dub <- Client.testDouble prot (-5.2098523)
+  when (abs (dub + 5.2098523) > 0.001) exitFailure
+
+  -- Binary Test
+  putStrLn "testBinary"
+  bin <- Client.testBinary prot (LBS.pack . reverse $ [-128..127])
+  when ((reverse [-128..127]) /= LBS.unpack bin) exitFailure
+  
+  -- Struct Test
+  let structIn = Xtruct{ xtruct_string_thing = "Zero"
+                       , xtruct_byte_thing   = 1
+                       , xtruct_i32_thing    = -3
+                       , xtruct_i64_thing    = -5
+                       }
+  putStrLn "testStruct"
+  structOut <- Client.testStruct prot structIn
+  when (structIn /= structOut) exitFailure
+
+  -- Nested Struct Test
+  let nestIn = Xtruct2{ xtruct2_byte_thing   = 1
+                      , xtruct2_struct_thing = structIn
+                      , xtruct2_i32_thing    = 5
+                      }
+  putStrLn "testNest"
+  nestOut <- Client.testNest prot nestIn
+  when (nestIn /= nestOut) exitFailure
+
+  -- Map Test
+  let mapIn = Map.fromList $ map (\i -> (i, i-10)) [1..5]
+  putStrLn "testMap"
+  mapOut <- Client.testMap prot mapIn
+  when (mapIn /= mapOut) exitFailure
+
+  -- Set Test
+  let setIn = Set.fromList [-2..3]
+  putStrLn "testSet"
+  setOut <- Client.testSet prot setIn
+  when (setIn /= setOut) exitFailure
+
+  -- List Test
+  let listIn = Vector.fromList [-2..3]
+  putStrLn "testList"
+  listOut <- Client.testList prot listIn
+  when (listIn /= listOut) exitFailure
+
+  -- Enum Test
+  putStrLn "testEnum"
+  numz1 <- Client.testEnum prot ONE
+  when (numz1 /= ONE) exitFailure
+
+  putStrLn "testEnum"
+  numz2 <- Client.testEnum prot TWO
+  when (numz2 /= TWO) exitFailure
+
+  putStrLn "testEnum"
+  numz5 <- Client.testEnum prot FIVE
+  when (numz5 /= FIVE) exitFailure
+
+  -- Typedef Test
+  putStrLn "testTypedef"
+  uid <- Client.testTypedef prot 309858235082523
+  when (uid /= 309858235082523) exitFailure
+
+  -- Nested Map Test
+  putStrLn "testMapMap"
+  _ <- Client.testMapMap prot 1
+
+  -- Exception Test
+  putStrLn "testException"
+  exn1 <- try $ Client.testException prot "Xception"
+  case exn1 of
+    Left (Xception _ _) -> return ()
+    _ -> putStrLn (show exn1) >> exitFailure
+
+  putStrLn "testException"
+  exn2 <- try $ Client.testException prot "TException"
+  case exn2 of
+    Left (_ :: SomeException) -> return ()
+    Right _ -> exitFailure
+
+  putStrLn "testException"
+  exn3 <- try $ Client.testException prot "success"
+  case exn3 of
+    Left (_ :: SomeException) -> exitFailure
+    Right _ -> return ()
+
+  -- Multi Exception Test
+  putStrLn "testMultiException"
+  multi1 <- try $ Client.testMultiException prot "Xception" "test 1"
+  case multi1 of
+    Left (Xception _ _) -> return ()
+    _ -> exitFailure
+
+  putStrLn "testMultiException"
+  multi2 <- try $ Client.testMultiException prot "Xception2" "test 2"
+  case multi2 of
+    Left (Xception2 _ _) -> return ()
+    _ -> exitFailure
+
+  putStrLn "testMultiException"
+  multi3 <- try $ Client.testMultiException prot "success" "test 3"
+  case multi3 of
+    Left (_ :: SomeException) -> exitFailure
+    Right _ -> return ()
+
+
+main :: IO ()
+main = do
+  options <- flip parseFlags defaultOptions <$> getArgs
+  case options of
+    Nothing -> showHelp
+    Just Options{..} -> do
+      trans <- Main.getTransport transport host port
+      case trans of
+        Buffered t -> runTest testLoops protocol t
+        Framed t   -> runTest testLoops protocol t
+        Http t     -> runTest testLoops protocol t
+        NoTransport err -> putStrLn err
+  where
+    makeClient p t = case p of
+                       Binary  -> runClient $ BinaryProtocol t
+                       Compact -> runClient $ CompactProtocol t
+                       JSON    -> runClient $ JSONProtocol t
+    runTest loops p t = do
+      let client = makeClient p t
+      replicateM_ loops client
+      putStrLn "COMPLETED SUCCESSFULLY"
+
+parseFlags :: [String] -> Options -> Maybe Options
+parseFlags (flag : flags) opts = do
+  let pieces = splitOn "=" flag
+  case pieces of
+    "--port" : arg : _ -> parseFlags flags opts{ port = read arg }
+    "--domain-socket" : arg : _ -> parseFlags flags opts{ domainSocket = read arg }
+    "--host" : arg : _ -> parseFlags flags opts{ host = arg }
+    "--transport" : arg : _ -> parseFlags flags opts{ transport = arg }
+    "--protocol" : arg : _ -> parseFlags flags opts{ protocol = getProtocol arg }
+    "-n" : arg : _ -> parseFlags flags opts{ testLoops = read arg }
+    "--h" : _ -> Nothing
+    "--help" : _ -> Nothing
+    "--ssl" : _ -> parseFlags flags opts{ ssl = True }
+    "--processor-events" : _ -> parseFlags flags opts
+    _ -> Nothing
+parseFlags [] opts = Just opts
+
+showHelp :: IO ()
+showHelp = putStrLn
+  "Allowed options:\n\
+  \  -h [ --help ]               produce help message\n\
+  \  --host arg (=localhost)     Host to connect\n\
+  \  --port arg (=9090)          Port number to connect\n\
+  \  --domain-socket arg         Domain Socket (e.g. /tmp/ThriftTest.thrift),\n\
+  \                              instead of host and port\n\
+  \  --transport arg (=buffered) Transport: buffered, framed, http\n\
+  \  --protocol arg (=binary)    Protocol: binary, compact, json\n\
+  \  --ssl                       Encrypted Transport using SSL\n\
+  \  -n [ --testloops ] arg (=1) Number of Tests"
diff --git a/vendor/src/github.com/apache/thrift/test/hs/TestServer.hs b/vendor/src/github.com/apache/thrift/test/hs/TestServer.hs
new file mode 100644
index 00000000..4a88649b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/TestServer.hs
@@ -0,0 +1,303 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE OverloadedStrings,RecordWildCards #-}
+module Main where
+
+import Control.Exception
+import Control.Monad
+import Data.Functor
+import Data.HashMap.Strict (HashMap)
+import Data.List
+import Data.List.Split
+import Data.String
+import Network
+import System.Environment
+import System.Exit
+import System.IO
+import Control.Concurrent (threadDelay)
+import qualified System.IO as IO
+import qualified Data.HashMap.Strict as Map
+import qualified Data.HashSet as Set
+import qualified Data.Text.Lazy as Text
+import qualified Data.Vector as Vector
+
+import ThriftTest
+import ThriftTest_Iface
+import ThriftTest_Types
+
+import Thrift
+import Thrift.Server
+import Thrift.Transport.Framed
+import Thrift.Transport.Handle
+import Thrift.Protocol.Binary
+import Thrift.Protocol.Compact
+import Thrift.Protocol.JSON
+
+data Options = Options
+  { port         :: Int
+  , domainSocket :: String
+  , serverType   :: ServerType
+  , transport    :: String
+  , protocol     :: ProtocolType
+  , ssl          :: Bool
+  , workers      :: Int
+  }
+
+data ServerType = Simple
+                | ThreadPool
+                | Threaded
+                | NonBlocking
+                deriving (Show, Eq)
+
+instance IsString ServerType where
+  fromString "simple"      = Simple
+  fromString "thread-pool" = ThreadPool
+  fromString "threaded"    = Threaded
+  fromString "nonblocking" = NonBlocking
+  fromString _ = error "not a valid server type"
+
+data TransportType = Buffered (Socket -> (IO IO.Handle))
+                   | Framed (Socket -> (IO (FramedTransport IO.Handle)))
+                   | NoTransport String
+
+getTransport :: String -> TransportType
+getTransport "buffered" = Buffered $ \s -> do
+  (h, _, _) <- (accept s)
+  IO.hSetBuffering h $ IO.BlockBuffering Nothing
+  return h
+getTransport "framed" = Framed $ \s -> do
+  (h, _, _) <- (accept s)
+  openFramedTransport h
+getTransport t = NoTransport $ "Unsupported transport: " ++ t
+
+data ProtocolType = Binary
+                  | Compact
+                  | JSON
+
+getProtocol :: String -> ProtocolType
+getProtocol "binary"  = Binary
+getProtocol "compact" = Compact
+getProtocol "json"    = JSON
+getProtocol p = error $"Unsupported Protocol: " ++ p
+
+defaultOptions :: Options
+defaultOptions = Options
+  { port         = 9090
+  , domainSocket = ""
+  , serverType   = Threaded
+  , transport    = "buffered"
+  , protocol     = Binary
+  -- TODO: Haskell lib does not have SSL support
+  , ssl          = False
+  , workers      = 4
+  }
+
+stringifyMap :: (Show a, Show b) => Map.HashMap a b -> String
+stringifyMap = Data.List.intercalate ", " . Data.List.map joinKV . Map.toList
+  where joinKV (k, v) = show k ++ " => " ++ show v
+
+stringifySet :: Show a => Set.HashSet a -> String
+stringifySet = Data.List.intercalate ", " . Data.List.map show . Set.toList
+
+stringifyList :: Show a => Vector.Vector a -> String
+stringifyList = Data.List.intercalate ", " . Data.List.map show . Vector.toList
+
+data TestHandler = TestHandler
+instance ThriftTest_Iface TestHandler where
+  testVoid _ = System.IO.putStrLn "testVoid()"
+
+  testString _ s = do
+    System.IO.putStrLn $ "testString(" ++ show s ++ ")"
+    return s
+
+  testBool _ x = do
+    System.IO.putStrLn $ "testBool(" ++ show x ++ ")"
+    return x
+
+  testByte _ x = do
+    System.IO.putStrLn $ "testByte(" ++ show x ++ ")"
+    return x
+
+  testI32 _ x = do
+    System.IO.putStrLn $ "testI32(" ++ show x ++ ")"
+    return x
+
+  testI64 _ x = do
+    System.IO.putStrLn $ "testI64(" ++ show x ++ ")"
+    return x
+
+  testDouble _ x = do
+    System.IO.putStrLn $ "testDouble(" ++ show x ++ ")"
+    return x
+
+  testBinary _ x = do
+    System.IO.putStrLn $ "testBinary(" ++ show x ++ ")"
+    return x
+
+  testStruct _ struct@Xtruct{..} = do
+    System.IO.putStrLn $ "testStruct({" ++ show xtruct_string_thing
+                      ++ ", " ++ show xtruct_byte_thing
+                      ++ ", " ++ show xtruct_i32_thing
+                      ++ ", " ++ show xtruct_i64_thing
+                      ++ "})"
+    return struct
+
+  testNest _ nest@Xtruct2{..} = do
+    let Xtruct{..} = xtruct2_struct_thing
+    System.IO.putStrLn $ "testNest({" ++ show xtruct2_byte_thing
+                   ++ "{, " ++ show xtruct_string_thing
+                   ++  ", " ++ show xtruct_byte_thing
+                   ++  ", " ++ show xtruct_i32_thing
+                   ++  ", " ++ show xtruct_i64_thing
+                   ++ "}, " ++ show xtruct2_i32_thing
+    return nest
+
+  testMap _ m = do
+    System.IO.putStrLn $ "testMap({" ++ stringifyMap m ++ "})"
+    return m
+
+  testStringMap _ m = do
+    System.IO.putStrLn $ "testStringMap(" ++ stringifyMap m ++ "})"
+    return m
+
+  testSet _ x = do
+    System.IO.putStrLn $ "testSet({" ++ stringifySet x ++ "})"
+    return x
+
+  testList _ x = do
+    System.IO.putStrLn $ "testList(" ++ stringifyList x ++ "})"
+    return x
+
+  testEnum _ x = do
+    System.IO.putStrLn $ "testEnum(" ++ show x ++ ")"
+    return x
+
+  testTypedef _ x = do
+    System.IO.putStrLn $ "testTypedef(" ++ show x ++ ")"
+    return x
+
+  testMapMap _ x = do
+    System.IO.putStrLn $ "testMapMap(" ++ show x ++ ")"
+    return $ Map.fromList [ (-4, Map.fromList [ (-4, -4)
+                                              , (-3, -3)
+                                              , (-2, -2)
+                                              , (-1, -1)
+                                              ])
+                          , (4,  Map.fromList [ (1, 1)
+                                              , (2, 2)
+                                              , (3, 3)
+                                              , (4, 4)
+                                              ])
+                          ]
+
+  testInsanity _ x = do
+    System.IO.putStrLn "testInsanity()"
+    return $ Map.fromList [ (1, Map.fromList [ (TWO  , x)
+                                             , (THREE, x)
+                                             ])
+                          , (2, Map.fromList [ (SIX, default_Insanity)
+                                             ])
+                          ]
+
+  testMulti _ byte i32 i64 _ _ _ = do
+    System.IO.putStrLn "testMulti()"
+    return Xtruct{ xtruct_string_thing = Text.pack "Hello2"
+                 , xtruct_byte_thing   = byte
+                 , xtruct_i32_thing    = i32
+                 , xtruct_i64_thing    = i64
+                 }
+
+  testException _ s = do
+    System.IO.putStrLn $ "testException(" ++ show s ++ ")"
+    case s of
+      "Xception"   -> throw $ Xception 1001 s
+      "TException" -> throw ThriftException
+      _ -> return ()
+
+  testMultiException _ s1 s2 = do
+    System.IO.putStrLn $ "testMultiException(" ++ show s1 ++ ", " ++ show s2 ++  ")"
+    case s1 of
+      "Xception"   -> throw $ Xception 1001 "This is an Xception"
+      "Xception2"  -> throw $ Xception2 2002 $ Xtruct "This is an Xception2" 0 0 0
+      "TException" -> throw ThriftException
+      _ -> return default_Xtruct{ xtruct_string_thing = s2 }
+
+  testOneway _ i = do
+    System.IO.putStrLn $ "testOneway(" ++ show i ++ "): Sleeping..."
+    threadDelay $ (fromIntegral i) * 1000000
+    System.IO.putStrLn $ "testOneway(" ++ show i ++ "): done sleeping!"
+
+main :: IO ()
+main = do
+  options <- flip parseFlags defaultOptions <$> getArgs
+  case options of
+    Nothing -> showHelp
+    Just Options{..} -> do
+      case Main.getTransport transport of
+        Buffered f -> runServer protocol f port
+        Framed   f -> runServer protocol f port
+        NoTransport err -> putStrLn err
+      System.IO.putStrLn $ "Starting \"" ++ show serverType ++ "\" server (" ++
+        show transport ++ ") listen on: " ++ domainSocket ++ show port
+      where
+        acceptor p f socket = do
+          t <- f socket
+          return (p t, p t)
+
+        doRunServer p f = do
+          runThreadedServer (acceptor p f) TestHandler ThriftTest.process . PortNumber . fromIntegral
+
+        runServer p f port = case p of
+          Binary  -> do doRunServer BinaryProtocol f port
+          Compact -> do doRunServer CompactProtocol f port
+          JSON    -> do doRunServer JSONProtocol f port
+
+parseFlags :: [String] -> Options -> Maybe Options
+parseFlags (flag : flags) opts = do
+  let pieces = splitOn "=" flag
+  case pieces of
+    "--port" : arg : _ -> parseFlags flags opts{ port = read arg }
+    "--domain-socket" : arg : _ -> parseFlags flags opts{ domainSocket = read arg }
+    "--server-type" : arg : _ -> parseFlags flags opts{ serverType = fromString arg }
+    "--transport" : arg : _ -> parseFlags flags opts{ transport = arg }
+    "--protocol" : arg : _ -> parseFlags flags opts{ protocol = getProtocol arg }
+    "--workers" : arg : _ -> parseFlags flags opts{ workers = read arg }
+    "-n" : arg : _ -> parseFlags flags opts{ workers = read arg }
+    "--h" : _ -> Nothing
+    "--help" : _ -> Nothing
+    "--ssl" : _ -> parseFlags flags opts{ ssl = True }
+    "--processor-events" : _ -> parseFlags flags opts
+    _ -> Nothing
+parseFlags [] opts = Just opts
+
+showHelp :: IO ()
+showHelp = System.IO.putStrLn
+  "Allowed options:\n\
+  \  -h [ --help ]               produce help message\n\
+  \  --port arg (=9090)          Port number to listen\n\
+  \  --domain-socket arg         Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)\n\
+  \  --server-type arg (=simple) type of server, \"simple\", \"thread-pool\",\n\
+  \                              \"threaded\", or \"nonblocking\"\n\
+  \  --transport arg (=buffered) transport: buffered, framed\n\
+  \  --protocol arg (=binary)    protocol: binary, compact, json\n\
+  \  --ssl                       Encrypted Transport using SSL\n\
+  \  --processor-events          processor-events\n\
+  \  -n [ --workers ] arg (=4)   Number of thread pools workers. Only valid for\n\
+  \                              thread-pool server type"
diff --git a/vendor/src/github.com/apache/thrift/test/hs/ThriftTestUtils.hs b/vendor/src/github.com/apache/thrift/test/hs/ThriftTestUtils.hs
new file mode 100644
index 00000000..9c19b56a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/ThriftTestUtils.hs
@@ -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.
+--
+
+module ThriftTestUtils (ClientFunc, ServerFunc, clientLog, serverLog, testLog, runTest) where
+
+
+import qualified Control.Concurrent
+import qualified Network
+import qualified System.IO
+
+
+serverPort :: Network.PortNumber
+serverPort = 9090
+
+serverAddress :: (String, Network.PortID)
+serverAddress = ("localhost", Network.PortNumber serverPort)
+
+
+testLog :: String -> IO ()
+testLog str = do
+    System.IO.hPutStr System.IO.stdout $ str ++ "\n"
+    System.IO.hFlush System.IO.stdout
+
+
+clientLog :: String -> IO ()
+clientLog str = testLog $ "CLIENT: " ++ str
+
+serverLog :: String -> IO ()
+serverLog str = testLog $ "SERVER: " ++ str
+
+
+type ServerFunc = Network.PortNumber -> IO ()
+type ClientFunc = (String, Network.PortID) -> IO ()
+
+runTest :: ServerFunc -> ClientFunc -> IO ()
+runTest server client = do
+    _ <- Control.Concurrent.forkIO (server serverPort)
+
+    -- Fairly horrible; this does not 100% guarantees that the other thread
+    -- has actually opened the socket we need... but not much else we can do
+    -- without this, the client races the server to the socket, and wins every
+    -- time
+    Control.Concurrent.yield
+    Control.Concurrent.threadDelay $ 500 * 1000 -- unit is in _micro_seconds
+    Control.Concurrent.yield
+
+    client serverAddress
+
+    testLog "SUCCESS"
diff --git a/vendor/src/github.com/apache/thrift/test/hs/ThriftTest_Main.hs b/vendor/src/github.com/apache/thrift/test/hs/ThriftTest_Main.hs
new file mode 100644
index 00000000..670023e2
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/ThriftTest_Main.hs
@@ -0,0 +1,214 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+
+import qualified Control.Exception
+import qualified Data.HashMap.Strict as Map
+import qualified Data.HashSet as Set
+import qualified Data.Vector as Vector
+
+import qualified Network
+
+import Thrift
+import Thrift.Protocol.Binary
+import Thrift.Server
+import Thrift.Transport.Handle
+
+import qualified ThriftTestUtils
+
+import qualified ThriftTest
+import qualified ThriftTest_Client as Client
+import qualified ThriftTest_Iface as Iface
+import qualified ThriftTest_Types as Types
+
+
+data TestHandler = TestHandler
+instance Iface.ThriftTest_Iface TestHandler where
+    testVoid _ = return ()
+
+    testString _ s = do
+        ThriftTestUtils.serverLog $ show s
+        return s
+
+    testByte _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testI32 _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testI64 _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testDouble _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testBinary _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testStruct _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testNest _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testMap _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testStringMap _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testSet _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testList _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testEnum _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testTypedef _ x = do
+        ThriftTestUtils.serverLog $ show x
+        return x
+
+    testMapMap _ _ = do
+        return (Map.fromList [(1, Map.fromList [(2, 2)])])
+
+    testInsanity _ x = do
+        return (Map.fromList [(1, Map.fromList [(Types.ONE, x)])])
+
+    testMulti _ _ _ _ _ _ _ = do
+        return (Types.Xtruct "" 0 0 0)
+
+    testException _ _ = do
+        Control.Exception.throw (Types.Xception 1 "bya")
+
+    testMultiException _ _ _ = do
+        Control.Exception.throw (Types.Xception 1 "xyz")
+
+    testOneway _ i = do
+        ThriftTestUtils.serverLog $ show i
+
+
+client :: (String, Network.PortID) -> IO ()
+client addr = do
+    to <- hOpen addr
+    let ps = (BinaryProtocol to, BinaryProtocol to)
+
+    v1 <- Client.testString ps "bya"
+    ThriftTestUtils.clientLog $ show v1
+
+    v2 <- Client.testByte ps 8
+    ThriftTestUtils.clientLog $ show v2
+
+    v3 <- Client.testByte ps (-8)
+    ThriftTestUtils.clientLog $ show v3
+
+    v4 <- Client.testI32 ps 32
+    ThriftTestUtils.clientLog $ show v4
+
+    v5 <- Client.testI32 ps (-32)
+    ThriftTestUtils.clientLog $ show v5
+
+    v6 <- Client.testI64 ps 64
+    ThriftTestUtils.clientLog $ show v6
+
+    v7 <- Client.testI64 ps (-64)
+    ThriftTestUtils.clientLog $ show v7
+
+    v8 <- Client.testDouble ps 3.14
+    ThriftTestUtils.clientLog $ show v8
+
+    v9 <- Client.testDouble ps (-3.14)
+    ThriftTestUtils.clientLog $ show v9
+
+    -- TODO: Client.testBinary ...
+	
+    v10 <- Client.testMap ps (Map.fromList [(1,1),(2,2),(3,3)])
+    ThriftTestUtils.clientLog $ show v10
+
+    v11 <- Client.testStringMap ps (Map.fromList [("a","123"),("a b","with spaces "),("same","same"),("0","numeric key")])
+    ThriftTestUtils.clientLog $ show v11
+
+    v12 <- Client.testList ps (Vector.fromList [1,2,3,4,5])
+    ThriftTestUtils.clientLog $ show v12
+
+    v13 <- Client.testSet ps (Set.fromList [1,2,3,4,5])
+    ThriftTestUtils.clientLog $ show v13
+
+    v14 <- Client.testStruct ps (Types.Xtruct "hi" 4 5 0)
+    ThriftTestUtils.clientLog $ show v14
+
+    (testException ps "bad") `Control.Exception.catch` testExceptionHandler
+
+    (testMultiException ps "ok") `Control.Exception.catch` testMultiExceptionHandler1
+    (testMultiException ps "bad") `Control.Exception.catch` testMultiExceptionHandler2 `Control.Exception.catch` testMultiExceptionHandler3
+
+    -- (  (Client.testMultiException ps "e" "e2">> ThriftTestUtils.clientLog "bad") `Control.Exception.catch` 
+
+    tClose to
+  where testException ps msg = do
+            _ <- Client.testException ps "e"
+            ThriftTestUtils.clientLog msg
+            return ()
+
+        testExceptionHandler (e :: Types.Xception) = do
+            ThriftTestUtils.clientLog $ show e
+
+        testMultiException ps msg = do
+            _ <- Client.testMultiException ps "e" "e2"
+            ThriftTestUtils.clientLog msg
+            return ()
+
+        testMultiExceptionHandler1 (e :: Types.Xception) = do
+            ThriftTestUtils.clientLog $ show e
+
+        testMultiExceptionHandler2 (e :: Types.Xception2) = do
+            ThriftTestUtils.clientLog $ show e
+
+        testMultiExceptionHandler3 (_ :: Control.Exception.SomeException) = do
+            ThriftTestUtils.clientLog "ok"
+
+
+server :: Network.PortNumber -> IO ()
+server port = do
+    ThriftTestUtils.serverLog "Ready..."
+    (runBasicServer TestHandler ThriftTest.process port)
+    `Control.Exception.catch`
+    (\(TransportExn s _) -> error $ "FAILURE: " ++ s)
+
+
+main :: IO ()
+main = ThriftTestUtils.runTest server client
diff --git a/vendor/src/github.com/apache/thrift/test/hs/run-test.sh b/vendor/src/github.com/apache/thrift/test/hs/run-test.sh
new file mode 100644
index 00000000..ecafc18b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/hs/run-test.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if [ "x" = "x$1" ]; then
+  printf "run-test.sh needs an argument, the name of the test to run. Try 'ThriftTest' or 'ProtoDebugTest'\n"
+  exit 2
+fi
+
+# Check some basics
+if [ -z $BASE ]; then
+    BASE=../..
+fi
+
+# Figure out what file to run has a server
+if [ -z $TEST_SOURCE_FILE ]; then
+    TEST_SOURCE_FILE=$BASE/test/hs/$1_Main.hs
+fi
+
+if [ ! -e $TEST_SOURCE_FILE ]; then
+    printf "Missing server code file $TEST_SOURCE_FILE \n"
+    exit 3
+fi
+
+printf "Running test... \n"
+runhaskell -Wall -XScopedTypeVariables -i$BASE/lib/hs/src -igen-hs $TEST_SOURCE_FILE
diff --git a/vendor/src/github.com/apache/thrift/test/index.html b/vendor/src/github.com/apache/thrift/test/index.html
new file mode 100644
index 00000000..3611a924
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/index.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+Apache Thrift - integration test suite
+
+
+
+
+
+
+

Apache Thrift - integration test suite: Results

+ + + + + + + + + + + +
ServerClientProtocolTransportResult (log)Expected
+

Test Information

+

+
+browse raw log
+
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/keys/CA.pem b/vendor/src/github.com/apache/thrift/test/keys/CA.pem
new file mode 100644
index 00000000..a747b9ac
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/CA.pem
@@ -0,0 +1,82 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 16582080088954381212 (0xe61f61fc3b34239c)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=US, ST=Maryland, L=Forest Hill, O=The Apache Software Foundation, OU=Apache Thrift, CN=localhost/emailAddress=dev@thrift.apache.org
+        Validity
+            Not Before: Apr  7 18:58:00 2014 GMT
+            Not After : Jun 24 18:58:00 2022 GMT
+        Subject: C=US, ST=Maryland, L=Forest Hill, O=The Apache Software Foundation, OU=Apache Thrift, CN=localhost/emailAddress=dev@thrift.apache.org
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:aa:13:d4:c4:f7:01:17:a7:92:d1:b4:b4:15:0d:
+                    21:90:19:5e:fc:fb:b6:6d:3f:f2:3f:65:a2:7a:43:
+                    a6:46:95:fc:43:16:f6:63:14:5e:f7:b1:e3:61:02:
+                    f9:4a:95:89:bf:8d:f9:48:1d:82:e7:34:e0:b2:48:
+                    df:08:d9:7c:3a:2f:d3:1b:0b:e8:ef:c2:41:0a:7d:
+                    0a:38:78:3a:31:66:73:99:8c:d1:79:27:5f:e5:66:
+                    d0:5e:3a:8c:0c:92:18:73:04:c1:f5:45:db:37:e7:
+                    5f:c7:8c:a3:60:e9:92:a0:d8:29:5d:77:48:fb:1d:
+                    b0:ed:12:2c:4e:2e:02:db:3d:1a:41:71:a6:2b:2e:
+                    b3:4c:6a:c7:f7:1d:a9:7e:c7:cf:db:f2:e7:b6:f3:
+                    1f:77:1d:24:01:1a:66:66:30:85:30:02:29:c4:bb:
+                    f7:cd:3f:89:4b:1a:5f:f4:91:96:fb:e9:39:f2:46:
+                    96:12:3d:8a:23:b5:2e:82:9e:41:fe:40:b6:27:b1:
+                    14:44:5c:96:30:0f:55:e4:bb:ad:8b:8a:99:17:c0:
+                    29:11:4e:76:79:9d:4b:03:31:7e:85:3c:a8:23:40:
+                    54:02:58:35:c6:fc:dd:3d:eb:e3:d1:51:00:02:86:
+                    1a:d7:b0:9f:a0:17:73:6a:5a:d0:e6:b6:b8:55:40:
+                    5e:27
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                28:F2:FD:30:CD:03:F1:DC:41:1E:C4:93:C6:97:13:CA:D4:FA:60:2A
+            X509v3 Authority Key Identifier: 
+                keyid:28:F2:FD:30:CD:03:F1:DC:41:1E:C4:93:C6:97:13:CA:D4:FA:60:2A
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         46:15:18:89:b2:57:17:d1:a2:64:c1:9a:73:4f:04:94:76:07:
+         1f:29:ba:6f:34:46:c2:36:d5:68:85:f4:15:4c:8e:1a:fe:83:
+         79:53:ec:aa:0d:92:60:de:f3:9a:3a:e8:80:66:ac:87:70:89:
+         59:f2:ac:9e:b0:28:11:37:7d:78:4e:5e:3f:25:0f:be:09:6f:
+         26:2a:3d:66:79:38:28:e5:81:71:71:96:26:4f:db:ec:23:70:
+         be:37:39:fc:e0:32:0d:80:8f:66:c7:ac:a4:b4:8b:77:40:e2:
+         99:44:3a:73:c8:f9:14:cf:1b:32:27:c2:78:db:b0:da:8a:60:
+         eb:8d:34:7e:7d:3c:03:d4:38:74:f7:17:9e:32:74:9a:e7:37:
+         95:d4:71:03:c8:94:ea:09:7b:ad:2d:eb:70:43:f2:32:7e:63:
+         01:84:8c:7e:9e:f0:79:7f:ae:e9:cf:f9:be:0e:fe:95:d2:bd:
+         c8:a7:81:c2:71:d9:c3:50:31:89:6d:fa:ad:a2:ab:00:01:34:
+         10:58:ef:96:5a:eb:30:07:a9:8e:84:36:ef:3d:3c:61:46:96:
+         6a:e8:09:20:5a:ab:f8:4b:eb:b7:33:61:8e:af:9a:7d:16:b0:
+         60:6a:f0:30:e5:b2:8e:e7:80:b4:a1:02:a9:37:fe:5f:b5:ae:
+         65:e9:6b:34
+-----BEGIN CERTIFICATE-----
+MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs
+MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV
+BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3
+DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy
+MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU
+MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh
+cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ
+bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ
+GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6
+L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg
+2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw
+AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX
+wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n
+AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME
+GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5
+U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm
+T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD
+1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I
+p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO
+r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0
+-----END CERTIFICATE-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/README.md b/vendor/src/github.com/apache/thrift/test/keys/README.md
new file mode 100644
index 00000000..010835d3
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/README.md
@@ -0,0 +1,102 @@
+# Test Keys and Certificates
+This folder is dedicated to test keys and certificates provided in multiple formats.
+Primary use are unit test suites and cross language tests.
+
+    test/keys
+
+**The files in this directory must never be used on production systems.**
+
+## SSL Keys and Certificates
+
+
+## create certificates
+
+we use the following parameters for test key and certificate creation
+
+    C=US,
+    ST=Maryland,
+    L=Forest Hill,
+    O=The Apache Software Foundation,
+    OU=Apache Thrift,
+    CN=localhost/emailAddress=dev@thrift.apache.org
+
+### create self-signed server key and certificate
+
+    openssl req -new -x509 -nodes  -days 3000 -out server.crt -keyout server.key
+    openssl x509 -in server.crt -text > CA.pem
+    cat server.crt server.key > server.pem
+
+Export password is "thrift" without the quotes
+
+    openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
+
+### create client key and certificate
+
+    openssl genrsa -out client.key
+
+create a signing request:
+
+    openssl req -new -key client.key -out client.csr
+
+sign the client certificate with the server.key
+
+    openssl x509 -req -days 3000 -in client.csr -CA CA.pem -CAkey server.key -set_serial 01 -out client.crt
+
+export certificate in PKCS12 format (Export password is "thrift" without the quotes)
+
+    openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
+
+export certificate in PEM format for OpenSSL usage
+
+    openssl pkcs12 -in client.p12 -out client.pem -clcerts
+
+### create client key and certificate with altnames
+
+copy openssl.cnf from your system e.g. /etc/ssl/openssl.cnf and append following to the end of [ v3_req ]
+
+    subjectAltName=@alternate_names
+
+    [ alternate_names ]
+    IP.1=127.0.0.1
+    IP.2=::1
+    IP.3=::ffff:127.0.0.1
+
+create a signing request:
+
+    openssl req -new -key client_v3.key -out client_v3.csr -config openssl.cnf \
+        -subj "/C=US/ST=Maryland/L=Forest Hill/O=The Apache Software Foundation/OU=Apache Thrift/CN=localhost" -extensions v3_req
+
+sign the client certificate with the server.key
+
+    openssl x509 -req -days 3000 -in client_v3.csr -CA CA.pem -CAkey server.key -set_serial 01 -out client_v3.crt -extensions v3_req -extfile openssl.cnf
+
+## Java key and certificate import
+Java Test Environment uses key and trust store password "thrift" without the quotes
+
+list keystore entries
+
+    keytool -list -storepass thrift -keystore ../../lib/java/test/.keystore
+
+list truststore entries
+
+    keytool -list -storepass thrift -keystore ../../lib/java/test/.truststore
+
+
+delete an entry
+
+    keytool -delete -storepass thrift -keystore ../../lib/java/test/.truststore -alias ssltest
+
+
+import certificate into truststore
+
+    keytool -importcert -storepass thrift -keystore ../../lib/java/test/.truststore -alias localhost --file server.crt
+
+import key into keystore
+
+    keytool -importkeystore -storepass thrift -keystore ../../lib/java/test/.keystore -srcstoretype pkcs12 -srckeystore server.p12
+
+# Test SSL server and clients
+
+    openssl s_client -connect localhost:9090
+    openssl s_server -accept 9090 -www
+
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client.crt b/vendor/src/github.com/apache/thrift/test/keys/client.crt
new file mode 100644
index 00000000..80a9ad0e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/client.crt
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACAQEwDQYJKoZIhvcNAQELBQAwgbExCzAJBgNVBAYTAlVTMREwDwYD
+VQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRo
+ZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRo
+cmlmdDESMBAGA1UEAwwJbG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhy
+aWZ0LmFwYWNoZS5vcmcwHhcNMTUxMjIzMDcwNzUwWhcNMjQwMzEwMDcwNzUwWjCB
+sTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1hcnlsYW5kMRQwEgYDVQQHDAtGb3Jl
+c3QgSGlsbDEnMCUGA1UECgweVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9u
+MRYwFAYDVQQLDA1BcGFjaGUgVGhyaWZ0MRIwEAYDVQQDDAlsb2NhbGhvc3QxJDAi
+BgkqhkiG9w0BCQEWFWRldkB0aHJpZnQuYXBhY2hlLm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALl/bJhg17Pu1t785JIuwlh7e2E51eoWFvuxMWfH
+dsUAVqDaOz99O5sv8pgWKXrkPBttE98T+Mx/Pwz5JHs4Qcre3A5adm6cdab0AKug
+eVG1PvHrWzV4aheg4KUfVSiMz8BG2K3Q1VGvZkgMRa/Q409FKjU6Z4D9vBG4Pas3
+PN5FxnAL6P70yWCEr2Vu/tWJlCN+qrRDfSaE2hqmT1irxaoPa61GvuRVsGQ3TeoE
+mGfENtvFqC6qQWuSCbG5aI47Rv66pqoWWxqkrcolNc7+vhx6rW+wdFO3IlqIT8IG
+sdQVp+Y4ir/dGp2ejzqGZCremPJ1uEKjLMpIukdg25ymHC0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEABcRdwc9mdY9zOvTixtThHrciVvqwp6RaiQqMEVomozYilZDR
+J90M3H6KnlADJbCv0FXujsaApB53awq5I7sObAYWFhDJZ9LKlw0lDF7KchLXiAlk
+XVyvL2nJjuJcxPCFPWktwrw5bTguRCBzG12Hzikn+xs6uPur7VQYM33jbXEda5PS
+UEqvAVWaONJCX8MDYOscVoJF0ESyI+fN92ipFaXR7fGajod/rc0AfeN2GVMZETve
+21pkUyDwwCGA44uvNUCd1e32NxE3ah9UApCRn5sGJ64R6urpFTXT3IALF3kaOO7U
+VsMhFrUiZ7+Bw5nIeiK0QOF6Eipj+bJvrLZpSg==
+-----END CERTIFICATE-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client.key b/vendor/src/github.com/apache/thrift/test/keys/client.key
new file mode 100644
index 00000000..25dcfd71
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/client.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAuX9smGDXs+7W3vzkki7CWHt7YTnV6hYW+7ExZ8d2xQBWoNo7
+P307my/ymBYpeuQ8G20T3xP4zH8/DPkkezhByt7cDlp2bpx1pvQAq6B5UbU+8etb
+NXhqF6DgpR9VKIzPwEbYrdDVUa9mSAxFr9DjT0UqNTpngP28Ebg9qzc83kXGcAvo
+/vTJYISvZW7+1YmUI36qtEN9JoTaGqZPWKvFqg9rrUa+5FWwZDdN6gSYZ8Q228Wo
+LqpBa5IJsblojjtG/rqmqhZbGqStyiU1zv6+HHqtb7B0U7ciWohPwgax1BWn5jiK
+v90anZ6POoZkKt6Y8nW4QqMsyki6R2DbnKYcLQIDAQABAoIBAFotbCmXysUaczLs
+VmIKgUhqn0xgxXGLU5kARzhga4jR5UtFTFBNHVEQOitdesTXd7ENkf98whMIOSqh
+Y+7TJojtVqVTrQeQ4FFNhZXp6ZCjP/pzpF+WLl1WRF+Bn/Cao9ShnGzDfTC8yEh2
+Ttpt/lNnGGHQBslakLc8jh5SODEFfbugX8SdTCwZYsesKNrXm1pS/5IEunPqaRi0
+II0EcnqHEsgqSo+CljpW7uNxSryA2vSAVdlPej6+9FZjdIHLP5AEKYvk7e9D2CMV
+1+grNe/QkQppShizPirbb93tHm86v5bkDFCM9yWrhcMcjvILMXETxIppMGPmacRu
+jqtYcAECgYEA8VDzylTz4kS3+D3n3hTgb41XVYa7feUsh99GWRO1wXIFpHjCIRjA
+9r/BXW9+Rx3puVPhS8hwLQ4BLdA7lFpV1C8ag0e3+vn6zVirnz1jtI+uHMvStzhO
+d6i0nf+w4HYXo7mN6o9ZdHEfC8SFNbymhCoVKh2DILDwb4EX9RXNpy0CgYEAxMj4
++vrklJ/ilH+Ry1zst4zQYIwmm3QWjarDrypGucHgd4jg5v9A/CJIKUi8x0MjrcuN
+wVb7R8XJyYzFQRXIUXR6GnLeeSnfpxzt4YlifCvXxnOi8w4fv7KeGBV5np1Egpo8
+nWNyZFxdvQDuCopr3SUoS9JI8JPwVgA7T+7DaQECgYAGoavhbo45NJw9pS3fC4HT
+bvXscsRqREcCAN/FCOagx0piZ7MmB7Ed1s0wjSTSPX8zyZtSYtK6Wj0sDiHlBMqB
+Bz5aRzlGG2KKDBrDSIOZ7aziO7Oxt0lovmkgQmuQ743cwPemb4QM0CMDRsZGYMXO
+sf1c5+y3lEU3Ozv2T0AUjQKBgBlnzOUyMQKTJcCAO8ViiNkln91nGrDlKug9TKg3
+sAvZYO5tyINqHuyuTFywHFcpbtjIN9PnM+fPPD7+IpVFh6gkfoMdo2VHJ62+iWOd
+xg475s6jLT1t7GFmYQzA8QOuUCMAYKT9Ks6UMjHthc3skwJpAqvPSUVuBBBGVWH7
+dFUBAoGBAL67ARLujiAEVNHt5rajixB6ncl7/R+Z2uawI1JfmdnCZonAKVZYHuXU
+/4j2+o4QhJIPLtWIoaxAkMigQtAkesqirn3Kk/c7kZRIoN549HTJuwZqYqNp7CB/
+kVi5R335+M9z49i6qA0RZsJGSoSBk7PufG4RmLimcRbGwrY93sPD
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client.p12 b/vendor/src/github.com/apache/thrift/test/keys/client.p12
new file mode 100644
index 00000000..5d2669c1
Binary files /dev/null and b/vendor/src/github.com/apache/thrift/test/keys/client.p12 differ
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client.pem b/vendor/src/github.com/apache/thrift/test/keys/client.pem
new file mode 100644
index 00000000..66ef626d
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/client.pem
@@ -0,0 +1,60 @@
+Bag Attributes
+    localKeyID: 39 EC 3D 5B 28 17 DA DD 09 7A 62 68 D5 44 1F C7 E2 F8 E0 CD 
+subject=/C=US/ST=Maryland/L=Forest Hill/O=The Apache Software Foundation/OU=Apache Thrift/CN=localhost/emailAddress=dev@thrift.apache.org
+issuer=/C=US/ST=Maryland/L=Forest Hill/O=The Apache Software Foundation/OU=Apache Thrift/CN=localhost/emailAddress=dev@thrift.apache.org
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACAQEwDQYJKoZIhvcNAQELBQAwgbExCzAJBgNVBAYTAlVTMREwDwYD
+VQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRo
+ZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRo
+cmlmdDESMBAGA1UEAwwJbG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhy
+aWZ0LmFwYWNoZS5vcmcwHhcNMTUxMjIzMDcwNzUwWhcNMjQwMzEwMDcwNzUwWjCB
+sTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1hcnlsYW5kMRQwEgYDVQQHDAtGb3Jl
+c3QgSGlsbDEnMCUGA1UECgweVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9u
+MRYwFAYDVQQLDA1BcGFjaGUgVGhyaWZ0MRIwEAYDVQQDDAlsb2NhbGhvc3QxJDAi
+BgkqhkiG9w0BCQEWFWRldkB0aHJpZnQuYXBhY2hlLm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALl/bJhg17Pu1t785JIuwlh7e2E51eoWFvuxMWfH
+dsUAVqDaOz99O5sv8pgWKXrkPBttE98T+Mx/Pwz5JHs4Qcre3A5adm6cdab0AKug
+eVG1PvHrWzV4aheg4KUfVSiMz8BG2K3Q1VGvZkgMRa/Q409FKjU6Z4D9vBG4Pas3
+PN5FxnAL6P70yWCEr2Vu/tWJlCN+qrRDfSaE2hqmT1irxaoPa61GvuRVsGQ3TeoE
+mGfENtvFqC6qQWuSCbG5aI47Rv66pqoWWxqkrcolNc7+vhx6rW+wdFO3IlqIT8IG
+sdQVp+Y4ir/dGp2ejzqGZCremPJ1uEKjLMpIukdg25ymHC0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEABcRdwc9mdY9zOvTixtThHrciVvqwp6RaiQqMEVomozYilZDR
+J90M3H6KnlADJbCv0FXujsaApB53awq5I7sObAYWFhDJZ9LKlw0lDF7KchLXiAlk
+XVyvL2nJjuJcxPCFPWktwrw5bTguRCBzG12Hzikn+xs6uPur7VQYM33jbXEda5PS
+UEqvAVWaONJCX8MDYOscVoJF0ESyI+fN92ipFaXR7fGajod/rc0AfeN2GVMZETve
+21pkUyDwwCGA44uvNUCd1e32NxE3ah9UApCRn5sGJ64R6urpFTXT3IALF3kaOO7U
+VsMhFrUiZ7+Bw5nIeiK0QOF6Eipj+bJvrLZpSg==
+-----END CERTIFICATE-----
+Bag Attributes
+    localKeyID: 39 EC 3D 5B 28 17 DA DD 09 7A 62 68 D5 44 1F C7 E2 F8 E0 CD 
+Key Attributes: 
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIRKol42bAS3ACAggA
+MBQGCCqGSIb3DQMHBAjOulcyHMaWsQSCBMgbeXQ8pIYTENKm08UPeqxkCR2nLxSO
+gtRmBxDYjqYv35y4K8KhybBvSBlIPgE5jEWxUCcc1Qvy5ydUQ/X9pmkU8dnmAmDC
+o0zwd3lt2eNPy+4WliqFDVmHrLfYQFOoIrHjuzC0lI4C2iPOjxhfa1zFumedgwGS
+Gx10X5uEVH5N64fW6B3BvzQM0rn2qr0ONuSRmteRtI8NsznfkgeJ9a4CIAF7E5Z2
+JTGI12edNzzUJ1JhX47ZW4miQwCU5Lcy01UZqTFuUpmK7FUQegtLy3e5eDKq+bIg
+ZYq6Hx7lu8hjT5kWZGKy71aYmHKEjI0f727cAbqDTG5uZBHBjwK/3p/znQVQXxBb
+1+E9CiKeFdXj2ElptsnDyoTvrSwJ/Jqu1wkXBcH5Embg7aJMod1IOs6OQB1rPDvd
+FFa84zbqRNWHSxxxYZxcB8YZinL6/dQJnisKu9LMQd3BBGsGWqH8Zz5tEvXjS5Kv
+3g9JRa7QDkSF005x6U+q/678G2MG+W+NWqje3NZx9Psh/Ptm+h+q9n2GSvnibiK5
+mEj9FIwGquGpbZUTK5aXHcKN657dKiICsEJeNar1iZznRmzrMbZJ+DxqJnTw+GAv
+7Yb63/CNAtqSxiyNHGZ6NM2ZA9vAKY1HXn0RVC0y1+9FmNpSRwv3u/+ydSCnJonR
+GEKjzOqM9Dn7qxd+h4UnnA7hXWxITageB6G6KmfiXRxhiWyqtOICdCneCwpq8UZ4
+e0fm05NRW6M2mqGQHsMNSvTWddwz5b8wgw4eVsb+xQytxVdj9lpBuB9KyjQjxUgU
+3oZx4KyWLoEWjkztPAiK3uv5GfotNIMdznRfON1+xm1M5swtn3y3Ru1f6STZC7Sp
+qvbG7jPmpB5gLEUri+chw+aKUYbJ0b820Od4FLQYnwLWr46VelYmV44xuR06wgqP
+1HchMSsHtS+ZlIiQQU9jhdyTrl86EQHH33dh+Sua8AhfewPRy2VFp3Zk34AUsWcX
+EfIYGemhqUD3drG0SMVbFFNOaFGp9e0tQouYOC6/qFBv/SNgQz3mAEkciJYbUuUZ
+V4YQPvtdvSrISV0e7bjFgdSEjG7P7F6CFrWTrjUlHZuWj6/rJ3+/1PHeJViyhsrJ
+ZYFe14W/48PDxBRl4IEAmxcN1Eb2Ez9eCqv0HW77HviG6zIgnkPrhWHjFGUpxKk4
+jLfuB2Tfq9F7ozv4L2QAn+F/yKt1Rm2Hh5J61eUJtAT60pajg+gJtjmpu5Pr4HDn
+b6p3xmYwaL5Let1zCAbbMfdlDK14YjdOdM/BEKpXb9y4EIubX5AMY4ljXeG9gx+T
+B1TuQVdJ0P5wIK/D10TQzAWDKam0kv3RXidlzRxpZ3snRnN/L3EVd58Rntj1Oc0y
+FiIiSKRszDbPzKDxQE2sNgQcdO24JNLSa/sZYtq2gRgspl/YqIDo4ZYqi9x8F5OS
+rdPU5D/H8LWR4vpJLL8DYrHh5qFG3BX2OJIhPRS+48pDYtrRjp7S/1ZU64OJAytk
+99hDqSrn1j2a6yFE8L2Ptz+4UCF2OQXEc9Rqqeb8QEUuMSkNH4oQ+A2F6uzLpZi0
+XH64R2niNC56LxV2i+3T5KREFLahyk8epLZlv8YdxYR4Sb7J/5yiooK3g9hmYVKO
+zLc=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client_v3.crt b/vendor/src/github.com/apache/thrift/test/keys/client_v3.crt
new file mode 100644
index 00000000..a47f156a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/client_v3.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIECDCCAvCgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVVMx
+ETAPBgNVBAgMCE1hcnlsYW5kMRQwEgYDVQQHDAtGb3Jlc3QgSGlsbDEnMCUGA1UE
+CgweVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRYwFAYDVQQLDA1BcGFj
+aGUgVGhyaWZ0MRIwEAYDVQQDDAlsb2NhbGhvc3QxJDAiBgkqhkiG9w0BCQEWFWRl
+dkB0aHJpZnQuYXBhY2hlLm9yZzAeFw0xNjAyMjIxMTU4NDFaFw0yNDA1MTAxMTU4
+NDFaMIGLMQswCQYDVQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcM
+C0ZvcmVzdCBIaWxsMScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5k
+YXRpb24xFjAUBgNVBAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9z
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALZ0wiQnXg5QMZZWugd/
+O3woatyHuczJuFSmYiRGWLr3PugB+xtvjy0rTcE2MNx/bdsVxrapCKA+tMFORbEl
+sF6jk0H+B7BzGoIwHr6N8GP1VOoA2esrhsNEz22aJI00VaFTFE8G/qgFcihyaVWH
+ZsLa3MakOzFUmOBaV2tLBjCjaznqXw3eo3XwUI0BkgS9b9vqXjScmfWXDw5+1is4
+bCgumG2zj9EpLypc9qCGNKFBO2YIg0XsIIJ8RprlianjL6P4MfC6GPOyW4NbZaLd
+ESv/bumpVyuV/C/xqkPahvOwBuPE1loxZZPx6Qv368qn7SVNVZOLyX722spooA5G
+6csCAwEAAaNPME0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwMwYDVR0RBCwwKocE
+fwAAAYcQAAAAAAAAAAAAAAAAAAAAAYcQAAAAAAAAAAAAAP//fwAAATANBgkqhkiG
+9w0BAQsFAAOCAQEABUEmeQkG/PS935jMHDrg/5zm4X2xrnFtmTwb0jdxfau6z/7h
+AbxD5ioyY7FUTNCzI6SyMo9vJJtYCTCuEGr84JjT2R7232z60k4c1z/av01W3Orv
+ExHfAZ8llhkfu0209T5TaIYCB7hDFj5KDbta8c6fEcwtmlHQWj3M31lSNsr4ZtWW
+wObhK3sqTsOluHbhKNwlNEat48lbOQUC19I1Wi3dAS6n8lr0lEhfGKvqxu0ViASS
+N1nLfdkREGp39bYpKg0n6EFw5bYyV4qE3cnIedFJp7NIOM/6xndJMh/c5l6N2uyZ
+upArRQpw/3j+HkL1x9bs+900QK0GI6AxgjbopA==
+-----END CERTIFICATE-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/client_v3.key b/vendor/src/github.com/apache/thrift/test/keys/client_v3.key
new file mode 100644
index 00000000..b989f738
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/client_v3.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAtnTCJCdeDlAxlla6B387fChq3Ie5zMm4VKZiJEZYuvc+6AH7
+G2+PLStNwTYw3H9t2xXGtqkIoD60wU5FsSWwXqOTQf4HsHMagjAevo3wY/VU6gDZ
+6yuGw0TPbZokjTRVoVMUTwb+qAVyKHJpVYdmwtrcxqQ7MVSY4FpXa0sGMKNrOepf
+Dd6jdfBQjQGSBL1v2+peNJyZ9ZcPDn7WKzhsKC6YbbOP0SkvKlz2oIY0oUE7ZgiD
+RewggnxGmuWJqeMvo/gx8LoY87Jbg1tlot0RK/9u6alXK5X8L/GqQ9qG87AG48TW
+WjFlk/HpC/fryqftJU1Vk4vJfvbaymigDkbpywIDAQABAoIBAQCJpyUhaaIIYnBG
+4D+RkGgsj8Gvh6ah3j53ft/kRj6DMC4BlB0C4fO/PEB5WI0cjfcvpwo4nOapHyX4
+ATmLIMgjXn2m+CSM9wo01mEbmrKWd20M7n96cWhGwg9MvVJ+RdGk2K0lwj02PoWW
+Blt576GTuNN/+j++Q/jiqsXxaLTO0/Wj+4b2gQh3n8I0u6bkolDLoERKIdrLGHH+
+FU3sk8bpUhHmeiUTfwwci+juhtOY9e30AEst6xakCHbq1lRRyEYPtWL7oLds6yv0
+UAKP7wS9Yl6dcekXSF1RZpB+fovTW+qPYn8aEuksaMz0wK96FCOjVNGYxMp+Xnvl
+sKx63UZBAoGBAOCbCbJtO0HsgIauvCvGZ50aZ1vDvQReCwri4ioutEg4JCAXHEsX
++axz2J5j3UEQhGKr0EX9BG6YbxGW0Mmjf3QxeRB+0WLpMMY2SFt93oC2R1AX9l0I
+h50O6tYv5SXm96pKxwRz01d84mCJgwn/G+cZ/EJj4rfZsNbQst6JQFvzAoGBAM/1
+gLVQt5l+IK+6s68EnADI66i7cKe6sj3rFRTahZJxL2vY28J9EB2mF/XEgARSNJQV
+X/H9zDrwKm9MX87/eCH2nEbc+5qSGpDPQm482C9DqsMitxCKD8bble1BlpjFb8hr
+R0Q3v5q8u5uomLBds5eUBeRKMtu9tOMA9KRSDGjJAoGAF44K2Ux9T2+XFwjSMSEQ
+krhHKKeBdijKrayXnWbif0Rr/XWPAQ0VoRFRIWNFu+IYkCSGpiBfy51u4IBZixv7
+bNsXYDR8jwv3koH02qt7nzH+jpbEvoL7fewnkqjZNj1fsds/vebLvjwZnZguRukb
+KwRdoTTKfQ92bUDb0VzBhCMCgYB7H+3ObDXoCQctRCsyilYbGNp+EkxG4oC5rD/V
+EvRWmfDrt3+VjRpHk5lIB8mLxWgf7O/bhNqwYpWdQ+jN0++6nBo20oudHrff2PaJ
+8jhE85lc42bjwfpJUKVZzaVuWicu0GVnfGJTKT8ikBWnBjNYoWlDmrK164H3jQ9L
+YtC6EQKBgQCabFXXHx5cIJ2XOm4K/nTOG7ClvD80xapqyGroQd9E/cJUHHPp/wQ4
+c1dMO5EViM7JRsKfxkl9vM5o9IM7swlYh4EMFSLJNjzgOY9XVkvQh0uGbiJOBO4f
+inUuWn1YWUj/HFtrT+0No+cYvZVcMKrFAy3K/AwpTbfKCk6roullNA==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/server.crt b/vendor/src/github.com/apache/thrift/test/keys/server.crt
new file mode 100644
index 00000000..8a5ef3c3
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/server.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs
+MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV
+BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3
+DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy
+MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU
+MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh
+cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ
+bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ
+GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6
+L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg
+2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw
+AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX
+wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n
+AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME
+GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5
+U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm
+T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD
+1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I
+p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO
+r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0
+-----END CERTIFICATE-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/server.key b/vendor/src/github.com/apache/thrift/test/keys/server.key
new file mode 100644
index 00000000..263cfce5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR
+tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy
+SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H
+jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB
+GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk
+u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm
+trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt
+fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR
+xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD
+bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck
+Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq
+57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9
+7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU
+8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE
+jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj
+V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ
+HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/
+LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3
+SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791
+Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS
+y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc
+ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW
+cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE
+c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1
+Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc
+rdPUV9/uQkdx8VrShxlD8A==
+-----END PRIVATE KEY-----
diff --git a/vendor/src/github.com/apache/thrift/test/keys/server.p12 b/vendor/src/github.com/apache/thrift/test/keys/server.p12
new file mode 100644
index 00000000..f3908a43
Binary files /dev/null and b/vendor/src/github.com/apache/thrift/test/keys/server.p12 differ
diff --git a/vendor/src/github.com/apache/thrift/test/keys/server.pem b/vendor/src/github.com/apache/thrift/test/keys/server.pem
new file mode 100644
index 00000000..c45a9f8a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/keys/server.pem
@@ -0,0 +1,53 @@
+-----BEGIN CERTIFICATE-----
+MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs
+MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV
+BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3
+DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy
+MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU
+MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh
+cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ
+bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ
+GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6
+L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg
+2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw
+AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX
+wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n
+AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME
+GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5
+U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm
+T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD
+1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I
+p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO
+r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR
+tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy
+SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H
+jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB
+GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk
+u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm
+trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt
+fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR
+xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD
+bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck
+Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq
+57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9
+7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU
+8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE
+jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj
+V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ
+HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/
+LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3
+SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791
+Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS
+y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc
+ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW
+cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE
+c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1
+Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc
+rdPUV9/uQkdx8VrShxlD8A==
+-----END PRIVATE KEY-----
diff --git a/vendor/src/github.com/apache/thrift/test/known_failures_Linux.json b/vendor/src/github.com/apache/thrift/test/known_failures_Linux.json
new file mode 100644
index 00000000..edf0fbb0
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/known_failures_Linux.json
@@ -0,0 +1,245 @@
+[
+  "cpp-cpp_binary_buffered-ip-ssl",
+  "cpp-cpp_binary_framed-ip-ssl",
+  "cpp-cpp_binary_http-domain",
+  "cpp-cpp_binary_http-ip",
+  "cpp-cpp_binary_http-ip-ssl",
+  "cpp-cpp_compact_buffered-ip-ssl",
+  "cpp-cpp_compact_framed-ip-ssl",
+  "cpp-cpp_compact_http-domain",
+  "cpp-cpp_compact_http-ip",
+  "cpp-cpp_compact_http-ip-ssl",
+  "cpp-cpp_header_buffered-ip-ssl",
+  "cpp-cpp_header_framed-ip-ssl",
+  "cpp-cpp_header_http-ip-ssl",
+  "cpp-cpp_json_buffered-ip-ssl",
+  "cpp-cpp_json_framed-ip",
+  "cpp-cpp_json_framed-ip-ssl",
+  "cpp-cpp_json_http-domain",
+  "cpp-cpp_json_http-ip",
+  "cpp-cpp_json_http-ip-ssl",
+  "cpp-dart_binary_http-ip",
+  "cpp-dart_compact_http-ip",
+  "cpp-dart_json_http-ip",
+  "cpp-go_binary_http-ip",
+  "cpp-go_binary_http-ip-ssl",
+  "cpp-go_compact_http-ip",
+  "cpp-go_compact_http-ip-ssl",
+  "cpp-go_json_http-ip",
+  "cpp-go_json_http-ip-ssl",
+  "cpp-java_binary_http-ip",
+  "cpp-java_binary_http-ip-ssl",
+  "cpp-java_compact_http-ip",
+  "cpp-java_compact_http-ip-ssl",
+  "cpp-java_json_http-ip",
+  "cpp-java_json_http-ip-ssl",
+  "cpp-perl_binary_buffered-ip-ssl",
+  "cpp-perl_binary_framed-ip-ssl",
+  "cpp-py_binary-accel_framed-ip-ssl",
+  "csharp-cpp_binary_buffered-ip-ssl",
+  "csharp-cpp_binary_framed-ip-ssl",
+  "csharp-cpp_compact_buffered-ip-ssl",
+  "csharp-cpp_compact_framed-ip-ssl",
+  "csharp-cpp_json_buffered-ip-ssl",
+  "csharp-cpp_json_framed-ip-ssl",
+  "csharp-d_binary_buffered-ip-ssl",
+  "csharp-d_compact_buffered-ip-ssl",
+  "csharp-d_json_buffered-ip-ssl",
+  "csharp-erl_binary_buffered-ip-ssl",
+  "csharp-erl_binary_framed-ip-ssl",
+  "csharp-erl_compact_buffered-ip-ssl",
+  "csharp-erl_compact_framed-ip-ssl",
+  "csharp-go_binary_buffered-ip-ssl",
+  "csharp-go_binary_framed-ip-ssl",
+  "csharp-go_compact_buffered-ip-ssl",
+  "csharp-go_compact_framed-ip-ssl",
+  "csharp-go_json_buffered-ip-ssl",
+  "csharp-go_json_framed-ip-ssl",
+  "csharp-nodejs_binary_buffered-ip-ssl",
+  "csharp-nodejs_binary_framed-ip-ssl",
+  "csharp-nodejs_compact_buffered-ip-ssl",
+  "csharp-nodejs_compact_framed-ip-ssl",
+  "csharp-nodejs_json_buffered-ip-ssl",
+  "csharp-nodejs_json_framed-ip-ssl",
+  "csharp-perl_binary_buffered-ip-ssl",
+  "csharp-perl_binary_framed-ip-ssl",
+  "csharp-py3_binary-accel_buffered-ip-ssl",
+  "csharp-py3_binary-accel_framed-ip-ssl",
+  "csharp-py3_binary_buffered-ip-ssl",
+  "csharp-py3_binary_framed-ip-ssl",
+  "csharp-py3_compact-accelc_buffered-ip-ssl",
+  "csharp-py3_compact-accelc_framed-ip-ssl",
+  "csharp-py3_compact_buffered-ip-ssl",
+  "csharp-py3_compact_framed-ip-ssl",
+  "csharp-py3_json_buffered-ip-ssl",
+  "csharp-py3_json_framed-ip-ssl",
+  "csharp-py_binary-accel_buffered-ip-ssl",
+  "csharp-py_binary-accel_framed-ip-ssl",
+  "csharp-py_binary_buffered-ip-ssl",
+  "csharp-py_binary_framed-ip-ssl",
+  "csharp-py_compact-accelc_buffered-ip-ssl",
+  "csharp-py_compact-accelc_framed-ip-ssl",
+  "csharp-py_compact_buffered-ip-ssl",
+  "csharp-py_compact_framed-ip-ssl",
+  "csharp-py_json_buffered-ip-ssl",
+  "csharp-py_json_framed-ip-ssl",
+  "d-cpp_binary_buffered-ip",
+  "d-cpp_binary_buffered-ip-ssl",
+  "d-cpp_binary_framed-ip",
+  "d-cpp_binary_framed-ip-ssl",
+  "d-cpp_binary_http-ip",
+  "d-cpp_binary_http-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_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-dart_binary_framed-ip",
+  "d-dart_binary_http-ip",
+  "d-dart_compact_framed-ip",
+  "d-dart_compact_http-ip",
+  "d-dart_json_framed-ip",
+  "d-dart_json_http-ip",
+  "d-go_binary_http-ip",
+  "d-go_binary_http-ip-ssl",
+  "d-go_compact_http-ip",
+  "d-go_compact_http-ip-ssl",
+  "d-go_json_http-ip",
+  "d-go_json_http-ip-ssl",
+  "d-java_binary_http-ip",
+  "d-java_binary_http-ip-ssl",
+  "d-java_compact_http-ip",
+  "d-java_compact_http-ip-ssl",
+  "d-java_json_http-ip",
+  "d-java_json_http-ip-ssl",
+  "d-js_json_http-ip",
+  "d-lua_json_buffered-ip",
+  "d-lua_json_framed-ip",
+  "d-nodejs_binary_buffered-ip",
+  "d-nodejs_binary_buffered-ip-ssl",
+  "d-nodejs_binary_framed-ip",
+  "d-nodejs_binary_framed-ip-ssl",
+  "d-nodejs_compact_buffered-ip",
+  "d-nodejs_compact_buffered-ip-ssl",
+  "d-nodejs_compact_framed-ip",
+  "d-nodejs_compact_framed-ip-ssl",
+  "d-nodejs_json_buffered-ip",
+  "d-nodejs_json_buffered-ip-ssl",
+  "d-nodejs_json_framed-ip",
+  "d-nodejs_json_framed-ip-ssl",
+  "d-perl_binary_buffered-ip-ssl",
+  "d-perl_binary_framed-ip-ssl",
+  "d-py3_binary-accel_buffered-ip",
+  "d-py3_binary-accel_buffered-ip-ssl",
+  "d-py3_binary-accel_framed-ip",
+  "d-py3_binary-accel_framed-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_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_buffered-ip",
+  "d-py3_compact_buffered-ip-ssl",
+  "d-py3_compact_framed-ip",
+  "d-py3_compact_framed-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-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_buffered-ip",
+  "d-py_binary_buffered-ip-ssl",
+  "d-py_binary_framed-ip",
+  "d-py_binary_framed-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_buffered-ip",
+  "d-py_compact_buffered-ip-ssl",
+  "d-py_compact_framed-ip",
+  "d-py_compact_framed-ip-ssl",
+  "d-py_json_buffered-ip",
+  "d-py_json_buffered-ip-ssl",
+  "d-py_json_framed-ip",
+  "d-py_json_framed-ip-ssl",
+  "erl-nodejs_binary_buffered-ip",
+  "erl-nodejs_compact_buffered-ip",
+  "erl-perl_binary_buffered-ip-ssl",
+  "erl-perl_binary_framed-ip-ssl",
+  "erl-rb_binary-accel_buffered-ip",
+  "erl-rb_binary-accel_framed-ip",
+  "erl-rb_binary_buffered-ip",
+  "erl-rb_binary_framed-ip",
+  "erl-rb_compact_buffered-ip",
+  "erl-rb_compact_framed-ip",
+  "go-cpp_binary_http-ip",
+  "go-cpp_binary_http-ip-ssl",
+  "go-cpp_compact_http-ip",
+  "go-cpp_compact_http-ip-ssl",
+  "go-cpp_json_http-ip",
+  "go-cpp_json_http-ip-ssl",
+  "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_framed-ip",
+  "go-dart_binary_http-ip",
+  "go-dart_compact_http-ip",
+  "go-dart_json_framed-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-nodejs_json_framed-ip",
+  "go-perl_binary_buffered-ip-ssl",
+  "go-perl_binary_framed-ip-ssl",
+  "hs-csharp_binary_framed-ip",
+  "hs-csharp_compact_framed-ip",
+  "hs-csharp_json_framed-ip",
+  "hs-dart_binary_framed-ip",
+  "hs-dart_compact_framed-ip",
+  "hs-dart_json_framed-ip",
+  "hs-py3_json_buffered-ip",
+  "hs-py3_json_framed-ip",
+  "hs-py_json_buffered-ip",
+  "hs-py_json_framed-ip",
+  "java-d_compact_buffered-ip",
+  "java-d_compact_buffered-ip-ssl",
+  "java-d_compact_framed-ip",
+  "java-perl_binary_buffered-ip-ssl",
+  "java-perl_binary_fastframed-framed-ip-ssl",
+  "java-perl_binary_framed-ip-ssl",
+  "nodejs-perl_binary_buffered-ip-ssl",
+  "nodejs-perl_binary_framed-ip-ssl",
+  "perl-perl_binary_buffered-ip-ssl",
+  "perl-perl_binary_framed-ip-ssl",
+  "perl-php_binary_framed-ip",
+  "py-cpp_compact_buffered-ip",
+  "py-perl_accel-binary_buffered-ip-ssl",
+  "py-perl_accel-binary_framed-ip-ssl",
+  "py-perl_binary_buffered-ip-ssl",
+  "py-perl_binary_framed-ip-ssl",
+  "py3-perl_accel-binary_buffered-ip-ssl",
+  "py3-perl_accel-binary_framed-ip-ssl",
+  "py3-perl_binary_buffered-ip-ssl",
+  "py3-perl_binary_framed-ip-ssl"
+]
diff --git a/vendor/src/github.com/apache/thrift/test/lua/Makefile.am b/vendor/src/github.com/apache/thrift/test/lua/Makefile.am
new file mode 100644
index 00000000..ed8c5aee
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/lua/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+# Remove "MapType =" line to ignore some map bug for now
+stubs: ../ThriftTest.thrift $(THRIFT)
+	$(THRIFT) --gen lua $<
+	$(SED) -i.bak 's/MapType =//g' gen-lua/ThriftTest_ttypes.lua
+	$(RM) gen-lua/ThriftTest_ttypes.lua.bak
+
+precross: stubs
+
+clean-local:
+	$(RM) -r gen-lua
diff --git a/vendor/src/github.com/apache/thrift/test/lua/test_basic_client.lua b/vendor/src/github.com/apache/thrift/test/lua/test_basic_client.lua
new file mode 100644
index 00000000..77d8d078
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/lua/test_basic_client.lua
@@ -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.
+
+
+require('TSocket')
+require('TBufferedTransport')
+require('TFramedTransport')
+require('THttpTransport')
+require('TCompactProtocol')
+require('TJsonProtocol')
+require('TBinaryProtocol')
+require('ThriftTest_ThriftTest')
+require('liblualongnumber')
+
+local client
+
+function teardown()
+  if client then
+    -- close the connection
+    client:close()
+  end
+end
+
+function parseArgs(rawArgs)
+  local opt = {
+    protocol='binary',
+    transport='buffered',
+    port='9090',
+  }
+  for i, str in pairs(rawArgs) do
+    if i > 0 then
+      k, v = string.match(str, '--(%w+)=(%w+)')
+      assert(opt[k] ~= nil, 'Unknown argument')
+      opt[k] = v
+    end
+  end
+  return opt
+end
+
+function assertEqual(val1, val2, msg)
+  assert(val1 == val2, msg)
+end
+
+function testBasicClient(rawArgs)
+  local opt = parseArgs(rawArgs)
+  local socket = TSocket:new{
+    port = tonumber(opt.port)
+  }
+  assert(socket, 'Failed to create client socket')
+  socket:setTimeout(5000)
+
+  local transports = {
+    buffered = TBufferedTransport,
+    framed = TFramedTransport,
+    http = THttpTransport,
+  }
+  assert(transports[opt.transport] ~= nil)
+  local transport = transports[opt.transport]:new{
+    trans = socket,
+    isServer = false
+  }
+
+  local protocols = {
+    binary = TBinaryProtocol,
+    compact = TCompactProtocol,
+    json = TJSONProtocol,
+  }
+  assert(protocols[opt.protocol] ~= nil)
+  local protocol = protocols[opt.protocol]:new{
+    trans = transport
+  }
+  assert(protocol, 'Failed to create binary protocol')
+
+  client = ThriftTestClient:new{
+    protocol = protocol
+  }
+  assert(client, 'Failed to create client')
+
+  -- Open the transport
+  local status, _ = pcall(transport.open, transport)
+  assert(status, 'Failed to connect to server')
+
+  -- String
+  assertEqual(client:testString('lala'),  'lala',  'Failed testString')
+  assertEqual(client:testString('wahoo'), 'wahoo', 'Failed testString')
+
+  -- Bool
+  assertEqual(client:testBool(true), true, 'Failed testBool true')
+  assertEqual(client:testBool(false), false, 'Failed testBool false')
+
+  -- Byte
+  assertEqual(client:testByte(0x01), 1,    'Failed testByte 1')
+  assertEqual(client:testByte(0x40), 64,   'Failed testByte 2')
+  assertEqual(client:testByte(0x7f), 127,  'Failed testByte 3')
+  assertEqual(client:testByte(0x80), -128, 'Failed testByte 4')
+  assertEqual(client:testByte(0xbf), -65,  'Failed testByte 5')
+  assertEqual(client:testByte(0xff), -1,   'Failed testByte 6')
+  assertEqual(client:testByte(128), -128,  'Failed testByte 7')
+  assertEqual(client:testByte(255), -1,    'Failed testByte 8')
+
+  -- I32
+  assertEqual(client:testI32(0x00000001), 1,           'Failed testI32 1')
+  assertEqual(client:testI32(0x40000000), 1073741824,  'Failed testI32 2')
+  assertEqual(client:testI32(0x7fffffff), 2147483647,  'Failed testI32 3')
+  assertEqual(client:testI32(0x80000000), -2147483648, 'Failed testI32 4')
+  assertEqual(client:testI32(0xbfffffff), -1073741825, 'Failed testI32 5')
+  assertEqual(client:testI32(0xffffffff), -1,          'Failed testI32 6')
+  assertEqual(client:testI32(2147483648), -2147483648, 'Failed testI32 7')
+  assertEqual(client:testI32(4294967295), -1,          'Failed testI32 8')
+
+  -- I64 (lua only supports 16 decimal precision so larger numbers are
+  -- initialized by their string value)
+  local long = liblualongnumber.new
+  assertEqual(client:testI64(long(0x0000000000000001)),
+                   long(1),
+                   'Failed testI64 1')
+  assertEqual(client:testI64(long(0x4000000000000000)),
+                   long(4611686018427387904),
+                   'Failed testI64 2')
+  assertEqual(client:testI64(long('0x7fffffffffffffff')),
+                   long('9223372036854775807'),
+                   'Failed testI64 3')
+  assertEqual(client:testI64(long(0x8000000000000000)),
+                   long(-9223372036854775808),
+                   'Failed testI64 4')
+  assertEqual(client:testI64(long('0xbfffffffffffffff')),
+                   long('-4611686018427387905'),
+                   'Failed testI64 5')
+  assertEqual(client:testI64(long('0xffffffffffffffff')),
+                   long(-1),
+                   'Failed testI64 6')
+
+  -- Double
+  assertEqual(
+      client:testDouble(1.23456789), 1.23456789, 'Failed testDouble 1')
+  assertEqual(
+      client:testDouble(0.123456789), 0.123456789, 'Failed testDouble 2')
+  assertEqual(
+      client:testDouble(0.123456789), 0.123456789, 'Failed testDouble 3')
+
+  -- TODO testBinary() ...
+	  
+  -- Accuracy of 16 decimal digits (rounds)
+  local a, b = 1.12345678906666663, 1.12345678906666661
+  assertEqual(a, b)
+  assertEqual(client:testDouble(a), b, 'Failed testDouble 5')
+
+  -- Struct
+  local o = Xtruct:new{
+    string_thing = 'Zero',
+    byte_thing = 1,
+    i32_thing = -3,
+    i64_thing = long(-5)
+  }
+  local r = client:testStruct(o)
+  assertEqual(o.string_thing, r.string_thing, 'Failed testStruct 1')
+  assertEqual(o.byte_thing, r.byte_thing, 'Failed testStruct 2')
+  assertEqual(o.i32_thing, r.i32_thing, 'Failed testStruct 3')
+  assertEqual(o.i64_thing, r.i64_thing, 'Failed testStruct 4')
+
+  -- TODO add list map set exception etc etc
+end
+
+testBasicClient(arg)
+teardown()
diff --git a/vendor/src/github.com/apache/thrift/test/lua/test_basic_server.lua b/vendor/src/github.com/apache/thrift/test/lua/test_basic_server.lua
new file mode 100644
index 00000000..acd2d79b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/lua/test_basic_server.lua
@@ -0,0 +1,142 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+
+--   http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+require('ThriftTest_ThriftTest')
+require('TSocket')
+require('TBufferedTransport')
+require('TFramedTransport')
+require('THttpTransport')
+require('TCompactProtocol')
+require('TJsonProtocol')
+require('TBinaryProtocol')
+require('TServer')
+require('liblualongnumber')
+
+--------------------------------------------------------------------------------
+-- Handler
+TestHandler = ThriftTestIface:new{}
+
+-- Stops the server
+function TestHandler:testVoid()
+end
+
+function TestHandler:testString(str)
+  return str
+end
+
+function TestHandler:testBool(bool)
+  return bool
+end
+
+function TestHandler:testByte(byte)
+  return byte
+end
+
+function TestHandler:testI32(i32)
+  return i32
+end
+
+function TestHandler:testI64(i64)
+  return i64
+end
+
+function TestHandler:testDouble(d)
+  return d
+end
+
+function TestHandler:testBinary(by)
+  return by
+end
+
+function TestHandler:testStruct(thing)
+  return thing
+end
+
+--------------------------------------------------------------------------------
+-- Test
+local server
+
+function teardown()
+  if server then
+    server:close()
+  end
+end
+
+function parseArgs(rawArgs)
+  local opt = {
+    protocol='binary',
+    transport='buffered',
+    port='9090',
+  }
+  for i, str in pairs(rawArgs) do
+    if i > 0 then
+      k, v = string.match(str, '--(%w+)=(%w+)')
+      assert(opt[k] ~= nil, 'Unknown argument')
+      opt[k] = v
+    end
+  end
+  return opt
+end
+
+function testBasicServer(rawArgs)
+  local opt = parseArgs(rawArgs)
+  -- Handler & Processor
+  local handler = TestHandler:new{}
+  assert(handler, 'Failed to create handler')
+  local processor = ThriftTestProcessor:new{
+    handler = handler
+  }
+  assert(processor, 'Failed to create processor')
+
+  -- Server Socket
+  local socket = TServerSocket:new{
+    port = opt.port
+  }
+  assert(socket, 'Failed to create server socket')
+
+  -- Transport & Factory
+  local transports = {
+    buffered = TBufferedTransportFactory,
+    framed = TFramedTransportFactory,
+    http = THttpTransportFactory,
+  }
+  assert(transports[opt.transport], 'Failed to create framed transport factory')
+  local trans_factory = transports[opt.transport]:new{}
+  local protocols = {
+    binary = TBinaryProtocolFactory,
+    compact = TCompactProtocolFactory,
+    json = TJSONProtocolFactory,
+  }
+  local prot_factory = protocols[opt.protocol]:new{}
+  assert(prot_factory, 'Failed to create binary protocol factory')
+
+  -- Simple Server
+  server = TSimpleServer:new{
+    processor = processor,
+    serverTransport = socket,
+    transportFactory = trans_factory,
+    protocolFactory = prot_factory
+  }
+  assert(server, 'Failed to create server')
+
+  -- Serve
+  server:serve()
+  server = nil
+end
+
+testBasicServer(arg)
+teardown()
diff --git a/vendor/src/github.com/apache/thrift/test/ocaml/Makefile b/vendor/src/github.com/apache/thrift/test/ocaml/Makefile
new file mode 100644
index 00000000..a543ce58
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/ocaml/Makefile
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+all:
+	cd client; make; cd ..; cd server; make
+clean:
+	cd client; make clean; cd ..; cd server; make clean
+
diff --git a/vendor/src/github.com/apache/thrift/test/ocaml/client/Makefile b/vendor/src/github.com/apache/thrift/test/ocaml/client/Makefile
new file mode 100644
index 00000000..806ed20a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/ocaml/client/Makefile
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SOURCES = ../gen-ocaml/ThriftTest_types.ml ../gen-ocaml/ThriftTest_consts.ml ../gen-ocaml/SecondService.ml ../gen-ocaml/ThriftTest.ml TestClient.ml
+RESULT = tc
+INCDIRS = "../../../lib/ocaml/src/" "../gen-ocaml/"
+LIBS = unix thrift
+all: nc
+OCAMLMAKEFILE = ../../../lib/ocaml/OCamlMakefile
+include $(OCAMLMAKEFILE)
diff --git a/vendor/src/github.com/apache/thrift/test/ocaml/client/TestClient.ml b/vendor/src/github.com/apache/thrift/test/ocaml/client/TestClient.ml
new file mode 100644
index 00000000..91783ae4
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/ocaml/client/TestClient.ml
@@ -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.
+*)
+
+open Thrift;;
+open ThriftTest_types;;
+
+let s = new TSocket.t "127.0.0.1" 9090;;
+let p = new TBinaryProtocol.t s;;
+let c = new ThriftTest.client p p;;
+let sod = function
+    Some v -> v
+  | None -> raise Thrift_error;;
+
+s#opn;
+print_string (c#testString "bya");
+print_char '\n';
+print_int (c#testByte 8);
+print_char '\n';
+print_int (c#testByte (-8));
+print_char '\n';
+print_int (c#testI32 32);
+print_char '\n';
+print_string (Int64.to_string (c#testI64 64L));
+print_char '\n';
+print_float (c#testDouble 3.14);
+print_char '\n';
+
+let l = [1;2;3;4] in
+  if l = (c#testList l) then print_string "list ok\n" else print_string "list fail\n";;
+let h = Hashtbl.create 5 in
+let a = Hashtbl.add h in
+  for i=1 to 10 do
+    a i (10*i)
+  done;
+  let r = c#testMap h in
+    for i=1 to 10 do
+      try
+        let g = Hashtbl.find r i in
+          print_int i;
+          print_char ' ';
+          print_int g;
+          print_char '\n'
+      with Not_found -> print_string ("Can't find "^(string_of_int i)^"\n")
+    done;;
+
+let s = Hashtbl.create 5 in
+let a = Hashtbl.add s in
+  for i = 1 to 10 do
+    a i true
+  done;
+  let r = c#testSet s in
+    for i = 1 to 10 do
+      try
+        let g = Hashtbl.find r i in
+          print_int i;
+          print_char '\n'
+      with Not_found -> print_string ("Can't find "^(string_of_int i)^"\n")
+    done;;
+try
+  c#testException "Xception"
+with Xception _ -> print_string "testException ok\n";;
+try
+  ignore(c#testMultiException "Xception" "bya")
+with Xception e -> Printf.printf "%d %s\n" (sod e#get_errorCode) (sod e#get_message);;
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/ocaml/server/Makefile b/vendor/src/github.com/apache/thrift/test/ocaml/server/Makefile
new file mode 100644
index 00000000..44dcac76
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/ocaml/server/Makefile
@@ -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.
+#
+
+SOURCES = ../gen-ocaml/ThriftTest_types.ml ../gen-ocaml/ThriftTest_consts.ml ../gen-ocaml/SecondService.ml ../gen-ocaml/ThriftTest.ml TestServer.ml
+RESULT = ts
+INCDIRS = "../../../lib/ocaml/src/" "../gen-ocaml/"
+LIBS = thrift
+THREADS = yes
+all: nc
+OCAMLMAKEFILE = ../../../lib/ocaml/OCamlMakefile
+include $(OCAMLMAKEFILE)
diff --git a/vendor/src/github.com/apache/thrift/test/ocaml/server/TestServer.ml b/vendor/src/github.com/apache/thrift/test/ocaml/server/TestServer.ml
new file mode 100644
index 00000000..efe0f4b2
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/ocaml/server/TestServer.ml
@@ -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.
+*)
+
+open Thrift
+open ThriftTest_types
+
+let p = Printf.printf;;
+exception Die;;
+let sod = function
+    Some v -> v
+  | None -> raise Die;;
+
+
+class test_handler =
+object (self)
+  inherit ThriftTest.iface
+  method testVoid = p "testVoid()\n"
+  method testString x = p "testString(%s)\n" (sod x); (sod x)
+  method testByte x = p "testByte(%d)\n" (sod x); (sod x)
+  method testI32 x = p "testI32(%d)\n" (sod x); (sod x)
+  method testI64 x = p "testI64(%s)\n" (Int64.to_string (sod x)); (sod x)
+  method testDouble x = p "testDouble(%f)\n" (sod x); (sod x)
+  method testBinary x = p "testBinary(%s)\n" (sod x); (sod x)
+  method testStruct x = p "testStruct(---)\n"; (sod x)
+  method testNest x = p "testNest(---)\n"; (sod x)
+  method testMap x = p "testMap(---)\n"; (sod x)
+  method testSet x = p "testSet(---)\n"; (sod x)
+  method testList x = p "testList(---)\n"; (sod x)
+  method testEnum x = p "testEnum(---)\n"; (sod x)
+  method testTypedef x = p "testTypedef(---)\n"; (sod x)
+  method testMapMap x = p "testMapMap(%d)\n" (sod x);
+    let mm = Hashtbl.create 3 in
+    let pos = Hashtbl.create 7 in
+    let neg = Hashtbl.create 7 in
+      for i=1 to 4 do
+        Hashtbl.add pos i i;
+        Hashtbl.add neg (-i) (-i);
+      done;
+      Hashtbl.add mm 4 pos;
+      Hashtbl.add mm (-4) neg;
+      mm
+  method testInsanity x = p "testInsanity()\n";
+    p "testinsanity()\n";
+    let hello = new xtruct in
+    let goodbye = new xtruct in
+    let crazy = new insanity in
+    let looney = new insanity in
+    let cumap = Hashtbl.create 7 in
+    let insane = Hashtbl.create 7 in
+    let firstmap = Hashtbl.create 7 in
+    let secondmap = Hashtbl.create 7 in
+      hello#set_string_thing "Hello2";
+      hello#set_byte_thing 2;
+      hello#set_i32_thing 2;
+      hello#set_i64_thing 2L;
+      goodbye#set_string_thing "Goodbye4";
+      goodbye#set_byte_thing 4;
+      goodbye#set_i32_thing 4;
+      goodbye#set_i64_thing 4L;
+      Hashtbl.add cumap Numberz.EIGHT 8L;
+      Hashtbl.add cumap Numberz.FIVE 5L;
+      crazy#set_userMap cumap;
+      crazy#set_xtructs [goodbye; hello];
+      Hashtbl.add firstmap Numberz.TWO crazy;
+      Hashtbl.add firstmap Numberz.THREE crazy;
+      Hashtbl.add secondmap Numberz.SIX looney;
+      Hashtbl.add insane 1L firstmap;
+      Hashtbl.add insane 2L secondmap;
+      insane
+  method testMulti a0 a1 a2 a3 a4 a5 =
+    p "testMulti()\n";
+    let hello = new xtruct in
+      hello#set_string_thing "Hello2";
+      hello#set_byte_thing (sod a0);
+      hello#set_i32_thing (sod a1);
+      hello#set_i64_thing (sod a2);
+      hello
+  method testException s =
+    p "testException(%S)\n" (sod s);
+    if (sod s) = "Xception" then
+      let x = new xception in
+        x#set_errorCode 1001;
+        x#set_message "This is an Xception";
+        raise (Xception x)
+    else ()
+  method testMultiException a0 a1 =
+    p "testMultiException(%S, %S)\n" (sod a0) (sod a1);
+    if (sod a0) = "Xception" then
+      let x = new xception in
+        x#set_errorCode 1001;
+        x#set_message "This is an Xception";
+        raise (Xception x)
+    else (if (sod a0) = "Xception2" then
+              let x = new xception2 in
+              let s = new xtruct in
+                x#set_errorCode 2002;
+                s#set_string_thing "This as an Xception2";
+                x#set_struct_thing s;
+                raise (Xception2 x)
+          else ());
+    let res = new xtruct in
+      res#set_string_thing (sod a1);
+      res
+  method testOneway i =
+    Unix.sleep (sod i)
+end;;
+
+let h = new test_handler in
+let proc = new ThriftTest.processor h in
+let port = 9090 in
+let pf = new TBinaryProtocol.factory in
+let server = new TThreadedServer.t
+  proc
+  (new TServerSocket.t port)
+  (new Transport.factory)
+  pf
+  pf
+in
+  server#serve
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/perl/Makefile.am b/vendor/src/github.com/apache/thrift/test/perl/Makefile.am
new file mode 100644
index 00000000..d975f693
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/perl/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: ../ThriftTest.thrift
+	$(THRIFT) --gen perl ../ThriftTest.thrift
+
+precross: stubs
+
+check: stubs
+
+clean-local:
+	$(RM) -r gen-perl
+
diff --git a/vendor/src/github.com/apache/thrift/test/perl/TestClient.pl b/vendor/src/github.com/apache/thrift/test/perl/TestClient.pl
new file mode 100644
index 00000000..7ec32bf3
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/perl/TestClient.pl
@@ -0,0 +1,427 @@
+#!/usr/bin/env perl
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 5.6.0;
+use strict;
+use warnings;
+use Data::Dumper;
+use Getopt::Long qw(GetOptions);
+use Time::HiRes qw(gettimeofday);
+
+use lib '../../lib/perl/lib';
+use lib 'gen-perl';
+
+use Thrift;
+use Thrift::BinaryProtocol;
+use Thrift::BufferedTransport;
+use Thrift::FramedTransport;
+use Thrift::SSLSocket;
+use Thrift::Socket;
+use Thrift::UnixSocket;
+
+use ThriftTest::ThriftTest;
+use ThriftTest::Types;
+
+$|++;
+
+sub usage {
+    print <                       Use a unix domain socket.
+  --help                                       Show usage.
+  --port                 9090         Port to use.
+  --protocol {binary}             binary       Protocol to use.
+  --ssl                                        If present, use SSL.
+  --transport {buffered|framed}   buffered     Transport to use.
+                                   
+EOF
+}
+
+my %opts = (
+    'port' => 9090,
+    'protocol' => 'binary',
+    'transport' => 'buffered'
+);
+
+GetOptions(\%opts, qw (
+    cert=s
+    domain-socket=s
+    help
+    host=s
+    port=i
+    protocol=s
+    ssl
+    transport=s
+)) || exit 1;
+
+if ($opts{help}) {
+    usage();
+    exit 0;
+}
+
+if ($opts{ssl} and not defined $opts{cert}) {
+    usage();
+    exit 1;
+}
+
+my $socket = undef;
+if ($opts{"domain-socket"}) {
+    $socket = new Thrift::UnixSocket($opts{"domain-socket"});
+} elsif ($opts{ssl}) {
+	$socket = new Thrift::SSLSocket($opts{host}, $opts{port});
+} else {
+	$socket = new Thrift::Socket($opts{host}, $opts{port});
+}
+
+my $transport;
+if ($opts{transport} eq 'buffered') {
+    $transport = new Thrift::BufferedTransport($socket, 1024, 1024);
+} elsif ($opts{transport} eq 'framed') {
+    $transport = new Thrift::FramedTransport($socket);
+} else {
+    usage();
+    exit 1;
+}
+
+my $protocol;
+if ($opts{protocol} eq 'binary') {
+    $protocol = new Thrift::BinaryProtocol($transport);
+} else {
+    usage();
+    exit 1;
+}
+
+my $testClient = new ThriftTest::ThriftTestClient($protocol);
+
+eval {
+  $transport->open();
+}; 
+if($@){
+    die(Dumper($@));
+}
+my $start = gettimeofday();
+
+#
+# VOID TEST
+#
+print("testVoid()");
+$testClient->testVoid();
+print(" = void\n");
+
+#
+# STRING TEST
+#
+print("testString(\"Test\")");
+my $s = $testClient->testString("Test");
+print(" = \"$s\"\n");
+
+#
+# BOOL TEST
+#
+print("testBool(1)");
+my $t = $testClient->testBool(1);
+print(" = $t\n");
+print("testBool(0)");
+my $f = $testClient->testBool(0);
+print(" = $f\n");
+
+
+#
+# BYTE TEST
+#
+print("testByte(1)");
+my $u8 = $testClient->testByte(1);
+print(" = $u8\n");
+
+#
+# I32 TEST
+#
+print("testI32(-1)");
+my $i32 = $testClient->testI32(-1);
+print(" = $i32\n");
+
+#
+#I64 TEST
+#
+print("testI64(-34359738368)");
+my $i64 = $testClient->testI64(-34359738368);
+print(" = $i64\n");
+
+#
+# DOUBLE TEST
+#
+print("testDouble(-852.234234234)");
+my $dub = $testClient->testDouble(-852.234234234);
+print(" = $dub\n");
+
+#
+# BINARY TEST   ---  TODO
+#
+
+
+#
+# STRUCT TEST
+#
+print("testStruct({\"Zero\", 1, -3, -5})");
+my $out = new ThriftTest::Xtruct();
+$out->string_thing("Zero");
+$out->byte_thing(1);
+$out->i32_thing(-3);
+$out->i64_thing(-5);
+my $in = $testClient->testStruct($out);
+print(" = {\"".$in->string_thing."\", ".
+        $in->byte_thing.", ".
+        $in->i32_thing.", ".
+        $in->i64_thing."}\n");
+
+#
+# NESTED STRUCT TEST
+#
+print("testNest({1, {\"Zero\", 1, -3, -5}, 5}");
+my $out2 = new ThriftTest::Xtruct2();
+$out2->byte_thing(1);
+$out2->struct_thing($out);
+$out2->i32_thing(5);
+my $in2 = $testClient->testNest($out2);
+$in = $in2->struct_thing;
+print(" = {".$in2->byte_thing.", {\"".
+      $in->string_thing."\", ".
+      $in->byte_thing.", ".
+      $in->i32_thing.", ".
+      $in->i64_thing."}, ".
+      $in2->i32_thing."}\n");
+
+#
+# MAP TEST
+#
+my $mapout = {};
+for (my $i = 0; $i < 5; ++$i) {
+  $mapout->{$i} = $i-10;
+}
+print("testMap({");
+my $first = 1;
+while( my($key,$val) = each %$mapout) {
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key => $val");
+}
+print("})");
+
+
+my $mapin = $testClient->testMap($mapout);
+print(" = {");
+
+$first = 1;
+while( my($key,$val) = each %$mapin){
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key => $val");
+}
+print("}\n");
+
+#
+# SET TEST
+#
+my $setout = [];
+for (my $i = -2; $i < 3; ++$i) {
+    push(@$setout, $i);
+}
+
+print("testSet({".join(",",@$setout)."})");
+
+my $setin = $testClient->testSet($setout);
+
+print(" = {".join(",",@$setout)."}\n");
+
+#
+# LIST TEST
+#
+my $listout = [];
+for (my $i = -2; $i < 3; ++$i) {
+    push(@$listout, $i);
+}
+
+print("testList({".join(",",@$listout)."})");
+
+my $listin = $testClient->testList($listout);
+
+print(" = {".join(",",@$listin)."}\n");
+
+#
+# ENUM TEST
+#
+print("testEnum(ONE)");
+my $ret = $testClient->testEnum(ThriftTest::Numberz::ONE);
+print(" = $ret\n");
+
+print("testEnum(TWO)");
+$ret = $testClient->testEnum(ThriftTest::Numberz::TWO);
+print(" = $ret\n");
+
+print("testEnum(THREE)");
+$ret = $testClient->testEnum(ThriftTest::Numberz::THREE);
+print(" = $ret\n");
+
+print("testEnum(FIVE)");
+$ret = $testClient->testEnum(ThriftTest::Numberz::FIVE);
+print(" = $ret\n");
+
+print("testEnum(EIGHT)");
+$ret = $testClient->testEnum(ThriftTest::Numberz::EIGHT);
+print(" = $ret\n");
+
+#
+# TYPEDEF TEST
+#
+print("testTypedef(309858235082523)");
+my $uid = $testClient->testTypedef(309858235082523);
+print(" = $uid\n");
+
+#
+# NESTED MAP TEST
+#
+print("testMapMap(1)");
+my $mm = $testClient->testMapMap(1);
+print(" = {");
+while( my ($key,$val) = each %$mm) {
+    print("$key => {");
+    while( my($k2,$v2) = each %$val) {
+        print("$k2 => $v2, ");
+    }
+    print("}, ");
+}
+print("}\n");
+
+#
+# INSANITY TEST
+#
+my $insane = new ThriftTest::Insanity();
+$insane->{userMap}->{ThriftTest::Numberz::FIVE} = 5000;
+my $truck = new ThriftTest::Xtruct();
+$truck->string_thing("Hello2");
+$truck->byte_thing(2);
+$truck->i32_thing(2);
+$truck->i64_thing(2);
+my $truck2 = new ThriftTest::Xtruct();
+$truck2->string_thing("Goodbye4");
+$truck2->byte_thing(4);
+$truck2->i32_thing(4);
+$truck2->i64_thing(4);
+push(@{$insane->{xtructs}}, $truck);
+push(@{$insane->{xtructs}}, $truck2);
+
+print("testInsanity()");
+my $whoa = $testClient->testInsanity($insane);
+print(" = {");
+while( my ($key,$val) = each %$whoa) {
+    print("$key => {");
+    while( my($k2,$v2) = each %$val) {
+        print("$k2 => {");
+        my $userMap = $v2->{userMap};
+        print("{");
+        if (ref($userMap) eq "HASH") {
+            while( my($k3,$v3) = each %$userMap) {
+                print("$k3 => $v3, ");
+            }
+        }
+        print("}, ");
+
+        my $xtructs = $v2->{xtructs};
+        print("{");
+        if (ref($xtructs) eq "ARRAY") {
+            foreach my $x (@$xtructs) {
+                print("{\"".$x->{string_thing}."\", ".
+                      $x->{byte_thing}.", ".$x->{i32_thing}.", ".$x->{i64_thing}."}, ");
+            }
+        }
+        print("}");
+
+        print("}, ");
+    }
+    print("}, ");
+}
+print("}\n");
+
+#
+# EXCEPTION TEST
+#
+print("testException('Xception')");
+eval {
+    $testClient->testException('Xception');
+    print("  void\nFAILURE\n");
+}; if($@ && $@->UNIVERSAL::isa('ThriftTest::Xception')) {
+    print(' caught xception '.$@->{errorCode}.': '.$@->{message}."\n");
+}
+
+
+#
+# Normal tests done.
+#
+my $stop = gettimeofday();
+my $elp  = sprintf("%d",1000*($stop - $start), 0);
+print("Total time: $elp ms\n");
+
+#
+# Extraneous "I don't trust PHP to pack/unpack integer" tests
+#
+
+# Max I32
+my $num = 2**30 + 2**30 - 1;
+my $num2 = $testClient->testI32($num);
+if ($num != $num2) {
+    print "Missed max32 $num = $num2\n";
+}
+
+# Min I32
+$num = 0 - 2**31;
+$num2 = $testClient->testI32($num);
+if ($num != $num2) {
+    print "Missed min32 $num = $num2\n";
+}
+
+# Max Number I can get out of my perl
+$num = 2**40;
+$num2 = $testClient->testI64($num);
+if ($num != $num2) {
+    print "Missed max64 $num = $num2\n";
+}
+
+# Max Number I can get out of my perl
+$num = 0 - 2**40;
+$num2 = $testClient->testI64($num);
+if ($num != $num2) {
+    print "Missed min64 $num = $num2\n";
+}
+
+$transport->close();
+
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/perl/TestServer.pl b/vendor/src/github.com/apache/thrift/test/perl/TestServer.pl
new file mode 100644
index 00000000..4e0cc79b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/perl/TestServer.pl
@@ -0,0 +1,398 @@
+#!/usr/bin/env perl
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 5.6.0;
+use strict;
+use warnings;
+use Data::Dumper;
+use Getopt::Long qw(GetOptions);
+use Time::HiRes qw(gettimeofday);
+
+use lib '../../lib/perl/lib';
+use lib 'gen-perl';
+
+use Thrift;
+use Thrift::BinaryProtocol;
+use Thrift::BufferedTransport;
+use Thrift::FramedTransport;
+use Thrift::SSLServerSocket;
+use Thrift::ServerSocket;
+use Thrift::Server;
+use Thrift::UnixServerSocket;
+
+use ThriftTest::ThriftTest;
+use ThriftTest::Types;
+
+$|++;
+
+sub usage {
+    print <                       Use a unix domain socket.
+  --help                                       Show usage.
+  --key                                        Private key file for certificate.
+                                               Required if using --ssl and private key is
+                                               not in the certificate file.
+  --port                 9090         Port to use.
+  --protocol {binary}             binary       Protocol to use.
+  --ssl                                        If present, use SSL/TLS.
+  --transport {buffered|framed}   buffered     Transport to use.
+                                   
+EOF
+}
+
+my %opts = (
+    'port' => 9090,
+    'protocol' => 'binary',
+    'transport' => 'buffered'
+);
+
+GetOptions(\%opts, qw (
+    ca=s
+    cert=s
+    domain-socket=s
+    help
+    host=s
+    key=s
+    port=i
+    protocol=s
+    ssl
+    transport=s
+)) || exit 1;
+
+if ($opts{help}) {
+    usage();
+    exit 0;
+}
+
+if ($opts{ssl} and not defined $opts{cert}) {
+    usage();
+    exit 1;
+}
+
+my $handler = new ThriftTestHandler();
+my $processor = new ThriftTest::ThriftTestProcessor($handler);
+my $serversocket;
+if ($opts{"domain-socket"}) {
+    unlink($opts{"domain-socket"});
+    $serversocket = new Thrift::UnixServerSocket($opts{"domain-socket"});
+} elsif ($opts{ssl}) {
+    $serversocket = new Thrift::SSLServerSocket(\%opts);
+} else {
+    $serversocket = new Thrift::ServerSocket(\%opts);
+}
+my $transport;
+if ($opts{transport} eq 'buffered') {
+    $transport = new Thrift::BufferedTransportFactory();
+} elsif ($opts{transport} eq 'framed') {
+    $transport = new Thrift::FramedTransportFactory();
+} else {
+    usage();
+    exit 1;
+}
+my $protocol;
+if ($opts{protocol} eq 'binary') {
+    $protocol = new Thrift::BinaryProtocolFactory();
+} else {
+    usage();
+    exit 1;
+}
+
+my $ssltag = '';
+if ($opts{ssl}) {
+    $ssltag = "(SSL)";
+}
+my $listening_on = "$opts{port} $ssltag";
+if ($opts{"domain-socket"}) {
+    $listening_on = $opts{"domain-socket"};
+}
+my $server = new Thrift::SimpleServer($processor, $serversocket, $transport, $protocol);
+print "Starting \"simple\" server ($opts{transport}/$opts{protocol}) listen on: $listening_on\n";
+$server->serve();
+
+###    
+### Test server implementation
+###
+
+package ThriftTestHandler;
+
+use base qw( ThriftTest::ThriftTestIf );
+
+sub new {
+    my $classname = shift;
+    my $self = {};
+    return bless($self, $classname);
+}
+
+sub testVoid() {
+  print("testVoid()\n"); 
+}
+
+sub testString() {
+  my $self = shift;
+  my $thing = shift;
+  print("testString($thing)\n");
+  return $thing;
+}
+
+sub testBool() {
+  my $self = shift;
+  my $thing = shift;
+  my $str = $thing ? "true" : "false";
+  print("testBool($str)\n");
+  return $thing;
+}
+
+sub testByte() {
+  my $self = shift;
+  my $thing = shift;
+  print("testByte($thing)\n");
+  return $thing;
+}
+
+sub testI32() {
+  my $self = shift;
+  my $thing = shift;
+  print("testI32($thing)\n");
+  return $thing;
+}
+
+sub testI64() {
+  my $self = shift;
+  my $thing = shift;
+  print("testI64($thing)\n");
+  return $thing;
+}
+
+sub testDouble() {
+  my $self = shift;
+  my $thing = shift;
+  print("testDouble($thing)\n");
+  return $thing;
+}
+
+sub testBinary() {
+    my $self = shift;
+    my $thing = shift;
+    my @bytes = split //, $thing;
+    print("testBinary(");
+    foreach (@bytes)
+    {
+        printf "%02lx", ord $_;
+    }
+    print(")\n");
+    return $thing;
+}
+
+sub testStruct() {
+  my $self = shift;
+  my $thing = shift;
+  printf("testStruct({\"%s\", %d, %d, %lld})\n",
+           $thing->{string_thing},
+           $thing->{byte_thing},
+           $thing->{i32_thing},
+           $thing->{i64_thing});
+  return $thing;
+}
+
+sub testNest() {
+  my $self = shift;
+  my $nest = shift;
+  my $thing = $nest->{struct_thing};
+  printf("testNest({%d, {\"%s\", %d, %d, %lld}, %d})\n",
+           $nest->{byte_thing},
+           $thing->{string_thing},
+           $thing->{byte_thing},
+           $thing->{i32_thing},
+           $thing->{i64_thing},
+           $nest->{i32_thing});
+  return $nest;
+}
+
+sub testMap() {
+  my $self = shift;
+  my $thing = shift;
+  print("testMap({");
+  my $first = 1;
+  foreach my $key (keys %$thing) {
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key => $thing->{$key}");
+  }
+  print("})\n");
+  return $thing;
+}
+
+sub testStringMap() {
+  my $self = shift;
+  my $thing = shift;
+  print("testStringMap({");
+  my $first = 1;
+  foreach my $key (keys %$thing) {
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key => $thing->{$key}");
+  }
+  print("})\n");
+  return $thing;
+}
+
+sub testSet() {
+  my $self = shift;
+  my $thing = shift;
+  my @arr;
+  my $result = \@arr;
+  print("testSet({");
+  my $first = 1;
+  foreach my $key (keys %$thing) {
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key");
+    push($result, $key);
+  }
+  print("})\n");
+  return $result;
+}
+
+sub testList() {
+  my $self = shift;
+  my $thing = shift;
+  print("testList({");
+  my $first = 1;
+  foreach my $key (@$thing) {
+    if ($first) {
+        $first = 0;
+    } else {
+        print(", ");
+    }
+    print("$key");
+  }
+  print("})\n");
+  return $thing;
+}
+
+sub testEnum() {
+  my $self = shift;
+  my $thing = shift;
+  print("testEnum($thing)\n");
+  return $thing;
+}
+
+sub testTypedef() {
+  my $self = shift;
+  my $thing = shift;
+  print("testTypedef($thing)\n");
+  return $thing;
+}
+
+sub testMapMap() {
+  my $self = shift;
+  my $hello = shift;
+  
+  printf("testMapMap(%d)\n", $hello);
+  my $result = { 4 => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }, -4 => { -1 => -1, -2 => -2, -3 => -3, -4 => -4 } };
+  return $result;
+}
+
+sub testInsanity() {
+  my $self = shift;
+  my $argument = shift;
+  print("testInsanity()\n");
+
+  my $hello = new ThriftTest::Xtruct({string_thing => "Hello2", byte_thing => 2, i32_thing => 2, i64_thing => 2});
+  my @hellos;
+  push(@hellos, $hello);
+  my $goodbye = new ThriftTest::Xtruct({string_thing => "Goodbye4", byte_thing => 4, i32_thing => 4, i64_thing => 4});
+  my @goodbyes;
+  push(@goodbyes, $goodbye);
+  my $crazy = new ThriftTest::Insanity({userMap => { ThriftTest::Numberz::EIGHT => 8 }, xtructs => \@goodbyes});
+  my $loony = new ThriftTest::Insanity();
+  my $result = { 1 => { ThriftTest::Numberz::TWO => $argument, ThriftTest::Numberz::THREE => $argument },
+                 2 => { ThriftTest::Numberz::SIX => $loony } };
+  return $result;
+}
+
+sub testMulti() {
+  my $self = shift;
+  my $arg0 = shift;
+  my $arg1 = shift;
+  my $arg2 = shift;
+  my $arg3 = shift;
+  my $arg4 = shift;
+  my $arg5 = shift;
+  
+  print("testMulti()\n");
+  return new ThriftTest::Xtruct({string_thing => "Hello2", byte_thing => $arg0, i32_thing => $arg1, i64_thing => $arg2});
+}
+
+sub testException() {
+  my $self = shift;
+  my $arg = shift;
+  print("testException($arg)\n");
+  if ($arg eq "Xception") {
+    die new ThriftTest::Xception({errorCode => 1001, message => $arg});
+  } elsif ($arg eq "TException") {
+    die "astring"; # all unhandled exceptions become TExceptions
+  } else {
+    return new ThriftTest::Xtruct({string_thing => $arg});
+  }
+}
+
+sub testMultiException() {
+  my $self = shift;
+  my $arg0 = shift;
+  my $arg1 = shift;
+
+  printf("testMultiException(%s, %s)\n", $arg0, $arg1);
+  if ($arg0 eq "Xception") {
+    die new ThriftTest::Xception({errorCode => 1001, message => "This is an Xception"});
+  } elsif ($arg0 eq "Xception2") {
+    my $struct_thing = new ThriftTest::Xtruct({string_thing => "This is an Xception2"});
+    die new ThriftTest::Xception2({errorCode => 2002, struct_thing => $struct_thing});
+  } else {
+    return new ThriftTest::Xtruct({string_thing => $arg1});
+  }
+}
+
+sub testOneway() {
+  my $self = shift;
+  my $sleepFor = shift;
+  print("testOneway($sleepFor): Sleeping...\n");
+  sleep $sleepFor;
+  print("testOneway($sleepFor): done sleeping!\n");
+}
+
+
+1;
diff --git a/vendor/src/github.com/apache/thrift/test/php/Makefile.am b/vendor/src/github.com/apache/thrift/test/php/Makefile.am
new file mode 100644
index 00000000..7c4347ff
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/php/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.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: ../ThriftTest.thrift
+	$(THRIFT) --gen php ../ThriftTest.thrift
+	$(THRIFT) --gen php:inlined ../ThriftTest.thrift
+	$(MKDIR_P) gen-php-psr4
+	$(THRIFT) -out gen-php-psr4 --gen php:psr4 ../ThriftTest.thrift
+
+precross: stubs
+
+check: stubs
+
+clean-local:
+	$(RM) -r gen-php gen-phpi gen-php-psr4
+
+client: stubs
+	php TestClient.php
diff --git a/vendor/src/github.com/apache/thrift/test/php/TestClient.php b/vendor/src/github.com/apache/thrift/test/php/TestClient.php
new file mode 100644
index 00000000..c1f64359
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/php/TestClient.php
@@ -0,0 +1,492 @@
+registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib');
+if ($GEN_DIR === 'gen-php-psr4') {
+  $loader->registerNamespace('ThriftTest', $GEN_DIR);
+} else {
+  $loader->registerDefinition('ThriftTest', $GEN_DIR);
+}
+$loader->register();
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 the Thrift base */
+/** Include the protocols */
+use Thrift\Protocol\TBinaryProtocol;
+use Thrift\Protocol\TBinaryProtocolAccelerated;
+use Thrift\Protocol\TCompactProtocol;
+use Thrift\Protocol\TJSONProtocol;
+
+/** Include the socket layer */
+use Thrift\Transport\TSocket;
+use Thrift\Transport\TSocketPool;
+
+/** Include the socket layer */
+use Thrift\Transport\TFramedTransport;
+use Thrift\Transport\TBufferedTransport;
+
+function makeProtocol($transport, $PROTO)
+{
+  if ($PROTO == 'binary') {
+    return new TBinaryProtocol($transport);
+  } else if ($PROTO == 'compact') {
+    return new TCompactProtocol($transport);
+  } else if ($PROTO == 'json') {
+    return new TJSONProtocol($transport);
+  } else if ($PROTO == 'accel') {
+    if (!function_exists('thrift_protocol_write_binary')) {
+      echo "Acceleration extension is not loaded\n";
+      exit(1);
+    }
+    return new TBinaryProtocolAccelerated($transport);
+  }
+
+  echo "--protocol must be one of {binary|compact|json|accel}\n";
+  exit(1);
+}
+
+$host = 'localhost';
+$port = 9090;
+
+if ($argc > 1) {
+  $host = $argv[0];
+}
+
+if ($argc > 2) {
+  $host = $argv[1];
+}
+
+foreach ($argv as $arg) {
+  if (substr($arg, 0, 7) == '--port=') {
+    $port = substr($arg, 7);
+  } else if (substr($arg, 0, 12) == '--transport=') {
+    $MODE = substr($arg, 12);
+  } else if (substr($arg, 0, 11) == '--protocol=') {
+    $PROTO = substr($arg, 11);
+  } 
+}
+
+$hosts = array('localhost');
+
+$socket = new TSocket($host, $port);
+$socket = new TSocketPool($hosts, $port);
+$socket->setDebug(TRUE);
+
+if ($MODE == 'inline') {
+  $transport = $socket;
+  $testClient = new \ThriftTest\ThriftTestClient($transport);
+} else if ($MODE == 'framed') {
+  $framedSocket = new TFramedTransport($socket);
+  $transport = $framedSocket;
+  $protocol = makeProtocol($transport, $PROTO);
+  $testClient = new \ThriftTest\ThriftTestClient($protocol);
+} else {
+  $bufferedSocket = new TBufferedTransport($socket, 1024, 1024);
+  $transport = $bufferedSocket;
+  $protocol = makeProtocol($transport, $PROTO);
+  $testClient = new \ThriftTest\ThriftTestClient($protocol);
+}
+
+$transport->open();
+
+$start = microtime(true);
+
+define('ERR_BASETYPES', 1);
+define('ERR_STRUCTS', 2);
+define('ERR_CONTAINERS', 4);
+define('ERR_EXCEPTIONS', 8);
+define('ERR_UNKNOWN', 64);
+$exitcode = 0;
+/**
+ * VOID TEST
+ */
+print_r("testVoid()");
+$testClient->testVoid();
+print_r(" = void\n");
+
+function roundtrip($testClient, $method, $value) {
+  global $exitcode;
+  print_r("$method($value)");
+  $ret = $testClient->$method($value);
+  print_r(" = \"$ret\"\n");
+  if ($value !== $ret) {
+    print_r("*** FAILED ***\n");
+    $exitcode |= ERR_BASETYPES;
+  }
+}
+
+/**
+ * STRING TEST
+ */
+roundtrip($testClient, 'testString', "Test");
+
+/**
+ * BOOL TEST
+ */
+roundtrip($testClient, 'testBool', true);
+roundtrip($testClient, 'testBool', false);
+
+/**
+ * BYTE TEST
+ */
+roundtrip($testClient, 'testByte', 1);
+roundtrip($testClient, 'testByte', -1);
+roundtrip($testClient, 'testByte', 127);
+roundtrip($testClient, 'testByte', -128);
+
+/**
+ * I32 TEST
+ */
+roundtrip($testClient, 'testI32', -1);
+
+/**
+ * I64 TEST
+ */
+roundtrip($testClient, 'testI64', 0);
+roundtrip($testClient, 'testI64', 1);
+roundtrip($testClient, 'testI64', -1);
+roundtrip($testClient, 'testI64', -34359738368);
+
+/**
+ * DOUBLE TEST
+ */
+roundtrip($testClient, 'testDouble', -852.234234234);
+
+/**
+ * BINARY TEST  --  TODO
+ */
+
+/**
+ * STRUCT TEST
+ */
+print_r("testStruct({\"Zero\", 1, -3, -5})");
+$out = new \ThriftTest\Xtruct();
+$out->string_thing = "Zero";
+$out->byte_thing = 1;
+$out->i32_thing = -3;
+$out->i64_thing = -5;
+$in = $testClient->testStruct($out);
+print_r(" = {\"".$in->string_thing."\", ".
+        $in->byte_thing.", ".
+        $in->i32_thing.", ".
+        $in->i64_thing."}\n");
+
+if ($in != $out) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+/**
+ * NESTED STRUCT TEST
+ */
+print_r("testNest({1, {\"Zero\", 1, -3, -5}), 5}");
+$out2 = new \ThriftTest\Xtruct2();
+$out2->byte_thing = 1;
+$out2->struct_thing = $out;
+$out2->i32_thing = 5;
+$in2 = $testClient->testNest($out2);
+$in = $in2->struct_thing;
+print_r(" = {".$in2->byte_thing.", {\"".
+        $in->string_thing."\", ".
+        $in->byte_thing.", ".
+        $in->i32_thing.", ".
+        $in->i64_thing."}, ".
+        $in2->i32_thing."}\n");
+
+if ($in2 != $out2) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+/**
+ * MAP TEST
+ */
+$mapout = array();
+for ($i = 0; $i < 5; ++$i) {
+  $mapout[$i] = $i-10;
+}
+print_r("testMap({");
+$first = true;
+foreach ($mapout as $key => $val) {
+  if ($first) {
+    $first = false;
+  } else {
+    print_r(", ");
+  }
+  print_r("$key => $val");
+}
+print_r("})");
+
+$mapin = $testClient->testMap($mapout);
+print_r(" = {");
+$first = true;
+foreach ($mapin as $key => $val) {
+  if ($first) {
+    $first = false;
+  } else {
+    print_r(", ");
+  }
+  print_r("$key => $val");
+}
+print_r("}\n");
+
+if ($mapin != $mapout) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_CONTAINERS;
+}
+
+/**
+ * SET TEST
+ */
+$setout = array();;
+for ($i = -2; $i < 3; ++$i) {
+  $setout[$i]= true;
+}
+print_r("testSet({");
+echo implode(',', array_keys($setout));
+print_r("})");
+$setin = $testClient->testSet($setout);
+print_r(" = {");
+echo implode(', ', array_keys($setin));
+print_r("}\n");
+// Order of keys in set does not matter
+ksort($setin);
+if ($setout !== $setin) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_CONTAINERS;
+}
+// Regression test for corrupted array
+if ($setin[2] !== $setout[2] || is_int($setin[2])) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_CONTAINERS;
+}
+
+/**
+ * LIST TEST
+ */
+$listout = array();
+for ($i = -2; $i < 3; ++$i) {
+  $listout []= $i;
+}
+print_r("testList({");
+$first = true;
+foreach ($listout as $val) {
+  if ($first) {
+    $first = false;
+  } else {
+    print_r(", ");
+  }
+  print_r($val);
+}
+print_r("})");
+$listin = $testClient->testList($listout);
+print_r(" = {");
+$first = true;
+foreach ($listin as $val) {
+  if ($first) {
+    $first = false;
+  } else {
+    print_r(", ");
+  }
+  print_r($val);
+}
+print_r("}\n");
+if ($listin !== $listout) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_CONTAINERS;
+}
+
+/**
+ * ENUM TEST
+ */
+print_r("testEnum(ONE)");
+$ret = $testClient->testEnum(\ThriftTest\Numberz::ONE);
+print_r(" = $ret\n");
+if ($ret != \ThriftTest\Numberz::ONE) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+print_r("testEnum(TWO)");
+$ret = $testClient->testEnum(\ThriftTest\Numberz::TWO);
+print_r(" = $ret\n");
+if ($ret != \ThriftTest\Numberz::TWO) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+print_r("testEnum(THREE)");
+$ret = $testClient->testEnum(\ThriftTest\Numberz::THREE);
+print_r(" = $ret\n");
+if ($ret != \ThriftTest\Numberz::THREE) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+print_r("testEnum(FIVE)");
+$ret = $testClient->testEnum(\ThriftTest\Numberz::FIVE);
+print_r(" = $ret\n");
+if ($ret != \ThriftTest\Numberz::FIVE) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+print_r("testEnum(EIGHT)");
+$ret = $testClient->testEnum(\ThriftTest\Numberz::EIGHT);
+print_r(" = $ret\n");
+if ($ret != \ThriftTest\Numberz::EIGHT) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+/**
+ * TYPEDEF TEST
+ */
+print_r("testTypedef(309858235082523)");
+$uid = $testClient->testTypedef(309858235082523);
+print_r(" = $uid\n");
+if ($uid !== 309858235082523) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_STRUCTS;
+}
+
+/**
+ * NESTED MAP TEST
+ */
+print_r("testMapMap(1)");
+$mm = $testClient->testMapMap(1);
+print_r(" = {");
+foreach ($mm as $key => $val) {
+  print_r("$key => {");
+  foreach ($val as $k2 => $v2) {
+    print_r("$k2 => $v2, ");
+  }
+  print_r("}, ");
+}
+print_r("}\n");
+$expected_mm = [
+  -4 => [-4 => -4, -3 => -3, -2 => -2, -1 => -1],
+  4 => [4 => 4, 3 => 3, 2 => 2, 1 => 1],
+];
+if ($mm != $expected_mm) {
+    echo "**FAILED**\n";
+    $exitcode |= ERR_CONTAINERS;
+}
+
+/**
+ * INSANITY TEST
+ */
+$insane = new \ThriftTest\Insanity();
+$insane->userMap[\ThriftTest\Numberz::FIVE] = 5000;
+$truck = new \ThriftTest\Xtruct();
+$truck->string_thing = "Truck";
+$truck->byte_thing = 8;
+$truck->i32_thing = 8;
+$truck->i64_thing = 8;
+$insane->xtructs []= $truck;
+print_r("testInsanity()");
+$whoa = $testClient->testInsanity($insane);
+print_r(" = {");
+foreach ($whoa as $key => $val) {
+  print_r("$key => {");
+  foreach ($val as $k2 => $v2) {
+    print_r("$k2 => {");
+    $userMap = $v2->userMap;
+    print_r("{");
+    if (is_array($userMap)) {
+      foreach ($userMap as $k3 => $v3) {
+        print_r("$k3 => $v3, ");
+      }
+    }
+    print_r("}, ");
+
+    $xtructs = $v2->xtructs;
+    print_r("{");
+    if (is_array($xtructs)) {
+      foreach ($xtructs as $x) {
+        print_r("{\"".$x->string_thing."\", ".
+                $x->byte_thing.", ".$x->i32_thing.", ".$x->i64_thing."}, ");
+      }
+    }
+    print_r("}");
+
+    print_r("}, ");
+  }
+  print_r("}, ");
+}
+print_r("}\n");
+
+/**
+ * EXCEPTION TEST
+ */
+print_r("testException('Xception')");
+try {
+  $testClient->testException('Xception');
+  print_r("  void\nFAILURE\n");
+  $exitcode |= ERR_EXCEPTIONS;
+} catch (\ThriftTest\Xception $x) {
+  print_r(' caught xception '.$x->errorCode.': '.$x->message."\n");
+}
+
+
+/**
+ * Normal tests done.
+ */
+
+$stop = microtime(true);
+$elp = round(1000*($stop - $start), 0);
+print_r("Total time: $elp ms\n");
+
+/**
+ * Extraneous "I don't trust PHP to pack/unpack integer" tests
+ */
+
+// Max I32
+$num = pow(2, 30) + (pow(2, 30) - 1);
+roundtrip($testClient, 'testI32', $num);
+
+// Min I32
+$num = 0 - pow(2, 31);
+roundtrip($testClient, 'testI32', $num);
+
+// Max I64
+$num = pow(2, 62) + (pow(2, 62) - 1);
+roundtrip($testClient, 'testI64', $num);
+
+// Min I64
+$num = 0 - pow(2, 62) - pow(2, 62);
+roundtrip($testClient, 'testI64', $num);
+
+$transport->close();
+exit($exitcode);
diff --git a/vendor/src/github.com/apache/thrift/test/php/TestInline.php b/vendor/src/github.com/apache/thrift/test/php/TestInline.php
new file mode 100644
index 00000000..7066c461
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/php/TestInline.php
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/php/TestPsr4.php b/vendor/src/github.com/apache/thrift/test/php/TestPsr4.php
new file mode 100644
index 00000000..d30bf1c4
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/php/TestPsr4.php
@@ -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.
+ */
+
+
diff --git a/vendor/src/github.com/apache/thrift/test/py.tornado/Makefile.am b/vendor/src/github.com/apache/thrift/test/py.tornado/Makefile.am
new file mode 100644
index 00000000..a8e680a9
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.tornado/Makefile.am
@@ -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.
+#
+
+THRIFT = $(top_srcdir)/compiler/cpp/thrift
+
+thrift_gen: ../ThriftTest.thrift ../SmallTest.thrift
+	$(THRIFT) --gen py:tornado ../ThriftTest.thrift
+	$(THRIFT) --gen py:tornado ../SmallTest.thrift
+
+check: thrift_gen
+	./test_suite.py
+
+clean-local:
+	$(RM) -r gen-py.tornado
diff --git a/vendor/src/github.com/apache/thrift/test/py.tornado/setup.cfg b/vendor/src/github.com/apache/thrift/test/py.tornado/setup.cfg
new file mode 100644
index 00000000..ae587c4f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.tornado/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E402
+max-line-length = 100
diff --git a/vendor/src/github.com/apache/thrift/test/py.tornado/test_suite.py b/vendor/src/github.com/apache/thrift/test/py.tornado/test_suite.py
new file mode 100644
index 00000000..32d1c2e5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.tornado/test_suite.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import datetime
+import glob
+import sys
+import os
+import time
+import unittest
+
+basepath = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, basepath + '/gen-py.tornado')
+sys.path.insert(0, glob.glob(os.path.join(basepath, '../../lib/py/build/lib*'))[0])
+
+try:
+    __import__('tornado')
+except ImportError:
+    print("module `tornado` not found, skipping test")
+    sys.exit(0)
+
+from tornado import gen
+from tornado.testing import AsyncTestCase, get_unused_port, gen_test
+
+from thrift import TTornado
+from thrift.protocol import TBinaryProtocol
+from thrift.transport.TTransport import TTransportException
+
+from ThriftTest import ThriftTest
+from ThriftTest.ttypes import Xception, Xtruct
+
+
+class TestHandler(object):
+    def __init__(self, test_instance):
+        self.test_instance = test_instance
+
+    def testVoid(self):
+        pass
+
+    def testString(self, s):
+        return s
+
+    def testByte(self, b):
+        return b
+
+    def testI16(self, i16):
+        return i16
+
+    def testI32(self, i32):
+        return i32
+
+    def testI64(self, i64):
+        return i64
+
+    def testDouble(self, dub):
+        return dub
+
+    def testBinary(self, thing):
+        return thing
+
+    def testStruct(self, thing):
+        return thing
+
+    def testException(self, s):
+        if s == 'Xception':
+            x = Xception()
+            x.errorCode = 1001
+            x.message = s
+            raise x
+        elif s == 'throw_undeclared':
+            raise ValueError("foo")
+
+    def testOneway(self, seconds):
+        start = time.time()
+
+        def fire_oneway():
+            end = time.time()
+            self.test_instance.stop((start, end, seconds))
+
+        self.test_instance.io_loop.add_timeout(
+            datetime.timedelta(seconds=seconds),
+            fire_oneway)
+
+    def testNest(self, thing):
+        return thing
+
+    @gen.coroutine
+    def testMap(self, thing):
+        yield gen.moment
+        raise gen.Return(thing)
+
+    def testSet(self, thing):
+        return thing
+
+    def testList(self, thing):
+        return thing
+
+    def testEnum(self, thing):
+        return thing
+
+    def testTypedef(self, thing):
+        return thing
+
+
+class ThriftTestCase(AsyncTestCase):
+    def setUp(self):
+        super(ThriftTestCase, self).setUp()
+
+        self.port = get_unused_port()
+
+        # server
+        self.handler = TestHandler(self)
+        self.processor = ThriftTest.Processor(self.handler)
+        self.pfactory = TBinaryProtocol.TBinaryProtocolFactory()
+
+        self.server = TTornado.TTornadoServer(self.processor, self.pfactory, io_loop=self.io_loop)
+        self.server.bind(self.port)
+        self.server.start(1)
+
+        # client
+        transport = TTornado.TTornadoStreamTransport('localhost', self.port, io_loop=self.io_loop)
+        pfactory = TBinaryProtocol.TBinaryProtocolFactory()
+        self.io_loop.run_sync(transport.open)
+        self.client = ThriftTest.Client(transport, pfactory)
+
+    @gen_test
+    def test_void(self):
+        v = yield self.client.testVoid()
+        self.assertEqual(v, None)
+
+    @gen_test
+    def test_string(self):
+        v = yield self.client.testString('Python')
+        self.assertEqual(v, 'Python')
+
+    @gen_test
+    def test_byte(self):
+        v = yield self.client.testByte(63)
+        self.assertEqual(v, 63)
+
+    @gen_test
+    def test_i32(self):
+        v = yield self.client.testI32(-1)
+        self.assertEqual(v, -1)
+
+        v = yield self.client.testI32(0)
+        self.assertEqual(v, 0)
+
+    @gen_test
+    def test_i64(self):
+        v = yield self.client.testI64(-34359738368)
+        self.assertEqual(v, -34359738368)
+
+    @gen_test
+    def test_double(self):
+        v = yield self.client.testDouble(-5.235098235)
+        self.assertEqual(v, -5.235098235)
+
+    @gen_test
+    def test_struct(self):
+        x = Xtruct()
+        x.string_thing = "Zero"
+        x.byte_thing = 1
+        x.i32_thing = -3
+        x.i64_thing = -5
+        y = yield self.client.testStruct(x)
+
+        self.assertEqual(y.string_thing, "Zero")
+        self.assertEqual(y.byte_thing, 1)
+        self.assertEqual(y.i32_thing, -3)
+        self.assertEqual(y.i64_thing, -5)
+
+    def test_oneway(self):
+        self.client.testOneway(0)
+        start, end, seconds = self.wait(timeout=1)
+        self.assertAlmostEqual(seconds, (end - start), places=3)
+
+    @gen_test
+    def test_map(self):
+        """
+        TestHandler.testMap is a coroutine, this test checks if gen.Return() from a coroutine works.
+        """
+        expected = {1: 1}
+        res = yield self.client.testMap(expected)
+        self.assertEqual(res, expected)
+
+    @gen_test
+    def test_exception(self):
+        yield self.client.testException('Safe')
+
+        try:
+            yield self.client.testException('Xception')
+        except Xception as ex:
+            self.assertEqual(ex.errorCode, 1001)
+            self.assertEqual(ex.message, 'Xception')
+        else:
+            self.fail("should have gotten exception")
+        try:
+            yield self.client.testException('throw_undeclared')
+        except TTransportException as ex:
+            pass
+        else:
+            self.fail("should have gotten exception")
+
+
+def suite():
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    suite.addTest(loader.loadTestsFromTestCase(ThriftTestCase))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.TestProgram(defaultTest='suite',
+                         testRunner=unittest.TextTestRunner(verbosity=1))
diff --git a/vendor/src/github.com/apache/thrift/test/py.twisted/Makefile.am b/vendor/src/github.com/apache/thrift/test/py.twisted/Makefile.am
new file mode 100644
index 00000000..78cde22b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.twisted/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+TRIAL ?= trial
+
+stubs: ../ThriftTest.thrift ../SmallTest.thrift
+	$(THRIFT) --gen py:twisted ../ThriftTest.thrift
+	$(THRIFT) --gen py:twisted ../SmallTest.thrift
+
+check: stubs
+	$(TRIAL) ./test_suite.py
+
+clean-local:
+	$(RM) -r gen-py.twisted
diff --git a/vendor/src/github.com/apache/thrift/test/py.twisted/setup.cfg b/vendor/src/github.com/apache/thrift/test/py.twisted/setup.cfg
new file mode 100644
index 00000000..ae587c4f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.twisted/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+ignore = E402
+max-line-length = 100
diff --git a/vendor/src/github.com/apache/thrift/test/py.twisted/test_suite.py b/vendor/src/github.com/apache/thrift/test/py.twisted/test_suite.py
new file mode 100644
index 00000000..43149a4c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py.twisted/test_suite.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import sys
+import os
+import glob
+import time
+basepath = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(basepath, 'gen-py.twisted'))
+sys.path.insert(0, glob.glob(os.path.join(basepath, '../../lib/py/build/lib.*'))[0])
+
+from ThriftTest import ThriftTest
+from ThriftTest.ttypes import Xception, Xtruct
+from thrift.transport import TTwisted
+from thrift.protocol import TBinaryProtocol
+
+from twisted.trial import unittest
+from twisted.internet import defer, reactor
+from twisted.internet.protocol import ClientCreator
+
+from zope.interface import implements
+
+
+class TestHandler:
+    implements(ThriftTest.Iface)
+
+    def __init__(self):
+        self.onewaysQueue = defer.DeferredQueue()
+
+    def testVoid(self):
+        pass
+
+    def testString(self, s):
+        return s
+
+    def testByte(self, b):
+        return b
+
+    def testI16(self, i16):
+        return i16
+
+    def testI32(self, i32):
+        return i32
+
+    def testI64(self, i64):
+        return i64
+
+    def testDouble(self, dub):
+        return dub
+
+    def testBinary(self, thing):
+        return thing
+
+    def testStruct(self, thing):
+        return thing
+
+    def testException(self, s):
+        if s == 'Xception':
+            x = Xception()
+            x.errorCode = 1001
+            x.message = s
+            raise x
+        elif s == "throw_undeclared":
+            raise ValueError("foo")
+
+    def testOneway(self, seconds):
+        def fireOneway(t):
+            self.onewaysQueue.put((t, time.time(), seconds))
+        reactor.callLater(seconds, fireOneway, time.time())
+
+    def testNest(self, thing):
+        return thing
+
+    def testMap(self, thing):
+        return thing
+
+    def testSet(self, thing):
+        return thing
+
+    def testList(self, thing):
+        return thing
+
+    def testEnum(self, thing):
+        return thing
+
+    def testTypedef(self, thing):
+        return thing
+
+
+class ThriftTestCase(unittest.TestCase):
+
+    @defer.inlineCallbacks
+    def setUp(self):
+        self.handler = TestHandler()
+        self.processor = ThriftTest.Processor(self.handler)
+        self.pfactory = TBinaryProtocol.TBinaryProtocolFactory()
+
+        self.server = reactor.listenTCP(
+            0, TTwisted.ThriftServerFactory(self.processor, self.pfactory), interface="127.0.0.1")
+
+        self.portNo = self.server.getHost().port
+
+        self.txclient = yield ClientCreator(reactor,
+                                            TTwisted.ThriftClientProtocol,
+                                            ThriftTest.Client,
+                                            self.pfactory).connectTCP("127.0.0.1", self.portNo)
+        self.client = self.txclient.client
+
+    @defer.inlineCallbacks
+    def tearDown(self):
+        yield self.server.stopListening()
+        self.txclient.transport.loseConnection()
+
+    @defer.inlineCallbacks
+    def testVoid(self):
+        self.assertEquals((yield self.client.testVoid()), None)
+
+    @defer.inlineCallbacks
+    def testString(self):
+        self.assertEquals((yield self.client.testString('Python')), 'Python')
+
+    @defer.inlineCallbacks
+    def testByte(self):
+        self.assertEquals((yield self.client.testByte(63)), 63)
+
+    @defer.inlineCallbacks
+    def testI32(self):
+        self.assertEquals((yield self.client.testI32(-1)), -1)
+        self.assertEquals((yield self.client.testI32(0)), 0)
+
+    @defer.inlineCallbacks
+    def testI64(self):
+        self.assertEquals((yield self.client.testI64(-34359738368)), -34359738368)
+
+    @defer.inlineCallbacks
+    def testDouble(self):
+        self.assertEquals((yield self.client.testDouble(-5.235098235)), -5.235098235)
+
+    # TODO: def testBinary(self) ...
+
+    @defer.inlineCallbacks
+    def testStruct(self):
+        x = Xtruct()
+        x.string_thing = "Zero"
+        x.byte_thing = 1
+        x.i32_thing = -3
+        x.i64_thing = -5
+        y = yield self.client.testStruct(x)
+
+        self.assertEquals(y.string_thing, "Zero")
+        self.assertEquals(y.byte_thing, 1)
+        self.assertEquals(y.i32_thing, -3)
+        self.assertEquals(y.i64_thing, -5)
+
+    @defer.inlineCallbacks
+    def testException(self):
+        yield self.client.testException('Safe')
+        try:
+            yield self.client.testException('Xception')
+            self.fail("should have gotten exception")
+        except Xception as x:
+            self.assertEquals(x.errorCode, 1001)
+            self.assertEquals(x.message, 'Xception')
+
+        try:
+            yield self.client.testException("throw_undeclared")
+            self.fail("should have thrown exception")
+        except Exception:  # type is undefined
+            pass
+
+    @defer.inlineCallbacks
+    def testOneway(self):
+        yield self.client.testOneway(1)
+        start, end, seconds = yield self.handler.onewaysQueue.get()
+        self.assertAlmostEquals(seconds, (end - start), places=1)
diff --git a/vendor/src/github.com/apache/thrift/test/py/CMakeLists.txt b/vendor/src/github.com/apache/thrift/test/py/CMakeLists.txt
new file mode 100644
index 00000000..fbc22173
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/CMakeLists.txt
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+add_test(NAME python_test_generate
+    COMMAND ${CMAKE_COMMAND}
+            -DTHRIFTCOMPILER=$
+            -DMY_PROJECT_DIR=${PROJECT_SOURCE_DIR}
+            -DMY_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
+            -DMY_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
+    -P ${CMAKE_CURRENT_SOURCE_DIR}/generate.cmake
+)
+
+add_test(NAME python_test
+    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/RunClientServer.py --gen-base=${CMAKE_CURRENT_BINARY_DIR}
+    DEPENDS python_test_generate
+    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
diff --git a/vendor/src/github.com/apache/thrift/test/py/FastbinaryTest.py b/vendor/src/github.com/apache/thrift/test/py/FastbinaryTest.py
new file mode 100644
index 00000000..db3ef8b4
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/FastbinaryTest.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+r"""
+PYTHONPATH=./gen-py:../../lib/py/build/lib... ./FastbinaryTest.py
+"""
+
+# TODO(dreiss): Test error cases.  Check for memory leaks.
+
+from __future__ import print_function
+
+import math
+import os
+import sys
+import timeit
+
+from copy import deepcopy
+from pprint import pprint
+
+from thrift.transport import TTransport
+from thrift.protocol.TBinaryProtocol import TBinaryProtocol, TBinaryProtocolAccelerated
+from thrift.protocol.TCompactProtocol import TCompactProtocol, TCompactProtocolAccelerated
+
+from DebugProtoTest import Srv
+from DebugProtoTest.ttypes import Backwards, Bonk, Empty, HolyMoley, OneOfEach, RandomStuff, Wrapper
+
+
+class TDevNullTransport(TTransport.TTransportBase):
+    def __init__(self):
+        pass
+
+    def isOpen(self):
+        return True
+
+ooe1 = OneOfEach()
+ooe1.im_true = True
+ooe1.im_false = False
+ooe1.a_bite = 0xd6
+ooe1.integer16 = 27000
+ooe1.integer32 = 1 << 24
+ooe1.integer64 = 6000 * 1000 * 1000
+ooe1.double_precision = math.pi
+ooe1.some_characters = "Debug THIS!"
+ooe1.zomg_unicode = u"\xd7\n\a\t"
+
+ooe2 = OneOfEach()
+ooe2.integer16 = 16
+ooe2.integer32 = 32
+ooe2.integer64 = 64
+ooe2.double_precision = (math.sqrt(5) + 1) / 2
+ooe2.some_characters = ":R (me going \"rrrr\")"
+ooe2.zomg_unicode = u"\xd3\x80\xe2\x85\xae\xce\x9d\x20"\
+                    u"\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\
+                    u"\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\
+                    u"\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\
+                    u"\xc7\x83\xe2\x80\xbc"
+
+if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
+    ooe1.zomg_unicode = ooe1.zomg_unicode.encode('utf8')
+    ooe2.zomg_unicode = ooe2.zomg_unicode.encode('utf8')
+
+hm = HolyMoley(**{"big": [], "contain": set(), "bonks": {}})
+hm.big.append(ooe1)
+hm.big.append(ooe2)
+hm.big[0].a_bite = 0x22
+hm.big[1].a_bite = 0x22
+
+hm.contain.add(("and a one", "and a two"))
+hm.contain.add(("then a one, two", "three!", "FOUR!"))
+hm.contain.add(())
+
+hm.bonks["nothing"] = []
+hm.bonks["something"] = [
+    Bonk(**{"type": 1, "message": "Wait."}),
+    Bonk(**{"type": 2, "message": "What?"}),
+]
+hm.bonks["poe"] = [
+    Bonk(**{"type": 3, "message": "quoth"}),
+    Bonk(**{"type": 4, "message": "the raven"}),
+    Bonk(**{"type": 5, "message": "nevermore"}),
+]
+
+rs = RandomStuff()
+rs.a = 1
+rs.b = 2
+rs.c = 3
+rs.myintlist = list(range(20))
+rs.maps = {1: Wrapper(**{"foo": Empty()}), 2: Wrapper(**{"foo": Empty()})}
+rs.bigint = 124523452435
+rs.triple = 3.14
+
+# make sure this splits two buffers in a buffered protocol
+rshuge = RandomStuff()
+rshuge.myintlist = list(range(10000))
+
+my_zero = Srv.Janky_result(**{"success": 5})
+
+
+class Test(object):
+    def __init__(self, fast, slow):
+        self._fast = fast
+        self._slow = slow
+
+    def _check_write(self, o):
+        trans_fast = TTransport.TMemoryBuffer()
+        trans_slow = TTransport.TMemoryBuffer()
+        prot_fast = self._fast(trans_fast, fallback=False)
+        prot_slow = self._slow(trans_slow)
+
+        o.write(prot_fast)
+        o.write(prot_slow)
+        ORIG = trans_slow.getvalue()
+        MINE = trans_fast.getvalue()
+        if ORIG != MINE:
+            print("actual  : %s\nexpected: %s" % (repr(MINE), repr(ORIG)))
+            raise Exception('write value mismatch')
+
+    def _check_read(self, o):
+        prot = self._slow(TTransport.TMemoryBuffer())
+        o.write(prot)
+
+        slow_version_binary = prot.trans.getvalue()
+
+        prot = self._fast(
+            TTransport.TMemoryBuffer(slow_version_binary), fallback=False)
+        c = o.__class__()
+        c.read(prot)
+        if c != o:
+            print("actual  : ")
+            pprint(repr(c))
+            print("expected: ")
+            pprint(repr(o))
+            raise Exception('read value mismatch')
+
+        prot = self._fast(
+            TTransport.TBufferedTransport(
+                TTransport.TMemoryBuffer(slow_version_binary)), fallback=False)
+        c = o.__class__()
+        c.read(prot)
+        if c != o:
+            print("actual  : ")
+            pprint(repr(c))
+            print("expected: ")
+            pprint(repr(o))
+            raise Exception('read value mismatch')
+
+    def do_test(self):
+        self._check_write(HolyMoley())
+        self._check_read(HolyMoley())
+
+        self._check_write(hm)
+        no_set = deepcopy(hm)
+        no_set.contain = set()
+        self._check_read(no_set)
+        self._check_read(hm)
+
+        self._check_write(rs)
+        self._check_read(rs)
+
+        self._check_write(rshuge)
+        self._check_read(rshuge)
+
+        self._check_write(my_zero)
+        self._check_read(my_zero)
+
+        self._check_read(Backwards(**{"first_tag2": 4, "second_tag1": 2}))
+
+        # One case where the serialized form changes, but only superficially.
+        o = Backwards(**{"first_tag2": 4, "second_tag1": 2})
+        trans_fast = TTransport.TMemoryBuffer()
+        trans_slow = TTransport.TMemoryBuffer()
+        prot_fast = self._fast(trans_fast, fallback=False)
+        prot_slow = self._slow(trans_slow)
+
+        o.write(prot_fast)
+        o.write(prot_slow)
+        ORIG = trans_slow.getvalue()
+        MINE = trans_fast.getvalue()
+        assert id(ORIG) != id(MINE)
+
+        prot = self._fast(TTransport.TMemoryBuffer(), fallback=False)
+        o.write(prot)
+        prot = self._slow(
+            TTransport.TMemoryBuffer(prot.trans.getvalue()))
+        c = o.__class__()
+        c.read(prot)
+        if c != o:
+            print("copy: ")
+            pprint(repr(c))
+            print("orig: ")
+            pprint(repr(o))
+
+
+def do_test(fast, slow):
+    Test(fast, slow).do_test()
+
+
+def do_benchmark(protocol, iters=5000, skip_slow=False):
+    setup = """
+from __main__ import hm, rs, TDevNullTransport
+from thrift.protocol.{0} import {0}{1}
+trans = TDevNullTransport()
+prot = {0}{1}(trans{2})
+"""
+
+    setup_fast = setup.format(protocol, 'Accelerated', ', fallback=False')
+    if not skip_slow:
+        setup_slow = setup.format(protocol, '', '')
+
+    print("Starting Benchmarks")
+
+    if not skip_slow:
+        print("HolyMoley Standard = %f" %
+              timeit.Timer('hm.write(prot)', setup_slow).timeit(number=iters))
+
+    print("HolyMoley Acceler. = %f" %
+          timeit.Timer('hm.write(prot)', setup_fast).timeit(number=iters))
+
+    if not skip_slow:
+        print("FastStruct Standard = %f" %
+              timeit.Timer('rs.write(prot)', setup_slow).timeit(number=iters))
+
+    print("FastStruct Acceler. = %f" %
+          timeit.Timer('rs.write(prot)', setup_fast).timeit(number=iters))
+
+
+if __name__ == '__main__':
+    print('Testing TBinaryAccelerated')
+    do_test(TBinaryProtocolAccelerated, TBinaryProtocol)
+    do_benchmark('TBinaryProtocol')
+    print('Testing TCompactAccelerated')
+    do_test(TCompactProtocolAccelerated, TCompactProtocol)
+    do_benchmark('TCompactProtocol')
diff --git a/vendor/src/github.com/apache/thrift/test/py/Makefile.am b/vendor/src/github.com/apache/thrift/test/py/Makefile.am
new file mode 100644
index 00000000..f105737c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/Makefile.am
@@ -0,0 +1,84 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = serial-tests
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+py_unit_tests = RunClientServer.py
+
+thrift_gen =                                    \
+        gen-py/ThriftTest/__init__.py           \
+        gen-py/DebugProtoTest/__init__.py \
+        gen-py-default/ThriftTest/__init__.py           \
+        gen-py-default/DebugProtoTest/__init__.py \
+        gen-py-slots/ThriftTest/__init__.py           \
+        gen-py-slots/DebugProtoTest/__init__.py \
+        gen-py-oldstyle/ThriftTest/__init__.py \
+        gen-py-oldstyle/DebugProtoTest/__init__.py \
+        gen-py-no_utf8strings/ThriftTest/__init__.py \
+        gen-py-no_utf8strings/DebugProtoTest/__init__.py \
+        gen-py-dynamic/ThriftTest/__init__.py           \
+        gen-py-dynamic/DebugProtoTest/__init__.py \
+        gen-py-dynamicslots/ThriftTest/__init__.py           \
+        gen-py-dynamicslots/DebugProtoTest/__init__.py
+
+precross: $(thrift_gen)
+BUILT_SOURCES = $(thrift_gen)
+
+helper_scripts=                                 \
+        TestClient.py                           \
+        TestServer.py
+
+check_SCRIPTS=                                  \
+        $(thrift_gen) \
+        $(py_unit_tests)                        \
+        $(helper_scripts)
+
+TESTS= $(py_unit_tests)
+
+
+gen-py/%/__init__.py: ../%.thrift $(THRIFT)
+	$(THRIFT) --gen py  $<
+
+gen-py-default/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-default || $(MKDIR_P) gen-py-default
+	$(THRIFT) --gen py -out gen-py-default $<
+
+gen-py-slots/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-slots || $(MKDIR_P) gen-py-slots
+	$(THRIFT) --gen py:slots -out gen-py-slots $<
+
+gen-py-oldstyle/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-oldstyle || $(MKDIR_P) gen-py-oldstyle
+	$(THRIFT) --gen py:old_style -out gen-py-oldstyle $<
+
+gen-py-no_utf8strings/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-no_utf8strings || $(MKDIR_P) gen-py-no_utf8strings
+	$(THRIFT) --gen py:no_utf8strings -out gen-py-no_utf8strings $<
+
+gen-py-dynamic/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-dynamic || $(MKDIR_P) gen-py-dynamic
+	$(THRIFT) --gen py:dynamic -out gen-py-dynamic $<
+
+gen-py-dynamicslots/%/__init__.py: ../%.thrift $(THRIFT)
+	test -d gen-py-dynamicslots || $(MKDIR_P) gen-py-dynamicslots
+	$(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
diff --git a/vendor/src/github.com/apache/thrift/test/py/RunClientServer.py b/vendor/src/github.com/apache/thrift/test/py/RunClientServer.py
new file mode 100644
index 00000000..c1443ec1
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/RunClientServer.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import division
+from __future__ import print_function
+import platform
+import copy
+import os
+import signal
+import socket
+import subprocess
+import sys
+import time
+from optparse import OptionParser
+
+from util import local_libpath
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+SCRIPTS = [
+    'FastbinaryTest.py',
+    'TestFrozen.py',
+    'TSimpleJSONProtocolTest.py',
+    'SerializationTest.py',
+    'TestEof.py',
+    'TestSyntax.py',
+    'TestSocket.py',
+]
+FRAMED = ["TNonblockingServer"]
+SKIP_ZLIB = ['TNonblockingServer', 'THttpServer']
+SKIP_SSL = ['TNonblockingServer', 'THttpServer']
+EXTRA_DELAY = dict(TProcessPoolServer=5.5)
+
+PROTOS = [
+    'accel',
+    'accelc',
+    'binary',
+    'compact',
+    'json',
+]
+
+
+def default_servers():
+    servers = [
+        'TSimpleServer',
+        'TThreadedServer',
+        'TThreadPoolServer',
+        'TNonblockingServer',
+        'THttpServer',
+    ]
+    if platform.system() != 'Windows':
+        servers.append('TProcessPoolServer')
+        servers.append('TForkingServer')
+    return servers
+
+
+def relfile(fname):
+    return os.path.join(SCRIPT_DIR, fname)
+
+
+def setup_pypath(libdir, gendir):
+    dirs = [libdir, gendir]
+    env = copy.deepcopy(os.environ)
+    pypath = env.get('PYTHONPATH', None)
+    if pypath:
+        dirs.append(pypath)
+    env['PYTHONPATH'] = os.pathsep.join(dirs)
+    if gendir.endswith('gen-py-no_utf8strings'):
+        env['THRIFT_TEST_PY_NO_UTF8STRINGS'] = '1'
+    return env
+
+
+def runScriptTest(libdir, genbase, genpydir, script):
+    env = setup_pypath(libdir, os.path.join(genbase, genpydir))
+    script_args = [sys.executable, relfile(script)]
+    print('\nTesting script: %s\n----' % (' '.join(script_args)))
+    ret = subprocess.call(script_args, env=env)
+    if ret != 0:
+        print('*** FAILED ***', file=sys.stderr)
+        print('LIBDIR: %s' % libdir, file=sys.stderr)
+        print('PY_GEN: %s' % genpydir, file=sys.stderr)
+        print('SCRIPT: %s' % script, file=sys.stderr)
+        raise Exception("Script subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(script_args)))
+
+
+def runServiceTest(libdir, genbase, genpydir, server_class, proto, port, use_zlib, use_ssl, verbose):
+    env = setup_pypath(libdir, os.path.join(genbase, genpydir))
+    # Build command line arguments
+    server_args = [sys.executable, relfile('TestServer.py')]
+    cli_args = [sys.executable, relfile('TestClient.py')]
+    for which in (server_args, cli_args):
+        which.append('--protocol=%s' % proto)  # accel, binary, compact or json
+        which.append('--port=%d' % port)  # default to 9090
+        if use_zlib:
+            which.append('--zlib')
+        if use_ssl:
+            which.append('--ssl')
+        if verbose == 0:
+            which.append('-q')
+        if verbose == 2:
+            which.append('-v')
+    # server-specific option to select server class
+    server_args.append(server_class)
+    # client-specific cmdline options
+    if server_class in FRAMED:
+        cli_args.append('--transport=framed')
+    else:
+        cli_args.append('--transport=buffered')
+    if server_class == 'THttpServer':
+        cli_args.append('--http=/')
+    if verbose > 0:
+        print('Testing server %s: %s' % (server_class, ' '.join(server_args)))
+    serverproc = subprocess.Popen(server_args, env=env)
+
+    def ensureServerAlive():
+        if serverproc.poll() is not None:
+            print(('FAIL: Server process (%s) failed with retcode %d')
+                  % (' '.join(server_args), serverproc.returncode))
+            raise Exception('Server subprocess %s died, args: %s'
+                            % (server_class, ' '.join(server_args)))
+
+    # Wait for the server to start accepting connections on the given port.
+    sleep_time = 0.1  # Seconds
+    max_attempts = 100
+    attempt = 0
+    while True:
+        sock4 = socket.socket()
+        sock6 = socket.socket(socket.AF_INET6)
+        try:
+            if sock4.connect_ex(('127.0.0.1', port)) == 0 \
+                    or sock6.connect_ex(('::1', port)) == 0:
+                break
+            attempt += 1
+            if attempt >= max_attempts:
+                raise Exception("TestServer not ready on port %d after %.2f seconds"
+                                % (port, sleep_time * attempt))
+            ensureServerAlive()
+            time.sleep(sleep_time)
+        finally:
+            sock4.close()
+            sock6.close()
+
+    try:
+        if verbose > 0:
+            print('Testing client: %s' % (' '.join(cli_args)))
+        ret = subprocess.call(cli_args, env=env)
+        if ret != 0:
+            print('*** FAILED ***', file=sys.stderr)
+            print('LIBDIR: %s' % libdir, file=sys.stderr)
+            print('PY_GEN: %s' % genpydir, file=sys.stderr)
+            raise Exception("Client subprocess failed, retcode=%d, args: %s" % (ret, ' '.join(cli_args)))
+    finally:
+        # check that server didn't die
+        ensureServerAlive()
+        extra_sleep = EXTRA_DELAY.get(server_class, 0)
+        if extra_sleep > 0 and verbose > 0:
+            print('Giving %s (proto=%s,zlib=%s,ssl=%s) an extra %d seconds for child'
+                  'processes to terminate via alarm'
+                  % (server_class, proto, use_zlib, use_ssl, extra_sleep))
+            time.sleep(extra_sleep)
+        sig = signal.SIGKILL if platform.system() != 'Windows' else signal.SIGABRT
+        os.kill(serverproc.pid, sig)
+        serverproc.wait()
+
+
+class TestCases(object):
+    def __init__(self, genbase, libdir, port, gendirs, servers, verbose):
+        self.genbase = genbase
+        self.libdir = libdir
+        self.port = port
+        self.verbose = verbose
+        self.gendirs = gendirs
+        self.servers = servers
+
+    def default_conf(self):
+        return {
+            'gendir': self.gendirs[0],
+            'server': self.servers[0],
+            'proto': PROTOS[0],
+            'zlib': False,
+            'ssl': False,
+        }
+
+    def run(self, conf, test_count):
+        with_zlib = conf['zlib']
+        with_ssl = conf['ssl']
+        try_server = conf['server']
+        try_proto = conf['proto']
+        genpydir = conf['gendir']
+        # skip any servers that don't work with the Zlib transport
+        if with_zlib and try_server in SKIP_ZLIB:
+            return False
+        # skip any servers that don't work with SSL
+        if with_ssl and try_server in SKIP_SSL:
+            return False
+        if self.verbose > 0:
+            print('\nTest run #%d:  (includes %s) Server=%s,  Proto=%s,  zlib=%s,  SSL=%s'
+                  % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
+        runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl, self.verbose)
+        if self.verbose > 0:
+            print('OK: Finished (includes %s)  %s / %s proto / zlib=%s / SSL=%s.   %d combinations tested.'
+                  % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
+        return True
+
+    def test_feature(self, name, values):
+        test_count = 0
+        conf = self.default_conf()
+        for try_server in values:
+            conf[name] = try_server
+            if self.run(conf, test_count):
+                test_count += 1
+        return test_count
+
+    def run_all_tests(self):
+        test_count = 0
+        for try_server in self.servers:
+            for genpydir in self.gendirs:
+                for try_proto in PROTOS:
+                    for with_zlib in (False, True):
+                        # skip any servers that don't work with the Zlib transport
+                        if with_zlib and try_server in SKIP_ZLIB:
+                            continue
+                        for with_ssl in (False, True):
+                            # skip any servers that don't work with SSL
+                            if with_ssl and try_server in SKIP_SSL:
+                                continue
+                            test_count += 1
+                            if self.verbose > 0:
+                                print('\nTest run #%d:  (includes %s) Server=%s,  Proto=%s,  zlib=%s,  SSL=%s'
+                                      % (test_count, genpydir, try_server, try_proto, with_zlib, with_ssl))
+                            runServiceTest(self.libdir, self.genbase, genpydir, try_server, try_proto, self.port, with_zlib, with_ssl)
+                            if self.verbose > 0:
+                                print('OK: Finished (includes %s)  %s / %s proto / zlib=%s / SSL=%s.   %d combinations tested.'
+                                      % (genpydir, try_server, try_proto, with_zlib, with_ssl, test_count))
+        return test_count
+
+
+def main():
+    parser = OptionParser()
+    parser.add_option('--all', action="store_true", dest='all')
+    parser.add_option('--genpydirs', type='string', dest='genpydirs',
+                      default='default,slots,oldstyle,no_utf8strings,dynamic,dynamicslots',
+                      help='directory extensions for generated code, used as suffixes for \"gen-py-*\" added sys.path for individual tests')
+    parser.add_option("--port", type="int", dest="port", default=9090,
+                      help="port number for server to listen on")
+    parser.add_option('-v', '--verbose', action="store_const",
+                      dest="verbose", const=2,
+                      help="verbose output")
+    parser.add_option('-q', '--quiet', action="store_const",
+                      dest="verbose", const=0,
+                      help="minimal output")
+    parser.add_option('-L', '--libdir', dest="libdir", default=local_libpath(),
+                      help="directory path that contains Thrift Python library")
+    parser.add_option('--gen-base', dest="gen_base", default=SCRIPT_DIR,
+                      help="directory path that contains Thrift Python library")
+    parser.set_defaults(verbose=1)
+    options, args = parser.parse_args()
+
+    generated_dirs = []
+    for gp_dir in options.genpydirs.split(','):
+        generated_dirs.append('gen-py-%s' % (gp_dir))
+
+    # commandline permits a single class name to be specified to override SERVERS=[...]
+    servers = default_servers()
+    if len(args) == 1:
+        if args[0] in servers:
+            servers = args
+        else:
+            print('Unavailable server type "%s", please choose one of: %s' % (args[0], servers))
+            sys.exit(0)
+
+    tests = TestCases(options.gen_base, options.libdir, options.port, generated_dirs, servers, options.verbose)
+
+    # run tests without a client/server first
+    print('----------------')
+    print(' Executing individual test scripts with various generated code directories')
+    print(' Directories to be tested: ' + ', '.join(generated_dirs))
+    print(' Scripts to be tested: ' + ', '.join(SCRIPTS))
+    print('----------------')
+    for genpydir in generated_dirs:
+        for script in SCRIPTS:
+            runScriptTest(options.libdir, options.gen_base, genpydir, script)
+
+    print('----------------')
+    print(' Executing Client/Server tests with various generated code directories')
+    print(' Servers to be tested: ' + ', '.join(servers))
+    print(' Directories to be tested: ' + ', '.join(generated_dirs))
+    print(' Protocols to be tested: ' + ', '.join(PROTOS))
+    print(' Options to be tested: ZLIB(yes/no), SSL(yes/no)')
+    print('----------------')
+
+    if options.all:
+        tests.run_all_tests()
+    else:
+        tests.test_feature('gendir', generated_dirs)
+        tests.test_feature('server', servers)
+        tests.test_feature('proto', PROTOS)
+        tests.test_feature('zlib', [False, True])
+        tests.test_feature('ssl', [False, True])
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/vendor/src/github.com/apache/thrift/test/py/SerializationTest.py b/vendor/src/github.com/apache/thrift/test/py/SerializationTest.py
new file mode 100644
index 00000000..efe3c6d8
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/SerializationTest.py
@@ -0,0 +1,390 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import (
+    Bonk,
+    Bools,
+    LargeDeltas,
+    ListBonks,
+    NestedListsBonk,
+    NestedListsI32x2,
+    NestedListsI32x3,
+    NestedMixedx2,
+    Numberz,
+    VersioningTestV1,
+    VersioningTestV2,
+    Xtruct,
+    Xtruct2,
+)
+
+from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol, TCompactProtocol, TJSONProtocol
+from thrift.TSerialization import serialize, deserialize
+import sys
+import unittest
+
+
+class AbstractTest(unittest.TestCase):
+
+    def setUp(self):
+        self.v1obj = VersioningTestV1(
+            begin_in_both=12345,
+            old_string='aaa',
+            end_in_both=54321,
+        )
+
+        self.v2obj = VersioningTestV2(
+            begin_in_both=12345,
+            newint=1,
+            newbyte=2,
+            newshort=3,
+            newlong=4,
+            newdouble=5.0,
+            newstruct=Bonk(message="Hello!", type=123),
+            newlist=[7, 8, 9],
+            newset=set([42, 1, 8]),
+            newmap={1: 2, 2: 3},
+            newstring="Hola!",
+            end_in_both=54321,
+        )
+
+        self.bools = Bools(im_true=True, im_false=False)
+        self.bools_flipped = Bools(im_true=False, im_false=True)
+
+        self.large_deltas = LargeDeltas(
+            b1=self.bools,
+            b10=self.bools_flipped,
+            b100=self.bools,
+            check_true=True,
+            b1000=self.bools_flipped,
+            check_false=False,
+            vertwo2000=VersioningTestV2(newstruct=Bonk(message='World!', type=314)),
+            a_set2500=set(['lazy', 'brown', 'cow']),
+            vertwo3000=VersioningTestV2(newset=set([2, 3, 5, 7, 11])),
+            big_numbers=[2 ** 8, 2 ** 16, 2 ** 31 - 1, -(2 ** 31 - 1)]
+        )
+
+        self.compact_struct = CompactProtoTestStruct(
+            a_byte=127,
+            a_i16=32000,
+            a_i32=1000000000,
+            a_i64=0xffffffffff,
+            a_double=5.6789,
+            a_string="my string",
+            true_field=True,
+            false_field=False,
+            empty_struct_field=Empty(),
+            byte_list=[-127, -1, 0, 1, 127],
+            i16_list=[-1, 0, 1, 0x7fff],
+            i32_list=[-1, 0, 0xff, 0xffff, 0xffffff, 0x7fffffff],
+            i64_list=[-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff],
+            double_list=[0.1, 0.2, 0.3],
+            string_list=["first", "second", "third"],
+            boolean_list=[True, True, True, False, False, False],
+            struct_list=[Empty(), Empty()],
+            byte_set=set([-127, -1, 0, 1, 127]),
+            i16_set=set([-1, 0, 1, 0x7fff]),
+            i32_set=set([1, 2, 3]),
+            i64_set=set([-1, 0, 0xff, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0x7fffffffffffffff]),
+            double_set=set([0.1, 0.2, 0.3]),
+            string_set=set(["first", "second", "third"]),
+            boolean_set=set([True, False]),
+            # struct_set=set([Empty()]), # unhashable instance
+            byte_byte_map={1: 2},
+            i16_byte_map={1: 1, -1: 1, 0x7fff: 1},
+            i32_byte_map={1: 1, -1: 1, 0x7fffffff: 1},
+            i64_byte_map={0: 1, 1: 1, -1: 1, 0x7fffffffffffffff: 1},
+            double_byte_map={-1.1: 1, 1.1: 1},
+            string_byte_map={"first": 1, "second": 2, "third": 3, "": 0},
+            boolean_byte_map={True: 1, False: 0},
+            byte_i16_map={1: 1, 2: -1, 3: 0x7fff},
+            byte_i32_map={1: 1, 2: -1, 3: 0x7fffffff},
+            byte_i64_map={1: 1, 2: -1, 3: 0x7fffffffffffffff},
+            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: True, 2: False},
+            # list_byte_map # unhashable
+            # set_byte_map={set([1, 2, 3]) : 1, set([0, 1]) : 2, set([]) : 0}, # unhashable
+            # map_byte_map # unhashable
+            byte_map_map={0: {}, 1: {1: 1}, 2: {1: 1, 2: 2}},
+            byte_set_map={0: set([]), 1: set([1]), 2: set([1, 2])},
+            byte_list_map={0: [], 1: [1], 2: [1, 2]},
+        )
+
+        self.nested_lists_i32x2 = NestedListsI32x2(
+            [
+                [1, 1, 2],
+                [2, 7, 9],
+                [3, 5, 8]
+            ]
+        )
+
+        self.nested_lists_i32x3 = NestedListsI32x3(
+            [
+                [
+                    [2, 7, 9],
+                    [3, 5, 8]
+                ],
+                [
+                    [1, 1, 2],
+                    [1, 4, 9]
+                ]
+            ]
+        )
+
+        self.nested_mixedx2 = NestedMixedx2(int_set_list=[
+            set([1, 2, 3]),
+            set([1, 4, 9]),
+            set([1, 2, 3, 5, 8, 13, 21]),
+            set([-1, 0, 1])
+        ],
+            # note, the sets below are sets of chars, since the strings are iterated
+            map_int_strset={10: set('abc'), 20: set('def'), 30: set('GHI')},
+            map_int_strset_list=[
+                {10: set('abc'), 20: set('def'), 30: set('GHI')},
+                {100: set('lmn'), 200: set('opq'), 300: set('RST')},
+                {1000: set('uvw'), 2000: set('wxy'), 3000: set('XYZ')}]
+        )
+
+        self.nested_lists_bonk = NestedListsBonk(
+            [
+                [
+                    [
+                        Bonk(message='inner A first', type=1),
+                        Bonk(message='inner A second', type=1)
+                    ],
+                    [
+                        Bonk(message='inner B first', type=2),
+                        Bonk(message='inner B second', type=2)
+                    ]
+                ]
+            ]
+        )
+
+        self.list_bonks = ListBonks(
+            [
+                Bonk(message='inner A', type=1),
+                Bonk(message='inner B', type=2),
+                Bonk(message='inner C', type=0)
+            ]
+        )
+
+    def _serialize(self, obj):
+        trans = TTransport.TMemoryBuffer()
+        prot = self.protocol_factory.getProtocol(trans)
+        obj.write(prot)
+        return trans.getvalue()
+
+    def _deserialize(self, objtype, data):
+        prot = self.protocol_factory.getProtocol(TTransport.TMemoryBuffer(data))
+        ret = objtype()
+        ret.read(prot)
+        return ret
+
+    def testForwards(self):
+        obj = self._deserialize(VersioningTestV2, self._serialize(self.v1obj))
+        self.assertEquals(obj.begin_in_both, self.v1obj.begin_in_both)
+        self.assertEquals(obj.end_in_both, self.v1obj.end_in_both)
+
+    def testBackwards(self):
+        obj = self._deserialize(VersioningTestV1, self._serialize(self.v2obj))
+        self.assertEquals(obj.begin_in_both, self.v2obj.begin_in_both)
+        self.assertEquals(obj.end_in_both, self.v2obj.end_in_both)
+
+    def testSerializeV1(self):
+        obj = self._deserialize(VersioningTestV1, self._serialize(self.v1obj))
+        self.assertEquals(obj, self.v1obj)
+
+    def testSerializeV2(self):
+        obj = self._deserialize(VersioningTestV2, self._serialize(self.v2obj))
+        self.assertEquals(obj, self.v2obj)
+
+    def testBools(self):
+        self.assertNotEquals(self.bools, self.bools_flipped)
+        self.assertNotEquals(self.bools, self.v1obj)
+        obj = self._deserialize(Bools, self._serialize(self.bools))
+        self.assertEquals(obj, self.bools)
+        obj = self._deserialize(Bools, self._serialize(self.bools_flipped))
+        self.assertEquals(obj, self.bools_flipped)
+        rep = repr(self.bools)
+        self.assertTrue(len(rep) > 0)
+
+    def testLargeDeltas(self):
+        # test large field deltas (meaningful in CompactProto only)
+        obj = self._deserialize(LargeDeltas, self._serialize(self.large_deltas))
+        self.assertEquals(obj, self.large_deltas)
+        rep = repr(self.large_deltas)
+        self.assertTrue(len(rep) > 0)
+
+    def testNestedListsI32x2(self):
+        obj = self._deserialize(NestedListsI32x2, self._serialize(self.nested_lists_i32x2))
+        self.assertEquals(obj, self.nested_lists_i32x2)
+        rep = repr(self.nested_lists_i32x2)
+        self.assertTrue(len(rep) > 0)
+
+    def testNestedListsI32x3(self):
+        obj = self._deserialize(NestedListsI32x3, self._serialize(self.nested_lists_i32x3))
+        self.assertEquals(obj, self.nested_lists_i32x3)
+        rep = repr(self.nested_lists_i32x3)
+        self.assertTrue(len(rep) > 0)
+
+    def testNestedMixedx2(self):
+        obj = self._deserialize(NestedMixedx2, self._serialize(self.nested_mixedx2))
+        self.assertEquals(obj, self.nested_mixedx2)
+        rep = repr(self.nested_mixedx2)
+        self.assertTrue(len(rep) > 0)
+
+    def testNestedListsBonk(self):
+        obj = self._deserialize(NestedListsBonk, self._serialize(self.nested_lists_bonk))
+        self.assertEquals(obj, self.nested_lists_bonk)
+        rep = repr(self.nested_lists_bonk)
+        self.assertTrue(len(rep) > 0)
+
+    def testListBonks(self):
+        obj = self._deserialize(ListBonks, self._serialize(self.list_bonks))
+        self.assertEquals(obj, self.list_bonks)
+        rep = repr(self.list_bonks)
+        self.assertTrue(len(rep) > 0)
+
+    def testCompactStruct(self):
+        # test large field deltas (meaningful in CompactProto only)
+        obj = self._deserialize(CompactProtoTestStruct, self._serialize(self.compact_struct))
+        self.assertEquals(obj, self.compact_struct)
+        rep = repr(self.compact_struct)
+        self.assertTrue(len(rep) > 0)
+
+    def testIntegerLimits(self):
+        if (sys.version_info[0] == 2 and sys.version_info[1] <= 6):
+            print('Skipping testIntegerLimits for Python 2.6')
+            return
+        bad_values = [CompactProtoTestStruct(a_byte=128), CompactProtoTestStruct(a_byte=-129),
+                      CompactProtoTestStruct(a_i16=32768), CompactProtoTestStruct(a_i16=-32769),
+                      CompactProtoTestStruct(a_i32=2147483648), CompactProtoTestStruct(a_i32=-2147483649),
+                      CompactProtoTestStruct(a_i64=9223372036854775808), CompactProtoTestStruct(a_i64=-9223372036854775809)
+                      ]
+
+        for value in bad_values:
+            self.assertRaises(Exception, self._serialize, value)
+
+
+class NormalBinaryTest(AbstractTest):
+    protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()
+
+
+class AcceleratedBinaryTest(AbstractTest):
+    protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False)
+
+
+class CompactProtocolTest(AbstractTest):
+    protocol_factory = TCompactProtocol.TCompactProtocolFactory()
+
+
+class AcceleratedCompactTest(AbstractTest):
+    protocol_factory = TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False)
+
+
+class JSONProtocolTest(AbstractTest):
+    protocol_factory = TJSONProtocol.TJSONProtocolFactory()
+
+
+class AcceleratedFramedTest(unittest.TestCase):
+    def testSplit(self):
+        """Test FramedTransport and BinaryProtocolAccelerated
+
+        Tests that TBinaryProtocolAccelerated and TFramedTransport
+        play nicely together when a read spans a frame"""
+
+        protocol_factory = TBinaryProtocol.TBinaryProtocolAcceleratedFactory()
+        bigstring = "".join(chr(byte) for byte in range(ord("a"), ord("z") + 1))
+
+        databuf = TTransport.TMemoryBuffer()
+        prot = protocol_factory.getProtocol(databuf)
+        prot.writeI32(42)
+        prot.writeString(bigstring)
+        prot.writeI16(24)
+        data = databuf.getvalue()
+        cutpoint = len(data) // 2
+        parts = [data[:cutpoint], data[cutpoint:]]
+
+        framed_buffer = TTransport.TMemoryBuffer()
+        framed_writer = TTransport.TFramedTransport(framed_buffer)
+        for part in parts:
+            framed_writer.write(part)
+            framed_writer.flush()
+        self.assertEquals(len(framed_buffer.getvalue()), len(data) + 8)
+
+        # Recreate framed_buffer so we can read from it.
+        framed_buffer = TTransport.TMemoryBuffer(framed_buffer.getvalue())
+        framed_reader = TTransport.TFramedTransport(framed_buffer)
+        prot = protocol_factory.getProtocol(framed_reader)
+        self.assertEqual(prot.readI32(), 42)
+        self.assertEqual(prot.readString(), bigstring)
+        self.assertEqual(prot.readI16(), 24)
+
+
+class SerializersTest(unittest.TestCase):
+
+    def testSerializeThenDeserialize(self):
+        obj = Xtruct2(i32_thing=1,
+                      struct_thing=Xtruct(string_thing="foo"))
+
+        s1 = serialize(obj)
+        for i in range(10):
+            self.assertEquals(s1, serialize(obj))
+            objcopy = Xtruct2()
+            deserialize(objcopy, serialize(obj))
+            self.assertEquals(obj, objcopy)
+
+        obj = Xtruct(string_thing="bar")
+        objcopy = Xtruct()
+        deserialize(objcopy, serialize(obj))
+        self.assertEquals(obj, objcopy)
+
+        # test booleans
+        obj = Bools(im_true=True, im_false=False)
+        objcopy = Bools()
+        deserialize(objcopy, serialize(obj))
+        self.assertEquals(obj, objcopy)
+
+        # test enums
+        for num, name in Numberz._VALUES_TO_NAMES.items():
+            obj = Bonk(message='enum Numberz value %d is string %s' % (num, name), type=num)
+            objcopy = Bonk()
+            deserialize(objcopy, serialize(obj))
+            self.assertEquals(obj, objcopy)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+
+    suite.addTest(loader.loadTestsFromTestCase(NormalBinaryTest))
+    suite.addTest(loader.loadTestsFromTestCase(AcceleratedBinaryTest))
+    suite.addTest(loader.loadTestsFromTestCase(AcceleratedCompactTest))
+    suite.addTest(loader.loadTestsFromTestCase(CompactProtocolTest))
+    suite.addTest(loader.loadTestsFromTestCase(JSONProtocolTest))
+    suite.addTest(loader.loadTestsFromTestCase(AcceleratedFramedTest))
+    suite.addTest(loader.loadTestsFromTestCase(SerializersTest))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/vendor/src/github.com/apache/thrift/test/py/TSimpleJSONProtocolTest.py b/vendor/src/github.com/apache/thrift/test/py/TSimpleJSONProtocolTest.py
new file mode 100644
index 00000000..72987602
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TSimpleJSONProtocolTest.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import Bonk, VersioningTestV1, VersioningTestV2
+from thrift.protocol import TJSONProtocol
+from thrift.transport import TTransport
+
+import json
+import unittest
+
+
+class SimpleJSONProtocolTest(unittest.TestCase):
+    protocol_factory = TJSONProtocol.TSimpleJSONProtocolFactory()
+
+    def _assertDictEqual(self, a, b, msg=None):
+        if hasattr(self, 'assertDictEqual'):
+            # assertDictEqual only in Python 2.7. Depends on your machine.
+            self.assertDictEqual(a, b, msg)
+            return
+
+        # Substitute implementation not as good as unittest library's
+        self.assertEquals(len(a), len(b), msg)
+        for k, v in a.iteritems():
+            self.assertTrue(k in b, msg)
+            self.assertEquals(b.get(k), v, msg)
+
+    def _serialize(self, obj):
+        trans = TTransport.TMemoryBuffer()
+        prot = self.protocol_factory.getProtocol(trans)
+        obj.write(prot)
+        return trans.getvalue()
+
+    def _deserialize(self, objtype, data):
+        prot = self.protocol_factory.getProtocol(TTransport.TMemoryBuffer(data))
+        ret = objtype()
+        ret.read(prot)
+        return ret
+
+    def testWriteOnly(self):
+        self.assertRaises(NotImplementedError,
+                          self._deserialize, VersioningTestV1, b'{}')
+
+    def testSimpleMessage(self):
+        v1obj = VersioningTestV1(
+            begin_in_both=12345,
+            old_string='aaa',
+            end_in_both=54321)
+        expected = dict(begin_in_both=v1obj.begin_in_both,
+                        old_string=v1obj.old_string,
+                        end_in_both=v1obj.end_in_both)
+        actual = json.loads(self._serialize(v1obj).decode('ascii'))
+
+        self._assertDictEqual(expected, actual)
+
+    def testComplicated(self):
+        v2obj = VersioningTestV2(
+            begin_in_both=12345,
+            newint=1,
+            newbyte=2,
+            newshort=3,
+            newlong=4,
+            newdouble=5.0,
+            newstruct=Bonk(message="Hello!", type=123),
+            newlist=[7, 8, 9],
+            newset=set([42, 1, 8]),
+            newmap={1: 2, 2: 3},
+            newstring="Hola!",
+            end_in_both=54321)
+        expected = dict(begin_in_both=v2obj.begin_in_both,
+                        newint=v2obj.newint,
+                        newbyte=v2obj.newbyte,
+                        newshort=v2obj.newshort,
+                        newlong=v2obj.newlong,
+                        newdouble=v2obj.newdouble,
+                        newstruct=dict(message=v2obj.newstruct.message,
+                                       type=v2obj.newstruct.type),
+                        newlist=v2obj.newlist,
+                        newset=list(v2obj.newset),
+                        newmap=v2obj.newmap,
+                        newstring=v2obj.newstring,
+                        end_in_both=v2obj.end_in_both)
+
+        # Need to load/dump because map keys get escaped.
+        expected = json.loads(json.dumps(expected))
+        actual = json.loads(self._serialize(v2obj).decode('ascii'))
+        self._assertDictEqual(expected, actual)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestClient.py b/vendor/src/github.com/apache/thrift/test/py/TestClient.py
new file mode 100644
index 00000000..18ef66bb
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestClient.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import os
+import sys
+import time
+import unittest
+from optparse import OptionParser
+
+from util import local_libpath
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+class AbstractTest(unittest.TestCase):
+    def setUp(self):
+        if options.http_path:
+            self.transport = THttpClient.THttpClient(options.host, port=options.port, path=options.http_path)
+        else:
+            if options.ssl:
+                from thrift.transport import TSSLSocket
+                socket = TSSLSocket.TSSLSocket(options.host, options.port, validate=False)
+            else:
+                socket = TSocket.TSocket(options.host, options.port)
+            # frame or buffer depending upon args
+            self.transport = TTransport.TBufferedTransport(socket)
+            if options.trans == 'framed':
+                self.transport = TTransport.TFramedTransport(socket)
+            elif options.trans == 'buffered':
+                self.transport = TTransport.TBufferedTransport(socket)
+            elif options.trans == '':
+                raise AssertionError('Unknown --transport option: %s' % options.trans)
+            if options.zlib:
+                self.transport = TZlibTransport.TZlibTransport(self.transport, 9)
+        self.transport.open()
+        protocol = self.get_protocol(self.transport)
+        self.client = ThriftTest.Client(protocol)
+
+    def tearDown(self):
+        self.transport.close()
+
+    def testVoid(self):
+        print('testVoid')
+        self.client.testVoid()
+
+    def testString(self):
+        print('testString')
+        self.assertEqual(self.client.testString('Python' * 20), 'Python' * 20)
+        self.assertEqual(self.client.testString(''), '')
+        s1 = u'\b\t\n/\\\\\r{}:パイソン"'
+        s2 = u"""Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
+        Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška,
+        Беларуская, Беларуская (тарашкевіца), Български, Bamanankan,
+        বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн,
+        Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg,
+        Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English,
+        Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt,
+        Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego,
+        Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski,
+        Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia,
+        Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa,
+        ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар,
+        Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino,
+        Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa
+        Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa
+        Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪
+        Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad,
+        Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو,
+        Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română,
+        Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple
+        English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk,
+        Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog,
+        Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
+        Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
+        Bân-lâm-gú, 粵語"""
+        if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'):
+            s1 = s1.encode('utf8')
+            s2 = s2.encode('utf8')
+        self.assertEqual(self.client.testString(s1), s1)
+        self.assertEqual(self.client.testString(s2), s2)
+
+    def testBool(self):
+        print('testBool')
+        self.assertEqual(self.client.testBool(True), True)
+        self.assertEqual(self.client.testBool(False), False)
+
+    def testByte(self):
+        print('testByte')
+        self.assertEqual(self.client.testByte(63), 63)
+        self.assertEqual(self.client.testByte(-127), -127)
+
+    def testI32(self):
+        print('testI32')
+        self.assertEqual(self.client.testI32(-1), -1)
+        self.assertEqual(self.client.testI32(0), 0)
+
+    def testI64(self):
+        print('testI64')
+        self.assertEqual(self.client.testI64(1), 1)
+        self.assertEqual(self.client.testI64(-34359738368), -34359738368)
+
+    def testDouble(self):
+        print('testDouble')
+        self.assertEqual(self.client.testDouble(-5.235098235), -5.235098235)
+        self.assertEqual(self.client.testDouble(0), 0)
+        self.assertEqual(self.client.testDouble(-1), -1)
+        self.assertEqual(self.client.testDouble(-0.000341012439638598279), -0.000341012439638598279)
+
+    def testBinary(self):
+        print('testBinary')
+        val = bytearray([i for i in range(0, 256)])
+        self.assertEqual(bytearray(self.client.testBinary(bytes(val))), val)
+
+    def testStruct(self):
+        print('testStruct')
+        x = Xtruct()
+        x.string_thing = "Zero"
+        x.byte_thing = 1
+        x.i32_thing = -3
+        x.i64_thing = -5
+        y = self.client.testStruct(x)
+        self.assertEqual(y, x)
+
+    def testNest(self):
+        print('testNest')
+        inner = Xtruct(string_thing="Zero", byte_thing=1, i32_thing=-3, i64_thing=-5)
+        x = Xtruct2(struct_thing=inner, byte_thing=0, i32_thing=0)
+        y = self.client.testNest(x)
+        self.assertEqual(y, x)
+
+    def testMap(self):
+        print('testMap')
+        x = {0: 1, 1: 2, 2: 3, 3: 4, -1: -2}
+        y = self.client.testMap(x)
+        self.assertEqual(y, x)
+
+    def testSet(self):
+        print('testSet')
+        x = set([8, 1, 42])
+        y = self.client.testSet(x)
+        self.assertEqual(y, x)
+
+    def testList(self):
+        print('testList')
+        x = [1, 4, 9, -42]
+        y = self.client.testList(x)
+        self.assertEqual(y, x)
+
+    def testEnum(self):
+        print('testEnum')
+        x = Numberz.FIVE
+        y = self.client.testEnum(x)
+        self.assertEqual(y, x)
+
+    def testTypedef(self):
+        print('testTypedef')
+        x = 0xffffffffffffff  # 7 bytes of 0xff
+        y = self.client.testTypedef(x)
+        self.assertEqual(y, x)
+
+    def testMapMap(self):
+        print('testMapMap')
+        x = {
+            -4: {-4: -4, -3: -3, -2: -2, -1: -1},
+            4: {4: 4, 3: 3, 2: 2, 1: 1},
+        }
+        y = self.client.testMapMap(42)
+        self.assertEqual(y, x)
+
+    def testMulti(self):
+        print('testMulti')
+        xpected = Xtruct(string_thing='Hello2', byte_thing=74, i32_thing=0xff00ff, i64_thing=0xffffffffd0d0)
+        y = self.client.testMulti(xpected.byte_thing,
+                                  xpected.i32_thing,
+                                  xpected.i64_thing,
+                                  {0: 'abc'},
+                                  Numberz.FIVE,
+                                  0xf0f0f0)
+        self.assertEqual(y, xpected)
+
+    def testException(self):
+        print('testException')
+        self.client.testException('Safe')
+        try:
+            self.client.testException('Xception')
+            self.fail("should have gotten exception")
+        except Xception as x:
+            self.assertEqual(x.errorCode, 1001)
+            self.assertEqual(x.message, 'Xception')
+            # TODO ensure same behavior for repr within generated python variants
+            # ensure exception's repr method works
+            # x_repr = repr(x)
+            # self.assertEqual(x_repr, 'Xception(errorCode=1001, message=\'Xception\')')
+
+        try:
+            self.client.testException('TException')
+            self.fail("should have gotten exception")
+        except TException as x:
+            pass
+
+        # Should not throw
+        self.client.testException('success')
+
+    def testMultiException(self):
+        print('testMultiException')
+        try:
+            self.client.testMultiException('Xception', 'ignore')
+        except Xception as ex:
+            self.assertEqual(ex.errorCode, 1001)
+            self.assertEqual(ex.message, 'This is an Xception')
+
+        try:
+            self.client.testMultiException('Xception2', 'ignore')
+        except Xception2 as ex:
+            self.assertEqual(ex.errorCode, 2002)
+            self.assertEqual(ex.struct_thing.string_thing, 'This is an Xception2')
+
+        y = self.client.testMultiException('success', 'foobar')
+        self.assertEqual(y.string_thing, 'foobar')
+
+    def testOneway(self):
+        print('testOneway')
+        start = time.time()
+        self.client.testOneway(1)  # type is int, not float
+        end = time.time()
+        self.assertTrue(end - start < 3,
+                        "oneway sleep took %f sec" % (end - start))
+
+    def testOnewayThenNormal(self):
+        print('testOnewayThenNormal')
+        self.client.testOneway(1)  # type is int, not float
+        self.assertEqual(self.client.testString('Python'), 'Python')
+
+
+class NormalBinaryTest(AbstractTest):
+    def get_protocol(self, transport):
+        return TBinaryProtocol.TBinaryProtocolFactory().getProtocol(transport)
+
+
+class CompactTest(AbstractTest):
+    def get_protocol(self, transport):
+        return TCompactProtocol.TCompactProtocolFactory().getProtocol(transport)
+
+
+class JSONTest(AbstractTest):
+    def get_protocol(self, transport):
+        return TJSONProtocol.TJSONProtocolFactory().getProtocol(transport)
+
+
+class AcceleratedBinaryTest(AbstractTest):
+    def get_protocol(self, transport):
+        return TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(transport)
+
+
+class AcceleratedCompactTest(AbstractTest):
+    def get_protocol(self, transport):
+        return TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(transport)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    if options.proto == 'binary':  # look for --proto on cmdline
+        suite.addTest(loader.loadTestsFromTestCase(NormalBinaryTest))
+    elif options.proto == 'accel':
+        suite.addTest(loader.loadTestsFromTestCase(AcceleratedBinaryTest))
+    elif options.proto == 'compact':
+        suite.addTest(loader.loadTestsFromTestCase(CompactTest))
+    elif options.proto == 'accelc':
+        suite.addTest(loader.loadTestsFromTestCase(AcceleratedCompactTest))
+    elif options.proto == 'json':
+        suite.addTest(loader.loadTestsFromTestCase(JSONTest))
+    else:
+        raise AssertionError('Unknown protocol given with --protocol: %s' % options.proto)
+    return suite
+
+
+class OwnArgsTestProgram(unittest.TestProgram):
+    def parseArgs(self, argv):
+        if args:
+            self.testNames = args
+        else:
+            self.testNames = ([self.defaultTest])
+        self.createTests()
+
+if __name__ == "__main__":
+    parser = OptionParser()
+    parser.add_option('--libpydir', type='string', dest='libpydir',
+                      help='include this directory in sys.path for locating library code')
+    parser.add_option('--genpydir', type='string', dest='genpydir',
+                      help='include this directory in sys.path for locating generated code')
+    parser.add_option("--port", type="int", dest="port",
+                      help="connect to server at port")
+    parser.add_option("--host", type="string", dest="host",
+                      help="connect to server")
+    parser.add_option("--zlib", action="store_true", dest="zlib",
+                      help="use zlib wrapper for compressed transport")
+    parser.add_option("--ssl", action="store_true", dest="ssl",
+                      help="use SSL for encrypted transport")
+    parser.add_option("--http", dest="http_path",
+                      help="Use the HTTP transport with the specified path")
+    parser.add_option('-v', '--verbose', action="store_const",
+                      dest="verbose", const=2,
+                      help="verbose output")
+    parser.add_option('-q', '--quiet', action="store_const",
+                      dest="verbose", const=0,
+                      help="minimal output")
+    parser.add_option('--protocol', dest="proto", type="string",
+                      help="protocol to use, one of: accel, binary, compact, json")
+    parser.add_option('--transport', dest="trans", type="string",
+                      help="transport to use, one of: buffered, framed")
+    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())
+
+    from ThriftTest import ThriftTest
+    from ThriftTest.ttypes import Xtruct, Xtruct2, Numberz, Xception, Xception2
+    from thrift.Thrift import TException
+    from thrift.transport import TTransport
+    from thrift.transport import TSocket
+    from thrift.transport import THttpClient
+    from thrift.transport import TZlibTransport
+    from thrift.protocol import TBinaryProtocol
+    from thrift.protocol import TCompactProtocol
+    from thrift.protocol import TJSONProtocol
+
+    OwnArgsTestProgram(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=1))
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestEof.py b/vendor/src/github.com/apache/thrift/test/py/TestEof.py
new file mode 100644
index 00000000..cda10509
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestEof.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from ThriftTest.ttypes import Xtruct
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol
+from thrift.protocol import TCompactProtocol
+import unittest
+
+
+class TestEof(unittest.TestCase):
+
+    def make_data(self, pfactory=None):
+        trans = TTransport.TMemoryBuffer()
+        if pfactory:
+            prot = pfactory.getProtocol(trans)
+        else:
+            prot = TBinaryProtocol.TBinaryProtocol(trans)
+
+        x = Xtruct()
+        x.string_thing = "Zero"
+        x.byte_thing = 0
+
+        x.write(prot)
+
+        x = Xtruct()
+        x.string_thing = "One"
+        x.byte_thing = 1
+
+        x.write(prot)
+
+        return trans.getvalue()
+
+    def testTransportReadAll(self):
+        """Test that readAll on any type of transport throws an EOFError"""
+        trans = TTransport.TMemoryBuffer(self.make_data())
+        trans.readAll(1)
+
+        try:
+            trans.readAll(10000)
+        except EOFError:
+            return
+
+        self.fail("Should have gotten EOFError")
+
+    def eofTestHelper(self, pfactory):
+        trans = TTransport.TMemoryBuffer(self.make_data(pfactory))
+        prot = pfactory.getProtocol(trans)
+
+        x = Xtruct()
+        x.read(prot)
+        self.assertEqual(x.string_thing, "Zero")
+        self.assertEqual(x.byte_thing, 0)
+
+        x = Xtruct()
+        x.read(prot)
+        self.assertEqual(x.string_thing, "One")
+        self.assertEqual(x.byte_thing, 1)
+
+        try:
+            x = Xtruct()
+            x.read(prot)
+        except EOFError:
+            return
+
+        self.fail("Should have gotten EOFError")
+
+    def eofTestHelperStress(self, pfactory):
+        """Test the ability of TBinaryProtocol to deal with the removal of every byte in the file"""
+        # TODO: we should make sure this covers more of the code paths
+
+        data = self.make_data(pfactory)
+        for i in range(0, len(data) + 1):
+            trans = TTransport.TMemoryBuffer(data[0:i])
+            prot = pfactory.getProtocol(trans)
+            try:
+                x = Xtruct()
+                x.read(prot)
+                x.read(prot)
+                x.read(prot)
+            except EOFError:
+                continue
+            self.fail("Should have gotten an EOFError")
+
+    def testBinaryProtocolEof(self):
+        """Test that TBinaryProtocol throws an EOFError when it reaches the end of the stream"""
+        self.eofTestHelper(TBinaryProtocol.TBinaryProtocolFactory())
+        self.eofTestHelperStress(TBinaryProtocol.TBinaryProtocolFactory())
+
+    def testBinaryProtocolAcceleratedBinaryEof(self):
+        """Test that TBinaryProtocolAccelerated throws an EOFError when it reaches the end of the stream"""
+        self.eofTestHelper(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False))
+        self.eofTestHelperStress(TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False))
+
+    def testCompactProtocolEof(self):
+        """Test that TCompactProtocol throws an EOFError when it reaches the end of the stream"""
+        self.eofTestHelper(TCompactProtocol.TCompactProtocolFactory())
+        self.eofTestHelperStress(TCompactProtocol.TCompactProtocolFactory())
+
+    def testCompactProtocolAcceleratedCompactEof(self):
+        """Test that TCompactProtocolAccelerated throws an EOFError when it reaches the end of the stream"""
+        self.eofTestHelper(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False))
+        self.eofTestHelperStress(TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False))
+
+
+def suite():
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    suite.addTest(loader.loadTestsFromTestCase(TestEof))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestFrozen.py b/vendor/src/github.com/apache/thrift/test/py/TestFrozen.py
new file mode 100644
index 00000000..e568e8cc
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestFrozen.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from DebugProtoTest.ttypes import CompactProtoTestStruct, Empty, Wrapper
+from thrift.Thrift import TFrozenDict
+from thrift.transport import TTransport
+from thrift.protocol import TBinaryProtocol, TCompactProtocol
+import collections
+import unittest
+
+
+class TestFrozenBase(unittest.TestCase):
+    def _roundtrip(self, src, dst):
+        otrans = TTransport.TMemoryBuffer()
+        optoro = self.protocol(otrans)
+        src.write(optoro)
+        itrans = TTransport.TMemoryBuffer(otrans.getvalue())
+        iproto = self.protocol(itrans)
+        return dst.read(iproto) or dst
+
+    def test_dict_is_hashable_only_after_frozen(self):
+        d0 = {}
+        self.assertFalse(isinstance(d0, collections.Hashable))
+        d1 = TFrozenDict(d0)
+        self.assertTrue(isinstance(d1, collections.Hashable))
+
+    def test_struct_with_collection_fields(self):
+        pass
+
+    def test_set(self):
+        """Test that annotated set field can be serialized and deserialized"""
+        x = CompactProtoTestStruct(set_byte_map={
+            frozenset([42, 100, -100]): 99,
+            frozenset([0]): 100,
+            frozenset([]): 0,
+        })
+        x2 = self._roundtrip(x, CompactProtoTestStruct())
+        self.assertEqual(x2.set_byte_map[frozenset([42, 100, -100])], 99)
+        self.assertEqual(x2.set_byte_map[frozenset([0])], 100)
+        self.assertEqual(x2.set_byte_map[frozenset([])], 0)
+
+    def test_map(self):
+        """Test that annotated map field can be serialized and deserialized"""
+        x = CompactProtoTestStruct(map_byte_map={
+            TFrozenDict({42: 42, 100: -100}): 99,
+            TFrozenDict({0: 0}): 100,
+            TFrozenDict({}): 0,
+        })
+        x2 = self._roundtrip(x, CompactProtoTestStruct())
+        self.assertEqual(x2.map_byte_map[TFrozenDict({42: 42, 100: -100})], 99)
+        self.assertEqual(x2.map_byte_map[TFrozenDict({0: 0})], 100)
+        self.assertEqual(x2.map_byte_map[TFrozenDict({})], 0)
+
+    def test_list(self):
+        """Test that annotated list field can be serialized and deserialized"""
+        x = CompactProtoTestStruct(list_byte_map={
+            (42, 100, -100): 99,
+            (0,): 100,
+            (): 0,
+        })
+        x2 = self._roundtrip(x, CompactProtoTestStruct())
+        self.assertEqual(x2.list_byte_map[(42, 100, -100)], 99)
+        self.assertEqual(x2.list_byte_map[(0,)], 100)
+        self.assertEqual(x2.list_byte_map[()], 0)
+
+    def test_empty_struct(self):
+        """Test that annotated empty struct can be serialized and deserialized"""
+        x = CompactProtoTestStruct(empty_struct_field=Empty())
+        x2 = self._roundtrip(x, CompactProtoTestStruct())
+        self.assertEqual(x2.empty_struct_field, Empty())
+
+    def test_struct(self):
+        """Test that annotated struct can be serialized and deserialized"""
+        x = Wrapper(foo=Empty())
+        self.assertEqual(x.foo, Empty())
+        x2 = self._roundtrip(x, Wrapper)
+        self.assertEqual(x2.foo, Empty())
+
+
+class TestFrozen(TestFrozenBase):
+    def protocol(self, trans):
+        return TBinaryProtocol.TBinaryProtocolFactory().getProtocol(trans)
+
+
+class TestFrozenAcceleratedBinary(TestFrozenBase):
+    def protocol(self, trans):
+        return TBinaryProtocol.TBinaryProtocolAcceleratedFactory(fallback=False).getProtocol(trans)
+
+
+class TestFrozenAcceleratedCompact(TestFrozenBase):
+    def protocol(self, trans):
+        return TCompactProtocol.TCompactProtocolAcceleratedFactory(fallback=False).getProtocol(trans)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    suite.addTest(loader.loadTestsFromTestCase(TestFrozen))
+    suite.addTest(loader.loadTestsFromTestCase(TestFrozenAcceleratedBinary))
+    suite.addTest(loader.loadTestsFromTestCase(TestFrozenAcceleratedCompact))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="suite", testRunner=unittest.TextTestRunner(verbosity=2))
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestServer.py b/vendor/src/github.com/apache/thrift/test/py/TestServer.py
new file mode 100644
index 00000000..070560c4
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestServer.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+from __future__ import division
+import logging
+import os
+import sys
+import time
+from optparse import OptionParser
+
+from util import local_libpath
+
+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestHandler(object):
+    def testVoid(self):
+        if options.verbose > 1:
+            logging.info('testVoid()')
+
+    def testString(self, str):
+        if options.verbose > 1:
+            logging.info('testString(%s)' % str)
+        return str
+
+    def testBool(self, boolean):
+        if options.verbose > 1:
+            logging.info('testBool(%s)' % str(boolean).lower())
+        return boolean
+
+    def testByte(self, byte):
+        if options.verbose > 1:
+            logging.info('testByte(%d)' % byte)
+        return byte
+
+    def testI16(self, i16):
+        if options.verbose > 1:
+            logging.info('testI16(%d)' % i16)
+        return i16
+
+    def testI32(self, i32):
+        if options.verbose > 1:
+            logging.info('testI32(%d)' % i32)
+        return i32
+
+    def testI64(self, i64):
+        if options.verbose > 1:
+            logging.info('testI64(%d)' % i64)
+        return i64
+
+    def testDouble(self, dub):
+        if options.verbose > 1:
+            logging.info('testDouble(%f)' % dub)
+        return dub
+
+    def testBinary(self, thing):
+        if options.verbose > 1:
+            logging.info('testBinary()')  # TODO: hex output
+        return thing
+
+    def testStruct(self, thing):
+        if options.verbose > 1:
+            logging.info('testStruct({%s, %s, %s, %s})' % (thing.string_thing, thing.byte_thing, thing.i32_thing, thing.i64_thing))
+        return thing
+
+    def testException(self, arg):
+        # if options.verbose > 1:
+        logging.info('testException(%s)' % arg)
+        if arg == 'Xception':
+            raise Xception(errorCode=1001, message=arg)
+        elif arg == 'TException':
+            raise TException(message='This is a TException')
+
+    def testMultiException(self, arg0, arg1):
+        if options.verbose > 1:
+            logging.info('testMultiException(%s, %s)' % (arg0, arg1))
+        if arg0 == 'Xception':
+            raise Xception(errorCode=1001, message='This is an Xception')
+        elif arg0 == 'Xception2':
+            raise Xception2(
+                errorCode=2002,
+                struct_thing=Xtruct(string_thing='This is an Xception2'))
+        return Xtruct(string_thing=arg1)
+
+    def testOneway(self, seconds):
+        if options.verbose > 1:
+            logging.info('testOneway(%d) => sleeping...' % seconds)
+        time.sleep(seconds / 3)  # be quick
+        if options.verbose > 1:
+            logging.info('done sleeping')
+
+    def testNest(self, thing):
+        if options.verbose > 1:
+            logging.info('testNest(%s)' % thing)
+        return thing
+
+    def testMap(self, thing):
+        if options.verbose > 1:
+            logging.info('testMap(%s)' % thing)
+        return thing
+
+    def testStringMap(self, thing):
+        if options.verbose > 1:
+            logging.info('testStringMap(%s)' % thing)
+        return thing
+
+    def testSet(self, thing):
+        if options.verbose > 1:
+            logging.info('testSet(%s)' % thing)
+        return thing
+
+    def testList(self, thing):
+        if options.verbose > 1:
+            logging.info('testList(%s)' % thing)
+        return thing
+
+    def testEnum(self, thing):
+        if options.verbose > 1:
+            logging.info('testEnum(%s)' % thing)
+        return thing
+
+    def testTypedef(self, thing):
+        if options.verbose > 1:
+            logging.info('testTypedef(%s)' % thing)
+        return thing
+
+    def testMapMap(self, thing):
+        if options.verbose > 1:
+            logging.info('testMapMap(%s)' % thing)
+        return {
+            -4: {
+                -4: -4,
+                -3: -3,
+                -2: -2,
+                -1: -1,
+            },
+            4: {
+                4: 4,
+                3: 3,
+                2: 2,
+                1: 1,
+            },
+        }
+
+    def testInsanity(self, argument):
+        if options.verbose > 1:
+            logging.info('testInsanity(%s)' % argument)
+        return {
+            1: {
+                2: argument,
+                3: argument,
+            },
+            2: {6: Insanity()},
+        }
+
+    def testMulti(self, arg0, arg1, arg2, arg3, arg4, arg5):
+        if options.verbose > 1:
+            logging.info('testMulti(%s)' % [arg0, arg1, arg2, arg3, arg4, arg5])
+        return Xtruct(string_thing='Hello2',
+                      byte_thing=arg0, i32_thing=arg1, i64_thing=arg2)
+
+
+def main(options):
+    # set up the protocol factory form the --protocol option
+    prot_factories = {
+        'binary': TBinaryProtocol.TBinaryProtocolFactory,
+        'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory,
+        'compact': TCompactProtocol.TCompactProtocolFactory,
+        'accelc': TCompactProtocol.TCompactProtocolAcceleratedFactory,
+        'json': TJSONProtocol.TJSONProtocolFactory,
+    }
+    pfactory_cls = prot_factories.get(options.proto, None)
+    if pfactory_cls is None:
+        raise AssertionError('Unknown --protocol option: %s' % options.proto)
+    pfactory = pfactory_cls()
+    try:
+        pfactory.string_length_limit = options.string_limit
+        pfactory.container_length_limit = options.container_limit
+    except:
+        # Ignore errors for those protocols that does not support length limit
+        pass
+
+    # get the server type (TSimpleServer, TNonblockingServer, etc...)
+    if len(args) > 1:
+        raise AssertionError('Only one server type may be specified, not multiple types.')
+    server_type = args[0]
+
+    # Set up the handler and processor objects
+    handler = TestHandler()
+    processor = ThriftTest.Processor(handler)
+
+    # Handle THttpServer as a special case
+    if server_type == 'THttpServer':
+        server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
+        server.serve()
+        sys.exit(0)
+
+    # set up server transport and transport factory
+
+    abs_key_path = os.path.join(os.path.dirname(SCRIPT_DIR), 'keys', 'server.pem')
+
+    host = None
+    if options.ssl:
+        from thrift.transport import TSSLSocket
+        transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path)
+    else:
+        transport = TSocket.TServerSocket(host, options.port)
+    tfactory = TTransport.TBufferedTransportFactory()
+    if options.trans == 'buffered':
+        tfactory = TTransport.TBufferedTransportFactory()
+    elif options.trans == 'framed':
+        tfactory = TTransport.TFramedTransportFactory()
+    elif options.trans == '':
+        raise AssertionError('Unknown --transport option: %s' % options.trans)
+    else:
+        tfactory = TTransport.TBufferedTransportFactory()
+    # if --zlib, then wrap server transport, and use a different transport factory
+    if options.zlib:
+        transport = TZlibTransport.TZlibTransport(transport)  # wrap  with zlib
+        tfactory = TZlibTransport.TZlibTransportFactory()
+
+    # do server-specific setup here:
+    if server_type == "TNonblockingServer":
+        server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
+    elif server_type == "TProcessPoolServer":
+        import signal
+        from thrift.server import TProcessPoolServer
+        server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
+        server.setNumWorkers(5)
+
+        def set_alarm():
+            def clean_shutdown(signum, frame):
+                for worker in server.workers:
+                    if options.verbose > 0:
+                        logging.info('Terminating worker: %s' % worker)
+                    worker.terminate()
+                if options.verbose > 0:
+                    logging.info('Requesting server to stop()')
+                try:
+                    server.stop()
+                except:
+                    pass
+            signal.signal(signal.SIGALRM, clean_shutdown)
+            signal.alarm(4)
+        set_alarm()
+    else:
+        # look up server class dynamically to instantiate server
+        ServerClass = getattr(TServer, server_type)
+        server = ServerClass(processor, transport, tfactory, pfactory)
+    # enter server main loop
+    server.serve()
+
+if __name__ == '__main__':
+    parser = OptionParser()
+    parser.add_option('--libpydir', type='string', dest='libpydir',
+                      help='include this directory to sys.path for locating library code')
+    parser.add_option('--genpydir', type='string', dest='genpydir',
+                      default='gen-py',
+                      help='include this directory to sys.path for locating generated code')
+    parser.add_option("--port", type="int", dest="port",
+                      help="port number for server to listen on")
+    parser.add_option("--zlib", action="store_true", dest="zlib",
+                      help="use zlib wrapper for compressed transport")
+    parser.add_option("--ssl", action="store_true", dest="ssl",
+                      help="use SSL for encrypted transport")
+    parser.add_option('-v', '--verbose', action="store_const",
+                      dest="verbose", const=2,
+                      help="verbose output")
+    parser.add_option('-q', '--quiet', action="store_const",
+                      dest="verbose", const=0,
+                      help="minimal output")
+    parser.add_option('--protocol', dest="proto", type="string",
+                      help="protocol to use, one of: accel, binary, compact, json")
+    parser.add_option('--transport', dest="trans", type="string",
+                      help="transport to use, one of: buffered, framed")
+    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')
+    options, args = parser.parse_args()
+
+    # Print TServer log to stdout so that the test-runner can redirect it to log files
+    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.ttypes import Xtruct, Xception, Xception2, Insanity
+    from thrift.Thrift import TException
+    from thrift.transport import TTransport
+    from thrift.transport import TSocket
+    from thrift.transport import TZlibTransport
+    from thrift.protocol import TBinaryProtocol
+    from thrift.protocol import TCompactProtocol
+    from thrift.protocol import TJSONProtocol
+    from thrift.server import TServer, TNonblockingServer, THttpServer
+
+    sys.exit(main(options))
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestSocket.py b/vendor/src/github.com/apache/thrift/test/py/TestSocket.py
new file mode 100644
index 00000000..a3c5ff0f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestSocket.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from thrift.transport import TSocket
+import unittest
+import time
+import socket
+import random
+
+
+class TimeoutTest(unittest.TestCase):
+    def setUp(self):
+        for i in range(50):
+            try:
+                # find a port we can use
+                self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                self.port = random.randint(10000, 30000)
+                self.listen_sock.bind(('localhost', self.port))
+                self.listen_sock.listen(5)
+                break
+            except:
+                if i == 49:
+                    raise
+
+    def testConnectTimeout(self):
+        starttime = time.time()
+
+        try:
+            leaky = []
+            for i in range(100):
+                socket = TSocket.TSocket('localhost', self.port)
+                socket.setTimeout(10)
+                socket.open()
+                leaky.append(socket)
+        except:
+            self.assert_(time.time() - starttime < 5.0)
+
+    def testWriteTimeout(self):
+        starttime = time.time()
+
+        try:
+            socket = TSocket.TSocket('localhost', self.port)
+            socket.setTimeout(10)
+            socket.open()
+            lsock = self.listen_sock.accept()
+            while True:
+                lsock.write("hi" * 100)
+
+        except:
+            self.assert_(time.time() - starttime < 5.0)
+
+if __name__ == '__main__':
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+
+    suite.addTest(loader.loadTestsFromTestCase(TimeoutTest))
+
+    testRunner = unittest.TextTestRunner(verbosity=2)
+    testRunner.run(suite)
diff --git a/vendor/src/github.com/apache/thrift/test/py/TestSyntax.py b/vendor/src/github.com/apache/thrift/test/py/TestSyntax.py
new file mode 100644
index 00000000..dbe7975e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/TestSyntax.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Just import these generated files to make sure they are syntactically valid
+from DebugProtoTest import EmptyService  # noqa
+from DebugProtoTest import Inherited  # noqa
diff --git a/vendor/src/github.com/apache/thrift/test/py/explicit_module/runtest.sh b/vendor/src/github.com/apache/thrift/test/py/explicit_module/runtest.sh
new file mode 100644
index 00000000..6d734628
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/explicit_module/runtest.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+rm -rf gen-py
+../../../compiler/cpp/thrift --gen py test1.thrift || exit 1
+../../../compiler/cpp/thrift --gen py test2.thrift || exit 1
+../../../compiler/cpp/thrift --gen py test3.thrift && exit 1  # Fail since test3.thrift has python keywords
+PYTHONPATH=./gen-py python -c 'import foo.bar.baz' || exit 1
+PYTHONPATH=./gen-py python -c 'import test2' || exit 1
+PYTHONPATH=./gen-py python -c 'import test1' &>/dev/null && exit 1  # Should fail.
+cp -r gen-py simple
+../../../compiler/cpp/thrift -r --gen py test2.thrift || exit 1
+PYTHONPATH=./gen-py python -c 'import test2' || exit 1
+diff -ur simple gen-py > thediffs
+file thediffs | grep -s -q empty || exit 1
+rm -rf simple thediffs
+echo 'All tests pass!'
diff --git a/vendor/src/github.com/apache/thrift/test/py/explicit_module/test1.thrift b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test1.thrift
new file mode 100644
index 00000000..ec600d7d
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test1.thrift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace py foo.bar.baz
+
+struct astruct {
+  1: i32 how_unoriginal;
+}
diff --git a/vendor/src/github.com/apache/thrift/test/py/explicit_module/test2.thrift b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test2.thrift
new file mode 100644
index 00000000..68f9da4d
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test2.thrift
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+include "test1.thrift"
+
+struct another {
+  1: test1.astruct something;
+}
diff --git a/vendor/src/github.com/apache/thrift/test/py/explicit_module/test3.thrift b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test3.thrift
new file mode 100644
index 00000000..154786bf
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/explicit_module/test3.thrift
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+namespace py validations
+
+struct from {
+  1: i32 def;
+}
+
diff --git a/vendor/src/github.com/apache/thrift/test/py/generate.cmake b/vendor/src/github.com/apache/thrift/test/py/generate.cmake
new file mode 100644
index 00000000..44c53571
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/generate.cmake
@@ -0,0 +1,22 @@
+macro(GENERATE FILENAME GENERATOR OUTPUTDIR)
+  file(MAKE_DIRECTORY ${MY_CURRENT_BINARY_DIR}/${OUTPUTDIR})
+  execute_process(COMMAND ${THRIFTCOMPILER} --gen ${GENERATOR} -out ${MY_CURRENT_BINARY_DIR}/${OUTPUTDIR} ${FILENAME}
+                  RESULT_VARIABLE CMD_RESULT)
+  if(CMD_RESULT)
+        message(FATAL_ERROR "Error generating ${FILENAME} with generator ${GENERATOR}")
+  endif()
+endmacro(GENERATE)
+
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/ThriftTest.thrift py:dynamic,slots gen-py-dynamicslots)
+
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py gen-py-default)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:slots gen-py-slots)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:old_style gen-py-oldstyle)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:no_utf8strings gen-py-no_utf8strings)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:dynamic gen-py-dynamic)
+generate(${MY_PROJECT_DIR}/test/DebugProtoTest.thrift py:dynamic,slots gen-py-dynamicslots)
diff --git a/vendor/src/github.com/apache/thrift/test/py/setup.cfg b/vendor/src/github.com/apache/thrift/test/py/setup.cfg
new file mode 100644
index 00000000..7da1f960
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 100
diff --git a/vendor/src/github.com/apache/thrift/test/py/util.py b/vendor/src/github.com/apache/thrift/test/py/util.py
new file mode 100644
index 00000000..c2b3f5cb
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/py/util.py
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import glob
+import os
+import sys
+
+_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+_ROOT_DIR = os.path.dirname(os.path.dirname(_SCRIPT_DIR))
+
+
+def local_libpath():
+    globdir = os.path.join(_ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
+    for libpath in glob.glob(globdir):
+        if libpath.endswith('-%d.%d' % (sys.version_info[0], sys.version_info[1])):
+            return libpath
diff --git a/vendor/src/github.com/apache/thrift/test/rb/Gemfile b/vendor/src/github.com/apache/thrift/test/rb/Gemfile
new file mode 100644
index 00000000..58c04aab
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/Gemfile
@@ -0,0 +1,7 @@
+source "http://rubygems.org"
+
+require "rubygems"
+
+gem "rack", "~> 1.5.2"
+gem "thin", "~> 1.5.0"
+gem "test-unit"
diff --git a/vendor/src/github.com/apache/thrift/test/rb/Makefile.am b/vendor/src/github.com/apache/thrift/test/rb/Makefile.am
new file mode 100644
index 00000000..7b74c6c8
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/Makefile.am
@@ -0,0 +1,33 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+stubs: $(THRIFT) ../ThriftTest.thrift ../SmallTest.thrift
+	$(THRIFT) --gen rb ../ThriftTest.thrift
+	$(THRIFT) --gen rb ../SmallTest.thrift
+
+precross: stubs
+
+check: stubs
+if HAVE_BUNDLER
+	$(BUNDLER) install
+	$(BUNDLER) exec $(RUBY) -I. test_suite.rb
+endif
+
diff --git a/vendor/src/github.com/apache/thrift/test/rb/benchmarks/protocol_benchmark.rb b/vendor/src/github.com/apache/thrift/test/rb/benchmarks/protocol_benchmark.rb
new file mode 100644
index 00000000..05a8ee53
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/benchmarks/protocol_benchmark.rb
@@ -0,0 +1,174 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. .. .. lib rb lib])
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w[.. .. .. lib rb ext])
+
+require 'thrift'
+
+require 'benchmark'
+require 'rubygems'
+require 'set'
+require 'pp'
+
+# require 'ruby-debug'
+# require 'ruby-prof'
+
+require File.join(File.dirname(__FILE__), '../fixtures/structs')
+
+transport1 = Thrift::MemoryBuffer.new
+ruby_binary_protocol = Thrift::BinaryProtocol.new(transport1)
+
+transport2 = Thrift::MemoryBuffer.new
+c_fast_binary_protocol = Thrift::BinaryProtocolAccelerated.new(transport2)
+
+
+ooe = Fixtures::Structs::OneOfEach.new
+ooe.im_true   = true
+ooe.im_false  = false
+ooe.a_bite    = -42
+ooe.integer16 = 27000
+ooe.integer32 = 1<<24
+ooe.integer64 = 6000 * 1000 * 1000
+ooe.double_precision = Math::PI
+ooe.some_characters  = "Debug THIS!"
+ooe.zomg_unicode     = "\xd7\n\a\t"
+
+n1 = Fixtures::Structs::Nested1.new
+n1.a_list = []
+n1.a_list << ooe << ooe << ooe << ooe
+n1.i32_map = {}
+n1.i32_map[1234] = ooe
+n1.i32_map[46345] = ooe
+n1.i32_map[-34264] = ooe
+n1.i64_map = {}
+n1.i64_map[43534986783945] = ooe
+n1.i64_map[-32434639875122] = ooe
+n1.dbl_map = {}
+n1.dbl_map[324.65469834] = ooe
+n1.dbl_map[-9458672340.4986798345112] = ooe
+n1.str_map = {}
+n1.str_map['sdoperuix'] = ooe
+n1.str_map['pwoerxclmn'] = ooe
+
+n2 = Fixtures::Structs::Nested2.new
+n2.a_list = []
+n2.a_list << n1 << n1 << n1 << n1 << n1
+n2.i32_map = {}
+n2.i32_map[398345] = n1
+n2.i32_map[-2345] = n1
+n2.i32_map[12312] = n1
+n2.i64_map = {}
+n2.i64_map[2349843765934] = n1
+n2.i64_map[-123234985495] = n1
+n2.i64_map[0] = n1
+n2.dbl_map = {}
+n2.dbl_map[23345345.38927834] = n1
+n2.dbl_map[-1232349.5489345] = n1
+n2.dbl_map[-234984574.23498725] = n1
+n2.str_map = {}
+n2.str_map[''] = n1
+n2.str_map['sdflkertpioux'] = n1
+n2.str_map['sdfwepwdcjpoi'] = n1
+
+n3 = Fixtures::Structs::Nested3.new
+n3.a_list = []
+n3.a_list << n2 << n2 << n2 << n2 << n2
+n3.i32_map = {}
+n3.i32_map[398345] = n2
+n3.i32_map[-2345] = n2
+n3.i32_map[12312] = n2
+n3.i64_map = {}
+n3.i64_map[2349843765934] = n2
+n3.i64_map[-123234985495] = n2
+n3.i64_map[0] = n2
+n3.dbl_map = {}
+n3.dbl_map[23345345.38927834] = n2
+n3.dbl_map[-1232349.5489345] = n2
+n3.dbl_map[-234984574.23498725] = n2
+n3.str_map = {}
+n3.str_map[''] = n2
+n3.str_map['sdflkertpioux'] = n2
+n3.str_map['sdfwepwdcjpoi'] = n2
+
+n4 = Fixtures::Structs::Nested4.new
+n4.a_list = []
+n4.a_list << n3
+n4.i32_map = {}
+n4.i32_map[-2345] = n3
+n4.i64_map = {}
+n4.i64_map[2349843765934] = n3
+n4.dbl_map = {}
+n4.dbl_map[-1232349.5489345] = n3
+n4.str_map = {}
+n4.str_map[''] = n3
+
+
+# prof = RubyProf.profile do
+#   n4.write(c_fast_binary_protocol)
+#   Fixtures::Structs::Nested4.new.read(c_fast_binary_protocol)
+# end
+# 
+# printer = RubyProf::GraphHtmlPrinter.new(prof)
+# printer.print(STDOUT, :min_percent=>0)
+
+Benchmark.bmbm do |x|
+  x.report("ruby write large (1MB) structure once") do
+    n4.write(ruby_binary_protocol)
+  end
+  
+  x.report("ruby read large (1MB) structure once") do
+    Fixtures::Structs::Nested4.new.read(ruby_binary_protocol)
+  end
+  
+  x.report("c write large (1MB) structure once") do    
+    n4.write(c_fast_binary_protocol)
+  end
+  
+  x.report("c read large (1MB) structure once") do
+    Fixtures::Structs::Nested4.new.read(c_fast_binary_protocol)
+  end
+  
+  
+  
+  x.report("ruby write 10_000 small structures") do
+    10_000.times do
+      ooe.write(ruby_binary_protocol)
+    end
+  end
+  
+  x.report("ruby read 10_000 small structures") do
+    10_000.times do
+      Fixtures::Structs::OneOfEach.new.read(ruby_binary_protocol)
+    end
+  end
+  
+  x.report("c write 10_000 small structures") do
+    10_000.times do
+      ooe.write(c_fast_binary_protocol)
+    end
+  end
+  
+  x.report("c read 10_000 small structures") do
+    10_000.times do
+      Fixtures::Structs::OneOfEach.new.read(c_fast_binary_protocol)
+    end
+  end
+  
+end
diff --git a/vendor/src/github.com/apache/thrift/test/rb/core/test_backwards_compatability.rb b/vendor/src/github.com/apache/thrift/test/rb/core/test_backwards_compatability.rb
new file mode 100644
index 00000000..0577515d
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/core/test_backwards_compatability.rb
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.join(File.dirname(__FILE__), '../test_helper')
+
+require 'thrift'
+
+class TestThriftException < Test::Unit::TestCase
+  def test_has_accessible_message
+    msg = "hi there thrift"
+    assert_equal msg, Thrift::Exception.new(msg).message
+  end
+end
+
diff --git a/vendor/src/github.com/apache/thrift/test/rb/core/test_exceptions.rb b/vendor/src/github.com/apache/thrift/test/rb/core/test_exceptions.rb
new file mode 100644
index 00000000..f41587a7
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/core/test_exceptions.rb
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.join(File.dirname(__FILE__), '../test_helper')
+
+require 'thrift'
+
+class TestException < Test::Unit::TestCase
+  def test_has_accessible_message
+    msg = "hi there thrift"
+    assert_equal msg, Thrift::Exception.new(msg).message
+  end
+end
+
diff --git a/vendor/src/github.com/apache/thrift/test/rb/core/transport/test_transport.rb b/vendor/src/github.com/apache/thrift/test/rb/core/transport/test_transport.rb
new file mode 100644
index 00000000..37afa858
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/core/transport/test_transport.rb
@@ -0,0 +1,70 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.join(File.dirname(__FILE__), '../../test_helper')
+
+require 'thrift'
+
+class DummyTransport < Thrift::BaseTransport
+  def initialize(data)
+    @data = data
+  end
+  
+  def read(size)
+    @data.slice!(0, size)
+  end
+end
+
+# TTransport is basically an abstract class, but isn't raising NotImplementedError
+class TestThriftTransport < Test::Unit::TestCase
+  def setup
+    @trans = Thrift::BaseTransport.new
+  end
+  
+  def test_open?
+    assert_nil @trans.open?
+  end
+  
+  def test_open
+    assert_nil @trans.open
+  end
+  
+  def test_close
+    assert_nil @trans.close
+  end
+  
+  # TODO:
+  # This doesn't necessarily test he right thing.
+  # It _looks_ like read isn't guaranteed to return the length
+  # you ask for and read_all is. This means our test needs to check
+  # for blocking. -- Kevin Clark 3/27/08
+  def test_read_all
+    # Implements read
+    t = DummyTransport.new("hello")
+    assert_equal "hello", t.read_all(5)
+  end
+  
+  def test_write
+    assert_nil @trans.write(5) # arbitrary value
+  end
+  
+  def test_flush
+    assert_nil @trans.flush
+  end
+end
diff --git a/vendor/src/github.com/apache/thrift/test/rb/fixtures/structs.rb b/vendor/src/github.com/apache/thrift/test/rb/fixtures/structs.rb
new file mode 100644
index 00000000..ebbeb0a7
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/fixtures/structs.rb
@@ -0,0 +1,298 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'thrift'
+
+module Fixtures
+  module Structs
+    class OneBool
+      include Thrift::Struct
+      attr_accessor :bool
+      FIELDS = {
+        1 => {:type => Thrift::Types::BOOL, :name => 'bool'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneByte
+      include Thrift::Struct
+      attr_accessor :byte
+      FIELDS = {
+        1 => {:type => Thrift::Types::BYTE, :name => 'byte'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneI16
+      include Thrift::Struct
+      attr_accessor :i16
+      FIELDS = {
+        1 => {:type => Thrift::Types::I16, :name => 'i16'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneI32
+      include Thrift::Struct
+      attr_accessor :i32
+      FIELDS = {
+        1 => {:type => Thrift::Types::I32, :name => 'i32'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneI64
+      include Thrift::Struct
+      attr_accessor :i64
+      FIELDS = {
+        1 => {:type => Thrift::Types::I64, :name => 'i64'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneDouble
+      include Thrift::Struct
+      attr_accessor :double
+      FIELDS = {
+        1 => {:type => Thrift::Types::DOUBLE, :name => 'double'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneString
+      include Thrift::Struct
+      attr_accessor :string
+      FIELDS = {
+        1 => {:type => Thrift::Types::STRING, :name => 'string'}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneMap
+      include Thrift::Struct
+      attr_accessor :map
+      FIELDS = {
+        1 => {:type => Thrift::Types::MAP, :name => 'map', :key => {:type => Thrift::Types::STRING}, :value => {:type => Thrift::Types::STRING}}
+      }
+
+      def validate
+      end
+    end
+    
+    class NestedMap
+      include Thrift::Struct
+      attr_accessor :map
+      FIELDS = {
+        0 => {:type => Thrift::Types::MAP, :name => 'map', :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::I32}}}
+      }
+
+      def validate
+      end
+    end
+    
+    class OneList
+      include Thrift::Struct
+      attr_accessor :list
+      FIELDS = {
+        1 => {:type => Thrift::Types::LIST, :name => 'list', :element => {:type => Thrift::Types::STRING}}
+      }
+
+      def validate
+      end
+    end
+    
+    class NestedList
+      include Thrift::Struct
+      attr_accessor :list
+      FIELDS = {
+        0 => {:type => Thrift::Types::LIST, :name => 'list', :element => {:type => Thrift::Types::LIST, :element => { :type => Thrift::Types::I32 } } }
+      }
+
+      def validate
+      end
+    end
+    
+    class OneSet
+      include Thrift::Struct
+      attr_accessor :set
+      FIELDS = {
+        1 => {:type => Thrift::Types::SET, :name => 'set', :element => {:type => Thrift::Types::STRING}}
+      }
+
+      def validate
+      end
+    end
+    
+    class NestedSet
+      include Thrift::Struct
+      attr_accessor :set
+      FIELDS = {
+        1 => {:type => Thrift::Types::SET, :name => 'set', :element => {:type => Thrift::Types::SET, :element => { :type => Thrift::Types::STRING } }}
+      }
+
+      def validate
+      end
+    end
+    
+    # struct OneOfEach {
+    #   1: bool im_true,
+    #   2: bool im_false,
+    #   3: byte a_bite,
+    #   4: i16 integer16,
+    #   5: i32 integer32,
+    #   6: i64 integer64,
+    #   7: double double_precision,
+    #   8: string some_characters,
+    #   9: string zomg_unicode,
+    #   10: bool what_who,
+    #   11: binary base64,
+    # }
+    class OneOfEach
+      include Thrift::Struct
+      attr_accessor :im_true, :im_false, :a_bite, :integer16, :integer32, :integer64, :double_precision, :some_characters, :zomg_unicode, :what_who, :base64
+      FIELDS = {
+        1 => {:type => Thrift::Types::BOOL, :name => 'im_true'},
+        2 => {:type => Thrift::Types::BOOL, :name => 'im_false'},
+        3 => {:type => Thrift::Types::BYTE, :name => 'a_bite'},
+        4 => {:type => Thrift::Types::I16, :name => 'integer16'},
+        5 => {:type => Thrift::Types::I32, :name => 'integer32'},
+        6 => {:type => Thrift::Types::I64, :name => 'integer64'},
+        7 => {:type => Thrift::Types::DOUBLE, :name => 'double_precision'},
+        8 => {:type => Thrift::Types::STRING, :name => 'some_characters'},
+        9 => {:type => Thrift::Types::STRING, :name => 'zomg_unicode'},
+        10 => {:type => Thrift::Types::BOOL, :name => 'what_who'},
+        11 => {:type => Thrift::Types::STRING, :name => 'base64'}
+      }
+
+      # Added for assert_equal
+      def ==(other)
+        [:im_true, :im_false, :a_bite, :integer16, :integer32, :integer64, :double_precision, :some_characters, :zomg_unicode, :what_who, :base64].each do |f|
+          var = "@#{f}"
+          return false if instance_variable_get(var) != other.instance_variable_get(var)
+        end
+        true
+      end
+
+      def validate
+      end
+    end
+
+    # struct Nested1 {
+    #   1: list a_list
+    #   2: map i32_map
+    #   3: map i64_map
+    #   4: map dbl_map
+    #   5: map str_map
+    # }
+    class Nested1
+      include Thrift::Struct
+      attr_accessor :a_list, :i32_map, :i64_map, :dbl_map, :str_map
+      FIELDS = {
+        1 => {:type => Thrift::Types::LIST, :name => 'a_list', :element => {:type => Thrift::Types::STRUCT, :class => OneOfEach}},
+        2 => {:type => Thrift::Types::MAP, :name => 'i32_map', :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRUCT, :class => OneOfEach}},
+        3 => {:type => Thrift::Types::MAP, :name => 'i64_map', :key => {:type => Thrift::Types::I64}, :value => {:type => Thrift::Types::STRUCT, :class => OneOfEach}},
+        4 => {:type => Thrift::Types::MAP, :name => 'dbl_map', :key => {:type => Thrift::Types::DOUBLE}, :value => {:type => Thrift::Types::STRUCT, :class => OneOfEach}},
+        5 => {:type => Thrift::Types::MAP, :name => 'str_map', :key => {:type => Thrift::Types::STRING}, :value => {:type => Thrift::Types::STRUCT, :class => OneOfEach}}
+      }
+
+      def validate
+      end
+    end
+
+    # struct Nested2 {
+    #   1: list a_list
+    #   2: map i32_map
+    #   3: map i64_map
+    #   4: map dbl_map
+    #   5: map str_map
+    # }
+    class Nested2
+      include Thrift::Struct
+      attr_accessor :a_list, :i32_map, :i64_map, :dbl_map, :str_map
+      FIELDS = {
+        1 => {:type => Thrift::Types::LIST, :name => 'a_list', :element => {:type => Thrift::Types::STRUCT, :class => Nested1}},
+        2 => {:type => Thrift::Types::MAP, :name => 'i32_map', :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRUCT, :class => Nested1}},
+        3 => {:type => Thrift::Types::MAP, :name => 'i64_map', :key => {:type => Thrift::Types::I64}, :value => {:type => Thrift::Types::STRUCT, :class => Nested1}},
+        4 => {:type => Thrift::Types::MAP, :name => 'dbl_map', :key => {:type => Thrift::Types::DOUBLE}, :value => {:type => Thrift::Types::STRUCT, :class => Nested1}},
+        5 => {:type => Thrift::Types::MAP, :name => 'str_map', :key => {:type => Thrift::Types::STRING}, :value => {:type => Thrift::Types::STRUCT, :class => Nested1}}
+      }
+
+      def validate
+      end
+    end
+
+    # struct Nested3 {
+    #   1: list a_list
+    #   2: map i32_map
+    #   3: map i64_map
+    #   4: map dbl_map
+    #   5: map str_map
+    # }
+    class Nested3
+      include Thrift::Struct
+      attr_accessor :a_list, :i32_map, :i64_map, :dbl_map, :str_map
+      FIELDS = {
+        1 => {:type => Thrift::Types::LIST, :name => 'a_list', :element => {:type => Thrift::Types::STRUCT, :class => Nested2}},
+        2 => {:type => Thrift::Types::MAP, :name => 'i32_map', :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRUCT, :class => Nested2}},
+        3 => {:type => Thrift::Types::MAP, :name => 'i64_map', :key => {:type => Thrift::Types::I64}, :value => {:type => Thrift::Types::STRUCT, :class => Nested2}},
+        4 => {:type => Thrift::Types::MAP, :name => 'dbl_map', :key => {:type => Thrift::Types::DOUBLE}, :value => {:type => Thrift::Types::STRUCT, :class => Nested2}},
+        5 => {:type => Thrift::Types::MAP, :name => 'str_map', :key => {:type => Thrift::Types::STRING}, :value => {:type => Thrift::Types::STRUCT, :class => Nested2}}
+      }
+
+      def validate
+      end
+    end
+
+    # struct Nested4 {
+    #   1: list a_list
+    #   2: map i32_map
+    #   3: map i64_map
+    #   4: map dbl_map
+    #   5: map str_map
+    # }
+    class Nested4
+      include Thrift::Struct
+      attr_accessor :a_list, :i32_map, :i64_map, :dbl_map, :str_map
+      FIELDS = {
+        1 => {:type => Thrift::Types::LIST, :name => 'a_list', :element => {:type => Thrift::Types::STRUCT, :class => Nested3}},
+        2 => {:type => Thrift::Types::MAP, :name => 'i32_map', :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRUCT, :class => Nested3}},
+        3 => {:type => Thrift::Types::MAP, :name => 'i64_map', :key => {:type => Thrift::Types::I64}, :value => {:type => Thrift::Types::STRUCT, :class => Nested3}},
+        4 => {:type => Thrift::Types::MAP, :name => 'dbl_map', :key => {:type => Thrift::Types::DOUBLE}, :value => {:type => Thrift::Types::STRUCT, :class => Nested3}},
+        5 => {:type => Thrift::Types::MAP, :name => 'str_map', :key => {:type => Thrift::Types::STRING}, :value => {:type => Thrift::Types::STRUCT, :class => Nested3}}
+      }
+
+      def validate
+      end
+    end
+  end
+end
diff --git a/vendor/src/github.com/apache/thrift/test/rb/generation/test_enum.rb b/vendor/src/github.com/apache/thrift/test/rb/generation/test_enum.rb
new file mode 100644
index 00000000..607ea66b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/generation/test_enum.rb
@@ -0,0 +1,34 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.join(File.dirname(__FILE__), '../test_helper')
+require 'thrift_test'
+
+class TestEnumGeneration < Test::Unit::TestCase
+  include Thrift::Test
+  def test_enum_valid_values
+    assert_equal(Numberz::VALID_VALUES, Set.new([Numberz::ONE, Numberz::TWO, Numberz::THREE, Numberz::FIVE, Numberz::SIX, Numberz::EIGHT]))
+  end
+  
+  def test_enum_hash
+    Numberz::VALID_VALUES.each do |value|
+      assert_equal(Numberz.const_get(Numberz::VALUE_MAP[value].to_sym), value)
+    end
+  end
+end
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/rb/generation/test_struct.rb b/vendor/src/github.com/apache/thrift/test/rb/generation/test_struct.rb
new file mode 100644
index 00000000..3bd4fc9b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/generation/test_struct.rb
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require File.join(File.dirname(__FILE__), '../test_helper')
+require 'small_service'
+
+class TestStructGeneration < Test::Unit::TestCase
+
+  def test_default_values
+    hello = TestNamespace::Hello.new
+
+    assert_kind_of(TestNamespace::Hello, hello)
+    assert_nil(hello.complexer)
+
+    assert_equal(hello.simple, 53)
+    assert_equal(hello.words, 'words')
+
+    assert_kind_of(TestNamespace::Goodbyez, hello.thinz)
+    assert_equal(hello.thinz.val, 36632)
+
+    assert_kind_of(Hash, hello.complex)
+    assert_equal(hello.complex, { 6243 => 632, 2355 => 532, 23 => 532})
+    
+    bool_passer = TestNamespace::BoolPasser.new(:value => false)
+    assert_equal false, bool_passer.value
+  end
+
+  def test_goodbyez
+    assert_equal(TestNamespace::Goodbyez.new.val, 325)
+  end
+
+end
diff --git a/vendor/src/github.com/apache/thrift/test/rb/integration/TestClient.rb b/vendor/src/github.com/apache/thrift/test/rb/integration/TestClient.rb
new file mode 100644
index 00000000..beebe44e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/integration/TestClient.rb
@@ -0,0 +1,353 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.push File.dirname(__FILE__) + '/..'
+
+require 'test_helper'
+require 'thrift'
+require 'thrift_test'
+
+$protocolType = "binary"
+$host = "localhost"
+$port = 9090
+$transport = "buffered"
+ARGV.each do|a|
+  if a == "--help"
+    puts "Allowed options:"
+    puts "\t -h [ --help ] \t produce help message"
+    puts "\t--host arg (=localhost) \t Host to connect"
+    puts "\t--port arg (=9090) \t Port number to listen"
+    puts "\t--protocol arg (=binary) \t protocol: binary, accel"
+    puts "\t--transport arg (=buffered) transport: buffered, framed, http"
+    exit
+  elsif a.start_with?("--host")
+    $host = a.split("=")[1]
+  elsif a.start_with?("--protocol")
+    $protocolType = a.split("=")[1]
+  elsif a.start_with?("--transport")
+    $transport = a.split("=")[1]
+  elsif a.start_with?("--port")
+    $port = a.split("=")[1].to_i
+  end
+end
+ARGV=[]
+
+class SimpleClientTest < Test::Unit::TestCase
+  def setup
+    unless @socket
+      @socket   = Thrift::Socket.new($host, $port)
+      if $transport == "buffered"
+        transportFactory = Thrift::BufferedTransport.new(@socket)
+      elsif $transport == "framed"
+        transportFactory = Thrift::FramedTransport.new(@socket)
+      else
+        raise 'Unknown transport type'
+      end
+
+      if $protocolType == "binary"
+        @protocol = Thrift::BinaryProtocol.new(transportFactory)
+      elsif $protocolType == "compact"
+        @protocol = Thrift::CompactProtocol.new(transportFactory)
+      elsif $protocolType == "json"
+        @protocol = Thrift::JsonProtocol.new(transportFactory)
+      elsif $protocolType == "accel"
+        @protocol = Thrift::BinaryProtocolAccelerated.new(transportFactory)
+      else
+        raise 'Unknown protocol type'
+      end
+      @client   = Thrift::Test::ThriftTest::Client.new(@protocol)
+      @socket.open
+    end
+  end
+
+  def teardown
+    @socket.close
+  end
+
+  def test_void
+    p 'test_void'
+    @client.testVoid()
+  end
+
+  def test_string
+    p 'test_string'
+    test_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: !@#$%&()(&%$#{}{}<><><' +
+      ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ '
+    test_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, Polski, پنجابی, پښتو, " +
+      "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " +
+      "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " +
+      "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, " +
+      "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " +
+      "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " +
+      "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
+      "Bân-lâm-gú, 粵語"
+
+    result_string = @client.testString(test_string)
+    assert_equal(test_string, result_string.force_encoding(Encoding::UTF_8))
+  end
+
+  def test_bool
+    p 'test_bool'
+    assert_equal(@client.testBool(true), true)
+    assert_equal(@client.testBool(false), false)
+  end
+
+  def test_byte
+    p 'test_byte'
+    val = 120
+    assert_equal(@client.testByte(val), val)
+    assert_equal(@client.testByte(-val), -val)
+  end
+
+  def test_i32
+    p 'test_i32'
+    val = 2000000032
+    assert_equal(@client.testI32(val), val)
+    assert_equal(@client.testI32(-val), -val)
+  end
+
+  def test_i64
+    p 'test_i64'
+    val = 9000000000000000064
+    assert_equal(@client.testI64(val), val)
+    assert_equal(@client.testI64(-val), -val)
+  end
+
+  def test_double
+    p 'test_double'
+    val = 3.14159265358979323846
+    assert_equal(@client.testDouble(val), val)
+    assert_equal(@client.testDouble(-val), -val)
+    assert_kind_of(Float, @client.testDouble(val))
+  end
+
+  def test_binary
+    p 'test_binary'
+    val = (0...256).reverse_each.to_a
+    ret = @client.testBinary(val.pack('C*'))
+    assert_equal(val, ret.bytes.to_a)
+  end
+
+  def test_map
+    p 'test_map'
+    val = {1 => 1, 2 => 2, 3 => 3}
+    assert_equal(@client.testMap(val), val)
+    assert_kind_of(Hash, @client.testMap(val))
+  end
+
+  def test_string_map
+    p 'test_string_map'
+    val = {'a' => '2', 'b' => 'blah', 'some' => 'thing'}
+    ret = @client.testStringMap(val)
+    assert_equal(val, ret)
+    assert_kind_of(Hash, ret)
+  end
+
+  def test_list
+    p 'test_list'
+    val = [1,2,3,4,5]
+    assert_equal(@client.testList(val), val)
+    assert_kind_of(Array, @client.testList(val))
+  end
+
+  def test_enum
+    p 'test_enum'
+    val = Thrift::Test::Numberz::SIX
+    ret = @client.testEnum(val)
+
+    assert_equal(ret, 6)
+    assert_kind_of(Fixnum, ret)
+  end
+
+  def test_typedef
+    p 'test_typedef'
+    #UserId  testTypedef(1: UserId thing),
+    assert_equal(@client.testTypedef(309858235082523), 309858235082523)
+    assert_kind_of(Fixnum, @client.testTypedef(309858235082523))
+    true
+  end
+
+  def test_set
+    p 'test_set'
+    val = Set.new([1,2,3])
+    assert_equal(@client.testSet(val), val)
+    assert_kind_of(Set, @client.testSet(val))
+  end
+
+  def get_struct
+    Thrift::Test::Xtruct.new({'string_thing' => 'hi!', 'i32_thing' => 4 })
+  end
+
+  def test_struct
+    p 'test_struct'
+    ret = @client.testStruct(get_struct)
+
+    # TODO: not sure what unspecified "default" requiredness values should be
+    assert(ret.byte_thing == nil || ret.byte_thing == 0)
+    assert(ret.i64_thing == nil || ret.i64_thing == 0)
+
+    assert_equal(ret.string_thing, 'hi!')
+    assert_equal(ret.i32_thing, 4)
+    assert_kind_of(Thrift::Test::Xtruct, ret)
+  end
+
+  def test_nest
+    p 'test_nest'
+    struct2 = Thrift::Test::Xtruct2.new({'struct_thing' => get_struct, 'i32_thing' => 10})
+
+    ret = @client.testNest(struct2)
+
+    # TODO: not sure what unspecified "default" requiredness values should be
+    assert(ret.struct_thing.byte_thing == nil || ret.struct_thing.byte_thing == 0)
+    assert(ret.struct_thing.i64_thing == nil || ret.struct_thing.i64_thing == 0)
+
+    assert_equal(ret.struct_thing.string_thing, 'hi!')
+    assert_equal(ret.struct_thing.i32_thing, 4)
+    assert_equal(ret.i32_thing, 10)
+
+    assert_kind_of(Thrift::Test::Xtruct, ret.struct_thing)
+    assert_kind_of(Thrift::Test::Xtruct2, ret)
+  end
+
+  def test_insanity
+    p 'test_insanity'
+    insane = Thrift::Test::Insanity.new({
+      'userMap' => {
+        Thrift::Test::Numberz::FIVE => 5,
+        Thrift::Test::Numberz::EIGHT => 8,
+      },
+      'xtructs' => [
+        Thrift::Test::Xtruct.new({
+          'string_thing' => 'Goodbye4',
+          'byte_thing' => 4,
+          'i32_thing' => 4,
+          'i64_thing' => 4,
+        }),
+        Thrift::Test::Xtruct.new({
+          'string_thing' => 'Hello2',
+          'byte_thing' => 2,
+          'i32_thing' => 2,
+          'i64_thing' => 2,
+        })
+      ]
+    })
+
+    ret = @client.testInsanity(insane)
+
+    assert_equal(insane, ret[1][2])
+    assert_equal(insane, ret[1][3])
+
+    assert(ret[2][6].userMap == nil || ret[2][6].userMap.length == 0)
+    assert(ret[2][6].xtructs == nil || ret[2][6].xtructs.length == 0)
+  end
+
+  def test_map_map
+    p 'test_map_map'
+    ret = @client.testMapMap(4)
+    assert_kind_of(Hash, ret)
+    expected = {
+      -4 => {
+        -4 => -4,
+        -3 => -3,
+        -2 => -2,
+        -1 => -1,
+      },
+      4 => {
+        4 => 4,
+        3 => 3,
+        2 => 2,
+        1 => 1,
+      }
+    }
+    assert_equal(expected, ret)
+  end
+
+  def test_multi
+    p 'test_multi'
+    ret = @client.testMulti(42, 4242, 424242, {1 => 'blah', 2 => 'thing'}, Thrift::Test::Numberz::EIGHT, 24)
+    expected = Thrift::Test::Xtruct.new({
+      :string_thing => 'Hello2',
+      :byte_thing =>   42,
+      :i32_thing =>    4242,
+      :i64_thing =>    424242
+    })
+    assert_equal(expected, ret)
+  end
+
+  def test_exception
+    p 'test_exception'
+    assert_raise Thrift::Test::Xception do
+      @client.testException('Xception')
+    end
+    begin
+      @client.testException('TException')
+    rescue => e
+      assert e.class.ancestors.include?(Thrift::Exception)
+    end
+    assert_nothing_raised do
+      @client.testException('test')
+    end
+  end
+
+  def test_multi_exception
+    p 'test_multi_exception'
+    assert_raise Thrift::Test::Xception do
+      @client.testMultiException("Xception", "test 1")
+    end
+    assert_raise Thrift::Test::Xception2 do
+      @client.testMultiException("Xception2", "test 2")
+    end
+    assert_equal( @client.testMultiException("Success", "test 3").string_thing, "test 3")
+  end
+
+  def test_oneway
+    p 'test_oneway'
+    time1 = Time.now.to_f
+    @client.testOneway(1)
+    time2 = Time.now.to_f
+    assert_operator (time2-time1), :<, 0.1
+  end
+
+end
+
diff --git a/vendor/src/github.com/apache/thrift/test/rb/integration/TestServer.rb b/vendor/src/github.com/apache/thrift/test/rb/integration/TestServer.rb
new file mode 100644
index 00000000..bab723a0
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/integration/TestServer.rb
@@ -0,0 +1,159 @@
+#!/usr/bin/env ruby
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.push File.dirname(__FILE__) + '/..'
+
+require 'test_helper'
+require 'thrift'
+require 'thrift_test'
+require 'thrift_test_types'
+
+class SimpleHandler
+  [:testVoid, :testString, :testBool, :testByte, :testI32, :testI64, :testDouble, :testBinary,
+   :testStruct, :testMap, :testStringMap, :testSet, :testList, :testNest, :testEnum, :testTypedef,
+   :testEnum, :testTypedef, :testMultiException].each do |meth|
+
+    define_method(meth) do |thing|
+      p meth
+      p thing
+      thing
+    end
+
+  end
+
+  def testVoid()
+  end
+
+  def testInsanity(thing)
+    return {
+      1 => {
+        2 => thing,
+        3 => thing
+      },
+      2 => {
+        6 => Thrift::Test::Insanity::new()
+      }
+    }
+  end
+
+  def testMapMap(thing)
+    return {
+      -4 => {
+        -4 => -4,
+        -3 => -3,
+        -2 => -2,
+        -1 => -1,
+      },
+      4 => {
+        4 => 4,
+        3 => 3,
+        2 => 2,
+        1 => 1,
+      }
+    }
+  end
+
+  def testMulti(arg0, arg1, arg2, arg3, arg4, arg5)
+    return Thrift::Test::Xtruct.new({
+      'string_thing' => 'Hello2',
+      'byte_thing' => arg0,
+      'i32_thing' => arg1,
+      'i64_thing' => arg2,
+    })
+  end
+
+  def testException(thing)
+    if thing == "Xception"
+      raise Thrift::Test::Xception, :errorCode => 1001, :message => thing
+    elsif thing == "TException"
+      raise Thrift::Exception, :message => thing
+    else
+      # no-op
+    end
+  end
+
+  def testMultiException(arg0, arg1)
+    if arg0 == "Xception2"
+      raise Thrift::Test::Xception2, :errorCode => 2002, :struct_thing => ::Thrift::Test::Xtruct.new({ :string_thing => 'This is an Xception2' })
+    elsif arg0 == "Xception"
+      raise Thrift::Test::Xception, :errorCode => 1001, :message => 'This is an Xception'
+    else
+      return ::Thrift::Test::Xtruct.new({'string_thing' => arg1})
+    end
+  end
+
+  def testOneway(arg0)
+    sleep(arg0)
+  end
+
+end
+
+protocol = "binary"
+port = 9090
+transport = "buffered"
+@transportFactory = Thrift::BufferedTransportFactory.new
+@protocolFactory = Thrift::BinaryProtocolFactory.new
+ARGV.each do|a|
+  if a == "--help"
+    puts "Allowed options:"
+    puts "\t -h [ --help ] \t produce help message"
+    puts "\t--port arg (=9090) \t Port number to listen"
+    puts "\t--protocol arg (=binary) \t protocol: binary, accel"
+    puts "\t--transport arg (=buffered) transport: buffered, framed, http"
+    exit
+  elsif a.start_with?("--protocol")
+    protocol = a.split("=")[1]
+  elsif a.start_with?("--transport")
+    transport = a.split("=")[1]
+  elsif a.start_with?("--port")
+    port = a.split("=")[1].to_i 
+  end
+end
+
+if protocol == "binary"
+  @protocolFactory = Thrift::BinaryProtocolFactory.new
+elsif protocol == ""
+  @protocolFactory = Thrift::BinaryProtocolFactory.new
+elsif protocol == "compact"
+  @protocolFactory = Thrift::CompactProtocolFactory.new
+elsif protocol == "json"
+  @protocolFactory = Thrift::JsonProtocolFactory.new
+elsif protocol == "accel"
+  @protocolFactory = Thrift::BinaryProtocolAcceleratedFactory.new
+else
+  raise 'Unknown protocol type'
+end
+
+if transport == "buffered"
+  @transportFactory = Thrift::BufferedTransportFactory.new
+elsif transport == ""
+  @transportFactory = Thrift::BufferedTransportFactory.new
+elsif transport == "framed"
+  @transportFactory = Thrift::FramedTransportFactory.new
+else
+  raise 'Unknown transport type'
+end
+
+@handler   = SimpleHandler.new
+@processor = Thrift::Test::ThriftTest::Processor.new(@handler)
+@transport = Thrift::ServerSocket.new(port)
+@server    = Thrift::ThreadedServer.new(@processor, @transport, @transportFactory, @protocolFactory)
+@server.serve
diff --git a/vendor/src/github.com/apache/thrift/test/rb/test_helper.rb b/vendor/src/github.com/apache/thrift/test/rb/test_helper.rb
new file mode 100644
index 00000000..c1ed779e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/test_helper.rb
@@ -0,0 +1,35 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.dirname(__FILE__) + '/gen-rb'
+$:.unshift File.join(File.dirname(__FILE__), '../../lib/rb/lib')
+$:.unshift File.join(File.dirname(__FILE__), '../../lib/rb/ext')
+
+require 'test/unit'
+
+module Thrift
+  module Struct
+    def ==(other)
+      return false unless other.is_a? self.class
+      self.class.const_get(:FIELDS).collect {|fid, data| data[:name] }.all? do |field|
+        send(field) == other.send(field)
+      end
+    end
+  end
+end
diff --git a/vendor/src/github.com/apache/thrift/test/rb/test_suite.rb b/vendor/src/github.com/apache/thrift/test/rb/test_suite.rb
new file mode 100644
index 00000000..b157c2c5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rb/test_suite.rb
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+Dir["{core,generation}/**/*.rb"].each {|f| require f }
\ No newline at end of file
diff --git a/vendor/src/github.com/apache/thrift/test/rebuild_known_failures.sh b/vendor/src/github.com/apache/thrift/test/rebuild_known_failures.sh
new file mode 100644
index 00000000..08869fe5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/rebuild_known_failures.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+if [ -z $1 ]; then
+  echo Usage: $0 LANGUAGE
+  echo Re-list all failures of a specific LANGUAGE into known_failures_Linux.json
+  echo LANGUAGE should be library name like cpp, java, py etc
+  exit 1
+fi
+
+if [ -z $PYTHON]; then
+  PYTHON=python
+fi
+
+TARGET_LANG=$1
+OUT_FILE=known_failures_Linux.json
+echo Rebuilding known failures for $TARGET_LANG
+
+TMPFILE=.__tmp__rebuild__
+grep -v -e "\"$1-" -e "\-$1_" $OUT_FILE > $TMPFILE
+mv $TMPFILE $OUT_FILE
+$PYTHON test.py --client $1
+$PYTHON test.py -U merge
+$PYTHON test.py --server $1
+$PYTHON test.py -U merge
diff --git a/vendor/src/github.com/apache/thrift/test/result.js b/vendor/src/github.com/apache/thrift/test/result.js
new file mode 100644
index 00000000..18b1a593
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/result.js
@@ -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.
+
+*/
+
+$.getJSON('results.json', function(results) {
+    $(document).ready(function() {
+        var transport = 3;
+        var socket = 4;
+        var success = 5;
+        var expected = 6;
+        var returnCode = 7;
+        var logFile = 8;
+        testTable = $('#test_results').DataTable({
+            data: results['results'],
+            columnDefs: [
+                {
+                    targets: 3,
+                    render: function(data, type, row) {
+                        return row[transport] + '-' + row[socket];
+                    },
+                },
+                {
+                    targets: 4,
+                    render: function(data, type, row) {
+                        return (row[success] ? 'success' : 'failure')
+                                + '(' + (row[returnCode] == 128 ? 'timeout' : row[returnCode]) + ')'
+                                + '(Server, '
+                                + 'Client)';
+                    },
+                },
+                {
+                    targets: 5,
+                    render: function(data, type, row) {
+                        // 'yes' rather than 'expected' to ease search
+                        return row[expected] ? 'yes' : 'unexpected';
+                    },
+                }
+            ],
+        });
+        $('#test_results_filter label input').focus().val('unexpected failure');
+        $('#test_info').text(
+            "Test Date:     " + results['date'] + "\n" +
+            "Revision:      " + results['revision'] + "\n" +
+            "Platform:      " + results['platform'] + "\n" +
+            "Test duration: " + results['duration']) + " seconds";
+    });
+});
+
diff --git a/vendor/src/github.com/apache/thrift/test/test.py b/vendor/src/github.com/apache/thrift/test/test.py
new file mode 100644
index 00000000..9305967c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/test.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Apache Thrift - integration test suite
+#
+# tests different server-client, protocol and transport combinations
+#
+# This script supports python 2.7 and later.
+# python 3.x is recommended for better stability.
+#
+
+from __future__ import print_function
+from itertools import chain
+import json
+import logging
+import multiprocessing
+import argparse
+import os
+import sys
+
+import crossrunner
+from crossrunner.compat import path_join
+
+ROOT_DIR = os.path.dirname(os.path.realpath(os.path.dirname(__file__)))
+TEST_DIR_RELATIVE = 'test'
+TEST_DIR = path_join(ROOT_DIR, TEST_DIR_RELATIVE)
+FEATURE_DIR_RELATIVE = path_join(TEST_DIR_RELATIVE, 'features')
+CONFIG_FILE = 'tests.json'
+
+
+def run_cross_tests(server_match, client_match, jobs, skip_known_failures, retry_count, regex):
+    logger = multiprocessing.get_logger()
+    logger.debug('Collecting tests')
+    with open(path_join(TEST_DIR, CONFIG_FILE), 'r') as fp:
+        j = json.load(fp)
+    tests = crossrunner.collect_cross_tests(j, server_match, client_match, regex)
+    if not tests:
+        print('No test found that matches the criteria', file=sys.stderr)
+        print('  servers: %s' % server_match, file=sys.stderr)
+        print('  clients: %s' % client_match, file=sys.stderr)
+        return False
+    if skip_known_failures:
+        logger.debug('Skipping known failures')
+        known = crossrunner.load_known_failures(TEST_DIR)
+        tests = list(filter(lambda t: crossrunner.test_name(**t) not in known, tests))
+
+    dispatcher = crossrunner.TestDispatcher(TEST_DIR, ROOT_DIR, TEST_DIR_RELATIVE, jobs)
+    logger.debug('Executing %d tests' % len(tests))
+    try:
+        for r in [dispatcher.dispatch(test, retry_count) for test in tests]:
+            r.wait()
+        logger.debug('Waiting for completion')
+        return dispatcher.wait()
+    except (KeyboardInterrupt, SystemExit):
+        logger.debug('Interrupted, shutting down')
+        dispatcher.terminate()
+        return False
+
+
+def run_feature_tests(server_match, feature_match, jobs, skip_known_failures, retry_count, regex):
+    basedir = path_join(ROOT_DIR, FEATURE_DIR_RELATIVE)
+    logger = multiprocessing.get_logger()
+    logger.debug('Collecting tests')
+    with open(path_join(TEST_DIR, CONFIG_FILE), 'r') as fp:
+        j = json.load(fp)
+    with open(path_join(basedir, CONFIG_FILE), 'r') as fp:
+        j2 = json.load(fp)
+    tests = crossrunner.collect_feature_tests(j, j2, server_match, feature_match, regex)
+    if not tests:
+        print('No test found that matches the criteria', file=sys.stderr)
+        print('  servers: %s' % server_match, file=sys.stderr)
+        print('  features: %s' % feature_match, file=sys.stderr)
+        return False
+    if skip_known_failures:
+        logger.debug('Skipping known failures')
+        known = crossrunner.load_known_failures(basedir)
+        tests = list(filter(lambda t: crossrunner.test_name(**t) not in known, tests))
+
+    dispatcher = crossrunner.TestDispatcher(TEST_DIR, ROOT_DIR, FEATURE_DIR_RELATIVE, jobs)
+    logger.debug('Executing %d tests' % len(tests))
+    try:
+        for r in [dispatcher.dispatch(test, retry_count) for test in tests]:
+            r.wait()
+        logger.debug('Waiting for completion')
+        return dispatcher.wait()
+    except (KeyboardInterrupt, SystemExit):
+        logger.debug('Interrupted, shutting down')
+        dispatcher.terminate()
+        return False
+
+
+def default_concurrenty():
+    try:
+        return int(os.environ.get('THRIFT_CROSSTEST_CONCURRENCY'))
+    except (TypeError, ValueError):
+        # Since much time is spent sleeping, use many threads
+        return int(multiprocessing.cpu_count() * 1.25) + 1
+
+
+def main(argv):
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--server', default='', nargs='*',
+                        help='list of servers to test')
+    parser.add_argument('--client', default='', nargs='*',
+                        help='list of clients to test')
+    parser.add_argument('-F', '--features', nargs='*', default=None,
+                        help='run server feature tests instead of cross language tests')
+    parser.add_argument('-R', '--regex', help='test name pattern to run')
+    parser.add_argument('-s', '--skip-known-failures', action='store_true', dest='skip_known_failures',
+                        help='do not execute tests that are known to fail')
+    parser.add_argument('-r', '--retry-count', type=int,
+                        default=0, help='maximum retry on failure')
+    parser.add_argument('-j', '--jobs', type=int,
+                        default=default_concurrenty(),
+                        help='number of concurrent test executions')
+
+    g = parser.add_argument_group(title='Advanced')
+    g.add_argument('-v', '--verbose', action='store_const',
+                   dest='log_level', const=logging.DEBUG, default=logging.WARNING,
+                   help='show debug output for test runner')
+    g.add_argument('-P', '--print-expected-failures', choices=['merge', 'overwrite'],
+                   dest='print_failures',
+                   help="generate expected failures based on last result and print to stdout")
+    g.add_argument('-U', '--update-expected-failures', choices=['merge', 'overwrite'],
+                   dest='update_failures',
+                   help="generate expected failures based on last result and save to default file location")
+    options = parser.parse_args(argv)
+
+    logger = multiprocessing.log_to_stderr()
+    logger.setLevel(options.log_level)
+
+    if options.features is not None and options.client:
+        print('Cannot specify both --features and --client ', file=sys.stderr)
+        return 1
+
+    # Allow multiple args separated with ',' for backward compatibility
+    server_match = list(chain(*[x.split(',') for x in options.server]))
+    client_match = list(chain(*[x.split(',') for x in options.client]))
+
+    if options.update_failures or options.print_failures:
+        dire = path_join(ROOT_DIR, FEATURE_DIR_RELATIVE) if options.features is not None else TEST_DIR
+        res = crossrunner.generate_known_failures(
+            dire, options.update_failures == 'overwrite',
+            options.update_failures, options.print_failures)
+    elif options.features is not None:
+        features = options.features or ['.*']
+        res = run_feature_tests(server_match, features, options.jobs, options.skip_known_failures, options.retry_count, options.regex)
+    else:
+        res = run_cross_tests(server_match, client_match, options.jobs, options.skip_known_failures, options.retry_count, options.regex)
+    return 0 if res else 1
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/vendor/src/github.com/apache/thrift/test/tests.json b/vendor/src/github.com/apache/thrift/test/tests.json
new file mode 100644
index 00000000..2460b837
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/tests.json
@@ -0,0 +1,550 @@
+[
+  {
+    "name": "c_glib",
+    "platforms": [
+      "Linux"
+    ],
+    "server": {
+      "command": [
+        "test_server"
+      ]
+    },
+    "client": {
+      "command": [
+        "test_client"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "binary",
+      "compact"
+    ],
+    "workdir": "c_glib"
+  },
+  {
+    "name": "d",
+    "server": {
+      "command": [
+        "thrift_test_server"
+      ]
+    },
+    "client": {
+      "command": [
+        "thrift_test_client"
+      ]
+    },
+    "transports": [
+      "http",
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl"
+    ],
+    "protocols": [
+      "binary",
+      "compact",
+      "json"
+    ],
+    "workdir": "../lib/d/test"
+  },
+  {
+    "name": "go",
+    "server": {
+      "command": [
+        "testserver",
+        "--certPath=../../keys"
+      ]
+    },
+    "client": {
+      "timeout": 6,
+      "command": [
+        "testclient"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed",
+      "http"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl"
+    ],
+    "protocols": [
+      "binary",
+      "compact",
+      "json"
+    ],
+    "workdir": "go/bin"
+  },
+  {
+    "name": "java",
+    "join_args": true,
+    "command": [
+      "ant",
+      "-f",
+      "build.xml",
+      "-Dno-gen-thrift=\"\"",
+      "-Dtestargs"
+    ],
+    "prepare": [
+      "ant",
+      "-f",
+      "build.xml",
+      "compile-test"
+    ],
+    "server": {
+      "delay": 10,
+      "extra_args": ["run-testserver"]
+    },
+    "client": {
+      "timeout": 13,
+      "extra_args": ["run-testclient"],
+      "transports": [
+        "http"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed",
+      "framed:fastframed"
+    ],
+    "sockets": [
+      "ip-ssl",
+      "ip"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json"
+    ],
+    "workdir": "../lib/java"
+  },
+  {
+    "name": "nodejs",
+    "env": {
+      "NODE_PATH": "../lib"
+    },
+    "server": {
+      "command": [
+        "node",
+        "server.js",
+        "--type=tcp"
+      ]
+    },
+    "client": {
+      "timeout": 2.9,
+      "command": [
+        "node",
+        "client.js",
+        "--type=tcp"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json"
+    ],
+    "workdir": "../lib/nodejs/test"
+  },
+  {
+    "name": "hs",
+    "server": {
+      "command": [
+        "TestServer"
+      ]
+    },
+    "client": {
+      "timeout": 6,
+      "transports": [
+        "http"
+      ],
+      "command": [
+        "TestClient"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json"
+    ],
+    "workdir": "hs"
+  },
+  {
+    "name": "py",
+    "server": {
+      "extra_args": ["TSimpleServer"],
+      "command": [
+        "TestServer.py",
+        "--verbose",
+        "--genpydir=gen-py"
+      ]
+    },
+    "client": {
+      "timeout": 10,
+      "command": [
+        "TestClient.py",
+        "--verbose",
+        "--host=localhost",
+        "--genpydir=gen-py"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json",
+      "binary:accel",
+      "compact:accelc"
+    ],
+    "workdir": "py"
+  },
+  {
+    "comment": "Using 'python3' executable to test py2 and 3 at once",
+    "name": "py3",
+    "server": {
+      "extra_args": ["TSimpleServer"],
+      "command": [
+        "python3",
+        "TestServer.py",
+        "--verbose",
+        "--genpydir=gen-py"
+      ]
+    },
+    "client": {
+      "timeout": 10,
+      "command": [
+        "python3",
+        "TestClient.py",
+        "--host=localhost",
+        "--genpydir=gen-py"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip-ssl",
+      "ip"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json",
+      "binary:accel",
+      "compact:accelc"
+    ],
+    "workdir": "py"
+  },
+  {
+    "name": "cpp",
+    "server": {
+      "command": [
+        "TestServer"
+      ]
+    },
+    "client": {
+      "timeout": 8,
+      "command": [
+        "TestClient"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "http",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl",
+      "domain"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json",
+      "header"
+    ],
+    "workdir": "cpp"
+  },
+  {
+    "name": "rb",
+    "server": {
+      "command": [
+        "ruby",
+        "../integration/TestServer.rb"
+      ]
+    },
+    "client": {
+      "timeout": 5,
+      "command": [
+        "ruby",
+        "../integration/TestClient.rb"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "compact",
+      "binary",
+      "json",
+      "binary:accel"
+    ],
+    "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"
+      ]
+    },
+    "workdir": "csharp"
+  },
+  {
+    "name": "perl",
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl",
+      "domain"
+    ],
+    "protocols": [
+      "binary"
+    ],
+    "client": {
+      "command": [
+        "perl",
+        "-Igen-perl/",
+        "-I../../lib/perl/lib/",
+        "TestClient.pl",
+        "--cert=../keys/client.pem"
+      ]
+    },
+    "server": {
+      "command": [
+        "perl",
+        "-Igen-perl/",
+        "-I../../lib/perl/lib/",
+        "TestServer.pl",
+        "--cert=../keys/server.pem",
+        "--key=../keys/server.key"
+      ]
+    },
+    "workdir": "perl"
+  },
+  {
+    "name": "php",
+    "client": {
+      "timeout": 6,
+      "transports": [
+        "buffered",
+        "framed"
+      ],
+      "sockets": [
+        "ip"
+      ],
+      "protocols": [
+        "binary",
+        "compact",
+        "binary:accel"
+      ],
+      "command": [
+        "php",
+        "-dextension_dir=../../lib/php/src/ext/thrift_protocol/modules/",
+        "--php-ini=../../lib/php/thrift_protocol.ini",
+        "--no-php-ini",
+        "-ddisplay_errors=stderr",
+        "-dlog_errors=0",
+        "-derror_reporting=E_ALL",
+        "TestClient.php"
+      ]
+    },
+    "workdir": "php"
+  },
+  {
+    "name": "dart",
+    "client": {
+      "transports": [
+        "buffered",
+        "framed",
+        "http"
+      ],
+      "sockets": [
+        "ip"
+      ],
+      "protocols": [
+        "binary",
+        "compact",
+        "json"
+      ],
+      "command": [
+        "dart",
+        "test_client/bin/main.dart"
+      ]
+    },
+    "workdir": "dart"
+  },
+  {
+    "name": "erl",
+    "transports": [
+      "buffered",
+      "framed"
+    ],
+    "sockets": [
+      "ip",
+      "ip-ssl"
+    ],
+    "protocols": [
+      "binary",
+      "compact"
+    ],
+    "client": {
+      "command": [
+        "erl",
+        "+K",
+        "true",
+        "-noshell",
+        "-pa",
+        "../../lib/erl/ebin/",
+        "-pa",
+        "./ebin",
+        "-s",
+        "test_client",
+        "-s",
+        "init",
+        "stop",
+        "-extra"
+      ]
+    },
+    "server": {
+      "command": [
+        "erl",
+        "+K",
+        "true",
+        "-noshell",
+        "-pa",
+        "../../lib/erl/ebin/",
+        "-pa",
+        "./ebin",
+        "-s",
+        "test_thrift_server",
+        "-extra"
+      ]
+    },
+    "workdir": "erl"
+  },
+  {
+    "name": "js",
+    "transports": [
+      "http"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "json"
+    ],
+    "client": {
+      "command": [
+        "phantomjs",
+        "test/phantom-client.js"
+      ]
+    },
+    "workdir": "../lib/js"
+  },
+  {
+    "name": "lua",
+    "TODO": "Add dll to LUA_CPATH",
+    "env": {
+      "LUA_PATH": ";;gen-lua/?.lua;../../lib/lua/?.lua",
+      "LUA_CPATH": ";;../../lib/lua/.libs/?.so"
+    },
+    "client": {
+      "timeout": 5,
+      "transports": [
+        "buffered",
+        "framed",
+        "http"
+      ],
+      "sockets": [
+        "ip"
+      ],
+      "protocols": [
+        "binary",
+        "compact",
+        "json"
+      ],
+      "command": [
+        "lua",
+        "test_basic_client.lua"
+      ]
+    },
+    "workdir": "lua"
+  }
+]
diff --git a/vendor/src/github.com/apache/thrift/test/threads/Makefile b/vendor/src/github.com/apache/thrift/test/threads/Makefile
new file mode 100644
index 00000000..df723969
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/threads/Makefile
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Default target is everything
+
+ifndef thrift_home
+thrift_home=../../
+endif #thrift_home
+
+target: all
+
+ifndef boost_home
+boost_home=/usr/local/include/boost-1_33_1
+endif #boost_home
+target: all
+
+include_paths = $(thrift_home)/lib/cpp/src \
+		$(boost_home)
+
+include_flags = $(patsubst %,-I%, $(include_paths))
+
+# Tools
+ifndef THRIFT
+THRIFT = ../../compiler/cpp/thrift
+endif # THRIFT
+
+CC     = g++
+LD     = g++
+
+# Compiler flags
+LFL   =  -L$(thrift_home)/lib/cpp/.libs -lthrift
+CCFL  = -Wall -O3 -g -I./gen-cpp $(include_flags)
+CFL   = $(CCFL) $(LFL)
+
+all: server client
+
+stubs: ThreadsTest.thrift
+	$(THRIFT) --gen cpp --gen py ThreadsTest.thrift
+
+server: stubs
+	$(CC) -o ThreadsServer $(CFL) ThreadsServer.cpp ./gen-cpp/ThreadsTest.cpp ./gen-cpp/ThreadsTest_types.cpp
+
+client: stubs
+	$(CC) -o ThreadsClient $(CFL) ThreadsClient.cpp ./gen-cpp/ThreadsTest.cpp ./gen-cpp/ThreadsTest_types.cpp
+
+clean:
+	$(RM) -r *.o ThreadsServer ThreadsClient gen-cpp gen-py
diff --git a/vendor/src/github.com/apache/thrift/test/threads/ThreadsClient.cpp b/vendor/src/github.com/apache/thrift/test/threads/ThreadsClient.cpp
new file mode 100644
index 00000000..9306a3f2
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/threads/ThreadsClient.cpp
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This autogenerated skeleton file illustrates how to build a server.
+// You should copy it to another filename to avoid overwriting it.
+
+#include "ThreadsTest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#if _WIN32
+   #include 
+#endif
+
+using boost::shared_ptr;
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::server;
+using namespace apache::thrift::concurrency;
+
+int main(int argc, char **argv) {
+#if _WIN32
+  transport::TWinsockSingleton::create();
+#endif
+  int port = 9090;
+  std::string host = "localhost";
+
+  shared_ptr transport(new TSocket(host, port));
+  shared_ptr protocol(new TBinaryProtocol(transport));
+
+  transport->open();
+
+  ThreadsTestClient client(protocol);
+  int val;
+  val = client.threadOne(5);
+  fprintf(stderr, "%d\n", val);
+  val = client.stop();
+  fprintf(stderr, "%d\n", val);
+  val = client.threadTwo(5);
+  fprintf(stderr, "%d\n", val);
+
+  transport->close();
+
+  fprintf(stderr, "done.\n");
+
+  return 0;
+}
+
diff --git a/vendor/src/github.com/apache/thrift/test/threads/ThreadsServer.cpp b/vendor/src/github.com/apache/thrift/test/threads/ThreadsServer.cpp
new file mode 100644
index 00000000..a267c3b9
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/threads/ThreadsServer.cpp
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This autogenerated skeleton file illustrates how to build a server.
+// You should copy it to another filename to avoid overwriting it.
+
+#include "ThreadsTest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#if _WIN32
+   #include 
+#endif
+
+using boost::shared_ptr;
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::server;
+using namespace apache::thrift::concurrency;
+
+
+class ThreadsTestHandler : virtual public ThreadsTestIf {
+ public:
+  ThreadsTestHandler() {
+    // Your initialization goes here
+  }
+
+  int32_t threadOne(const int32_t sleep) {
+    // Your implementation goes here
+    printf("threadOne\n");
+    go2sleep(1, sleep);
+    return 1;
+  }
+
+  int32_t threadTwo(const int32_t sleep) {
+    // Your implementation goes here
+    printf("threadTwo\n");
+    go2sleep(2, sleep);
+    return 1;
+  }
+
+  int32_t threadThree(const int32_t sleep) {
+    // Your implementation goes here
+    printf("threadThree\n");
+    go2sleep(3, sleep);
+    return 1;
+  }
+
+  int32_t threadFour(const int32_t sleep) {
+    // Your implementation goes here
+    printf("threadFour\n");
+    go2sleep(4, sleep);
+    return 1;
+  }
+
+  int32_t stop() {
+    printf("stop\n");
+    server_->stop();
+    return 1;
+  }
+
+  void setServer(boost::shared_ptr server) {
+    server_ = server;
+  }
+
+protected:
+  void go2sleep(int thread, int seconds) {
+    Monitor m;
+    Synchronized s(m);
+    for (int i = 0; i < seconds; ++i) {
+      fprintf(stderr, "Thread %d: sleep %d\n", thread, i);
+      try {
+        m.wait(1000);
+      } catch(const TimedOutException&) {
+      }
+    }
+    fprintf(stderr, "THREAD %d DONE\n", thread);
+  }
+
+private:
+  boost::shared_ptr server_;
+
+};
+
+int main(int argc, char **argv) {
+#if _WIN32
+  transport::TWinsockSingleton::create();
+#endif
+  int port = 9090;
+  shared_ptr handler(new ThreadsTestHandler());
+  shared_ptr processor(new ThreadsTestProcessor(handler));
+  shared_ptr serverTransport(new TServerSocket(port));
+  shared_ptr transportFactory(new TBufferedTransportFactory());
+  shared_ptr protocolFactory(new TBinaryProtocolFactory());
+
+  /*
+  shared_ptr threadManager =
+    ThreadManager::newSimpleThreadManager(10);
+  shared_ptr threadFactory =
+    shared_ptr(new PlatformThreadFactory());
+  threadManager->threadFactory(threadFactory);
+  threadManager->start();
+
+  shared_ptr server =
+    shared_ptr(new TThreadPoolServer(processor,
+                                              serverTransport,
+                                              transportFactory,
+                                              protocolFactory,
+                                              threadManager));
+  */
+
+  shared_ptr server =
+    shared_ptr(new TThreadedServer(processor,
+                                            serverTransport,
+                                            transportFactory,
+                                            protocolFactory));
+
+  handler->setServer(server);
+
+  server->serve();
+
+  fprintf(stderr, "done.\n");
+
+  return 0;
+}
+
diff --git a/vendor/src/github.com/apache/thrift/test/threads/ThreadsTest.thrift b/vendor/src/github.com/apache/thrift/test/threads/ThreadsTest.thrift
new file mode 100644
index 00000000..caa93460
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/threads/ThreadsTest.thrift
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+service ThreadsTest {
+  i32 threadOne(1: i32 sleep=15),
+  i32 threadTwo(2: i32 sleep=15),
+  i32 threadThree(3: i32 sleep=15),
+  i32 threadFour(4: i32 sleep=15)
+
+  i32 stop();
+
+}
diff --git a/vendor/src/github.com/apache/thrift/test/valgrind.suppress b/vendor/src/github.com/apache/thrift/test/valgrind.suppress
new file mode 100644
index 00000000..41f9414e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/test/valgrind.suppress
@@ -0,0 +1,9 @@
+{
+   boost/get_once_per_thread_epoch/ignore
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   fun:malloc
+   fun:_ZN5boost6detail25get_once_per_thread_epochEv
+}
+
+
diff --git a/vendor/src/github.com/apache/thrift/tutorial/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/Makefile.am
new file mode 100644
index 00000000..5865c54a
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/Makefile.am
@@ -0,0 +1,98 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+SUBDIRS =
+
+if MINGW
+# do nothing, just build the compiler
+else
+
+if WITH_C_GLIB
+SUBDIRS += c_glib
+endif
+
+if WITH_CPP
+SUBDIRS += cpp
+endif
+
+if WITH_D
+SUBDIRS += d
+endif
+
+if WITH_JAVA
+SUBDIRS += java
+SUBDIRS += js
+endif
+
+if WITH_PYTHON
+SUBDIRS += py
+SUBDIRS += py.twisted
+SUBDIRS += py.tornado
+endif
+
+if WITH_RUBY
+SUBDIRS += rb
+endif
+
+if WITH_HASKELL
+SUBDIRS += hs
+endif
+
+if WITH_HAXE
+SUBDIRS += haxe
+endif
+
+if WITH_GO
+SUBDIRS += go
+endif
+
+if WITH_NODEJS
+SUBDIRS += nodejs
+endif
+
+if WITH_DART
+SUBDIRS += dart
+endif
+
+#
+# generate html for ThriftTest.thrift
+#
+all-local:
+	$(top_builddir)/compiler/cpp/thrift --gen html -r $(top_srcdir)/tutorial/tutorial.thrift
+
+clean-local:
+	rm -rf $(top_srcdir)/tutorial/gen-html
+
+endif
+
+# Any folders or files not listed above being added to SUBDIR need to be placed here in
+# 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/vendor/src/github.com/apache/thrift/tutorial/README.md b/vendor/src/github.com/apache/thrift/tutorial/README.md
new file mode 100644
index 00000000..7772bf3e
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/README.md
@@ -0,0 +1,42 @@
+Thrift Tutorial
+
+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.
+
+Tutorial
+========
+
+1) First things first, you'll need to install the Thrift compiler and the
+   language libraries. Do that using the instructions in the top level
+   README.md file.
+
+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.
+
+6) That's about it for now. This tutorial is intentionally brief. It should be
+   just enough to get you started and ready to build your own project.
diff --git a/vendor/src/github.com/apache/thrift/tutorial/as3/build.xml b/vendor/src/github.com/apache/thrift/tutorial/as3/build.xml
new file mode 100644
index 00000000..f7ed32d0
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/as3/build.xml
@@ -0,0 +1,50 @@
+
+
+  Thrift actionscript 3.0 tutorial.
+
+  
+  
+  
+  
+
+  
+  
+
+  
+    
+  
+
+  
+    
+    
+  
+
+  
+    
+    
+  
+
+  
+    
+      
+      
+      
+    
+  
+
+  
+    
+    
+      
+    
+    
+      
+    
+  
+
+  
+    
+    
+  
+
+
diff --git a/vendor/src/github.com/apache/thrift/tutorial/as3/src/CalculatorUI.as b/vendor/src/github.com/apache/thrift/tutorial/as3/src/CalculatorUI.as
new file mode 100644
index 00000000..d996df5f
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/as3/src/CalculatorUI.as
@@ -0,0 +1,142 @@
+package {
+  import flash.display.Sprite;
+  import flash.text.TextField;
+  import flash.text.TextFieldType;
+  import flash.events.MouseEvent;
+  import flash.system.Security;
+
+  import org.apache.thrift.transport.TSocket;
+  import org.apache.thrift.transport.TTransport;
+  import org.apache.thrift.protocol.TProtocol;
+  import org.apache.thrift.protocol.TBinaryProtocol;
+
+  /**
+   * Simple interface and connection logic implementation for tutorial.
+   */
+  public class CalculatorUI extends Sprite {
+    public static const BUTTON_PADDING:uint = 5;
+
+    private var mCalculatorClient:Calculator; // we use calculator through interface
+    private var mTransport:TTransport; // Transport, used to comunicate with server
+
+    private var mAddButton:Sprite;
+    private var mLeft:TextField;
+    private var mRight:TextField;
+    private var mResult:TextField;
+
+    private var pingButton:Sprite;
+
+    public function CalculatorUI() {
+      buildInterface();
+      initSecurity();
+      initConnection();
+    }
+
+    private function initSecurity():void {
+      Security.loadPolicyFile("xmlsocket://127.0.0.1:9092");
+    }
+
+    /**
+     * Example of initializing connection.
+     */
+    private function initConnection():void {
+      mTransport = new TSocket("127.0.0.1", 9090); // we connect to server
+      mTransport.open();
+      // initialize protocol:
+      var protocol:TProtocol = new TBinaryProtocol(mTransport, false, false);
+      mCalculatorClient = new CalculatorImpl(protocol); // finally, we create calculator client instance
+    }
+
+    private function onPingClick(me:MouseEvent):void {
+      if(!mTransport.isOpen()) return;
+      mCalculatorClient.ping(onPingError, onPingSuccess);
+    }
+
+    private function onPingError(error:Error):void {
+      trace("Error, while requesting ping.");
+      throw error;
+    }
+
+    private function onPingSuccess():void {
+      trace("Ping returned successfully");
+    }
+
+    private function onAddClick(me:MouseEvent):void {
+      if(!mTransport.isOpen()) return;
+      var num1:Number = Number(mLeft.text);
+      var num2:Number = Number(mRight.text);
+      mResult.text = "Processing...";
+      mCalculatorClient.add(num1, num2, onAddError, onAddSuccess);
+    }
+
+    private function onAddError(error:Error):void {
+      trace("Error, while requesting add.");
+      throw error;
+    }
+
+    private function onAddSuccess(res:Number):void {
+      mResult.text = String(res);
+    }
+
+    private function buildInterface():void {
+      addChild(pingButton = buildButton("PING"));
+      pingButton.x = (stage.stageWidth - pingButton.width) / 2;
+      pingButton.y = 10;
+      pingButton.addEventListener(MouseEvent.CLICK, onPingClick);
+
+      var top:Number = pingButton.y + pingButton.height + 20;
+      addChild(mLeft = buildDigitInput());
+      mLeft.x = 15;
+      mLeft.y = top + BUTTON_PADDING;
+      addChild(mRight = buildDigitInput());
+      mRight.x = mLeft.x + mLeft.width + 15;
+      mRight.y = top + BUTTON_PADDING;
+      addChild(mAddButton = buildButton("ADD"));
+      mAddButton.x = mRight.x + mRight.width + 15;
+      mAddButton.y = top;
+      mAddButton.addEventListener(MouseEvent.CLICK, onAddClick);
+      addChild(mResult = buildDigitInput());
+      mResult.x = mAddButton.x + mAddButton.width + 15;
+      mResult.y = top + BUTTON_PADDING;
+    }
+
+    /**
+     * Simple digit-only input field.
+     */
+    private function buildDigitInput():TextField {
+      var textField:TextField = new TextField;
+      textField.width = 75;
+      textField.height = 20;
+      textField.restrict = "0987654321.";
+      textField.type = TextFieldType.INPUT;
+      textField.background = true;
+      textField.backgroundColor = 0xaaaaff;
+      textField.textColor = 0xffff00;
+      return textField;
+    }
+
+    /**
+     * Simple button drawing.
+     */
+    private function buildButton(text:String):Sprite {
+      var button:Sprite = new Sprite;
+      var textField:TextField = new TextField;
+      textField.width = 4000;
+      textField.text = text;
+      textField.textColor = 0xffff00;
+      textField.width = textField.textWidth + 4;
+      textField.height = textField.textHeight + 4;
+      textField.mouseEnabled = false;
+      button.graphics.beginFill(0x0000ff);
+      button.graphics.lineStyle(0, 0x000000);
+      button.graphics.drawRoundRect(0, 0, textField.width + BUTTON_PADDING * 2,
+                                    textField.height + BUTTON_PADDING * 2, BUTTON_PADDING);
+      button.graphics.endFill();
+      button.addChild(textField);
+      textField.x = BUTTON_PADDING;
+      textField.y = BUTTON_PADDING;
+      button.useHandCursor = button.buttonMode = true;
+      return button;
+    }
+  }
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/c_glib/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/c_glib/Makefile.am
new file mode 100644
index 00000000..565f6352
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/c_glib/Makefile.am
@@ -0,0 +1,85 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = subdir-objects serial-tests
+
+BUILT_SOURCES = \
+	gen-c_glib/calculator.h \
+	gen-c_glib/shared_service.h \
+	gen-c_glib/shared_types.h \
+	gen-c_glib/tutorial_types.h
+
+AM_CFLAGS = -g -Wall -Wextra -pedantic $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) @GCOV_CFLAGS@
+AM_CPPFLAGS = -I$(top_srcdir)/lib/c_glib/src -Igen-c_glib
+AM_LDFLAGS = $(GLIB_LIBS) $(GOBJECT_LIBS) @GCOV_LDFLAGS@
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+noinst_LTLIBRARIES = \
+	libtutorialgencglib.la
+
+nodist_libtutorialgencglib_la_SOURCES = \
+	gen-c_glib/calculator.c \
+	gen-c_glib/calculator.h \
+	gen-c_glib/shared_service.c \
+	gen-c_glib/shared_service.h \
+	gen-c_glib/shared_types.c \
+	gen-c_glib/shared_types.h \
+	gen-c_glib/tutorial_types.c \
+	gen-c_glib/tutorial_types.h
+
+libtutorialgencglib_la_LIBADD = \
+	$(top_builddir)/lib/c_glib/libthrift_c_glib.la
+
+libtutorialgencglib_la_CFLAGS = \
+	$(AM_CFLAGS) -Wno-unused-function
+
+noinst_PROGRAMS = \
+	tutorial_server \
+	tutorial_client
+
+tutorial_server_SOURCES = \
+	c_glib_server.c
+
+tutorial_server_LDADD = \
+	libtutorialgencglib.la \
+	$(top_builddir)/lib/c_glib/libthrift_c_glib.la
+
+tutorial_client_SOURCES = \
+	c_glib_client.c
+
+tutorial_client_LDADD = \
+	libtutorialgencglib.la \
+	$(top_builddir)/lib/c_glib/libthrift_c_glib.la
+
+
+gen-c_glib/calculator.c gen-c_glib/calculator.h gen-c_glib/shared_service.c gen-c_glib/shared_service.h gen-c_glib/shared_types.c gen-c_glib/shared_types.h gen-c_glib/tutorial_types.c gen-c_glib/tutorial_types.h: $(top_srcdir)/tutorial/tutorial.thrift
+	$(THRIFT) --gen c_glib -r $<
+
+clean-local:
+	$(RM) gen-c_glib/*
+
+tutorialserver: all
+	./tutorial_server
+
+tutorialclient: all
+	./tutorial_client
+
+EXTRA_DIST = \
+	c_glib_server.c \
+	c_glib_client.c
diff --git a/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_client.c b/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_client.c
new file mode 100644
index 00000000..986d5174
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_client.c
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+#include "gen-c_glib/calculator.h"
+
+int main (void)
+{
+  ThriftSocket *socket;
+  ThriftTransport *transport;
+  ThriftProtocol *protocol;
+  CalculatorIf *client;
+
+  GError *error = NULL;
+  InvalidOperation *invalid_operation = NULL;
+
+  Work *work;
+
+  gint32 sum;
+  gint32 diff;
+
+  int exit_status = 0;
+
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+  g_type_init ();
+#endif
+
+  socket    = g_object_new (THRIFT_TYPE_SOCKET,
+                            "hostname",  "localhost",
+                            "port",      9090,
+                            NULL);
+  transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
+                            "transport", socket,
+                            NULL);
+  protocol  = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
+                            "transport", transport,
+                            NULL);
+
+  thrift_transport_open (transport, &error);
+
+
+  /* In the C (GLib) implementation of Thrift, service methods on the
+     server are accessed via a generated client class that implements
+     the service interface. In this tutorial, we access a Calculator
+     service through an instance of CalculatorClient, which implements
+     CalculatorIf. */
+  client = g_object_new (TYPE_CALCULATOR_CLIENT,
+                         "input_protocol",  protocol,
+                         "output_protocol", protocol,
+                         NULL);
+
+  /* Each of the client methods requires at least two parameters: A
+     pointer to the client-interface implementation (the client
+     object), and a handle to a GError structure to receive
+     information about any error that occurs.
+
+     On success, client methods return TRUE. A return value of FALSE
+     indicates an error occurred and the error parameter has been
+     set. */
+  if (!error && calculator_if_ping (client, &error)) {
+    puts ("ping()");
+  }
+
+  /* Service methods that return a value do so by passing the result
+     back via an output parameter (here, "sum"). */
+  if (!error && calculator_if_add (client, &sum, 1, 1, &error)) {
+    printf ("1+1=%d\n", sum);
+  }
+
+  /* Thrift structs are implemented as GObjects, with each of the
+     struct's members exposed as an object property. */
+  work = g_object_new (TYPE_WORK, NULL);
+
+  if (!error) {
+    g_object_set (work,
+                  "num1", 1,
+                  "num2", 0,
+                  "op",   OPERATION_DIVIDE,
+                  NULL);
+
+    /* Exceptions are passed back from service methods in a manner
+       similar to return values. */
+    if (calculator_if_calculate (client,
+                                 NULL,
+                                 1,
+                                 work,
+                                 &invalid_operation,
+                                 &error)) {
+      puts ("Whoa? We can divide by zero!");
+    }
+    else {
+      if (invalid_operation) {
+        gchar *why;
+
+        /* Like structs, exceptions are implemented as objects with
+           properties. */
+        g_object_get (invalid_operation, "why", &why, NULL);
+
+        printf ("InvalidOperation: %s\n", why);
+
+        if (why != NULL)
+          g_free (why);
+        g_object_unref (invalid_operation);
+        invalid_operation = NULL;
+      }
+
+      g_clear_error (&error);
+    }
+  }
+
+  if (!error) {
+    /* Struct objects can be reused across method invocations. */
+    g_object_set (work,
+                  "num1", 15,
+                  "num2", 10,
+                  "op",   OPERATION_SUBTRACT,
+                  NULL);
+
+    if (calculator_if_calculate (client,
+                                 &diff,
+                                 1,
+                                 work,
+                                 &invalid_operation,
+                                 &error)) {
+      printf ("15-10=%d\n", diff);
+    }
+  }
+
+  g_object_unref (work);
+
+  if (!error) {
+    SharedStruct *shared_struct;
+    gchar *value;
+
+    shared_struct = g_object_new (TYPE_SHARED_STRUCT, NULL);
+
+    /* As defined in the Thrift file, the Calculator service extends
+       the SharedService service. Correspondingly, in the generated
+       code CalculatorIf inherits from SharedServiceIf, and the parent
+       service's methods are accessible through a simple cast. */
+    if (shared_service_client_get_struct (SHARED_SERVICE_IF (client),
+                                          &shared_struct,
+                                          1,
+                                          &error)) {
+      g_object_get (shared_struct, "value", &value, NULL);
+      printf ("Check log: %s\n", value);
+      g_free (value);
+    }
+
+    g_object_unref (shared_struct);
+  }
+
+  if (error) {
+    printf ("ERROR: %s\n", error->message);
+    g_clear_error (&error);
+
+    exit_status = 1;
+  }
+
+  thrift_transport_close (transport, NULL);
+
+  g_object_unref (client);
+  g_object_unref (protocol);
+  g_object_unref (transport);
+  g_object_unref (socket);
+
+  return exit_status;
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_server.c b/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_server.c
new file mode 100644
index 00000000..47bf47fa
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/c_glib/c_glib_server.c
@@ -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.
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "gen-c_glib/calculator.h"
+
+G_BEGIN_DECLS
+
+/* In the C (GLib) implementation of Thrift, the actual work done by a
+   server---that is, the code that runs when a client invokes a
+   service method---is defined in a separate "handler" class that
+   implements the service interface. Here we define the
+   TutorialCalculatorHandler class, which implements the CalculatorIf
+   interface and provides the behavior expected by tutorial clients.
+   (Typically this code would be placed in its own module but for
+   clarity this tutorial is presented entirely in a single file.)
+
+   For each service the Thrift compiler generates an abstract base
+   class from which handler implementations should inherit. In our
+   case TutorialCalculatorHandler inherits from CalculatorHandler,
+   defined in gen-c_glib/calculator.h.
+
+   If you're new to GObject, try not to be intimidated by the quantity
+   of code here---much of it is boilerplate and can mostly be
+   copied-and-pasted from existing work. For more information refer to
+   the GObject Reference Manual, available online at
+   https://developer.gnome.org/gobject/. */
+
+#define TYPE_TUTORIAL_CALCULATOR_HANDLER \
+  (tutorial_calculator_handler_get_type ())
+
+#define TUTORIAL_CALCULATOR_HANDLER(obj)                                \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                                   \
+                               TYPE_TUTORIAL_CALCULATOR_HANDLER,        \
+                               TutorialCalculatorHandler))
+#define TUTORIAL_CALCULATOR_HANDLER_CLASS(c)                    \
+  (G_TYPE_CHECK_CLASS_CAST ((c),                                \
+                            TYPE_TUTORIAL_CALCULATOR_HANDLER,   \
+                            TutorialCalculatorHandlerClass))
+#define IS_TUTORIAL_CALCULATOR_HANDLER(obj)                             \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                                   \
+                               TYPE_TUTORIAL_CALCULATOR_HANDLER))
+#define IS_TUTORIAL_CALCULATOR_HANDLER_CLASS(c)                 \
+  (G_TYPE_CHECK_CLASS_TYPE ((c),                                \
+                            TYPE_TUTORIAL_CALCULATOR_HANDLER))
+#define TUTORIAL_CALCULATOR_HANDLER_GET_CLASS(obj)              \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                            \
+                              TYPE_TUTORIAL_CALCULATOR_HANDLER, \
+                              TutorialCalculatorHandlerClass))
+
+struct _TutorialCalculatorHandler {
+  CalculatorHandler parent_instance;
+
+  /* private */
+  GHashTable *log;
+};
+typedef struct _TutorialCalculatorHandler TutorialCalculatorHandler;
+
+struct _TutorialCalculatorHandlerClass {
+  CalculatorHandlerClass parent_class;
+};
+typedef struct _TutorialCalculatorHandlerClass TutorialCalculatorHandlerClass;
+
+GType tutorial_calculator_handler_get_type (void);
+
+G_END_DECLS
+
+/* ---------------------------------------------------------------- */
+
+/* The implementation of TutorialCalculatorHandler follows. */
+
+G_DEFINE_TYPE (TutorialCalculatorHandler,
+               tutorial_calculator_handler,
+               TYPE_CALCULATOR_HANDLER)
+
+/* Each of a handler's methods accepts at least two parameters: A
+   pointer to the service-interface implementation (the handler object
+   itself) and a handle to a GError structure to receive information
+   about any error that occurs.
+
+   On success, a handler method returns TRUE. A return value of FALSE
+   indicates an error occurred and the error parameter has been
+   set. (Methods should not return FALSE without first setting the
+   error parameter.) */
+static gboolean
+tutorial_calculator_handler_ping (CalculatorIf  *iface,
+                                  GError       **error)
+{
+  THRIFT_UNUSED_VAR (iface);
+  THRIFT_UNUSED_VAR (error);
+
+  puts ("ping()");
+
+  return TRUE;
+}
+
+/* Service-method parameters are passed through as parameters to the
+   handler method.
+
+   If the service method returns a value an output parameter, _return,
+   is additionally passed to the handler method. This parameter should
+   be set appropriately before the method returns, whenever it
+   succeeds.
+
+   The return value from this method happens to be of a base type,
+   i32, but note if a method returns a complex type such as a map or
+   list *_return will point to a pre-allocated data structure that
+   does not need to be re-allocated and should not be destroyed. */
+static gboolean
+tutorial_calculator_handler_add (CalculatorIf  *iface,
+                                 gint32        *_return,
+                                 const gint32   num1,
+                                 const gint32   num2,
+                                 GError       **error)
+{
+  THRIFT_UNUSED_VAR (iface);
+  THRIFT_UNUSED_VAR (error);
+
+  printf ("add(%d,%d)\n", num1, num2);
+  *_return = num1 + num2;
+
+  return TRUE;
+}
+
+/* Any handler method can return a ThriftApplicationException to the
+   client by setting its error parameter appropriately and returning
+   FALSE. See the ThriftApplicationExceptionError enumeration defined
+   in thrift_application_exception.h for a list of recognized
+   exception types (GError codes).
+
+   If a service method can also throw a custom exception (that is, one
+   defined in the .thrift file) an additional output parameter will be
+   provided (here, "ouch") to hold an instance of the exception, when
+   necessary. Note there will be a separate parameter added for each
+   type of exception the method can throw.
+
+   Unlike return values, exception objects are never pre-created; this
+   is always the responsibility of the handler method. */
+static gboolean
+tutorial_calculator_handler_calculate (CalculatorIf      *iface,
+                                       gint32            *_return,
+                                       const gint32       logid,
+                                       const Work        *w,
+                                       InvalidOperation **ouch,
+                                       GError           **error)
+{
+  TutorialCalculatorHandler *self;
+
+  gint *log_key;
+  gchar log_value[12];
+  SharedStruct *log_struct;
+
+  gint num1;
+  gint num2;
+  Operation op;
+  gboolean result = TRUE;
+
+  THRIFT_UNUSED_VAR (error);
+
+  g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
+                        FALSE);
+  self = TUTORIAL_CALCULATOR_HANDLER (iface);
+
+  /* Remember: Exception objects are never pre-created */
+  g_assert (*ouch == NULL);
+
+  /* Fetch the contents of our Work parameter.
+
+     Note that integer properties of thirty-two bits or fewer in width
+     are _always_ of type gint, regardless of the range of values they
+     hold. A common error is trying to retrieve, say, a structure
+     member defined in the .thrift file as type i16 into a variable of
+     type gint16, which will clobber variables adjacent on the
+     stack. Remember: If you're retrieving an integer property the
+     receiving variable must be of either type gint or gint64, as
+     appropriate. */
+  g_object_get ((Work *)w,
+                "num1", &num1,
+                "num2", &num2,
+                "op",   &op,
+                NULL);
+
+  printf ("calculate(%d,{%d,%d,%d})\n", logid, op, num1, num2);
+
+  switch (op) {
+  case OPERATION_ADD:
+    *_return = num1 + num2;
+    break;
+
+  case OPERATION_SUBTRACT:
+    *_return = num1 - num2;
+    break;
+
+  case OPERATION_MULTIPLY:
+    *_return = num1 * num2;
+    break;
+
+  case OPERATION_DIVIDE:
+    if (num2 == 0) {
+      /* For each custom exception type a subclass of ThriftStruct is
+         generated by the Thrift compiler. Throw an exception by
+         setting the corresponding output parameter to a new instance
+         of its type and returning FALSE. */
+      *ouch = g_object_new (TYPE_INVALID_OPERATION,
+                            "whatOp", op,
+                            "why",  g_strdup ("Cannot divide by 0"),
+                            NULL);
+      result = FALSE;
+
+      /* Note the call to g_strdup above: All the memory used by a
+         ThriftStruct's properties belongs to the object itself and
+         will be freed on destruction. Removing this call to g_strdup
+         will lead to a segmentation fault as the object tries to
+         release memory allocated statically to the program. */
+    }
+    else {
+      *_return = num1 / num2;
+    }
+    break;
+
+  default:
+    *ouch = g_object_new (TYPE_INVALID_OPERATION,
+                          "whatOp", op,
+                          "why",  g_strdup ("Invalid Operation"),
+                          NULL);
+    result = FALSE;
+  }
+
+  /* On success, log a record of the result to our hash table */
+  if (result) {
+    log_key = g_malloc (sizeof *log_key);
+    *log_key = logid;
+
+    snprintf (log_value, sizeof log_value, "%d", *_return);
+
+    log_struct = g_object_new (TYPE_SHARED_STRUCT,
+                               "key",   *log_key,
+                               "value",  g_strdup (log_value),
+                               NULL);
+    g_hash_table_replace (self->log, log_key, log_struct);
+  }
+
+  return result;
+}
+
+/* A one-way method has the same signature as an equivalent, regular
+   method that returns no value. */
+static gboolean
+tutorial_calculator_handler_zip (CalculatorIf  *iface,
+                                 GError       **error)
+{
+  THRIFT_UNUSED_VAR (iface);
+  THRIFT_UNUSED_VAR (error);
+
+  puts ("zip()");
+
+  return TRUE;
+}
+
+/* As specified in the .thrift file (tutorial.thrift), the Calculator
+   service extends the SharedService service. Correspondingly, in the
+   generated code the Calculator interface, CalculatorIf, extends the
+   SharedService interface, SharedServiceIf, and subclasses of
+   CalculatorHandler should implement its methods as well.
+
+   Here we provide an implementation for the getStruct method from the
+   parent service. */
+static gboolean
+tutorial_calculator_handler_get_struct (SharedServiceIf  *iface,
+                                        SharedStruct    **_return,
+                                        const gint32      key32,
+                                        GError          **error)
+{
+  gint key = (gint)key32;
+  TutorialCalculatorHandler *self;
+  SharedStruct *log_struct;
+  gint log_key;
+  gchar *log_value;
+
+  THRIFT_UNUSED_VAR (error);
+
+  g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
+                        FALSE);
+  self = TUTORIAL_CALCULATOR_HANDLER (iface);
+
+  /* Remember: Complex return types are always pre-created and need
+     only be populated */
+  g_assert (*_return != NULL);
+
+  printf ("getStruct(%d)\n", key);
+
+  /* If the key exists in our log, return the corresponding logged
+     data (or an empty SharedStruct structure if it does not).
+
+     Incidentally, note we _must_ here copy the values from the hash
+     table into the return structure. All memory used by the return
+     structure belongs to the structure itself and will be freed once
+     a response is sent to the client. If we merely freed *_return and
+     set it to point to our hash-table entry, that would mean memory
+     would be released (effectively, data erased) out of the hash
+     table! */
+  log_struct = g_hash_table_lookup (self->log, &key);
+  if (log_struct != NULL) {
+    g_object_get (log_struct,
+                  "key",   &log_key,
+                  "value", &log_value,
+                  NULL);
+    g_object_set (*_return,
+                  "key",   log_key,
+                  "value", g_strdup (log_value),
+                  NULL);
+  }
+
+  return TRUE;
+}
+
+/* TutorialCalculatorHandler's instance finalizer (destructor) */
+static void
+tutorial_calculator_handler_finalize (GObject *object)
+{
+  TutorialCalculatorHandler *self =
+    TUTORIAL_CALCULATOR_HANDLER (object);
+
+  /* Free our calculation-log hash table */
+  g_hash_table_unref (self->log);
+  self->log = NULL;
+
+  /* Chain up to the parent class */
+  G_OBJECT_CLASS (tutorial_calculator_handler_parent_class)->
+    finalize (object);
+}
+
+/* TutorialCalculatorHandler's instance initializer (constructor) */
+static void
+tutorial_calculator_handler_init (TutorialCalculatorHandler *self)
+{
+  /* Create our calculation-log hash table */
+  self->log = g_hash_table_new_full (g_int_hash,
+                                     g_int_equal,
+                                     g_free,
+                                     g_object_unref);
+}
+
+/* TutorialCalculatorHandler's class initializer */
+static void
+tutorial_calculator_handler_class_init (TutorialCalculatorHandlerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  SharedServiceHandlerClass *shared_service_handler_class =
+    SHARED_SERVICE_HANDLER_CLASS (klass);
+  CalculatorHandlerClass *calculator_handler_class =
+    CALCULATOR_HANDLER_CLASS (klass);
+
+  /* Register our destructor */
+  gobject_class->finalize = tutorial_calculator_handler_finalize;
+
+  /* Register our implementations of CalculatorHandler's methods */
+  calculator_handler_class->ping =
+    tutorial_calculator_handler_ping;
+  calculator_handler_class->add =
+    tutorial_calculator_handler_add;
+  calculator_handler_class->calculate =
+    tutorial_calculator_handler_calculate;
+  calculator_handler_class->zip =
+    tutorial_calculator_handler_zip;
+
+  /* Register our implementation of SharedServiceHandler's method */
+  shared_service_handler_class->get_struct =
+    tutorial_calculator_handler_get_struct;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* That ends the implementation of TutorialCalculatorHandler.
+   Everything below is fairly generic code that sets up a minimal
+   Thrift server for tutorial clients. */
+
+
+/* Our server object, declared globally so it is accessible within the
+   SIGINT signal handler */
+ThriftServer *server = NULL;
+
+/* A flag that indicates whether the server was interrupted with
+   SIGINT (i.e. Ctrl-C) so we can tell whether its termination was
+   abnormal */
+gboolean sigint_received = FALSE;
+
+/* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
+   server */
+static void
+sigint_handler (int signal_number)
+{
+  THRIFT_UNUSED_VAR (signal_number);
+
+  /* Take note we were called */
+  sigint_received = TRUE;
+
+  /* Shut down the server gracefully */
+  if (server != NULL)
+    thrift_server_stop (server);
+}
+
+int main (void)
+{
+  TutorialCalculatorHandler *handler;
+  CalculatorProcessor *processor;
+
+  ThriftServerTransport *server_transport;
+  ThriftTransportFactory *transport_factory;
+  ThriftProtocolFactory *protocol_factory;
+
+  struct sigaction sigint_action;
+
+  GError *error = NULL;
+  int exit_status = 0;
+
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+  g_type_init ();
+#endif
+
+  /* Create an instance of our handler, which provides the service's
+     methods' implementation */
+  handler =
+    g_object_new (TYPE_TUTORIAL_CALCULATOR_HANDLER,
+                  NULL);
+
+  /* Create an instance of the service's processor, automatically
+     generated by the Thrift compiler, which parses incoming messages
+     and dispatches them to the appropriate method in the handler */
+  processor =
+    g_object_new (TYPE_CALCULATOR_PROCESSOR,
+                  "handler", handler,
+                  NULL);
+
+  /* Create our server socket, which binds to the specified port and
+     listens for client connections */
+  server_transport =
+    g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+                  "port", 9090,
+                  NULL);
+
+  /* Create our transport factory, used by the server to wrap "raw"
+     incoming connections from the client (in this case with a
+     ThriftBufferedTransport to improve performance) */
+  transport_factory =
+    g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
+                  NULL);
+
+  /* Create our protocol factory, which determines which wire protocol
+     the server will use (in this case, Thrift's binary protocol) */
+  protocol_factory =
+    g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
+                  NULL);
+
+  /* Create the server itself */
+  server =
+    g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
+                  "processor",                processor,
+                  "server_transport",         server_transport,
+                  "input_transport_factory",  transport_factory,
+                  "output_transport_factory", transport_factory,
+                  "input_protocol_factory",   protocol_factory,
+                  "output_protocol_factory",  protocol_factory,
+                  NULL);
+
+  /* Install our SIGINT handler, which handles Ctrl-C being pressed by
+     stopping the server gracefully (not strictly necessary, but a
+     nice touch) */
+  memset (&sigint_action, 0, sizeof (sigint_action));
+  sigint_action.sa_handler = sigint_handler;
+  sigint_action.sa_flags = SA_RESETHAND;
+  sigaction (SIGINT, &sigint_action, NULL);
+
+  /* Start the server, which will run until its stop method is invoked
+     (from within the SIGINT handler, in this case) */
+  puts ("Starting the server...");
+  thrift_server_serve (server, &error);
+
+  /* If the server stopped for any reason other than having been
+     interrupted by the user, report the error */
+  if (!sigint_received) {
+    g_message ("thrift_server_serve: %s",
+               error != NULL ? error->message : "(null)");
+    g_clear_error (&error);
+  }
+
+  puts ("done.");
+
+  g_object_unref (server);
+  g_object_unref (transport_factory);
+  g_object_unref (protocol_factory);
+  g_object_unref (server_transport);
+
+  g_object_unref (processor);
+  g_object_unref (handler);
+
+  return exit_status;
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/cpp/CMakeLists.txt b/vendor/src/github.com/apache/thrift/tutorial/cpp/CMakeLists.txt
new file mode 100644
index 00000000..1feec2c3
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/cpp/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.
+#
+
+include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
+
+#Make sure gen-cpp files can be included
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-cpp")
+include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src")
+
+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
+)
+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
+    COMMAND ${THRIFT_COMPILER} --gen cpp -r ${PROJECT_SOURCE_DIR}/tutorial/tutorial.thrift
+)
+
+add_executable(TutorialServer CppServer.cpp)
+target_link_libraries(TutorialServer tutorialgencpp)
+LINK_AGAINST_THRIFT_LIBRARY(TutorialServer thrift)
+target_link_libraries(TutorialServer ${ZLIB_LIBRARIES})
+
+add_executable(TutorialClient CppClient.cpp)
+target_link_libraries(TutorialClient tutorialgencpp)
+LINK_AGAINST_THRIFT_LIBRARY(TutorialClient thrift)
+target_link_libraries(TutorialClient ${ZLIB_LIBRARIES})
diff --git a/vendor/src/github.com/apache/thrift/tutorial/cpp/CppClient.cpp b/vendor/src/github.com/apache/thrift/tutorial/cpp/CppClient.cpp
new file mode 100644
index 00000000..2763fee5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/cpp/CppClient.cpp
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+
+#include "../gen-cpp/Calculator.h"
+
+using namespace std;
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+
+using namespace tutorial;
+using namespace shared;
+
+int main() {
+  boost::shared_ptr socket(new TSocket("localhost", 9090));
+  boost::shared_ptr transport(new TBufferedTransport(socket));
+  boost::shared_ptr protocol(new TBinaryProtocol(transport));
+  CalculatorClient client(protocol);
+
+  try {
+    transport->open();
+
+    client.ping();
+    cout << "ping()" << endl;
+
+    cout << "1 + 1 = " << client.add(1, 1) << endl;
+
+    Work work;
+    work.op = Operation::DIVIDE;
+    work.num1 = 1;
+    work.num2 = 0;
+
+    try {
+      client.calculate(1, work);
+      cout << "Whoa? We can divide by zero!" << endl;
+    } catch (InvalidOperation& io) {
+      cout << "InvalidOperation: " << io.why << endl;
+      // or using generated operator<<: cout << io << endl;
+      // or by using std::exception native method what(): cout << io.what() << endl;
+    }
+
+    work.op = Operation::SUBTRACT;
+    work.num1 = 15;
+    work.num2 = 10;
+    int32_t diff = client.calculate(1, work);
+    cout << "15 - 10 = " << diff << endl;
+
+    // Note that C++ uses return by reference for complex types to avoid
+    // costly copy construction
+    SharedStruct ss;
+    client.getStruct(ss, 1);
+    cout << "Received log: " << ss << endl;
+
+    transport->close();
+  } catch (TException& tx) {
+    cout << "ERROR: " << tx.what() << endl;
+  }
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/cpp/CppServer.cpp b/vendor/src/github.com/apache/thrift/tutorial/cpp/CppServer.cpp
new file mode 100644
index 00000000..eafffa97
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/cpp/CppServer.cpp
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 
+
+#include 
+
+#include 
+#include 
+#include 
+
+#include "../gen-cpp/Calculator.h"
+
+using namespace std;
+using namespace apache::thrift;
+using namespace apache::thrift::concurrency;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::server;
+
+using namespace tutorial;
+using namespace shared;
+
+class CalculatorHandler : public CalculatorIf {
+public:
+  CalculatorHandler() {}
+
+  void ping() { cout << "ping()" << endl; }
+
+  int32_t add(const int32_t n1, const int32_t n2) {
+    cout << "add(" << n1 << ", " << n2 << ")" << endl;
+    return n1 + n2;
+  }
+
+  int32_t calculate(const int32_t logid, const Work& work) {
+    cout << "calculate(" << logid << ", " << work << ")" << endl;
+    int32_t val;
+
+    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;
+        io.whatOp = work.op;
+        io.why = "Cannot divide by 0";
+        throw io;
+      }
+      val = work.num1 / work.num2;
+      break;
+    default:
+      InvalidOperation io;
+      io.whatOp = work.op;
+      io.why = "Invalid Operation";
+      throw io;
+    }
+
+    SharedStruct ss;
+    ss.key = logid;
+    ss.value = to_string(val);
+
+    log[logid] = ss;
+
+    return val;
+  }
+
+  void getStruct(SharedStruct& ret, const int32_t logid) {
+    cout << "getStruct(" << logid << ")" << endl;
+    ret = log[logid];
+  }
+
+  void zip() { cout << "zip()" << endl; }
+
+protected:
+  map log;
+};
+
+/*
+  CalculatorIfFactory is code generated.
+  CalculatorCloneFactory is useful for getting access to the server side of the
+  transport.  It is also useful for making per-connection state.  Without this
+  CloneFactory, all connections will end up sharing the same handler instance.
+*/
+class CalculatorCloneFactory : virtual public CalculatorIfFactory {
+ public:
+  virtual ~CalculatorCloneFactory() {}
+  virtual CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo)
+  {
+    boost::shared_ptr sock = boost::dynamic_pointer_cast(connInfo.transport);
+    cout << "Incoming connection\n";
+    cout << "\tSocketInfo: "  << sock->getSocketInfo() << "\n";
+    cout << "\tPeerHost: "    << sock->getPeerHost() << "\n";
+    cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
+    cout << "\tPeerPort: "    << sock->getPeerPort() << "\n";
+    return new CalculatorHandler;
+  }
+  virtual void releaseHandler( ::shared::SharedServiceIf* handler) {
+    delete handler;
+  }
+};
+
+int main() {
+  TThreadedServer server(
+    boost::make_shared(boost::make_shared()),
+    boost::make_shared(9090), //port
+    boost::make_shared(),
+    boost::make_shared());
+
+  /*
+  // if you don't need per-connection state, do the following instead
+  TThreadedServer server(
+    boost::make_shared(boost::make_shared()),
+    boost::make_shared(9090), //port
+    boost::make_shared(),
+    boost::make_shared());
+  */
+
+  /**
+   * Here are some alternate server types...
+
+  // This server only allows one connection at a time, but spawns no threads
+  TSimpleServer server(
+    boost::make_shared(boost::make_shared()),
+    boost::make_shared(9090),
+    boost::make_shared(),
+    boost::make_shared());
+
+  const int workerCount = 4;
+
+  boost::shared_ptr threadManager =
+    ThreadManager::newSimpleThreadManager(workerCount);
+  threadManager->threadFactory(
+    boost::make_shared());
+  threadManager->start();
+
+  // This server allows "workerCount" connection at a time, and reuses threads
+  TThreadPoolServer server(
+    boost::make_shared(boost::make_shared()),
+    boost::make_shared(9090),
+    boost::make_shared(),
+    boost::make_shared(),
+    threadManager);
+  */
+
+  cout << "Starting the server..." << endl;
+  server.serve();
+  cout << "Done." << endl;
+  return 0;
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/cpp/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/cpp/Makefile.am
new file mode 100644
index 00000000..184a69d6
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/cpp/Makefile.am
@@ -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.
+#
+AUTOMAKE_OPTIONS = subdir-objects serial-tests
+
+BUILT_SOURCES = gen-cpp/shared_types.cpp \
+                gen-cpp/tutorial_types.cpp
+
+noinst_LTLIBRARIES = libtutorialgencpp.la
+nodist_libtutorialgencpp_la_SOURCES = \
+	gen-cpp/Calculator.cpp \
+	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 \
+	gen-cpp/tutorial_constants.h \
+	gen-cpp/tutorial_types.cpp \
+	gen-cpp/tutorial_types.h
+
+
+
+libtutorialgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+
+noinst_PROGRAMS = \
+	TutorialServer \
+	TutorialClient
+
+TutorialServer_SOURCES = \
+	CppServer.cpp
+
+TutorialServer_LDADD = \
+	libtutorialgencpp.la \
+	$(top_builddir)/lib/cpp/libthrift.la
+
+TutorialClient_SOURCES = \
+	CppClient.cpp
+
+TutorialClient_LDADD = \
+	libtutorialgencpp.la \
+	$(top_builddir)/lib/cpp/libthrift.la
+
+#
+# Common thrift code generation rules
+#
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+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
+	$(THRIFT) --gen cpp -r $<
+
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(LIBEVENT_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -Igen-cpp
+AM_CXXFLAGS = -Wall -Wextra -pedantic
+AM_LDFLAGS = $(BOOST_LDFLAGS) $(LIBEVENT_LDFLAGS)
+
+clean-local:
+	$(RM) gen-cpp/*
+
+tutorialserver: all
+	./TutorialServer
+
+tutorialclient: all
+	./TutorialClient
+
+style-local:
+	$(CPPSTYLE_CMD)
+
+EXTRA_DIST = \
+	CMakeLists.txt \
+	CppClient.cpp \
+	CppServer.cpp
diff --git a/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.cs b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.cs
new file mode 100644
index 00000000..113a4722
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.cs
@@ -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.
+ */
+
+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/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.csproj b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.csproj
new file mode 100644
index 00000000..1ea7ff63
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/CsharpClient.csproj
@@ -0,0 +1,110 @@
+
+
+
+  
+    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/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..bf4bc5d7
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpClient/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CsharpClient")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CsharpClient")]
+[assembly: AssemblyCopyright("Copyright © 2010 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.10.0.0")]
+[assembly: AssemblyFileVersion("0.10.0.0")]
diff --git a/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.cs b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.cs
new file mode 100644
index 00000000..439790aa
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.cs
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+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/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.csproj b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.csproj
new file mode 100644
index 00000000..07481806
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/CsharpServer.csproj
@@ -0,0 +1,111 @@
+
+
+
+  
+    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/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..c416acc2
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/CsharpServer/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CsharpServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CsharpServer")]
+[assembly: AssemblyCopyright("Copyright © 2010 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.10.0.0")]
+[assembly: AssemblyFileVersion("0.10.0.0")]
diff --git a/vendor/src/github.com/apache/thrift/tutorial/csharp/tutorial.sln b/vendor/src/github.com/apache/thrift/tutorial/csharp/tutorial.sln
new file mode 100644
index 00000000..ec57a188
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/csharp/tutorial.sln
@@ -0,0 +1,39 @@
+
+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/vendor/src/github.com/apache/thrift/tutorial/d/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/d/Makefile.am
new file mode 100644
index 00000000..d8c8b291
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/d/Makefile.am
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+LIB_D_DIR = $(top_srcdir)/lib/d
+
+GEN_SRC = gen-d/share/SharedService.d gen-d/share/shared_types.d \
+	gen-d/tutorial/tutorial_types.d gen-d/tutorial/Calculator.d
+
+$(GEN_SRC): $(top_srcdir)/tutorial/tutorial.thrift
+	$(top_builddir)/compiler/cpp/thrift --gen d -r $<
+
+server: server.d $(GEN_SRC)
+	$(DMD) -I${LIB_D_DIR}/src -L-L${LIB_D_DIR} -L-lthriftd server.d ${GEN_SRC}
+
+client: client.d $(GEN_SRC)
+	$(DMD) -I${LIB_D_DIR}/src -L-L${LIB_D_DIR} -L-lthriftd client.d ${GEN_SRC}
+
+PROGS = server client
+
+if WITH_D_EVENT_TESTS
+async_client: async_client.d $(GEN_SRC)
+	$(DMD) -I${LIB_D_DIR}/src -L-L${LIB_D_DIR} -L-lthriftd-event -L-lthriftd -L-levent async_client.d ${GEN_SRC}
+
+PROGS += async_client
+endif
+
+all-local: $(PROGS)
+
+clean:
+	$(RM) -f $(PROGS)
diff --git a/vendor/src/github.com/apache/thrift/tutorial/d/async_client.d b/vendor/src/github.com/apache/thrift/tutorial/d/async_client.d
new file mode 100644
index 00000000..1defce08
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/d/async_client.d
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module async_client;
+
+import std.exception;
+import std.stdio;
+import thrift.async.libevent;
+import thrift.async.socket;
+import thrift.base;
+import thrift.codegen.async_client;
+import thrift.protocol.binary;
+import thrift.transport.buffered;
+
+import tutorial.Calculator;
+import tutorial.tutorial_types;
+
+void main() {
+  auto asyncManager = new TLibeventAsyncManager;
+
+  // If we are done, gracefully stop the async manager to avoid hanging on
+  // appplication shutdown.
+  scope (exit) asyncManager.stop();
+
+  auto socket = new TAsyncSocket(asyncManager, "localhost", 9090);
+  auto client = new TAsyncClient!Calculator(
+    socket,
+    new TBufferedTransportFactory,
+    new TBinaryProtocolFactory!TBufferedTransport
+  );
+
+  socket.open();
+
+  // Invoke all the methods.
+  auto pingResult = client.ping();
+
+  auto addResult = client.add(1, 1);
+
+  auto work = Work();
+  work.op = Operation.DIVIDE;
+  work.num1 = 1;
+  work.num2 = 0;
+  auto quotientResult = client.calculate(1, work);
+
+  work.op = Operation.SUBTRACT;
+  work.num1 = 15;
+  work.num2 = 10;
+  auto diffResult = client.calculate(1, work);
+
+  auto logResult = client.getStruct(1);
+
+  // Await the responses.
+  pingResult.waitGet();
+  writeln("ping()");
+
+  int sum = addResult.waitGet();
+  writefln("1 + 1 = %s", sum);
+
+  try {
+    quotientResult.waitGet();
+    writeln("Whoa we can divide by 0");
+  } catch (InvalidOperation io) {
+    writeln("Invalid operation: " ~ io.why);
+  }
+
+  writefln("15 - 10 = %s", diffResult.waitGet());
+
+  // TFuture is implicitly convertible to the result type via »alias this«,
+  // for which it (eagerly, of course) awaits completion.
+  writefln("Check log: %s", logResult.value);
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/d/client.d b/vendor/src/github.com/apache/thrift/tutorial/d/client.d
new file mode 100644
index 00000000..1867a17b
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/d/client.d
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module client;
+
+import std.stdio;
+import thrift.base;
+import thrift.codegen.client;
+import thrift.protocol.binary;
+import thrift.transport.buffered;
+import thrift.transport.socket;
+
+import tutorial.Calculator;
+import tutorial.tutorial_types;
+
+void main() {
+  auto socket = new TSocket("localhost", 9090);
+  auto transport = new TBufferedTransport(socket);
+  auto protocol = tBinaryProtocol(transport);
+  auto client = tClient!Calculator(protocol);
+
+  transport.open();
+
+  client.ping();
+  writeln("ping()");
+
+  int sum = client.add(1, 1);
+  writefln("1 + 1 = %s", sum);
+
+  auto work = Work();
+  work.op = Operation.DIVIDE;
+  work.num1 = 1;
+  work.num2 = 0;
+  try {
+    int quotient = client.calculate(1, work);
+    writeln("Whoa we can divide by 0");
+  } catch (InvalidOperation io) {
+    writeln("Invalid operation: " ~ io.why);
+  }
+
+  work.op = Operation.SUBTRACT;
+  work.num1 = 15;
+  work.num2 = 10;
+  int diff = client.calculate(1, work);
+  writefln("15 - 10 = %s", diff);
+
+  auto log = client.getStruct(1);
+  writefln("Check log: %s", log.value);
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/d/server.d b/vendor/src/github.com/apache/thrift/tutorial/d/server.d
new file mode 100644
index 00000000..cbcedccf
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/d/server.d
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+module server;
+
+import std.conv : to;
+import std.stdio;
+import thrift.codegen.processor;
+import thrift.protocol.binary;
+import thrift.server.simple;
+import thrift.server.transport.socket;
+import thrift.transport.buffered;
+
+import share.SharedService;
+import share.shared_types;
+import tutorial.Calculator;
+import tutorial.tutorial_types;
+
+/**
+ * The actual implementation of the Calculator interface that is called by
+ * the server to answer the requests.
+ */
+class CalculatorHandler : Calculator {
+  void ping() {
+    writeln("ping()");
+  }
+
+  int add(int n1, int n2) {
+    writefln("add(%s,%s)", n1, n2);
+    return n1 + n2;
+  }
+
+  int calculate(int logid, ref const(Work) work) {
+    writefln("calculate(%s, {%s, %s, %s})", logid, work.op, work.num1, work.num2);
+    int val;
+
+    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) {
+        auto io = new InvalidOperation();
+        io.whatOp = work.op;
+        io.why = "Cannot divide by 0";
+        throw io;
+      }
+      val = work.num1 / work.num2;
+      break;
+    default:
+      auto io = new InvalidOperation();
+      io.whatOp = work.op;
+      io.why = "Invalid Operation";
+      throw io;
+    }
+
+    auto ss = SharedStruct();
+    ss.key = logid;
+    ss.value = to!string(val);
+    log[logid] = ss;
+
+    return val;
+  }
+
+  SharedStruct getStruct(int logid) {
+    writefln("getStruct(%s)", logid);
+    return log[logid];
+  }
+
+  void zip() {
+    writeln("zip()");
+  }
+
+protected:
+  SharedStruct[int] log;
+}
+
+void main() {
+  auto protocolFactory = new TBinaryProtocolFactory!();
+  auto processor = new TServiceProcessor!Calculator(new CalculatorHandler);
+  auto serverTransport = new TServerSocket(9090);
+  auto transportFactory = new TBufferedTransportFactory;
+
+  auto server = new TSimpleServer(
+    processor, serverTransport, transportFactory, protocolFactory);
+
+  writeln("Starting the server on port 9090...");
+  server.serve();
+  writeln("done.");
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/dart/Makefile.am
new file mode 100644
index 00000000..ccca5982
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/dart/Makefile.am
@@ -0,0 +1,71 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+BUILT_SOURCES = gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart
+
+THRIFT = $(top_builddir)/compiler/cpp/thrift
+
+gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart: $(top_srcdir)/tutorial/tutorial.thrift
+	$(THRIFT) --gen dart -r $<
+
+all-local: gen-dart/tutorial/lib/tutorial.dart pub-get
+
+clean-local:
+	$(RM) -r gen-*
+	find . -type d -name "packages" | xargs $(RM) -r
+	find . -type f -name ".packages" | 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
+
+pub-get-tutorial: gen-dart/tutorial/lib/tutorial.dart
+	cd gen-dart/tutorial; ${DARTPUB} get
+
+pub-get-shared: gen-dart/shared/lib/shared.dart
+	cd gen-dart/shared; ${DARTPUB} get
+
+pub-get-client:
+	cd client; ${DARTPUB} get
+
+pub-get-console-client:
+	cd console_client; ${DARTPUB} get
+
+pub-get-server:
+	cd server; ${DARTPUB} get
+
+tutorialserver: pub-get-gen pub-get-server
+	${DART} server/bin/main.dart
+
+tutorialclient: pub-get-gen pub-get-client
+	cd client; ${DARTPUB} serve
+
+tutorialconsoleclient: pub-get-console-client
+	${DART} console_client/bin/main.dart
+
+EXTRA_DIST = \
+	client/web/client.dart \
+	client/web/index.html \
+	client/web/styles.css \
+	client/pubspec.yaml \
+	console_client/bin/main.dart \
+	console_client/pubspec.yaml \
+	server/bin/main.dart \
+	server/pubspec.yaml \
+	build.sh
diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/build.sh b/vendor/src/github.com/apache/thrift/tutorial/dart/build.sh
new file mode 100644
index 00000000..eabe04a1
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/dart/build.sh
@@ -0,0 +1,56 @@
+#!/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.
+
+set -e;
+rm -r gen-dart || true;
+
+thrift --gen dart ../shared.thrift;
+cd gen-dart/shared;
+pub get;
+cd ../..;
+
+thrift --gen dart ../tutorial.thrift;
+cd gen-dart/tutorial;
+pub get;
+cd ../..;
+
+cd client;
+pub get;
+cd ..;
+
+cd console_client;
+pub get;
+cd ..;
+
+cd server;
+pub get;
+cd ..;
+
+dartfmt -w gen-dart;
+
+echo "\nEnjoy the Dart tutorial!";
+echo "\nTo run the server:";
+echo "> dart server/bin/main.dart";
+echo "\nTo run the client:";
+echo "# Serve the app from the client directory and view in a browser";
+echo "> cd client;";
+echo "> pub serve;";
+echo "\nTo run the console client:";
+echo "> dart console_client/bin/main.dart";
+echo "";
diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/client/pubspec.yaml b/vendor/src/github.com/apache/thrift/tutorial/dart/client/pubspec.yaml
new file mode 100644
index 00000000..5f60f67c
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/dart/client/pubspec.yaml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# 'License'); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: tutorial_client
+version: 0.10.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"
+
+dependencies:
+  browser: ^0.10.0
+  shared:
+    path: ../gen-dart/shared
+  thrift:
+    path: ../../../lib/dart
+  tutorial:
+    path: ../gen-dart/tutorial
diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/client.dart b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/client.dart
new file mode 100644
index 00000000..4f02d0d9
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/client.dart
@@ -0,0 +1,278 @@
+/// Licensed to the Apache Software Foundation (ASF) under one
+/// or more contributor license agreements. See the NOTICE file
+/// distributed with this work for additional information
+/// regarding copyright ownership. The ASF licenses this file
+/// to you under the Apache License, Version 2.0 (the
+/// "License"); you may not use this file except in compliance
+/// with the License. You may obtain a copy of the License at
+///
+/// http://www.apache.org/licenses/LICENSE-2.0
+///
+/// Unless required by applicable law or agreed to in writing,
+/// software distributed under the License is distributed on an
+/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+/// KIND, either express or implied. See the License for the
+/// specific language governing permissions and limitations
+/// under the License.
+
+import 'dart:html';
+
+import 'package:thrift/thrift.dart';
+import 'package:thrift/thrift_browser.dart';
+import 'package:shared/shared.dart';
+import 'package:tutorial/tutorial.dart';
+
+/// Adapted from the AS3 tutorial
+void main() {
+  new CalculatorUI(querySelector('#output')).start();
+}
+
+class CalculatorUI {
+  final DivElement output;
+
+  CalculatorUI(this.output);
+
+  TTransport _transport;
+  Calculator _calculatorClient;
+
+  void start() {
+    _buildInterface();
+    _initConnection();
+  }
+
+  void _validate() {
+    if (!_transport.isOpen) {
+      window.alert("The transport is not open!");
+    }
+  }
+
+  void _initConnection() {
+    _transport = new TAsyncClientSocketTransport(
+        new TWebSocket(Uri.parse('ws://127.0.0.1:9090/ws')),
+        new TMessageReader(new TBinaryProtocolFactory()));
+    TProtocol protocol = new TBinaryProtocol(_transport);
+    _transport.open();
+
+    _calculatorClient = new CalculatorClient(protocol);
+  }
+
+  void _buildInterface() {
+    output.children.forEach((e) {
+      e.remove();
+    });
+
+    _buildPingComponent();
+
+    _buildAddComponent();
+
+    _buildCalculatorComponent();
+
+    _buildGetStructComponent();
+  }
+
+  void _buildPingComponent() {
+    output.append(new HeadingElement.h3()..text = "Ping");
+    ButtonElement pingButton = new ButtonElement()
+      ..text = "PING"
+      ..onClick.listen(_onPingClick);
+    output.append(pingButton);
+  }
+
+  void _onPingClick(MouseEvent e) {
+    _validate();
+
+    _calculatorClient.ping();
+  }
+
+  void _buildAddComponent() {
+    output.append(new HeadingElement.h3()..text = "Add");
+    InputElement num1 = new InputElement()
+      ..id = "add1"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px";
+    output.append(num1);
+    SpanElement op = new SpanElement()
+      ..text = "+"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(op);
+    InputElement num2 = new InputElement()
+      ..id = "add2"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(num2);
+    ButtonElement addButton = new ButtonElement()
+      ..text = "="
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onAddClick);
+    output.append(addButton);
+    SpanElement result = new SpanElement()
+      ..id = "addResult"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+  }
+
+  void _onAddClick(MouseEvent e) {
+    _validate();
+
+    InputElement num1 = querySelector("#add1");
+    InputElement num2 = querySelector("#add2");
+    SpanElement result = querySelector("#addResult");
+
+    _calculatorClient
+        .add(int.parse(num1.value), int.parse(num2.value))
+        .then((int n) {
+      result.text = "$n";
+    });
+  }
+
+  void _buildCalculatorComponent() {
+    output.append(new HeadingElement.h3()..text = "Calculator");
+    InputElement num1 = new InputElement()
+      ..id = "calc1"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px";
+    output.append(num1);
+    SelectElement op = new SelectElement()
+      ..id = "calcOp"
+      ..multiple = false
+      ..selectedIndex = 0
+      ..style.fontSize = "16px"
+      ..style.marginLeft = "10px"
+      ..style.width = "50px";
+    OptionElement addOp = new OptionElement()
+      ..text = "+"
+      ..value = Operation.ADD.toString();
+    op.add(addOp, 0);
+    OptionElement subtractOp = new OptionElement()
+      ..text = "-"
+      ..value = Operation.SUBTRACT.toString();
+    op.add(subtractOp, 1);
+    OptionElement multiplyOp = new OptionElement()
+      ..text = "*"
+      ..value = Operation.MULTIPLY.toString();
+    op.add(multiplyOp, 2);
+    OptionElement divideOp = new OptionElement()
+      ..text = "/"
+      ..value = Operation.DIVIDE.toString();
+    op.add(divideOp, 3);
+    output.append(op);
+    InputElement num2 = new InputElement()
+      ..id = "calc2"
+      ..type = "number"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(num2);
+    ButtonElement calcButton = new ButtonElement()
+      ..text = "="
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onCalcClick);
+    output.append(calcButton);
+    SpanElement result = new SpanElement()
+      ..id = "calcResult"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+    output.append(new BRElement());
+    output.append(new BRElement());
+    LabelElement logIdLabel = new LabelElement()
+      ..text = "Log ID:"
+      ..style.fontSize = "14px";
+    output.append(logIdLabel);
+    InputElement logId = new InputElement()
+      ..id = "logId"
+      ..type = "number"
+      ..value = "1"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(logId);
+    LabelElement commentLabel = new LabelElement()
+      ..text = "Comment:"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px";
+    output.append(commentLabel);
+    InputElement comment = new InputElement()
+      ..id = "comment"
+      ..style.fontSize = "14px"
+      ..style.width = "100px"
+      ..style.marginLeft = "10px";
+    output.append(comment);
+  }
+
+  void _onCalcClick(MouseEvent e) {
+    _validate();
+
+    InputElement num1 = querySelector("#calc1");
+    InputElement num2 = querySelector("#calc2");
+    SelectElement op = querySelector("#calcOp");
+    SpanElement result = querySelector("#calcResult");
+    InputElement logId = querySelector("#logId");
+    InputElement comment = querySelector("#comment");
+
+    int logIdValue = int.parse(logId.value);
+    logId.value = (logIdValue + 1).toString();
+
+    Work work = new Work();
+    work.num1 = int.parse(num1.value);
+    work.num2 = int.parse(num2.value);
+    work.op = int.parse(op.options[op.selectedIndex].value);
+    work.comment = comment.value;
+
+    _calculatorClient.calculate(logIdValue, work).then((int n) {
+      result.text = "$n";
+    });
+  }
+
+  void _buildGetStructComponent() {
+    output.append(new HeadingElement.h3()..text = "Get Struct");
+    LabelElement logIdLabel = new LabelElement()
+      ..text = "Struct Key:"
+      ..style.fontSize = "14px";
+    output.append(logIdLabel);
+    InputElement logId = new InputElement()
+      ..id = "structKey"
+      ..type = "number"
+      ..value = "1"
+      ..style.fontSize = "14px"
+      ..style.width = "50px"
+      ..style.marginLeft = "10px";
+    output.append(logId);
+    ButtonElement getStructButton = new ButtonElement()
+      ..text = "GET"
+      ..style.fontSize = "14px"
+      ..style.marginLeft = "10px"
+      ..onClick.listen(_onGetStructClick);
+    output.append(getStructButton);
+    output.append(new BRElement());
+    output.append(new BRElement());
+    TextAreaElement result = new TextAreaElement()
+      ..id = "getStructResult"
+      ..style.fontSize = "14px"
+      ..style.width = "300px"
+      ..style.height = "50px"
+      ..style.marginLeft = "10px";
+    output.append(result);
+  }
+
+  void _onGetStructClick(MouseEvent e) {
+    _validate();
+
+    InputElement structKey = querySelector("#structKey");
+    TextAreaElement result = querySelector("#getStructResult");
+
+    _calculatorClient
+        .getStruct(int.parse(structKey.value))
+        .then((SharedStruct s) {
+      result.text = "${s.toString()}";
+    });
+  }
+}
diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/index.html b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/index.html
new file mode 100644
index 00000000..64b184e5
--- /dev/null
+++ b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+    
+    
+    
+    Thrift Tutorial
+    
+    
+    
+
+
+
+
+  
+ + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/styles.css b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/styles.css new file mode 100644 index 00000000..c0315025 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/dart/client/web/styles.css @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +@import url(https://fonts.googleapis.com/css?family=Roboto); + +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 10px; + font-family: 'Roboto', sans-serif; +} + +h3 { + border-bottom: solid; + border-width: thin; + padding-top: 20px; +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/bin/main.dart b/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/bin/main.dart new file mode 100644 index 00000000..fda206ab --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/bin/main.dart @@ -0,0 +1,149 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT 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 'dart:async'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:logging/logging.dart'; +import 'package:thrift/thrift.dart'; +import 'package:thrift/thrift_console.dart'; +import 'package:tutorial/tutorial.dart'; + +TTransport _transport; +Calculator _calculator; +int logid = 0; + +const Map operationLookup = const { + '+': Operation.ADD, + '-': Operation.SUBTRACT, + '*': Operation.MULTIPLY, + '/': Operation.DIVIDE +}; + +main(List args) { + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((LogRecord rec) { + print('${rec.level.name}: ${rec.time}: ${rec.message}'); + }); + + var parser = new ArgParser(); + parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to'); + + ArgResults results; + try { + results = parser.parse(args); + } catch (e) { + results = null; + } + + if (results == null) { + print(parser.usage); + exit(0); + } + + int port = int.parse(results['port']); + + _initConnection(port).then((_) => _run()); +} + +Future _initConnection(int port) async { + var socket = await Socket.connect('127.0.0.1', port); + _transport = new TAsyncClientSocketTransport( + new TTcpSocket(socket), new TMessageReader(new TBinaryProtocolFactory())); + TProtocol protocol = new TBinaryProtocol(_transport); + await _transport.open(); + + _calculator = new CalculatorClient(protocol); +} + +Future _run() async { + _help(); + + while (true) { + stdout.write("> "); + var input = stdin.readLineSync(); + var parts = input.split(' '); + var command = parts[0]; + var args = parts.length > 1 ? parts.sublist(1) : []; + + switch (command) { + case 'ping': + await _ping(); + break; + + case 'add': + await _add(int.parse(args[0]), int.parse(args[1])); + break; + + case 'calc': + int op = operationLookup[args[1]]; + if (!Operation.VALID_VALUES.contains(op)) { + stdout.writeln('Unknown operator ${args[1]}'); + break; + } + + var work = new Work() + ..num1 = int.parse(args[0]) + ..op = op + ..num2 = int.parse(args[2]) + ..comment = args.length > 3 ? args[3] : ''; + + await _calc(work); + break; + + case 'struct': + await _struct(int.parse(args[0])); + break; + + case 'help': + default: + _help(); + break; + } + } +} + +void _help() { + stdout.writeln('Commands:'); + stdout.writeln(' help'); + stdout.writeln(' ping'); + stdout.writeln(' add x y'); + stdout.writeln(' calc x op y [comment]'); + stdout.writeln(' struct id'); + stdout.writeln(''); +} + +Future _ping() async { + await _calculator.ping(); + stdout.writeln('ping succeeded'); +} + +Future _add(int x, int y) async { + int result = await _calculator.add(x, y); + stdout.writeln('= $result'); +} + +Future _calc(Work work) async { + int result = await _calculator.calculate(logid++, work); + stdout.writeln('= $result'); +} + +Future _struct(int key) async { + var struct = await _calculator.getStruct(key); + stdout.writeln(struct.toString()); +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/pubspec.yaml b/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/pubspec.yaml new file mode 100644 index 00000000..32fd4ff2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/dart/console_client/pubspec.yaml @@ -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. + +name: tutorial_console_client +version: 0.10.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" + +dependencies: + args: ^0.13.0 + collection: ^1.1.0 + shared: + path: ../gen-dart/shared + thrift: + path: ../../../lib/dart + tutorial: + path: ../gen-dart/tutorial diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/server/bin/main.dart b/vendor/src/github.com/apache/thrift/tutorial/dart/server/bin/main.dart new file mode 100644 index 00000000..b8ac30d3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/dart/server/bin/main.dart @@ -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. + +import 'dart:async'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:logging/logging.dart'; +import 'package:thrift/thrift.dart'; +import 'package:thrift/thrift_console.dart'; +import 'package:tutorial/tutorial.dart'; +import 'package:shared/shared.dart'; + +TProtocol _protocol; +TProcessor _processor; +WebSocket _webSocket; + +main(List args) { + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((LogRecord rec) { + print('${rec.level.name}: ${rec.time}: ${rec.message}'); + }); + + var parser = new ArgParser(); + parser.addOption('port', defaultsTo: '9090', help: 'The port to listen on'); + parser.addOption('type', + defaultsTo: 'ws', + allowed: ['ws', 'tcp'], + help: 'The type of socket', + allowedHelp: {'ws': 'WebSocket', 'tcp': 'TCP Socket'}); + + ArgResults results; + try { + results = parser.parse(args); + } catch (e) { + results = null; + } + + if (results == null) { + print(parser.usage); + exit(0); + } + + int port = int.parse(results['port']); + String socketType = results['type']; + + if (socketType == 'tcp') { + _runTcpServer(port); + } else if (socketType == 'ws') { + _runWebSocketServer(port); + } +} + +Future _runWebSocketServer(int port) async { + var httpServer = await HttpServer.bind('127.0.0.1', port); + print('listening for WebSocket connections on $port'); + + httpServer.listen((HttpRequest request) async { + if (request.uri.path == '/ws') { + _webSocket = await WebSocketTransformer.upgrade(request); + await _initProcessor(new TWebSocket(_webSocket)); + } else { + print('Invalid path: ${request.uri.path}'); + } + }); +} + +Future _runTcpServer(int port) async { + var serverSocket = await ServerSocket.bind('127.0.0.1', port); + print('listening for TCP connections on $port'); + + Socket socket = await serverSocket.first; + await _initProcessor(new TTcpSocket(socket)); +} + +Future _initProcessor(TSocket socket) async { + TServerSocketTransport transport = new TServerSocketTransport(socket); + transport.onIncomingMessage.listen(_processMessage); + _processor = new CalculatorProcessor(new CalculatorServer()); + _protocol = new TBinaryProtocol(transport); + await _protocol.transport.open(); + + print('connected'); +} + +Future _processMessage(_) async { + _processor.process(_protocol, _protocol); +} + +class CalculatorServer implements Calculator { + final Map _log = {}; + + Future ping() async { + print('ping()'); + } + + Future add(int num1, int num2) async { + print('add($num1, $num2)'); + + return num1 + num2; + } + + Future calculate(int logid, Work work) async { + print('calulate($logid, ${work.toString()})'); + + int val; + + 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) { + var x = new InvalidOperation(); + x.whatOp = work.op; + x.why = 'Cannot divide by 0'; + throw x; + } + val = (work.num1 / work.num2).floor(); + break; + } + + var log = new SharedStruct(); + log.key = logid; + log.value = '$val "${work.comment}"'; + this._log[logid] = log; + + return val; + } + + Future zip() async { + print('zip()'); + } + + Future getStruct(int key) async { + print('getStruct($key)'); + + return _log[key]; + } +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/dart/server/pubspec.yaml b/vendor/src/github.com/apache/thrift/tutorial/dart/server/pubspec.yaml new file mode 100644 index 00000000..2a264486 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/dart/server/pubspec.yaml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# 'License'); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: tutorial_server +version: 0.10.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" + +dependencies: + args: ^0.13.0 + shared: + path: ../gen-dart/shared + thrift: + path: ../../../lib/dart + tutorial: + path: ../gen-dart/tutorial diff --git a/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dpr b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dpr new file mode 100644 index 00000000..0f380b0a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dpr @@ -0,0 +1,114 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) +program DelphiClient; + +{$APPTYPE CONSOLE} +{$D 'Copyright (c) 2012 The Apache Software Foundation'} + +uses + SysUtils, + Generics.Collections, + Thrift in '..\..\..\lib\delphi\src\Thrift.pas', + Thrift.Collections in '..\..\..\lib\delphi\src\Thrift.Collections.pas', + Thrift.Console in '..\..\..\lib\delphi\src\Thrift.Console.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', + Shared in '..\..\gen-delphi\Shared.pas', + Tutorial in '..\..\gen-delphi\Tutorial.pas'; + + +type + DelphiTutorialClient = class + public + class procedure Main; + end; + + +//--- DelphiTutorialClient --------------------------------------- + + +class procedure DelphiTutorialClient.Main; +var transport : ITransport; + protocol : IProtocol; + client : TCalculator.Iface; + work : IWork; + sum, quotient, diff : Integer; + log : ISharedStruct; +begin + try + transport := TSocketImpl.Create( 'localhost', 9090); + protocol := TBinaryProtocolImpl.Create( transport); + client := TCalculator.TClient.Create( protocol); + + transport.Open; + + client.ping; + Console.WriteLine('ping()'); + + sum := client.add( 1, 1); + Console.WriteLine( Format( '1+1=%d', [sum])); + + work := TWorkImpl.Create; + + work.Op := TOperation.DIVIDE; + work.Num1 := 1; + work.Num2 := 0; + try + quotient := client.calculate(1, work); + Console.WriteLine( 'Whoa we can divide by 0'); + Console.WriteLine( Format('1/0=%d',[quotient])); + except + on io: TInvalidOperation + do Console.WriteLine( 'Invalid operation: ' + io.Why); + end; + + work.Op := TOperation.SUBTRACT; + work.Num1 := 15; + work.Num2 := 10; + try + diff := client.calculate( 1, work); + Console.WriteLine( Format('15-10=%d', [diff])); + except + on io: TInvalidOperation + do Console.WriteLine( 'Invalid operation: ' + io.Why); + end; + + log := client.getStruct(1); + Console.WriteLine( Format( 'Check log: %s', [log.Value])); + + transport.Close(); + + except + on e : Exception + do Console.WriteLine( e.ClassName+': '+e.Message); + end; +end; + + +begin + try + DelphiTutorialClient.Main; + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dproj b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dproj new file mode 100644 index 00000000..34aa5338 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiClient/DelphiClient.dproj @@ -0,0 +1,119 @@ + + + {2B8FB3A1-2F9E-4883-8C53-0F56220B34F6} + DelphiClient.dpr + 12.3 + True + Debug + Win32 + Console + None + DCC32 + + + true + + + true + Base + true + + + true + Base + true + + + ..\..\..\lib\delphi\src;$(DCC_UnitSearchPath) + 00400000 + .\dcu\$(Config)\$(Platform) + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias) + ..\bin\$(Config)\$(Platform) + false + false + false + false + false + + + DEBUG;$(DCC_Define) + false + true + + + false + RELEASE;$(DCC_Define) + 0 + false + + + + MainSource + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + + + Delphi.Personality.12 + + + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + Thrift Tutorial + 1.0.0.0 + DelphiClient + Copyright © 2012 The Apache Software Foundation + + DelphiClient.exe + Thrift + 1.0.0.0 + + + + DelphiClient.dpr + + + + True + + + 12 + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dpr b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dpr new file mode 100644 index 00000000..9d54a2e6 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dpr @@ -0,0 +1,173 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) +program DelphiServer; + +{$APPTYPE CONSOLE} +{$D 'Copyright (c) 2012 The Apache Software Foundation'} + +{$Q+} // throws exceptions on numeric overflows + +uses + SysUtils, + Generics.Collections, + Thrift in '..\..\..\lib\delphi\src\Thrift.pas', + Thrift.Collections in '..\..\..\lib\delphi\src\Thrift.Collections.pas', + Thrift.Console in '..\..\..\lib\delphi\src\Thrift.Console.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', + Shared in '..\..\gen-delphi\Shared.pas', + Tutorial in '..\..\gen-delphi\Tutorial.pas'; + + +type + TCalculatorHandler = class( TInterfacedObject, TCalculator.Iface) + protected + FLog : TDictionary< Integer, ISharedStruct>; + + // TSharedService.Iface + function getStruct(key: Integer): ISharedStruct; + + // TCalculator.Iface + procedure ping(); + function add(num1: Integer; num2: Integer): Integer; + function calculate(logid: Integer; const w: IWork): Integer; + procedure zip(); + + public + constructor Create; + destructor Destroy; override; + + end; + + DelphiTutorialServer = class + public + class procedure Main; + end; + + +//--- TCalculatorHandler --------------------------------------------------- + + +constructor TCalculatorHandler.Create; +begin + inherited Create; + FLog := TDictionary< Integer, ISharedStruct>.Create(); +end; + + +destructor TCalculatorHandler.Destroy; +begin + try + FreeAndNil( FLog); + finally + inherited Destroy; + end; +end; + + +procedure TCalculatorHandler.ping; +begin + Console.WriteLine( 'ping()'); +end; + + +function TCalculatorHandler.add(num1: Integer; num2: Integer): Integer; +begin + Console.WriteLine( Format( 'add( %d, %d)', [num1, num2])); + result := num1 + num2; +end; + + +function TCalculatorHandler.calculate(logid: Integer; const w: IWork): Integer; +var entry : ISharedStruct; +begin + try + Console.WriteLine( Format('calculate( %d, [%d,%d,%d])', [logid, Ord(w.Op), w.Num1, w.Num2])); + + case w.Op of + TOperation.ADD : result := w.Num1 + w.Num2; + TOperation.SUBTRACT : result := w.Num1 - w.Num2; + TOperation.MULTIPLY : result := w.Num1 * w.Num2; + TOperation.DIVIDE : result := Round( w.Num1 / w.Num2); + else + raise TInvalidOperation.Create( Ord(w.Op), 'Unknown operation'); + end; + + except + on e:Thrift.TException do raise; // let Thrift Exceptions pass through + on e:Exception do raise TInvalidOperation.Create( Ord(w.Op), e.Message); // repackage all other + end; + + entry := TSharedStructImpl.Create; + entry.Key := logid; + entry.Value := IntToStr( result); + FLog.AddOrSetValue( logid, entry); +end; + + +function TCalculatorHandler.getStruct(key: Integer): ISharedStruct; +begin + Console.WriteLine( Format( 'getStruct(%d)', [key])); + result := FLog[key]; +end; + + +procedure TCalculatorHandler.zip; +begin + Console.WriteLine( 'zip()'); +end; + + +//--- DelphiTutorialServer ---------------------------------------------------------------------- + + +class procedure DelphiTutorialServer.Main; +var handler : TCalculator.Iface; + processor : IProcessor; + transport : IServerTransport; + server : IServer; +begin + try + handler := TCalculatorHandler.Create; + processor := TCalculator.TProcessorImpl.Create( handler); + transport := TServerSocketImpl.Create( 9090); + server := TSimpleServer.Create( processor, transport); + + Console.WriteLine( 'Starting the server...'); + server.Serve(); + + except + on e: Exception do Console.WriteLine( e.Message); + end; + + Console.WriteLine('done.'); +end; + + +begin + try + DelphiTutorialServer.Main; + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dproj b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dproj new file mode 100644 index 00000000..74811bc1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/delphi/DelphiServer/DelphiServer.dproj @@ -0,0 +1,118 @@ + + + {2B8FB3A1-2F9E-4883-8C53-0F56220B34F6} + DelphiServer.dpr + 12.3 + True + Debug + Win32 + Console + None + DCC32 + + + true + + + true + Base + true + + + true + Base + true + + + 00400000 + .\dcu\$(Config)\$(Platform) + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;$(DCC_UnitAlias) + ..\bin\$(Config)\$(Platform) + false + false + false + false + false + + + DEBUG;$(DCC_Define) + false + true + + + false + RELEASE;$(DCC_Define) + 0 + false + + + + MainSource + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + + + Delphi.Personality.12 + + + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + Thrift Tutorial + 1.0.0.0 + DelphiServer + Copyright © 2012 The Apache Software Foundation + + DelphiServer.exe + Thrift + 1.0.0.0 + + + + DelphiServer.dpr + + + + True + + + 12 + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/delphi/Tutorial.groupproj b/vendor/src/github.com/apache/thrift/tutorial/delphi/Tutorial.groupproj new file mode 100644 index 00000000..3a2a2375 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/delphi/Tutorial.groupproj @@ -0,0 +1,48 @@ + + + {3D042C7F-3EF2-4574-8304-AB7FB79F814C} + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/erl/README.md b/vendor/src/github.com/apache/thrift/tutorial/erl/README.md new file mode 100644 index 00000000..9d17cd0c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/erl/README.md @@ -0,0 +1,8 @@ +To try things out, run + +% ./server.sh +Erlang R14B (erts-5.8.1) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.8.1 (abort with ^G) +> server:start(). +> client:t(). diff --git a/vendor/src/github.com/apache/thrift/tutorial/erl/client.erl b/vendor/src/github.com/apache/thrift/tutorial/erl/client.erl new file mode 100644 index 00000000..5d40916b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/erl/client.erl @@ -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. +%% + +-module(client). + +-include("calculator_thrift.hrl"). + +-export([t/0]). + +p(X) -> + io:format("~p~n", [X]), + ok. + +t() -> + Port = 9999, + + {ok, Client0} = thrift_client_util:new("127.0.0.1", + Port, + calculator_thrift, + []), + + {Client1, {ok, ok}} = thrift_client:call(Client0, ping, []), + io:format("ping~n", []), + + {Client2, {ok, Sum}} = thrift_client:call(Client1, add, [1, 1]), + io:format("1+1=~p~n", [Sum]), + + {Client3, {ok, Sum1}} = thrift_client:call(Client2, add, [1, 4]), + io:format("1+4=~p~n", [Sum1]), + + Work = #work{op=?tutorial_Operation_SUBTRACT, + num1=15, + num2=10}, + {Client4, {ok, Diff}} = thrift_client:call(Client3, calculate, [1, Work]), + io:format("15-10=~p~n", [Diff]), + + {Client5, {ok, Log}} = thrift_client:call(Client4, getStruct, [1]), + io:format("Log: ~p~n", [Log]), + + Client6 = + try + Work1 = #work{op=?tutorial_Operation_DIVIDE, + num1=1, + num2=0}, + {ClientS1, {ok, _Quot}} = thrift_client:call(Client5, calculate, [2, Work1]), + + io:format("LAME: exception handling is broken~n", []), + ClientS1 + catch + throw:{ClientS2, Z} -> + io:format("Got exception where expecting - the " ++ + "following is NOT a problem!!!~n"), + p(Z), + ClientS2 + end, + + + {Client7, {ok, ok}} = thrift_client:call(Client6, zip, []), + io:format("zip~n", []), + + {_Client8, ok} = thrift_client:close(Client7), + ok. diff --git a/vendor/src/github.com/apache/thrift/tutorial/erl/json_client.erl b/vendor/src/github.com/apache/thrift/tutorial/erl/json_client.erl new file mode 100644 index 00000000..312b01e2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/erl/json_client.erl @@ -0,0 +1,89 @@ +%% +%% Licensed to the Apache Software Foundation (ASF) under one +%% or more contributor license agreements. See the NOTICE file +%% distributed with this work for additional information +%% regarding copyright ownership. The ASF licenses this file +%% to you under the Apache License, Version 2.0 (the +%% "License"); you may not use this file except in compliance +%% with the License. You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% The JSON protocol over HTTP implementation was created by +%% Peter Neumark based on +%% the binary protocol + socket tutorial. Use with the same server +%% that the Javascript tutorial uses! + +-module(json_client). + +-include("calculator_thrift.hrl"). + +-export([t/0]). + +%% Client constructor for the http transports +%% with the json protocol +new_client(Host, Path, Service, _Options) -> + {ProtoOpts, TransOpts} = {[],[]}, + TransportFactory = fun() -> thrift_http_transport:new(Host, Path, TransOpts) end, + {ok, ProtocolFactory} = thrift_json_protocol:new_protocol_factory( + TransportFactory, ProtoOpts), + {ok, Protocol} = ProtocolFactory(), + thrift_client:new(Protocol, Service). + +p(X) -> + io:format("~p~n", [X]), + ok. + +t() -> + inets:start(), + {ok, Client0} = new_client("127.0.0.1:8088", "/thrift/service/tutorial/", + calculator_thrift, + []), + {Client1, {ok, ok}} = thrift_client:call(Client0, ping, []), + io:format("ping~n", []), + + {Client2, {ok, Sum}} = thrift_client:call(Client1, add, [1, 1]), + io:format("1+1=~p~n", [Sum]), + + {Client3, {ok, Sum1}} = thrift_client:call(Client2, add, [1, 4]), + io:format("1+4=~p~n", [Sum1]), + + Work = #work{op=?tutorial_Operation_SUBTRACT, + num1=15, + num2=10}, + {Client4, {ok, Diff}} = thrift_client:call(Client3, calculate, [1, Work]), + io:format("15-10=~p~n", [Diff]), + + {Client5, {ok, Log}} = thrift_client:call(Client4, getStruct, [1]), + io:format("Log: ~p~n", [Log]), + + Client6 = + try + Work1 = #work{op=?tutorial_Operation_DIVIDE, + num1=1, + num2=0}, + {ClientS1, {ok, _Quot}} = thrift_client:call(Client5, calculate, [2, Work1]), + + io:format("LAME: exception handling is broken~n", []), + ClientS1 + catch + throw:{ClientS2, Z} -> + io:format("Got exception where expecting - the " ++ + "following is NOT a problem!!!~n"), + p(Z), + ClientS2 + end, + + + {Client7, {ok, ok}} = thrift_client:call(Client6, zip, []), + io:format("zip~n", []), + + {_Client8, ok} = thrift_client:close(Client7), + ok. diff --git a/vendor/src/github.com/apache/thrift/tutorial/erl/server.erl b/vendor/src/github.com/apache/thrift/tutorial/erl/server.erl new file mode 100644 index 00000000..f1e73576 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/erl/server.erl @@ -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. +%% + +-module(server). + +-include("calculator_thrift.hrl"). + +-export([start/0, start/1, handle_function/2, + stop/1, ping/0, add/2, calculate/2, getStruct/1, zip/0]). + +debug(Format, Data) -> + error_logger:info_msg(Format, Data). + +ping() -> + debug("ping()",[]), + ok. + +add(N1, N2) -> + debug("add(~p,~p)",[N1,N2]), + N1+N2. + +calculate(Logid, Work) -> + { Op, Num1, Num2 } = { Work#work.op, Work#work.num1, Work#work.num2 }, + debug("calculate(~p, {~p,~p,~p})", [Logid, Op, Num1, Num2]), + case Op of + ?tutorial_Operation_ADD -> Num1 + Num2; + ?tutorial_Operation_SUBTRACT -> Num1 - Num2; + ?tutorial_Operation_MULTIPLY -> Num1 * Num2; + + ?tutorial_Operation_DIVIDE when Num2 == 0 -> + throw(#invalidOperation{whatOp=Op, why="Cannot divide by 0"}); + ?tutorial_Operation_DIVIDE -> + Num1 div Num2; + + _Else -> + throw(#invalidOperation{whatOp=Op, why="Invalid operation"}) + end. + +getStruct(Key) -> + debug("getStruct(~p)", [Key]), + #sharedStruct{key=Key, value="RARG"}. + +zip() -> + debug("zip", []), + ok. + +%% + +start() -> + start(9999). + +start(Port) -> + Handler = ?MODULE, + thrift_socket_server:start([{handler, Handler}, + {service, calculator_thrift}, + {port, Port}, + {name, tutorial_server}]). + +stop(Server) -> + thrift_socket_server:stop(Server). + +handle_function(Function, Args) when is_atom(Function), is_tuple(Args) -> + case apply(?MODULE, Function, tuple_to_list(Args)) of + ok -> ok; + Reply -> {reply, Reply} + end. diff --git a/vendor/src/github.com/apache/thrift/tutorial/erl/server.sh b/vendor/src/github.com/apache/thrift/tutorial/erl/server.sh new file mode 100644 index 00000000..775afb62 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/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/vendor/src/github.com/apache/thrift/tutorial/go/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/go/Makefile.am new file mode 100644 index 00000000..a707d5dd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/Makefile.am @@ -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. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-go/tutorial/calculator.go gen-go/shared/shared_service.go: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen go -r $< + +all-local: gen-go/tutorial/calculator.go + + +check: src/git.apache.org/thrift.git/lib/go/thrift + $(THRIFT) -r --gen go $(top_srcdir)/tutorial/tutorial.thrift + cp -r gen-go/* src/ + GOPATH=`pwd` $(GO) build ./... + GOPATH=`pwd` $(GO) build -o go-tutorial src/*.go + GOPATH=`pwd` $(GO) build -o calculator-remote src/tutorial/calculator-remote/calculator-remote.go + +src/git.apache.org/thrift.git/lib/go/thrift: + mkdir -p src/git.apache.org/thrift.git/lib/go + ln -sf $(realpath $(top_srcdir)/lib/go/thrift) src/git.apache.org/thrift.git/lib/go/thrift + +tutorialserver: all + GOPATH=`pwd` $(GO) run src/*.go -server=true + +tutorialclient: all + GOPATH=`pwd` $(GO) run src/*.go + +tutorialsecureserver: all + GOPATH=`pwd` $(GO) run src/*.go -server=true -secure=true + +tutorialsecureclient: all + GOPATH=`pwd` $(GO) run src/*.go -secure=true + +clean-local: + $(RM) -r gen-* src/shared src/tutorial src/git.apache.org go-tutorial calculator-remote + +EXTRA_DIST = \ + src/client.go \ + src/handler.go \ + src/server.go \ + src/main.go \ + server.crt \ + server.key + diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/server.crt b/vendor/src/github.com/apache/thrift/tutorial/go/server.crt new file mode 100644 index 00000000..8a5ef3c3 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/server.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD +VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs +MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV +BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3 +DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy +MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU +MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh +cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ +bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ +GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6 +L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg +2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw +AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX +wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n +AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME +GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5 +U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm +T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD +1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I +p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO +r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0 +-----END CERTIFICATE----- diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/server.key b/vendor/src/github.com/apache/thrift/tutorial/go/server.key new file mode 100644 index 00000000..263cfce5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR +tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy +SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H +jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB +GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk +u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm +trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt +fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR +xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD +bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck +Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq +57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9 +7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU +8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE +jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj +V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ +HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/ +LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3 +SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791 +Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS +y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc +ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW +cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE +c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1 +Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc +rdPUV9/uQkdx8VrShxlD8A== +-----END PRIVATE KEY----- diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/src/client.go b/vendor/src/github.com/apache/thrift/tutorial/go/src/client.go new file mode 100644 index 00000000..a497d7f8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/src/client.go @@ -0,0 +1,99 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 ( + "crypto/tls" + "fmt" + "git.apache.org/thrift.git/lib/go/thrift" + "tutorial" +) + +func handleClient(client *tutorial.CalculatorClient) (err error) { + client.Ping() + fmt.Println("ping()") + + sum, _ := client.Add(1, 1) + fmt.Print("1+1=", sum, "\n") + + work := tutorial.NewWork() + work.Op = tutorial.Operation_DIVIDE + work.Num1 = 1 + work.Num2 = 0 + quotient, err := client.Calculate(1, work) + if err != nil { + switch v := err.(type) { + case *tutorial.InvalidOperation: + fmt.Println("Invalid operation:", v) + default: + fmt.Println("Error during operation:", err) + } + return err + } else { + fmt.Println("Whoa we can divide by 0 with new value:", quotient) + } + + work.Op = tutorial.Operation_SUBTRACT + work.Num1 = 15 + work.Num2 = 10 + diff, err := client.Calculate(1, work) + if err != nil { + switch v := err.(type) { + case *tutorial.InvalidOperation: + fmt.Println("Invalid operation:", v) + default: + fmt.Println("Error during operation:", err) + } + return err + } else { + fmt.Print("15-10=", diff, "\n") + } + + log, err := client.GetStruct(1) + if err != nil { + fmt.Println("Unable to get struct:", err) + return err + } else { + fmt.Println("Check log:", log.Value) + } + return err +} + +func runClient(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error { + var transport thrift.TTransport + var err error + if secure { + cfg := new(tls.Config) + cfg.InsecureSkipVerify = true + transport, err = thrift.NewTSSLSocket(addr, cfg) + } else { + transport, err = thrift.NewTSocket(addr) + } + if err != nil { + fmt.Println("Error opening socket:", err) + return err + } + transport = transportFactory.GetTransport(transport) + defer transport.Close() + if err := transport.Open(); err != nil { + return err + } + return handleClient(tutorial.NewCalculatorClientFactory(transport, protocolFactory)) +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/src/handler.go b/vendor/src/github.com/apache/thrift/tutorial/go/src/handler.go new file mode 100644 index 00000000..17638322 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/src/handler.go @@ -0,0 +1,101 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 ( + "fmt" + "shared" + "strconv" + "tutorial" +) + +type CalculatorHandler struct { + log map[int]*shared.SharedStruct +} + +func NewCalculatorHandler() *CalculatorHandler { + return &CalculatorHandler{log: make(map[int]*shared.SharedStruct)} +} + +func (p *CalculatorHandler) Ping() (err error) { + fmt.Print("ping()\n") + return nil +} + +func (p *CalculatorHandler) Add(num1 int32, num2 int32) (retval17 int32, err error) { + fmt.Print("add(", num1, ",", num2, ")\n") + return num1 + num2, nil +} + +func (p *CalculatorHandler) Calculate(logid int32, w *tutorial.Work) (val int32, err error) { + fmt.Print("calculate(", logid, ", {", w.Op, ",", w.Num1, ",", w.Num2, "})\n") + switch w.Op { + case tutorial.Operation_ADD: + val = w.Num1 + w.Num2 + break + case tutorial.Operation_SUBTRACT: + val = w.Num1 - w.Num2 + break + case tutorial.Operation_MULTIPLY: + val = w.Num1 * w.Num2 + break + case tutorial.Operation_DIVIDE: + if w.Num2 == 0 { + ouch := tutorial.NewInvalidOperation() + ouch.WhatOp = int32(w.Op) + ouch.Why = "Cannot divide by 0" + err = ouch + return + } + val = w.Num1 / w.Num2 + break + default: + ouch := tutorial.NewInvalidOperation() + ouch.WhatOp = int32(w.Op) + ouch.Why = "Unknown operation" + err = ouch + return + } + entry := shared.NewSharedStruct() + entry.Key = logid + entry.Value = strconv.Itoa(int(val)) + k := int(logid) + /* + oldvalue, exists := p.log[k] + if exists { + fmt.Print("Replacing ", oldvalue, " with ", entry, " for key ", k, "\n") + } else { + fmt.Print("Adding ", entry, " for key ", k, "\n") + } + */ + p.log[k] = entry + return val, err +} + +func (p *CalculatorHandler) GetStruct(key int32) (*shared.SharedStruct, error) { + fmt.Print("getStruct(", key, ")\n") + v, _ := p.log[int(key)] + return v, nil +} + +func (p *CalculatorHandler) Zip() (err error) { + fmt.Print("zip()\n") + return nil +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/src/main.go b/vendor/src/github.com/apache/thrift/tutorial/go/src/main.go new file mode 100644 index 00000000..63154e3b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/src/main.go @@ -0,0 +1,82 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 ( + "flag" + "fmt" + "git.apache.org/thrift.git/lib/go/thrift" + "os" +) + +func Usage() { + fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], ":\n") + flag.PrintDefaults() + fmt.Fprint(os.Stderr, "\n") +} + +func main() { + flag.Usage = Usage + server := flag.Bool("server", false, "Run server") + protocol := flag.String("P", "binary", "Specify the protocol (binary, compact, json, simplejson)") + framed := flag.Bool("framed", false, "Use framed transport") + buffered := flag.Bool("buffered", false, "Use buffered transport") + addr := flag.String("addr", "localhost:9090", "Address to listen to") + secure := flag.Bool("secure", false, "Use tls secure transport") + + flag.Parse() + + var protocolFactory thrift.TProtocolFactory + switch *protocol { + case "compact": + protocolFactory = thrift.NewTCompactProtocolFactory() + case "simplejson": + protocolFactory = thrift.NewTSimpleJSONProtocolFactory() + case "json": + protocolFactory = thrift.NewTJSONProtocolFactory() + case "binary", "": + protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() + default: + fmt.Fprint(os.Stderr, "Invalid protocol specified", protocol, "\n") + Usage() + os.Exit(1) + } + + var transportFactory thrift.TTransportFactory + if *buffered { + transportFactory = thrift.NewTBufferedTransportFactory(8192) + } else { + transportFactory = thrift.NewTTransportFactory() + } + + if *framed { + transportFactory = thrift.NewTFramedTransportFactory(transportFactory) + } + + if *server { + if err := runServer(transportFactory, protocolFactory, *addr, *secure); err != nil { + fmt.Println("error running server:", err) + } + } else { + if err := runClient(transportFactory, protocolFactory, *addr, *secure); err != nil { + fmt.Println("error running client:", err) + } + } +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/go/src/server.go b/vendor/src/github.com/apache/thrift/tutorial/go/src/server.go new file mode 100644 index 00000000..e4c4b970 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/go/src/server.go @@ -0,0 +1,54 @@ +package main + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 ( + "crypto/tls" + "fmt" + "git.apache.org/thrift.git/lib/go/thrift" + "tutorial" +) + +func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error { + var transport thrift.TServerTransport + var err error + if secure { + cfg := new(tls.Config) + if cert, err := tls.LoadX509KeyPair("server.crt", "server.key"); err == nil { + cfg.Certificates = append(cfg.Certificates, cert) + } else { + return err + } + transport, err = thrift.NewTSSLServerSocket(addr, cfg) + } else { + transport, err = thrift.NewTServerSocket(addr) + } + + if err != nil { + return err + } + fmt.Printf("%T\n", transport) + handler := NewCalculatorHandler() + processor := tutorial.NewCalculatorProcessor(handler) + server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory) + + fmt.Println("Starting the simple server... on ", addr) + return server.Serve() +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/haxe/Makefile.am new file mode 100644 index 00000000..e9c88204 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/Makefile.am @@ -0,0 +1,98 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +BIN_CPP = bin/Main-debug +BIN_PHP = bin/php/Main-debug.php +BIN_PHP_WEB = bin/php-web-server/Main-debug.php + + +gen-haxe/tutorial/calculator.hx gen-haxe/shared/shared_service.hx: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen haxe -r $< + +all-local: $(BIN_CPP) $(BIN_PHP) $(BIN_PHP_WEB) + +check: gen-haxe/tutorial/calculator.hx + +$(BIN_CPP): \ + src/*.hx \ + ../../lib/haxe/src/org/apache/thrift/**/*.hx \ + gen-haxe/tutorial/calculator.hx + $(HAXE) --cwd . cpp.hxml + +$(BIN_PHP): \ + src/*.hx \ + ../../lib/haxe/src/org/apache/thrift/**/*.hx \ + gen-haxe/tutorial/calculator.hx + $(HAXE) --cwd . php.hxml + +$(BIN_PHP_WEB): \ + src/*.hx \ + ../../lib/haxe/src/org/apache/thrift/**/*.hx \ + gen-haxe/tutorial/calculator.hx + $(HAXE) --cwd . php-web-server.hxml + +tutorialserver: all + $(BIN_CPP) server + +tutorialserver_php: all + php -f $(BIN_PHP) server + +tutorialclient: all + $(BIN_CPP) + +tutorialclient_php: all + php -f $(BIN_PHP) + +tutorialsecureserver: all + $(BIN_CPP) server secure + +tutorialsecureserver_php: all + php -f $(BIN_PHP) server secure + +tutorialsecureclient: all + $(BIN_CPP) secure + +tutorialsecureclient_php: all + php -f $(BIN_PHP) secure + +tutorialserver_php_http: all + php -S 127.0.0.1:9090 router.php + +tutorialclient_http: all + $(BIN_CPP) client http + +clean-local: + $(RM) -r gen-haxe bin + +EXTRA_DIST = \ + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + Tutorial.hxproj \ + make_all.bat \ + make_all.sh diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/Tutorial.hxproj b/vendor/src/github.com/apache/thrift/tutorial/haxe/Tutorial.hxproj new file mode 100644 index 00000000..796f648a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/Tutorial.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../tutorial.thrift + + + + + + + + \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/cpp.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/cpp.hxml new file mode 100644 index 00000000..6adb52d7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/cpp.hxml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#CPP target +-cpp bin + +#To produce 64 bit binaries the file should define the HXCPP_M64 compile variable: +#-D HXCPP_M64 + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/csharp.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/csharp.hxml new file mode 100644 index 00000000..295c017e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/csharp.hxml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#CSHARP target +-cs bin/Tutorial.exe + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/flash.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/flash.hxml new file mode 100644 index 00000000..a1f0568a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/flash.hxml @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#Flash target +-swf bin/Tutorial.swf + +#Add debug information +-debug + +# we need some goodies from sys.net +# --macro allowPackage("sys") + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/java.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/java.hxml new file mode 100644 index 00000000..c615565a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/java.hxml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#Java target +-java bin/Tutorial.jar + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/javascript.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/javascript.hxml new file mode 100644 index 00000000..b2b3876c --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/javascript.hxml @@ -0,0 +1,44 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#JavaScript target +-js bin/Tutorial.js + +#You can use -D source-map-content (requires Haxe 3.1+) to have the .hx +#files directly embedded into the map file, this way you only have to +#upload it, and it will be always in sync with the compiled .js even if +#you modify your .hx files. +-D source-map-content + +#Generate source map and add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.bat b/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.bat new file mode 100644 index 00000000..656dd153 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.bat @@ -0,0 +1,68 @@ +@echo off +rem /* +rem * Licensed to the Apache Software Foundation (ASF) under one +rem * or more contributor license agreements. See the NOTICE file +rem * distributed with this work for additional information +rem * regarding copyright ownership. The ASF licenses this file +rem * to you under the Apache License, Version 2.0 (the +rem * "License"); you may not use this file except in compliance +rem * with the License. You may obtain a copy of the License at +rem * +rem * http://www.apache.org/licenses/LICENSE-2.0 +rem * +rem * Unless required by applicable law or agreed to in writing, +rem * software distributed under the License is distributed on an +rem * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem * KIND, either express or implied. See the License for the +rem * specific language governing permissions and limitations +rem * under the License. +rem */ + +setlocal +if "%HOMEDRIVE%"=="" goto MISSINGVARS +if "%HOMEPATH%"=="" goto MISSINGVARS +if "%HAXEPATH%"=="" goto NOTINSTALLED + +set path=%HAXEPATH%;%HAXEPATH%\..\neko;%path% + +rem # invoke Thrift comnpiler +thrift -r -gen haxe ..\tutorial.thrift +if errorlevel 1 goto STOP + +rem # invoke Haxe compiler for all targets +for %%a in (*.hxml) do ( + rem * filter Python, as it is not supported by Haxe 3.1.3 (but will be in 3.1.4) + if not "%%a"=="python.hxml" ( + echo -------------------------- + echo Building %%a ... + echo -------------------------- + haxe --cwd . %%a + ) +) + + +echo. +echo done. +pause +goto eof + +:NOTINSTALLED +echo FATAL: Either Haxe is not installed, or the HAXEPATH variable is not set. +pause +goto eof + +:MISSINGVARS +echo FATAL: Unable to locate home folder. +echo. +echo Both HOMEDRIVE and HOMEPATH need to be set to point to your Home folder. +echo The current values are: +echo HOMEDRIVE=%HOMEDRIVE% +echo HOMEPATH=%HOMEPATH% +pause +goto eof + +:STOP +pause +goto eof + +:eof diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.sh b/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.sh new file mode 100644 index 00000000..2ee650dc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/make_all.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# invoke Thrift comnpiler +thrift -r -gen haxe ../tutorial.thrift + +# output folder +if [ ! -d bin ]; then + mkdir bin +fi + +# invoke Haxe compoiler +for target in *.hxml; do + echo -------------------------- + echo Building ${target} ... + echo -------------------------- + if [ ! -d bin/${target} ]; then + mkdir bin/${target} + fi + haxe --cwd . ${target} +done + + +#eof diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/neko.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/neko.hxml new file mode 100644 index 00000000..6161f697 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/neko.hxml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#neko target +-neko bin/Tutorial.n + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/php-web-server.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/php-web-server.hxml new file mode 100644 index 00000000..395a8521 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/php-web-server.hxml @@ -0,0 +1,43 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#PHP target +-php bin/php-web-server/ +--php-front Main-debug.php + +#defines +-D phpwebserver + + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/php.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/php.hxml new file mode 100644 index 00000000..c2f68878 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/php.hxml @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#PHP target +-php bin/php/ +--php-front Main-debug.php + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/project.hide b/vendor/src/github.com/apache/thrift/tutorial/haxe/project.hide new file mode 100644 index 00000000..8d5d4ec5 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/project.hide @@ -0,0 +1,105 @@ +{ + "type" : 0 + ,"target" : 4 + ,"name" : "Apache Thrift Tutorial" + ,"main" : null + ,"projectPackage" : "" + ,"company" : "Apache Software Foundation (ASF)" + ,"license" : "Apache License, Version 2.0" + ,"url" : "http://www.apache.org/licenses/LICENSE-2.0" + ,"targetData" : [ + { + "pathToHxml" : "flash.hxml" + ,"runActionType" : 1 + ,"runActionText" : "bin/Tutorial.swf" + } + ,{ + "pathToHxml" : "javascript.hxml" + ,"runActionType" : 1 + ,"runActionText" : "bin\\index.html" + } + ,{ + "pathToHxml" : "neko.hxml" + ,"runActionType" : 2 + ,"runActionText" : "neko bin/Tutorial.n" + } + ,{ + "pathToHxml" : "php.hxml" + } + ,{ + "pathToHxml" : "cpp.hxml" + ,"runActionType" : 2 + ,"runActionText" : "bin/Main-debug.exe" + } + ,{ + "pathToHxml" : "java.hxml" + } + ,{ + "pathToHxml" : "csharp.hxml" + ,"runActionType" : 2 + ,"runActionText" : "bin\\Tutorial.exe\\bin\\Main-Debug.exe" + } + ,{ + "pathToHxml" : "python.hxml" + ,"runActionType" : 2 + ,"runActionText" : "python bin/Tutorial.py" + } + ] + ,"files" : [ + { + "path" : "src\\org\\apache\\thrift\\server\\TServer.hx" + ,"useTabs" : true + ,"indentSize" : 4 + ,"foldedRegions" : [ + + ] + ,"activeLine" : 76 + } + ,{ + "path" : "src\\org\\apache\\thrift\\server\\TSimpleServer.hx" + ,"useTabs" : true + ,"indentSize" : 4 + ,"foldedRegions" : [ + + ] + ,"activeLine" : 100 + } + ,{ + "path" : "src\\shared\\SharedServiceProcessor.hx" + ,"useTabs" : true + ,"indentSize" : 4 + ,"foldedRegions" : [ + + ] + ,"activeLine" : 20 + } + ,{ + "path" : "src\\tutorial\\CalculatorProcessor.hx" + ,"useTabs" : true + ,"indentSize" : 4 + ,"foldedRegions" : [ + + ] + ,"activeLine" : 79 + } + ,{ + "path" : "src\\Main.hx" + ,"useTabs" : true + ,"indentSize" : 4 + ,"foldedRegions" : [ + + ] + ,"activeLine" : 0 + } + ] + ,"activeFile" : "src\\Main.hx" + ,"openFLTarget" : null + ,"openFLBuildMode" : "Debug" + ,"runActionType" : null + ,"runActionText" : null + ,"buildActionCommand" : null + ,"hiddenItems" : [ + + ] + ,"showHiddenItems" : false +} \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/python.hxml b/vendor/src/github.com/apache/thrift/tutorial/haxe/python.hxml new file mode 100644 index 00000000..f2c19fa9 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/python.hxml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +#integrate files to classpath +-cp src +-cp gen-haxe +-cp ../../lib/haxe/src + +#this class wil be used as entry point for your app. +-main Main + +#Python target +-python bin/Tutorial.py + +#Add debug information +-debug + +#dead code elimination : remove unused code +#"-dce no" : do not remove unused code +#"-dce std" : remove unused code in the std lib (default) +#"-dce full" : remove all unused code +-dce full \ No newline at end of file diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/router.php b/vendor/src/github.com/apache/thrift/tutorial/haxe/router.php new file mode 100644 index 00000000..e34135cc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/router.php @@ -0,0 +1,31 @@ +(); + + public function new() { + } + + public function ping() : Void { + trace("ping()"); + } + + + public function add( num1 : haxe.Int32, num2 : haxe.Int32) : haxe.Int32 { + trace('add( $num1, $num2)'); + return num1 + num2; + } + + public function calculate( logid : haxe.Int32, work : Work) : haxe.Int32 { + trace('calculate( $logid, '+work.op+","+work.num1+","+work.num2+")"); + + var val : haxe.Int32 = 0; + switch (work.op) + { + case Operation.ADD: + val = work.num1 + work.num2; + + case Operation.SUBTRACT: + val = work.num1 - work.num2; + + case Operation.MULTIPLY: + val = work.num1 * work.num2; + + case Operation.DIVIDE: + if (work.num2 == 0) + { + var io = new InvalidOperation(); + io.whatOp = work.op; + io.why = "Cannot divide by 0"; + throw io; + } + val = Std.int( work.num1 / work.num2); + + default: + var io = new InvalidOperation(); + io.whatOp = work.op; + io.why = "Unknown operation"; + throw io; + } + + var entry = new SharedStruct(); + entry.key = logid; + entry.value = '$val'; + log.set(logid, entry); + + return val; + } + + public function getStruct( key : haxe.Int32) : SharedStruct { + trace('getStruct($key)'); + return log.get(key); + } + + // oneway method, no args + public function zip() : Void { + trace("zip()"); + } + +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/haxe/src/Main.hx b/vendor/src/github.com/apache/thrift/tutorial/haxe/src/Main.hx new file mode 100644 index 00000000..6bebe716 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/haxe/src/Main.hx @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package; + +import org.apache.thrift.*; +import org.apache.thrift.protocol.*; +import org.apache.thrift.transport.*; +import org.apache.thrift.server.*; +import org.apache.thrift.meta_data.*; + +import tutorial.*; +import shared.*; + + +enum Prot { + binary; + json; +} + +enum Trns { + socket; + http; +} + +class Main { + + private static var server : Bool = false; + private static var framed : Bool = false; + private static var buffered : Bool = false; + private static var prot : Prot = binary; + private static var trns : Trns = socket; + + private static var targetHost : String = "localhost"; + private static var targetPort : Int = 9090; + + static function main() { + + #if ! (flash || js || phpwebserver) + try { + ParseArgs(); + } catch (e : String) { + trace(e); + trace(GetHelp()); + return; + } + + #elseif phpwebserver + //forcing server + server = true; + trns = http; + initPhpWebServer(); + //check method + if(php.Web.getMethod() != 'POST') { + Sys.println('http endpoint for thrift test server'); + return; + } + #end + + try { + if (server) + RunServer(); + else + RunClient(); + } catch (e : String) { + trace(e); + } + + trace("Completed."); + } + + #if phpwebserver + private static function initPhpWebServer() + { + //remap trace to error log + haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos) + { + // handle trace + var newValue : Dynamic; + if (infos != null && infos.customParams!=null) { + var extra:String = ""; + for( v in infos.customParams ) + extra += "," + v; + newValue = v + extra; + } + else { + newValue = v; + } + var msg = infos != null ? infos.fileName + ':' + infos.lineNumber + ': ' : ''; + Sys.stderr().writeString('${msg}${newValue}\n'); + } + } + #end + + + #if ! (flash || js) + + private static function GetHelp() : String { + return Sys.executablePath()+" modus trnsOption transport protocol\n" + +"Options:\n" + +" modus: client, server (default: client)\n" + +" trnsOption: framed, buffered (default: none)\n" + +" transport: socket, http (default: socket)\n" + +" protocol: binary, json (default: binary)\n" + +"\n" + +"All arguments are optional.\n"; + } + + + private static function ParseArgs() : Void { + var step = 0; + for (arg in Sys.args()) { + + // server|client + switch(step) { + case 0: + ++step; + if ( arg == "client") + server = false; + else if ( arg == "server") + server = true; + else + throw "First argument must be 'server' or 'client'"; + + case 1: + if ( arg == "framed") { + framed = true; + } else if ( arg == "buffered") { + buffered = true; + } else if ( arg == "socket") { + trns = socket; + ++step; + } else if ( arg == "http") { + trns = http; + ++step; + } else { + throw "Unknown transport "+arg; + } + + case 2: + if ( arg == "binary") { + prot = binary; + ++step; + } else if ( arg == "json") { + prot = json; + ++step; + } else { + throw "Unknown protocol "+arg; + } + + default: + throw "Unexpected argument "+arg; + } + + if ( framed && buffered) + { + trace("WN: framed supersedes buffered"); + } + + } + } + + #end + + private static function ClientSetup() : Calculator { + trace("Client configuration:"); + + // endpoint transport + var transport : TTransport; + switch(trns) + { + case socket: + trace('- socket transport $targetHost:$targetPort'); + transport = new TSocket( targetHost, targetPort); + case http: + var uri = 'http://${targetHost}:${targetPort}'; + trace('- HTTP transport $uri'); + transport = new THttpClient(uri); + default: + throw "Unhandled transport"; + } + + + // optinal layered transport + if ( framed) { + trace("- framed transport"); + transport = new TFramedTransport(transport); + } else if ( buffered) { + trace("- buffered transport"); + transport = new TBufferedTransport(transport); + } + + + // protocol + var protocol : TProtocol; + switch(prot) + { + case binary: + trace("- binary protocol"); + protocol = new TBinaryProtocol( transport); + case json: + trace("- JSON protocol"); + protocol = new TJSONProtocol( transport); + default: + throw "Unhandled protocol"; + } + + + // put everything together + transport.open(); + return new CalculatorImpl(protocol,protocol); + } + + + private static function RunClient() : Void { + var client = ClientSetup(); + + try { + client.ping(); + trace("ping() successful"); + } catch(error : TException) { + trace('ping() failed: $error'); + } catch(error : Dynamic) { + trace('ping() failed: $error'); + } + + try { + var sum = client.add( 1, 1); + trace('1+1=$sum'); + } catch(error : TException) { + trace('add() failed: $error'); + } catch(error : Dynamic) { + trace('add() failed: $error'); + } + + + var work = new tutorial.Work(); + work.op = tutorial.Operation.DIVIDE; + work.num1 = 1; + work.num2 = 0; + try { + var quotient = client.calculate( 1, work); + trace('Whoa we can divide by 0! Result = $quotient'); + } catch(error : TException) { + trace('calculate() failed: $error'); + } catch(error : Dynamic) { + trace('calculate() failed: $error'); + } + + work.op = tutorial.Operation.SUBTRACT; + work.num1 = 15; + work.num2 = 10; + try { + var diff = client.calculate( 1, work); + trace('15-10=$diff'); + } catch(error : TException) { + trace('calculate() failed: $error'); + } catch(error : Dynamic) { + trace('calculate() failed: $error'); + } + + + try { + var log : SharedStruct = client.getStruct( 1); + var logval = log.value; + trace('Check log: $logval'); + } catch(error : TException) { + trace('getStruct() failed: $error'); + } catch(error : Dynamic) { + trace('getStruct() failed: $error'); + } + } + + + private static function ServerSetup() : TServer { + trace("Server configuration:"); + + // endpoint transport + var transport : TServerTransport = null; + switch(trns) + { + case socket: + #if (flash || js) + throw 'current platform does not support socket servers'; + #else + trace('- socket transport port $targetPort'); + transport = new TServerSocket( targetPort); + #end + case http: + #if !phpwebserver + throw "HTTP server not implemented yet"; + //trace("- http transport"); + //transport = new THttpClient( targetHost); + #else + trace("- http transport"); + transport = new TWrappingServerTransport( + new TStreamTransport( + new TFileStream("php://input", Read), + new TFileStream("php://output", Append) + ) + ); + + #end + default: + throw "Unhandled transport"; + } + + // optional: layered transport + var transfactory : TTransportFactory = null; + if ( framed) { + trace("- framed transport"); + transfactory = new TFramedTransportFactory(); + } else if ( buffered) { + trace("- buffered transport"); + transfactory = new TBufferedTransportFactory(); + } + + // protocol + var protfactory : TProtocolFactory = null; + switch(prot) + { + case binary: + trace("- binary protocol"); + protfactory = new TBinaryProtocolFactory(); + case json: + trace("- JSON protocol"); + protfactory = new TJSONProtocolFactory(); + default: + throw "Unhandled protocol"; + } + + var handler = new CalculatorHandler(); + var processor = new CalculatorProcessor(handler); + var server = new TSimpleServer( processor, transport, transfactory, protfactory); + #if phpwebserver + server.runOnce = true; + #end + + return server; + } + + + private static function RunServer() : Void { + try + { + var server = ServerSetup(); + + trace("\nStarting the server..."); + server.Serve(); + } + catch( e : Dynamic) + { + trace('RunServer() failed: $e'); + } + trace("done."); + } + +} + diff --git a/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellClient.hs b/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellClient.hs new file mode 100644 index 00000000..bd29df06 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellClient.hs @@ -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. +-- + +import qualified Calculator +import qualified Calculator_Client as Client +import qualified SharedService_Client as SClient +import Tutorial_Types +import SharedService_Iface +import Shared_Types + +import Thrift +import Thrift.Protocol.Binary +import Thrift.Transport +import Thrift.Transport.Handle +import Thrift.Server + +import Control.Exception +import Data.Maybe +import Data.Text.Lazy +import Text.Printf +import Network + +main = do + transport <- hOpen ("localhost", PortNumber 9090) + let binProto = BinaryProtocol transport + let client = (binProto, binProto) + + Client.ping client + print "ping()" + + sum <- Client.add client 1 1 + printf "1+1=%d\n" sum + + + let work = Work { work_op = DIVIDE, + work_num1 = 1, + work_num2 = 0, + work_comment = Nothing + } + + Control.Exception.catch (printf "1/0=%d\n" =<< Client.calculate client 1 work) + (\e -> printf "InvalidOperation %s\n" (show (e :: InvalidOperation))) + + + let work = Work { work_op = SUBTRACT, + work_num1 = 15, + work_num2 = 10, + work_comment = Nothing + } + + diff <- Client.calculate client 1 work + printf "15-10=%d\n" diff + + log <- SClient.getStruct client 1 + printf "Check log: %s\n" $ unpack $ sharedStruct_value log + + -- Close! + tClose transport + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellServer.hs b/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellServer.hs new file mode 100644 index 00000000..cfe13441 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/hs/HaskellServer.hs @@ -0,0 +1,103 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +{-# LANGUAGE OverloadedStrings #-} + +import qualified Calculator +import Calculator_Iface +import Tutorial_Types +import SharedService_Iface +import Shared_Types + +import Thrift +import Thrift.Protocol.Binary +import Thrift.Transport +import Thrift.Server + +import Data.Int +import Data.String +import Data.Maybe +import Text.Printf +import Control.Exception (throw) +import Control.Concurrent.MVar +import qualified Data.Map as M +import Data.Map ((!)) +import Data.Monoid + +data CalculatorHandler = CalculatorHandler {mathLog :: MVar (M.Map Int32 SharedStruct)} + +newCalculatorHandler = do + log <- newMVar mempty + return $ CalculatorHandler log + +instance SharedService_Iface CalculatorHandler where + getStruct self k = do + myLog <- readMVar (mathLog self) + return $ (myLog ! k) + + +instance Calculator_Iface CalculatorHandler where + ping _ = + print "ping()" + + add _ n1 n2 = do + printf "add(%d,%d)\n" n1 n2 + return (n1 + n2) + + calculate self mlogid mwork = do + printf "calculate(%d, %s)\n" logid (show work) + + let val = case op work of + ADD -> + num1 work + num2 work + SUBTRACT -> + num1 work - num2 work + MULTIPLY -> + num1 work * num2 work + DIVIDE -> + if num2 work == 0 then + throw $ + InvalidOperation { + invalidOperation_whatOp = fromIntegral $ fromEnum $ op work, + invalidOperation_why = "Cannot divide by 0" + } + else + num1 work `div` num2 work + + let logEntry = SharedStruct logid (fromString $ show $ val) + modifyMVar_ (mathLog self) $ return .(M.insert logid logEntry) + + return $! val + + where + -- stupid dynamic languages f'ing it up + num1 = work_num1 + num2 = work_num2 + op = work_op + logid = mlogid + work = mwork + + zip _ = + print "zip()" + +main = do + handler <- newCalculatorHandler + print "Starting the server..." + runBasicServer handler Calculator.process 9090 + print "done." diff --git a/vendor/src/github.com/apache/thrift/tutorial/hs/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/hs/Makefile.am new file mode 100644 index 00000000..f274eb62 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/hs/Makefile.am @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +all-local: + $(top_builddir)/compiler/cpp/thrift --gen hs -r $(top_srcdir)/tutorial/tutorial.thrift + $(CABAL) install + +install-exec-hook: + $(CABAL) install + +# Make sure this doesn't fail if Haskell is not configured. +clean-local: + $(CABAL) clean + $(RM) -r gen-* + +check-local: + $(CABAL) check + +tutorialserver: all + dist/build/HaskellServer/HaskellServer + +tutorialclient: all + dist/build/HaskellClient/HaskellClient diff --git a/vendor/src/github.com/apache/thrift/tutorial/hs/Setup.lhs b/vendor/src/github.com/apache/thrift/tutorial/hs/Setup.lhs new file mode 100644 index 00000000..c7df182d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/hs/Setup.lhs @@ -0,0 +1,21 @@ +#!/usr/bin/env runhaskell + +> -- Licensed to the Apache Software Foundation (ASF) under one +> -- or more contributor license agreements. See the NOTICE file +> -- distributed with this work for additional information +> -- regarding copyright ownership. The ASF licenses this file +> -- to you under the Apache License, Version 2.0 (the +> -- "License"); you may not use this file except in compliance +> -- with the License. You may obtain a copy of the License at +> -- +> -- http://www.apache.org/licenses/LICENSE-2.0 +> -- +> -- Unless required by applicable law or agreed to in writing, +> -- software distributed under the License is distributed on an +> -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +> -- KIND, either express or implied. See the License for the +> -- specific language governing permissions and limitations +> -- under the License. + +> import Distribution.Simple +> main = defaultMain diff --git a/vendor/src/github.com/apache/thrift/tutorial/hs/ThriftTutorial.cabal b/vendor/src/github.com/apache/thrift/tutorial/hs/ThriftTutorial.cabal new file mode 100644 index 00000000..d342ad53 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/hs/ThriftTutorial.cabal @@ -0,0 +1,73 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +Name: ThriftTutorial +Version: 0.10.0 +Cabal-Version: >= 1.4 +License: OtherLicense +Category: Foreign +Build-Type: Simple +Synopsis: Thrift Tutorial library package +Homepage: http://thrift.apache.org +Bug-Reports: https://issues.apache.org/jira/browse/THRIFT +Maintainer: dev@thrift.apache.org +License-File: ../../LICENSE + +Description: + Haskell tutorial for the Apache Thrift RPC system. Requires the use of the thrift code generator. + +flag network-uri + description: Get Network.URI from the network-uri package + default: True + +Executable HaskellServer + Main-is: HaskellServer.hs + Hs-Source-Dirs: + ., gen-hs/ + Build-Depends: + base >= 4, base < 5, ghc-prim, containers, thrift, vector, unordered-containers, text, hashable, bytestring, QuickCheck + Extensions: + DeriveDataTypeable, + ExistentialQuantification, + FlexibleInstances, + KindSignatures, + MagicHash, + RankNTypes, + ScopedTypeVariables, + TypeSynonymInstances + +Executable HaskellClient + Main-is: HaskellClient.hs + Hs-Source-Dirs: + ., gen-hs/ + Build-Depends: + base >= 4, base < 5, ghc-prim, containers, thrift, vector, QuickCheck + if flag(network-uri) + build-depends: network-uri >= 2.6, network >= 2.6 + else + build-depends: network < 2.6 + Extensions: + DeriveDataTypeable, + ExistentialQuantification, + FlexibleInstances, + KindSignatures, + MagicHash, + RankNTypes, + ScopedTypeVariables, + TypeSynonymInstances diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/java/Makefile.am new file mode 100644 index 00000000..95908b15 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/Makefile.am @@ -0,0 +1,45 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export CLASSPATH + +# Make sure this doesn't fail if ant is not configured. +clean-local: + ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ + $$ANT $(ANT_FLAGS) clean + +all-local: + $(ANT) $(ANT_FLAGS) compile + +check-local: all + $(ANT) $(ANT_FLAGS) test + +tutorial: all + $(ANT) $(ANT_FLAGS) tutorial + +tutorialserver: all + $(ANT) $(ANT_FLAGS) tutorialserver + +tutorialclient: all + $(ANT) $(ANT_FLAGS) tutorialclient + +EXTRA_DIST = \ + build.xml \ + src \ + README.md diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/README.md b/vendor/src/github.com/apache/thrift/tutorial/java/README.md new file mode 100644 index 00000000..f109fea4 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/README.md @@ -0,0 +1,24 @@ +Thrift Java Tutorial +================================================== +1) Compile the Java library + + thrift/lib/java$ make +or: + + thrift/lib/java$ ant + +4) Run the tutorial: + +start server and client with one step: + + thrift/tutorial/java$ make tutorial + +or: + + thrift/tutorial/java$ make tutorialserver + thrift/tutorial/java$ make tutorialclient + +or: + + thrift/tutorial/java$ ant tutorialserver + thrift/tutorial/java$ ant tutorialclient diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/build.xml b/vendor/src/github.com/apache/thrift/tutorial/java/build.xml new file mode 100644 index 00000000..7638d5bd --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/build.xml @@ -0,0 +1,113 @@ + + + + Thrift Java Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tutorial client simple: + + + + tutorial client secure: + + + + + + + + + + + + + + + + tutorial client simple: + + + + tutorial client secure: + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/src/CalculatorHandler.java b/vendor/src/github.com/apache/thrift/tutorial/java/src/CalculatorHandler.java new file mode 100644 index 00000000..92944b07 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/src/CalculatorHandler.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. + */ + +import org.apache.thrift.TException; + +// Generated code +import tutorial.*; +import shared.*; + +import java.util.HashMap; + +public class CalculatorHandler implements Calculator.Iface { + + private HashMap log; + + public CalculatorHandler() { + log = new HashMap(); + } + + public void ping() { + System.out.println("ping()"); + } + + public int add(int n1, int n2) { + System.out.println("add(" + n1 + "," + n2 + ")"); + return n1 + n2; + } + + public int calculate(int logid, Work work) throws InvalidOperation { + System.out.println("calculate(" + logid + ", {" + work.op + "," + work.num1 + "," + work.num2 + "})"); + int val = 0; + switch (work.op) { + case ADD: + val = work.num1 + work.num2; + break; + case SUBTRACT: + val = work.num1 - work.num2; + break; + case MULTIPLY: + val = work.num1 * work.num2; + break; + case DIVIDE: + if (work.num2 == 0) { + InvalidOperation io = new InvalidOperation(); + io.whatOp = work.op.getValue(); + io.why = "Cannot divide by 0"; + throw io; + } + val = work.num1 / work.num2; + break; + default: + InvalidOperation io = new InvalidOperation(); + io.whatOp = work.op.getValue(); + io.why = "Unknown operation"; + throw io; + } + + SharedStruct entry = new SharedStruct(); + entry.key = logid; + entry.value = Integer.toString(val); + log.put(logid, entry); + + return val; + } + + public SharedStruct getStruct(int key) { + System.out.println("getStruct(" + key + ")"); + return log.get(key); + } + + public void zip() { + System.out.println("zip()"); + } + +} + diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaClient.java b/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaClient.java new file mode 100644 index 00000000..2e35d412 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaClient.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Generated code +import tutorial.*; +import shared.*; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TSSLTransportFactory; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; + +public class JavaClient { + public static void main(String [] args) { + + if (args.length != 1) { + System.out.println("Please enter 'simple' or 'secure'"); + System.exit(0); + } + + try { + TTransport transport; + if (args[0].contains("simple")) { + transport = new TSocket("localhost", 9090); + transport.open(); + } + else { + /* + * Similar to the server, you can use the parameters to setup client parameters or + * use the default settings. On the client side, you will need a TrustStore which + * contains the trusted certificate along with the public key. + * For this example it's a self-signed cert. + */ + TSSLTransportParameters params = new TSSLTransportParameters(); + params.setTrustStore("../../lib/java/test/.truststore", "thrift", "SunX509", "JKS"); + /* + * Get a client transport instead of a server transport. The connection is opened on + * invocation of the factory method, no need to specifically call open() + */ + transport = TSSLTransportFactory.getClientSocket("localhost", 9091, 0, params); + } + + TProtocol protocol = new TBinaryProtocol(transport); + Calculator.Client client = new Calculator.Client(protocol); + + perform(client); + + transport.close(); + } catch (TException x) { + x.printStackTrace(); + } + } + + private static void perform(Calculator.Client client) throws TException + { + client.ping(); + System.out.println("ping()"); + + int sum = client.add(1,1); + System.out.println("1+1=" + sum); + + Work work = new Work(); + + work.op = Operation.DIVIDE; + work.num1 = 1; + work.num2 = 0; + try { + int quotient = client.calculate(1, work); + System.out.println("Whoa we can divide by 0"); + } catch (InvalidOperation io) { + System.out.println("Invalid operation: " + io.why); + } + + work.op = Operation.SUBTRACT; + work.num1 = 15; + work.num2 = 10; + try { + int diff = client.calculate(1, work); + System.out.println("15-10=" + diff); + } catch (InvalidOperation io) { + System.out.println("Invalid operation: " + io.why); + } + + SharedStruct log = client.getStruct(1); + System.out.println("Check log: " + log.value); + } +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaServer.java b/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaServer.java new file mode 100644 index 00000000..788473a8 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/java/src/JavaServer.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +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.transport.TSSLTransportFactory; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TSSLTransportFactory.TSSLTransportParameters; + +// Generated code +import tutorial.*; +import shared.*; + +import java.util.HashMap; + +public class JavaServer { + + public static CalculatorHandler handler; + + public static Calculator.Processor processor; + + public static void main(String [] args) { + try { + handler = new CalculatorHandler(); + processor = new Calculator.Processor(handler); + + Runnable simple = new Runnable() { + public void run() { + simple(processor); + } + }; + Runnable secure = new Runnable() { + public void run() { + secure(processor); + } + }; + + new Thread(simple).start(); + new Thread(secure).start(); + } catch (Exception x) { + x.printStackTrace(); + } + } + + public static void simple(Calculator.Processor processor) { + try { + TServerTransport serverTransport = new TServerSocket(9090); + TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); + + // Use this for a multithreaded server + // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); + + System.out.println("Starting the simple server..."); + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void secure(Calculator.Processor processor) { + try { + /* + * Use TSSLTransportParameters to setup the required SSL parameters. In this example + * we are setting the keystore and the keystore password. Other things like algorithms, + * cipher suites, client auth etc can be set. + */ + TSSLTransportParameters params = new TSSLTransportParameters(); + // The Keystore contains the private key + params.setKeyStore("../../lib/java/test/.keystore", "thrift", null, null); + + /* + * Use any of the TSSLTransportFactory to get a server transport with the appropriate + * SSL configuration. You can use the default settings if properties are set in the command line. + * Ex: -Djavax.net.ssl.keyStore=.keystore and -Djavax.net.ssl.keyStorePassword=thrift + * + * Note: You need not explicitly call open(). The underlying server socket is bound on return + * from the factory class. + */ + TServerTransport serverTransport = TSSLTransportFactory.getServerSocket(9091, 0, null, params); + TServer server = new TSimpleServer(new Args(serverTransport).processor(processor)); + + // Use this for a multi threaded server + // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); + + System.out.println("Starting the secure server..."); + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/js/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/js/Makefile.am new file mode 100644 index 00000000..3fe08884 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/js/Makefile.am @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export CLASSPATH + +# Make sure this doesn't fail if ant is not configured. +clean-local: + ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \ + $$ANT $(ANT_FLAGS) clean + +all-local: + $(ANT) $(ANT_FLAGS) compile + +check-local: all + $(ANT) $(ANT_FLAGS) test + +tutorialserver: all + $(ANT) $(ANT_FLAGS) tutorialserver + +EXTRA_DIST = \ + build.xml \ + src \ + tutorial.html diff --git a/vendor/src/github.com/apache/thrift/tutorial/js/build.xml b/vendor/src/github.com/apache/thrift/tutorial/js/build.xml new file mode 100644 index 00000000..a9a9ad4f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/js/build.xml @@ -0,0 +1,90 @@ + + + + Thrift JavaScript Tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/src/github.com/apache/thrift/tutorial/js/src/Httpd.java b/vendor/src/github.com/apache/thrift/tutorial/js/src/Httpd.java new file mode 100644 index 00000000..4985471a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/js/src/Httpd.java @@ -0,0 +1,299 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +import java.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URLDecoder; +import java.util.Locale; + +import org.apache.http.ConnectionClosedException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpServerConnection; +import org.apache.http.HttpStatus; +import org.apache.http.MethodNotSupportedException; +import org.apache.http.entity.ContentProducer; +import org.apache.http.entity.EntityTemplate; +import org.apache.http.entity.FileEntity; +import org.apache.http.impl.DefaultHttpResponseFactory; +import org.apache.http.impl.DefaultHttpServerConnection; +import org.apache.http.impl.NoConnectionReuseStrategy; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.BasicHttpProcessor; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpProcessor; +import org.apache.http.protocol.HttpRequestHandler; +import org.apache.http.protocol.HttpRequestHandlerRegistry; +import org.apache.http.protocol.HttpService; +import org.apache.http.util.EntityUtils; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TMemoryBuffer; + +// Generated code +import tutorial.*; +import shared.*; + +import java.util.HashMap; + +/** + * Basic, yet fully functional and spec compliant, HTTP/1.1 file server. + *

+ * Please note the purpose of this application is demonstrate the usage of + * HttpCore APIs. It is NOT intended to demonstrate the most efficient way of + * building an HTTP file server. + * + * + */ +public class Httpd { + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.err.println("Please specify document root directory"); + System.exit(1); + } + Thread t = new RequestListenerThread(8088, args[0]); + t.setDaemon(false); + t.start(); + } + + static class HttpFileHandler implements HttpRequestHandler { + + private final String docRoot; + + public HttpFileHandler(final String docRoot) { + super(); + this.docRoot = docRoot; + } + + public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { + + String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH); + if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { + throw new MethodNotSupportedException(method + " method not supported"); + } + String target = request.getRequestLine().getUri(); + + if (request instanceof HttpEntityEnclosingRequest && target.equals("/thrift/service/tutorial/")) { + HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); + byte[] entityContent = EntityUtils.toByteArray(entity); + System.out.println("Incoming content: " + new String(entityContent)); + + final String output = this.thriftRequest(entityContent); + + System.out.println("Outgoing content: "+output); + + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write(output); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + } else { + final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8")); + if (!file.exists()) { + + response.setStatusCode(HttpStatus.SC_NOT_FOUND); + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write("

"); + writer.write("File "); + writer.write(file.getPath()); + writer.write(" not found"); + writer.write("

"); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + System.out.println("File " + file.getPath() + " not found"); + + } else if (!file.canRead() || file.isDirectory()) { + + response.setStatusCode(HttpStatus.SC_FORBIDDEN); + EntityTemplate body = new EntityTemplate(new ContentProducer() { + + public void writeTo(final OutputStream outstream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); + writer.write("

"); + writer.write("Access denied"); + writer.write("

"); + writer.flush(); + } + + }); + body.setContentType("text/html; charset=UTF-8"); + response.setEntity(body); + System.out.println("Cannot read file " + file.getPath()); + + } else { + + response.setStatusCode(HttpStatus.SC_OK); + FileEntity body = new FileEntity(file, "text/html"); + response.setEntity(body); + System.out.println("Serving file " + file.getPath()); + + } + } + } + + private String thriftRequest(byte[] input){ + try{ + + //Input + TMemoryBuffer inbuffer = new TMemoryBuffer(input.length); + inbuffer.write(input); + TProtocol inprotocol = new TJSONProtocol(inbuffer); + + //Output + TMemoryBuffer outbuffer = new TMemoryBuffer(100); + TProtocol outprotocol = new TJSONProtocol(outbuffer); + + TProcessor processor = new Calculator.Processor(new CalculatorHandler()); + processor.process(inprotocol, outprotocol); + + byte[] output = new byte[outbuffer.length()]; + outbuffer.readAll(output, 0, output.length); + + return new String(output,"UTF-8"); + }catch(Throwable t){ + return "Error:"+t.getMessage(); + } + + + } + + } + + static class RequestListenerThread extends Thread { + + private final ServerSocket serversocket; + private final HttpParams params; + private final HttpService httpService; + + public RequestListenerThread(int port, final String docroot) throws IOException { + this.serversocket = new ServerSocket(port); + this.params = new BasicHttpParams(); + this.params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 1000).setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024) + .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true) + .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); + + // Set up the HTTP protocol processor + HttpProcessor httpproc = new BasicHttpProcessor(); + + // Set up request handlers + HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); + reqistry.register("*", new HttpFileHandler(docroot)); + + // Set up the HTTP service + this.httpService = new HttpService(httpproc, new NoConnectionReuseStrategy(), new DefaultHttpResponseFactory()); + this.httpService.setParams(this.params); + this.httpService.setHandlerResolver(reqistry); + } + + public void run() { + System.out.println("Listening on port " + this.serversocket.getLocalPort()); + System.out.println("Point your browser to http://localhost:8088/tutorial/js/tutorial.html"); + + while (!Thread.interrupted()) { + try { + // Set up HTTP connection + Socket socket = this.serversocket.accept(); + DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + System.out.println("Incoming connection from " + socket.getInetAddress()); + conn.bind(socket, this.params); + + // Start worker thread + Thread t = new WorkerThread(this.httpService, conn); + t.setDaemon(true); + t.start(); + } catch (InterruptedIOException ex) { + break; + } catch (IOException e) { + System.err.println("I/O error initialising connection thread: " + e.getMessage()); + break; + } + } + } + } + + static class WorkerThread extends Thread { + + private final HttpService httpservice; + private final HttpServerConnection conn; + + public WorkerThread(final HttpService httpservice, final HttpServerConnection conn) { + super(); + this.httpservice = httpservice; + this.conn = conn; + } + + public void run() { + System.out.println("New connection thread"); + HttpContext context = new BasicHttpContext(null); + try { + while (!Thread.interrupted() && this.conn.isOpen()) { + this.httpservice.handleRequest(this.conn, context); + } + } catch (ConnectionClosedException ex) { + System.err.println("Client closed connection"); + } catch (IOException ex) { + System.err.println("I/O error: " + ex.getMessage()); + } catch (HttpException ex) { + System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage()); + } finally { + try { + this.conn.shutdown(); + } catch (IOException ignore) { + } + } + } + + } + +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/js/tutorial.html b/vendor/src/github.com/apache/thrift/tutorial/js/tutorial.html new file mode 100644 index 00000000..d7f3945f --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/js/tutorial.html @@ -0,0 +1,109 @@ + + + + + + Thrift Javascript Bindings - Tutorial Example + + + + + + + + + + + + + + +

Thrift Javascript Bindings

+
+ + + + + + + + + + + + + + + + + + + +
num1
Operation
num2
result
autoupdate
+
+ +

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

+

+ Valid XHTML 1.0! +

+ + diff --git a/vendor/src/github.com/apache/thrift/tutorial/nodejs/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/nodejs/Makefile.am new file mode 100644 index 00000000..ecf3b2ba --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/nodejs/Makefile.am @@ -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. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-nodejs/Calculator.js gen-nodejs/SharedService.js: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen js:node -r $< + +all-local: gen-nodejs/Calculator.js + +tutorialserver: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeServer.js + +tutorialclient: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeClient.js + +tutorialserver_promise: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeServerPromise.js + +tutorialclient_promise: all + NODE_PATH="$(top_builddir)/lib/nodejs:$(top_builddir)/lib/nodejs/lib:$(NODEPATH)" $(NODEJS) NodeClientPromise.js + + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + NodeServer.js \ + NodeClient.js \ + NodeServerPromise.js \ + NodeClientPromise.js diff --git a/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClient.js b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClient.js new file mode 100644 index 00000000..130ba57d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClient.js @@ -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. + */ + +var thrift = require('thrift'); +var Calculator = require('./gen-nodejs/Calculator'); +var ttypes = require('./gen-nodejs/tutorial_types'); + + +var transport = thrift.TBufferedTransport; +var protocol = thrift.TBinaryProtocol; + +var connection = thrift.createConnection("localhost", 9090, { + transport : transport, + protocol : protocol +}); + +connection.on('error', function(err) { + assert(false, err); +}); + +// Create a Calculator client with the connection +var client = thrift.createClient(Calculator, connection); + + +client.ping(function(err, response) { + console.log('ping()'); +}); + + +client.add(1,1, function(err, response) { + console.log("1+1=" + response); +}); + + +work = new ttypes.Work(); +work.op = ttypes.Operation.DIVIDE; +work.num1 = 1; +work.num2 = 0; + +client.calculate(1, work, function(err, message) { + if (err) { + console.log("InvalidOperation " + err); + } else { + console.log('Whoa? You know how to divide by zero?'); + } +}); + +work.op = ttypes.Operation.SUBTRACT; +work.num1 = 15; +work.num2 = 10; + +client.calculate(1, work, function(err, message) { + console.log('15-10=' + message); + + client.getStruct(1, function(err, message){ + console.log('Check log: ' + message.value); + + //close the connection once we're done + connection.end(); + }); +}); diff --git a/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClientPromise.js b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClientPromise.js new file mode 100644 index 00000000..fac12ba2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeClientPromise.js @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var thrift = require('thrift'); +var Calculator = require('./gen-nodejs/Calculator'); +var ttypes = require('./gen-nodejs/tutorial_types'); + + +var transport = thrift.TBufferedTransport; +var protocol = thrift.TBinaryProtocol; + +var connection = thrift.createConnection("localhost", 9090, { + transport : transport, + protocol : protocol +}); + +connection.on('error', function(err) { + assert(false, err); +}); + +// Create a Calculator client with the connection +var client = thrift.createClient(Calculator, connection); + + +client.ping() + .then(function() { + console.log('ping()'); + }); + +client.add(1,1) + .then(function(response) { + console.log("1+1=" + response); + }); + +work = new ttypes.Work(); +work.op = ttypes.Operation.DIVIDE; +work.num1 = 1; +work.num2 = 0; + +client.calculate(1, work) + .then(function(message) { + console.log('Whoa? You know how to divide by zero?'); + }) + .fail(function(err) { + console.log("InvalidOperation " + err); + }); + + +work.op = ttypes.Operation.SUBTRACT; +work.num1 = 15; +work.num2 = 10; + +client.calculate(1, work) + .then(function(value) { + console.log('15-10=' + value); + return client.getStruct(1); + }) + .then(function(message) { + console.log('Check log: ' + message.value); + }) + .fin(function() { + //close the connection once we're done + connection.end(); + }); diff --git a/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServer.js b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServer.js new file mode 100644 index 00000000..55d3817e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServer.js @@ -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. + */ + +var thrift = require("thrift"); +var Calculator = require("./gen-nodejs/Calculator"); +var ttypes = require("./gen-nodejs/tutorial_types"); +var SharedStruct = require("./gen-nodejs/shared_types").SharedStruct; + +var data = {}; + +var server = thrift.createServer(Calculator, { + ping: function(result) { + console.log("ping()"); + result(null); + }, + + add: function(n1, n2, result) { + console.log("add(", n1, ",", n2, ")"); + result(null, n1 + n2); + }, + + calculate: function(logid, work, result) { + console.log("calculate(", logid, ",", work, ")"); + + var val = 0; + if (work.op == ttypes.Operation.ADD) { + val = work.num1 + work.num2; + } else if (work.op === ttypes.Operation.SUBTRACT) { + val = work.num1 - work.num2; + } else if (work.op === ttypes.Operation.MULTIPLY) { + val = work.num1 * work.num2; + } else if (work.op === ttypes.Operation.DIVIDE) { + if (work.num2 === 0) { + var x = new ttypes.InvalidOperation(); + x.whatOp = work.op; + x.why = 'Cannot divide by 0'; + result(x); + return; + } + val = work.num1 / work.num2; + } else { + var x = new ttypes.InvalidOperation(); + x.whatOp = work.op; + x.why = 'Invalid operation'; + result(x); + return; + } + + var entry = new SharedStruct(); + entry.key = logid; + entry.value = ""+val; + data[logid] = entry; + + result(null, val); + }, + + getStruct: function(key, result) { + console.log("getStruct(", key, ")"); + result(null, data[key]); + }, + + zip: function() { + console.log("zip()"); + result(null); + } + +}); + +server.listen(9090); diff --git a/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServerPromise.js b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServerPromise.js new file mode 100644 index 00000000..bff287b1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/nodejs/NodeServerPromise.js @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var thrift = require("thrift"); +var Calculator = require("./gen-nodejs/Calculator"); +var ttypes = require("./gen-nodejs/tutorial_types"); +var SharedStruct = require("./gen-nodejs/shared_types").SharedStruct; + +var data = {}; + +var server = thrift.createServer(Calculator, { + ping: function() { + console.log("ping()"); + }, + + add: function(n1, n2) { + console.log("add(", n1, ",", n2, ")"); + return n1 + n2; + }, + + calculate: function(logid, work) { + console.log("calculate(", logid, ",", work, ")"); + + var val = 0; + if (work.op == ttypes.Operation.ADD) { + val = work.num1 + work.num2; + } else if (work.op === ttypes.Operation.SUBTRACT) { + val = work.num1 - work.num2; + } else if (work.op === ttypes.Operation.MULTIPLY) { + val = work.num1 * work.num2; + } else if (work.op === ttypes.Operation.DIVIDE) { + if (work.num2 === 0) { + var x = new ttypes.InvalidOperation(); + x.whatOp = work.op; + x.why = 'Cannot divide by 0'; + throw x; + } + val = work.num1 / work.num2; + } else { + var x = new ttypes.InvalidOperation(); + x.whatOp = work.op; + x.why = 'Invalid operation'; + throw x; + } + + var entry = new SharedStruct(); + entry.key = logid; + entry.value = ""+val; + data[logid] = entry; + return val; + }, + + getStruct: function(key) { + console.log("getStruct(", key, ")"); + return data[key]; + }, + + zip: function() { + console.log("zip()"); + } + +}); + +server.listen(9090); diff --git a/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcClient.ml b/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcClient.ml new file mode 100644 index 00000000..5a8467be --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcClient.ml @@ -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. +*) + +open Arg +open Thrift +open Tutorial_types +open Shared_types + +exception Die;; +let sod = function + Some v -> v + | None -> raise Die;; + +type connection = { + trans : Transport.t ; + proto : Thrift.Protocol.t; + calc : Calculator.client ; +} + +let connect ~host port = + let tx = new TSocket.t host port in + let proto = new TBinaryProtocol.t tx in + let calc = new Calculator.client proto proto in + tx#opn; + { trans = tx ; proto = proto; calc = calc } +;; + +let doclient () = + let cli = connect ~host:"127.0.0.1" 9090 in + try + cli.calc#ping ; + Printf.printf "ping()\n" ; flush stdout ; + (let sum = cli.calc#add (Int32.of_int 1) (Int32.of_int 1) in + Printf.printf "1+1=%ld\n" sum ; + flush stdout) ; + (let w = new work in + w#set_op Operation.DIVIDE ; + w#set_num1 (Int32.of_int 1) ; + w#set_num2 (Int32.of_int 0) ; + try + let quotient = cli.calc#calculate (Int32.of_int 1) w in + Printf.printf "Whoa? We can divide by zero!\n" ; flush stdout + with InvalidOperation io -> + Printf.printf "InvalidOperation: %s\n" io#grab_why ; flush stdout) ; + (let w = new work in + w#set_op Operation.SUBTRACT ; + w#set_num1 (Int32.of_int 15) ; + w#set_num2 (Int32.of_int 10) ; + let diff = cli.calc#calculate (Int32.of_int 1) w in + Printf.printf "15-10=%ld\n" diff ; flush stdout) ; + (let ss = cli.calc#getStruct (Int32.of_int 1) in + Printf.printf "Check log: %s\n" ss#grab_value ; flush stdout) ; + cli.trans#close + with Transport.E (_,what) -> + Printf.printf "ERROR: %s\n" what ; flush stdout +;; + +doclient();; diff --git a/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcServer.ml b/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcServer.ml new file mode 100644 index 00000000..b5facb79 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/ocaml/CalcServer.ml @@ -0,0 +1,89 @@ +(* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*) + +open Arg +open Thrift +open Tutorial_types +open Shared_types + +exception Die;; +let sod = function + Some v -> v + | None -> raise Die;; + +class calc_handler = +object (self) + inherit Calculator.iface + val log = Hashtbl.create 23 + method ping = Printf.printf "ping()\n" ; flush stdout + method add a b = + Printf.printf"add(%ld,%ld)\n" (sod a) (sod b); flush stdout ; + Int32.add (sod a) (sod b) + method calculate logid w = + let w = sod w in + Printf.printf "calculate(%ld,{%ld,%ld,%ld})\n" (sod logid) (Operation.to_i w#grab_op) w#grab_num1 w#grab_num2; flush stdout ; + let rv = + match w#grab_op with + Operation.ADD -> + Int32.add w#grab_num1 w#grab_num2 + | Operation.SUBTRACT -> + Int32.sub w#grab_num1 w#grab_num2 + | Operation.MULTIPLY -> + Int32.mul w#grab_num1 w#grab_num2 + | Operation.DIVIDE -> + if w#grab_num2 = Int32.zero then + let io = new invalidOperation in + io#set_whatOp (Operation.to_i w#grab_op) ; + io#set_why "Cannot divide by 0" ; + raise (InvalidOperation io) + else + Int32.div w#grab_num1 w#grab_num2 in + + let ss = new sharedStruct in + ss#set_key (sod logid) ; + let buffer = Int32.to_string rv in + ss#set_value buffer ; + Hashtbl.add log (sod logid) ss ; + rv + + method zip = + Printf.printf "zip()\n"; flush stdout + + method getStruct logid = + Printf.printf "getStruct(%ld)\n" (sod logid) ; flush stdout ; + Hashtbl.find log (sod logid) + +end + +let doserver () = + let h = new calc_handler in + let proc = new Calculator.processor h in + let port = 9090 in + let pf = new TBinaryProtocol.factory in + let server = new TThreadedServer.t + proc + (new TServerSocket.t port) + (new Transport.factory) + pf + pf + in + server#serve +;; + +doserver();; diff --git a/vendor/src/github.com/apache/thrift/tutorial/ocaml/README.md b/vendor/src/github.com/apache/thrift/tutorial/ocaml/README.md new file mode 100644 index 00000000..f68e8352 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/ocaml/README.md @@ -0,0 +1,15 @@ + +This is the ocaml tutorial example. It assumes that you've already +built and installed the thrift ocaml runtime libraries in lib/ocaml. + +To compile this, you will need to generate the Thrift sources for +ocaml in this directory (due to limitations in the OASIS build-tool): + + % thrift -r --gen ocaml ../tutorial.thrift + % oasis setup + % make + +This will produce two executables Calc{Server,Client}. where + is one of "byte" or "native", depending on your ocaml +installation. Just run the server in the background, then the client +(as you would do for the C++ example). diff --git a/vendor/src/github.com/apache/thrift/tutorial/ocaml/_oasis b/vendor/src/github.com/apache/thrift/tutorial/ocaml/_oasis new file mode 100644 index 00000000..23abdc62 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/ocaml/_oasis @@ -0,0 +1,32 @@ +Name: tutorial +Version: 0.10.0 +OASISFormat: 0.3 +Synopsis: OCaml Tutorial example +Authors: Apache Thrift Developers +License: Apache-2.0 +Homepage: http://thrift.apache.org +BuildTools: ocamlbuild +Plugins: META (0.3), + DevFiles (0.3) + +Library tutorial_thrift + Path: gen-ocaml + FindlibName: tutorial_thrift + buildTools: ocamlbuild + BuildDepends: threads,thrift + Modules: Calculator,Shared_consts,Tutorial_consts,SharedService,Shared_types,Tutorial_types + XMETARequires: threads + +Executable CalcClient + Path: . + MainIs: CalcClient.ml + Build$: true + CompiledObject: best + BuildDepends: thrift, tutorial_thrift, threads + +Executable CalcServer + Path: . + MainIs: CalcServer.ml + Build$: true + CompiledObject: best + BuildDepends: thrift, tutorial_thrift, threads diff --git a/vendor/src/github.com/apache/thrift/tutorial/perl/PerlClient.pl b/vendor/src/github.com/apache/thrift/tutorial/perl/PerlClient.pl new file mode 100644 index 00000000..1d596568 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/perl/PerlClient.pl @@ -0,0 +1,82 @@ +#!/usr/bin/env perl + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use warnings; + +use lib '../../lib/perl/lib'; +use lib '../gen-perl'; + +use Thrift; +use Thrift::BinaryProtocol; +use Thrift::Socket; +use Thrift::BufferedTransport; + +use shared::SharedService; +use tutorial::Calculator; +use shared::Types; +use tutorial::Types; + +use Data::Dumper; + +my $socket = new Thrift::Socket('localhost',9090); +my $transport = new Thrift::BufferedTransport($socket,1024,1024); +my $protocol = new Thrift::BinaryProtocol($transport); +my $client = new tutorial::CalculatorClient($protocol); + + +eval{ + $transport->open(); + + $client->ping(); + print "ping()\n"; + + + my $sum = $client->add(1,1); + print "1+1=$sum\n"; + + my $work = new tutorial::Work(); + + $work->op(tutorial::Operation::DIVIDE); + $work->num1(1); + $work->num2(0); + + eval { + $client->calculate(1, $work); + print "Whoa! We can divide by zero?\n"; + }; if($@) { + warn "InvalidOperation: ".Dumper($@); + } + + $work->op(tutorial::Operation::SUBTRACT); + $work->num1(15); + $work->num2(10); + my $diff = $client->calculate(1, $work); + print "15-10=$diff\n"; + + my $log = $client->getStruct(1); + print "Log: $log->{value}\n"; + + $transport->close(); + +}; if($@){ + warn(Dumper($@)); +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/perl/PerlServer.pl b/vendor/src/github.com/apache/thrift/tutorial/perl/PerlServer.pl new file mode 100644 index 00000000..adec9786 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/perl/PerlServer.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +use strict; +use lib '../gen-perl'; +use Thrift::Socket; +use Thrift::Server; +use tutorial::Calculator; + +package CalculatorHandler; +use base qw(tutorial::CalculatorIf); + +sub new { + my $classname = shift; + my $self = {}; + + return bless($self,$classname); +} + + +sub ping +{ + print "ping()\n"; +} + +sub add +{ + my($self, $n1, $n2) = @_; + printf("add(%d,%d)\n", $n1, $n2); + return $n1 + $n2; +} + +sub calculate +{ + my($self, $logid, $work) = @_; + my $op = $work->{op}; + my $num1 = $work->{num1}; + my $num2 = $work->{num2}; + printf("calculate(%d, %d %d %d)\n", $logid, $num1, $num2, $op); + + my $val; + + if ($op == tutorial::Operation::ADD) { + $val = $num1 + $num2; + } elsif ($op == tutorial::Operation::SUBTRACT) { + $val = $num1 - $num2; + } elsif ($op == tutorial::Operation::MULTIPLY) { + $val = $num1 * $num2; + } elsif ($op == tutorial::Operation::DIVIDE) { + if ($num2 == 0) + { + my $x = new tutorial::InvalidOperation; + $x->whatOp($op); + $x->why('Cannot divide by 0'); + die $x; + } + $val = $num1 / $num2; + } else { + my $x = new tutorial::InvalidOperation; + $x->whatOp($op); + $x->why('Invalid operation'); + die $x; + } + + my $log = new shared::SharedStruct; + $log->key($logid); + $log->value(int($val)); + $self->{log}->{$logid} = $log; + + return $val; +} + +sub getStruct +{ + my($self, $key) = @_; + printf("getStruct(%d)\n", $key); + return $self->{log}->{$key}; +} + +sub zip +{ + my($self) = @_; + print "zip()\n"; +} + + + +eval { + my $handler = new CalculatorHandler; + my $processor = new tutorial::CalculatorProcessor($handler); + my $serversocket = new Thrift::ServerSocket(9090); + my $forkingserver = new Thrift::ForkingServer($processor, $serversocket); + print "Starting the server...\n"; + $forkingserver->serve(); + print "done.\n"; +}; if ($@) { + if ($@ =~ m/TException/ and exists $@->{message}) { + my $message = $@->{message}; + my $code = $@->{code}; + my $out = $code . ':' . $message; + die $out; + } else { + die $@; + } +} + diff --git a/vendor/src/github.com/apache/thrift/tutorial/php/PhpClient.php b/vendor/src/github.com/apache/thrift/tutorial/php/PhpClient.php new file mode 100644 index 00000000..d262b8fe --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/php/PhpClient.php @@ -0,0 +1,91 @@ +#!/usr/bin/env php +registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); +$loader->registerDefinition('shared', $GEN_DIR); +$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->register(); + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +use Thrift\Protocol\TBinaryProtocol; +use Thrift\Transport\TSocket; +use Thrift\Transport\THttpClient; +use Thrift\Transport\TBufferedTransport; +use Thrift\Exception\TException; + +try { + if (array_search('--http', $argv)) { + $socket = new THttpClient('localhost', 8080, '/php/PhpServer.php'); + } else { + $socket = new TSocket('localhost', 9090); + } + $transport = new TBufferedTransport($socket, 1024, 1024); + $protocol = new TBinaryProtocol($transport); + $client = new \tutorial\CalculatorClient($protocol); + + $transport->open(); + + $client->ping(); + print "ping()\n"; + + $sum = $client->add(1,1); + print "1+1=$sum\n"; + + $work = new \tutorial\Work(); + + $work->op = \tutorial\Operation::DIVIDE; + $work->num1 = 1; + $work->num2 = 0; + + try { + $client->calculate(1, $work); + print "Whoa! We can divide by zero?\n"; + } catch (\tutorial\InvalidOperation $io) { + print "InvalidOperation: $io->why\n"; + } + + $work->op = \tutorial\Operation::SUBTRACT; + $work->num1 = 15; + $work->num2 = 10; + $diff = $client->calculate(1, $work); + print "15-10=$diff\n"; + + $log = $client->getStruct(1); + print "Log: $log->value\n"; + + $transport->close(); + +} catch (TException $tx) { + print 'TException: '.$tx->getMessage()."\n"; +} + +?> diff --git a/vendor/src/github.com/apache/thrift/tutorial/php/PhpServer.php b/vendor/src/github.com/apache/thrift/tutorial/php/PhpServer.php new file mode 100644 index 00000000..22ae43eb --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/php/PhpServer.php @@ -0,0 +1,130 @@ +#!/usr/bin/env php +registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); +$loader->registerDefinition('shared', $GEN_DIR); +$loader->registerDefinition('tutorial', $GEN_DIR); +$loader->register(); + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 not a stand-alone server. It should be run as a normal + * php web script (like through Apache's mod_php) or as a cgi script + * (like with the included runserver.py). You can connect to it with + * THttpClient in any language that supports it. The PHP tutorial client + * will work if you pass it the argument "--http". + */ + +if (php_sapi_name() == 'cli') { + ini_set("display_errors", "stderr"); +} + +use Thrift\Protocol\TBinaryProtocol; +use Thrift\Transport\TPhpStream; +use Thrift\Transport\TBufferedTransport; + +class CalculatorHandler implements \tutorial\CalculatorIf { + protected $log = array(); + + public function ping() { + error_log("ping()"); + } + + public function add($num1, $num2) { + error_log("add({$num1}, {$num2})"); + return $num1 + $num2; + } + + public function calculate($logid, \tutorial\Work $w) { + error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})"); + switch ($w->op) { + case \tutorial\Operation::ADD: + $val = $w->num1 + $w->num2; + break; + case \tutorial\Operation::SUBTRACT: + $val = $w->num1 - $w->num2; + break; + case \tutorial\Operation::MULTIPLY: + $val = $w->num1 * $w->num2; + break; + case \tutorial\Operation::DIVIDE: + if ($w->num2 == 0) { + $io = new \tutorial\InvalidOperation(); + $io->whatOp = $w->op; + $io->why = "Cannot divide by 0"; + throw $io; + } + $val = $w->num1 / $w->num2; + break; + default: + $io = new \tutorial\InvalidOperation(); + $io->whatOp = $w->op; + $io->why = "Invalid Operation"; + throw $io; + } + + $log = new \shared\SharedStruct(); + $log->key = $logid; + $log->value = (string)$val; + $this->log[$logid] = $log; + + return $val; + } + + public function getStruct($key) { + error_log("getStruct({$key})"); + // This actually doesn't work because the PHP interpreter is + // restarted for every request. + //return $this->log[$key]; + return new \shared\SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); + } + + public function zip() { + error_log("zip()"); + } + +}; + +header('Content-Type', 'application/x-thrift'); +if (php_sapi_name() == 'cli') { + echo "\r\n"; +} + +$handler = new CalculatorHandler(); +$processor = new \tutorial\CalculatorProcessor($handler); + +$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); +$protocol = new TBinaryProtocol($transport, true, true); + +$transport->open(); +$processor->process($protocol, $protocol); +$transport->close(); diff --git a/vendor/src/github.com/apache/thrift/tutorial/php/runserver.py b/vendor/src/github.com/apache/thrift/tutorial/php/runserver.py new file mode 100644 index 00000000..077daa10 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/php/runserver.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import os +import BaseHTTPServer +import CGIHTTPServer + +# chdir(2) into the tutorial directory. +os.chdir(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + + +class Handler(CGIHTTPServer.CGIHTTPRequestHandler): + cgi_directories = ['/php'] + +BaseHTTPServer.HTTPServer(('', 8080), Handler).serve_forever() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.tornado/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/Makefile.am new file mode 100644 index 00000000..6ac60234 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/Makefile.am @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-py.tornado/tutorial/Calculator.py gen-py.tornado/shared/SharedService.py: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen py:tornado -r $< + +all-local: gen-py.tornado/tutorial/Calculator.py + +tutorialserver: all + ${PYTHON} PythonServer.py + +tutorialclient: all + ${PYTHON} PythonClient.py + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + PythonServer.py \ + PythonClient.py diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonClient.py b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonClient.py new file mode 100644 index 00000000..426146fc --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonClient.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import logging +import sys + +sys.path.append('gen-py.tornado') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import Operation, Work, InvalidOperation + +from thrift import TTornado +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol + +from tornado import gen +from tornado import ioloop + + +@gen.coroutine +def communicate(): + # create client + transport = TTornado.TTornadoStreamTransport('localhost', 9090) + # open the transport, bail on error + try: + yield transport.open() + print('Transport is opened') + except TTransport.TTransportException as ex: + logging.error(ex) + raise gen.Return() + + pfactory = TBinaryProtocol.TBinaryProtocolFactory() + client = Calculator.Client(transport, pfactory) + + # ping + yield client.ping() + print("ping()") + + # add + sum_ = yield client.add(1, 1) + print("1 + 1 = {0}".format(sum_)) + + # make a oneway call without a callback (schedule the write and continue + # without blocking) + client.zip() + print("zip() without callback") + + # make a oneway call with a callback (we'll wait for the stream write to + # complete before continuing) + client.zip() + print("zip() with callback") + + # calculate 1/0 + work = Work() + work.op = Operation.DIVIDE + work.num1 = 1 + work.num2 = 0 + + try: + quotient = yield client.calculate(1, work) + print("Whoa? You know how to divide by zero ? -> {0}".format(quotient)) + except InvalidOperation as io: + print("InvalidOperation: {0}".format(io)) + + # calculate 15-10 + work.op = Operation.SUBTRACT + work.num1 = 15 + work.num2 = 10 + + diff = yield client.calculate(1, work) + print("15 - 10 = {0}".format(diff)) + + # getStruct + log = yield client.getStruct(1) + print("Check log: {0}".format(log.value)) + + # close the transport + client._transport.close() + raise gen.Return() + + +def main(): + # create an ioloop, do the above, then stop + ioloop.IOLoop.current().run_sync(communicate) + + +if __name__ == "__main__": + main() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonServer.py b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonServer.py new file mode 100644 index 00000000..e0229a26 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.tornado/PythonServer.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import sys + +sys.path.append('gen-py.tornado') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import Operation, InvalidOperation + +from shared.ttypes import SharedStruct + +from thrift import TTornado +from thrift.protocol import TBinaryProtocol + +from tornado import ioloop + + +class CalculatorHandler(object): + def __init__(self): + self.log = {} + + def ping(self): + print("ping()") + + def add(self, n1, n2): + print("add({}, {})".format(n1, n2)) + return n1 + n2 + + def calculate(self, logid, work): + print("calculate({}, {})".format(logid, work)) + + if work.op == Operation.ADD: + val = work.num1 + work.num2 + elif work.op == Operation.SUBTRACT: + val = work.num1 - work.num2 + elif work.op == Operation.MULTIPLY: + 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 + val = work.num1 / work.num2 + else: + x = InvalidOperation() + x.whatOp = work.op + x.why = "Invalid operation" + raise x + + log = SharedStruct() + log.key = logid + log.value = '%d' % (val) + self.log[logid] = log + return val + + def getStruct(self, key): + print("getStruct({})".format(key)) + return self.log[key] + + def zip(self): + print("zip()") + + +def main(): + handler = CalculatorHandler() + processor = Calculator.Processor(handler) + pfactory = TBinaryProtocol.TBinaryProtocolFactory() + server = TTornado.TTornadoServer(processor, pfactory) + + print("Starting the server...") + server.bind(9090) + server.start(1) + ioloop.IOLoop.instance().start() + print("done.") + + +if __name__ == "__main__": + main() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.twisted/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/Makefile.am new file mode 100644 index 00000000..c6cbd45e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/Makefile.am @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-py/tutorial/Calculator.py gen-py/shared/SharedService.py: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen py:twisted -r $< + +all-local: gen-py/tutorial/Calculator.py + +tutorialserver: all + ${PYTHON} PythonServer.py + +tutorialclient: all + ${PYTHON} PythonClient.py + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + PythonClient.py \ + PythonServer.py \ + PythonServer.tac diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonClient.py b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonClient.py new file mode 100644 index 00000000..63dde7e7 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonClient.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import sys +sys.path.append('gen-py.twisted') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import InvalidOperation, Operation, Work + +from twisted.internet.defer import inlineCallbacks +from twisted.internet import reactor +from twisted.internet.protocol import ClientCreator + +from thrift.transport import TTwisted +from thrift.protocol import TBinaryProtocol + + +@inlineCallbacks +def main(client): + yield client.ping() + print('ping()') + + sum = yield client.add(1, 1) + print(('1+1=%d' % (sum))) + + work = Work() + + work.op = Operation.DIVIDE + work.num1 = 1 + work.num2 = 0 + + try: + quotient = yield client.calculate(1, work) + print('Whoa? You know how to divide by zero?') + print('FYI the answer is %d' % quotient) + except InvalidOperation as e: + print(('InvalidOperation: %r' % e)) + + work.op = Operation.SUBTRACT + work.num1 = 15 + work.num2 = 10 + + diff = yield client.calculate(1, work) + print(('15-10=%d' % (diff))) + + log = yield client.getStruct(1) + print(('Check log: %s' % (log.value))) + reactor.stop() + +if __name__ == '__main__': + d = ClientCreator(reactor, + TTwisted.ThriftClientProtocol, + Calculator.Client, + TBinaryProtocol.TBinaryProtocolFactory(), + ).connectTCP("127.0.0.1", 9090) + d.addCallback(lambda conn: conn.client) + d.addCallback(main) + + reactor.run() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.py b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.py new file mode 100644 index 00000000..1b0e2d5b --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import sys +sys.path.append('gen-py.twisted') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import InvalidOperation, Operation + +from shared.ttypes import SharedStruct + +from zope.interface import implements +from twisted.internet import reactor + +from thrift.transport import TTwisted +from thrift.protocol import TBinaryProtocol + + +class CalculatorHandler: + implements(Calculator.Iface) + + def __init__(self): + self.log = {} + + def ping(self): + print('ping()') + + def add(self, n1, n2): + print('add(%d,%d)' % (n1, n2)) + return n1 + n2 + + def calculate(self, logid, work): + print('calculate(%d, %r)' % (logid, work)) + + if work.op == Operation.ADD: + val = work.num1 + work.num2 + elif work.op == Operation.SUBTRACT: + val = work.num1 - work.num2 + elif work.op == Operation.MULTIPLY: + 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 + val = work.num1 / work.num2 + else: + x = InvalidOperation() + x.whatOp = work.op + x.why = 'Invalid operation' + raise x + + log = SharedStruct() + log.key = logid + log.value = '%d' % (val) + self.log[logid] = log + + return val + + def getStruct(self, key): + print('getStruct(%d)' % (key)) + return self.log[key] + + def zip(self): + print('zip()') + +if __name__ == '__main__': + handler = CalculatorHandler() + processor = Calculator.Processor(handler) + pfactory = TBinaryProtocol.TBinaryProtocolFactory() + server = reactor.listenTCP( + 9090, + TTwisted.ThriftServerFactory(processor, pfactory), + interface="127.0.0.1") + reactor.run() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.tac b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.tac new file mode 100644 index 00000000..0479636d --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py.twisted/PythonServer.tac @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from twisted.application import internet, service +from thrift.transport import TTwisted + +import glob +import sys +sys.path.append('gen-py.twisted') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) +from tutorial import Calculator +from PythonServer import CalculatorHandler +from thrift.protocol import TBinaryProtocol + + +def make_application(): + application = service.Application('CalcServer') + + handler = CalculatorHandler() + processor = Calculator.Processor(handler) + + serverFactory = TTwisted.ThriftServerFactory( + processor, + TBinaryProtocol.TBinaryProtocolFactory()) + + calcService = internet.TCPServer(9090, serverFactory) + + multiService = service.MultiService() + calcService.setServiceParent(multiService) + multiService.setServiceParent(application) + + return application + +if __name__ == '__main__': + application = make_application() diff --git a/vendor/src/github.com/apache/thrift/tutorial/py/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/py/Makefile.am new file mode 100644 index 00000000..d891640a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py/Makefile.am @@ -0,0 +1,39 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-py/tutorial/Calculator.py gen-py/shared/SharedService.py: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen py -r $< + +all-local: gen-py/tutorial/Calculator.py + +tutorialserver: all + ${PYTHON} PythonServer.py + +tutorialclient: all + ${PYTHON} PythonClient.py + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + setup.cfg \ + PythonServer.py \ + PythonClient.py diff --git a/vendor/src/github.com/apache/thrift/tutorial/py/PythonClient.py b/vendor/src/github.com/apache/thrift/tutorial/py/PythonClient.py new file mode 100644 index 00000000..c659716e --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py/PythonClient.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import sys +import glob +sys.path.append('gen-py') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import InvalidOperation, Operation, Work + +from thrift import Thrift +from thrift.transport import TSocket +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol + + +def main(): + # Make socket + transport = TSocket.TSocket('localhost', 9090) + + # Buffering is critical. Raw sockets are very slow + transport = TTransport.TBufferedTransport(transport) + + # Wrap in a protocol + protocol = TBinaryProtocol.TBinaryProtocol(transport) + + # Create a client to use the protocol encoder + client = Calculator.Client(protocol) + + # Connect! + transport.open() + + client.ping() + print('ping()') + + sum_ = client.add(1, 1) + print('1+1=%d' % sum_) + + work = Work() + + work.op = Operation.DIVIDE + work.num1 = 1 + work.num2 = 0 + + try: + quotient = client.calculate(1, work) + print('Whoa? You know how to divide by zero?') + print('FYI the answer is %d' % quotient) + except InvalidOperation as e: + print('InvalidOperation: %r' % e) + + work.op = Operation.SUBTRACT + work.num1 = 15 + work.num2 = 10 + + diff = client.calculate(1, work) + print('15-10=%d' % diff) + + log = client.getStruct(1) + print('Check log: %s' % log.value) + + # Close! + transport.close() + +if __name__ == '__main__': + try: + main() + except Thrift.TException as tx: + print('%s' % tx.message) diff --git a/vendor/src/github.com/apache/thrift/tutorial/py/PythonServer.py b/vendor/src/github.com/apache/thrift/tutorial/py/PythonServer.py new file mode 100644 index 00000000..eb132a10 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py/PythonServer.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +import glob +import sys +sys.path.append('gen-py') +sys.path.insert(0, glob.glob('../../lib/py/build/lib*')[0]) + +from tutorial import Calculator +from tutorial.ttypes import InvalidOperation, Operation + +from shared.ttypes import SharedStruct + +from thrift.transport import TSocket +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol +from thrift.server import TServer + + +class CalculatorHandler: + def __init__(self): + self.log = {} + + def ping(self): + print('ping()') + + def add(self, n1, n2): + print('add(%d,%d)' % (n1, n2)) + return n1 + n2 + + def calculate(self, logid, work): + print('calculate(%d, %r)' % (logid, work)) + + if work.op == Operation.ADD: + val = work.num1 + work.num2 + elif work.op == Operation.SUBTRACT: + val = work.num1 - work.num2 + elif work.op == Operation.MULTIPLY: + 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 + val = work.num1 / work.num2 + else: + x = InvalidOperation() + x.whatOp = work.op + x.why = 'Invalid operation' + raise x + + log = SharedStruct() + log.key = logid + log.value = '%d' % (val) + self.log[logid] = log + + return val + + def getStruct(self, key): + print('getStruct(%d)' % (key)) + return self.log[key] + + def zip(self): + print('zip()') + +if __name__ == '__main__': + handler = CalculatorHandler() + processor = Calculator.Processor(handler) + transport = TSocket.TServerSocket(port=9090) + tfactory = TTransport.TBufferedTransportFactory() + pfactory = TBinaryProtocol.TBinaryProtocolFactory() + + server = TServer.TSimpleServer(processor, transport, tfactory, pfactory) + + # You could do one of these for a multithreaded server + # server = TServer.TThreadedServer( + # processor, transport, tfactory, pfactory) + # server = TServer.TThreadPoolServer( + # processor, transport, tfactory, pfactory) + + print('Starting the server...') + server.serve() + print('done.') diff --git a/vendor/src/github.com/apache/thrift/tutorial/py/setup.cfg b/vendor/src/github.com/apache/thrift/tutorial/py/setup.cfg new file mode 100644 index 00000000..2a7120a2 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/py/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +ignore = E402 diff --git a/vendor/src/github.com/apache/thrift/tutorial/rb/Makefile.am b/vendor/src/github.com/apache/thrift/tutorial/rb/Makefile.am new file mode 100644 index 00000000..369e903a --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/rb/Makefile.am @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-py/calculator.rb gen-py/shared_service.rb: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen rb -r $< + +all-local: gen-py/calculator.rb + +tutorialserver: all + ${RUBY} RubyServer.rb + +tutorialclient: all + ${RUBY} RubyClient.rb + +clean-local: + $(RM) -r gen-* + +EXTRA_DIST = \ + RubyServer.rb \ + RubyClient.rb diff --git a/vendor/src/github.com/apache/thrift/tutorial/rb/RubyClient.rb b/vendor/src/github.com/apache/thrift/tutorial/rb/RubyClient.rb new file mode 100644 index 00000000..9a5aa432 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/rb/RubyClient.rb @@ -0,0 +1,75 @@ +#!/usr/bin/env ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$:.push('gen-rb') +$:.unshift '../../lib/rb/lib' + +require 'thrift' + +require 'calculator' + +begin + port = ARGV[0] || 9090 + + transport = Thrift::BufferedTransport.new(Thrift::Socket.new('localhost', port)) + protocol = Thrift::BinaryProtocol.new(transport) + client = Calculator::Client.new(protocol) + + transport.open() + + client.ping() + print "ping()\n" + + sum = client.add(1,1) + print "1+1=", sum, "\n" + + sum = client.add(1,4) + print "1+4=", sum, "\n" + + work = Work.new() + + work.op = Operation::SUBTRACT + work.num1 = 15 + work.num2 = 10 + diff = client.calculate(1, work) + print "15-10=", diff, "\n" + + log = client.getStruct(1) + print "Log: ", log.value, "\n" + + begin + work.op = Operation::DIVIDE + work.num1 = 1 + work.num2 = 0 + quot = client.calculate(1, work) + puts "Whoa, we can divide by 0 now?" + rescue InvalidOperation => io + print "InvalidOperation: ", io.why, "\n" + end + + client.zip() + print "zip\n" + + transport.close() + +rescue Thrift::Exception => tx + print 'Thrift::Exception: ', tx.message, "\n" +end diff --git a/vendor/src/github.com/apache/thrift/tutorial/rb/RubyServer.rb b/vendor/src/github.com/apache/thrift/tutorial/rb/RubyServer.rb new file mode 100644 index 00000000..1d707d74 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/rb/RubyServer.rb @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +$:.push('gen-rb') +$:.unshift '../../lib/rb/lib' + +require 'thrift' + +require 'calculator' +require 'shared_types' + +class CalculatorHandler + def initialize() + @log = {} + end + + def ping() + puts "ping()" + end + + def add(n1, n2) + print "add(", n1, ",", n2, ")\n" + return n1 + n2 + end + + def calculate(logid, work) + print "calculate(", logid, ", {", work.op, ",", work.num1, ",", work.num2,"})\n" + if work.op == Operation::ADD + val = work.num1 + work.num2 + elsif work.op == Operation::SUBTRACT + val = work.num1 - work.num2 + elsif work.op == Operation::MULTIPLY + val = work.num1 * work.num2 + elsif work.op == Operation::DIVIDE + if work.num2 == 0 + x = InvalidOperation.new() + x.whatOp = work.op + x.why = "Cannot divide by 0" + raise x + end + val = work.num1 / work.num2 + else + x = InvalidOperation.new() + x.whatOp = work.op + x.why = "Invalid operation" + raise x + end + + entry = SharedStruct.new() + entry.key = logid + entry.value = "#{val}" + @log[logid] = entry + + return val + + end + + def getStruct(key) + print "getStruct(", key, ")\n" + return @log[key] + end + + def zip() + print "zip\n" + end + +end + +handler = CalculatorHandler.new() +processor = Calculator::Processor.new(handler) +transport = Thrift::ServerSocket.new(9090) +transportFactory = Thrift::BufferedTransportFactory.new() +server = Thrift::SimpleServer.new(processor, transport, transportFactory) + +puts "Starting the server..." +server.serve() +puts "done." diff --git a/vendor/src/github.com/apache/thrift/tutorial/shared.thrift b/vendor/src/github.com/apache/thrift/tutorial/shared.thrift new file mode 100644 index 00000000..386000b1 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/shared.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. + */ + +/** + * This Thrift file can be included by other Thrift files that want to share + * these definitions. + */ + +namespace cpp shared +namespace d share // "shared" would collide with the eponymous D keyword. +namespace dart shared +namespace java shared +namespace perl shared +namespace php shared +namespace haxe shared + +struct SharedStruct { + 1: i32 key + 2: string value +} + +service SharedService { + SharedStruct getStruct(1: i32 key) +} diff --git a/vendor/src/github.com/apache/thrift/tutorial/tutorial.thrift b/vendor/src/github.com/apache/thrift/tutorial/tutorial.thrift new file mode 100644 index 00000000..c4a96f02 --- /dev/null +++ b/vendor/src/github.com/apache/thrift/tutorial/tutorial.thrift @@ -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. + */ + +# Thrift Tutorial +# Mark Slee (mcslee@facebook.com) +# +# This file aims to teach you how to use Thrift, in a .thrift file. Neato. The +# first thing to notice is that .thrift files support standard shell comments. +# This lets you make your thrift file executable and include your Thrift build +# step on the top line. And you can place comments like this anywhere you like. +# +# Before running this file, you will need to have installed the thrift compiler +# into /usr/local/bin. + +/** + * The first thing to know about are types. The available types in Thrift are: + * + * bool Boolean, one byte + * i8 (byte) Signed 8-bit integer + * i16 Signed 16-bit integer + * i32 Signed 32-bit integer + * i64 Signed 64-bit integer + * double 64-bit floating point value + * string String + * binary Blob (byte array) + * map Map from one type to another + * list Ordered list of one type + * set Set of unique elements of one type + * + * Did you also notice that Thrift supports C style comments? + */ + +// Just in case you were wondering... yes. We support simple C comments too. + +/** + * Thrift files can reference other Thrift files to include common struct + * and service definitions. These are found using the current path, or by + * searching relative to any paths specified with the -I compiler flag. + * + * Included objects are accessed using the name of the .thrift file as a + * prefix. i.e. shared.SharedObject + */ +include "shared.thrift" + +/** + * Thrift files can namespace, package, or prefix their output in various + * target languages. + */ +namespace cpp tutorial +namespace d tutorial +namespace dart tutorial +namespace java tutorial +namespace php tutorial +namespace perl tutorial +namespace haxe tutorial + +/** + * Thrift lets you do typedefs to get pretty names for your types. Standard + * C style here. + */ +typedef i32 MyInteger + +/** + * Thrift also lets you define constants for use across languages. Complex + * types and structs are specified using JSON notation. + */ +const i32 INT32CONSTANT = 9853 +const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} + +/** + * You can define enums, which are just 32 bit integers. Values are optional + * and start at 1 if not supplied, C style again. + */ +enum Operation { + ADD = 1, + SUBTRACT = 2, + MULTIPLY = 3, + DIVIDE = 4 +} + +/** + * Structs are the basic complex data structures. They are comprised of fields + * which each have an integer identifier, a type, a symbolic name, and an + * optional default value. + * + * Fields can be declared "optional", which ensures they will not be included + * in the serialized output if they aren't set. Note that this requires some + * manual management in some languages. + */ +struct Work { + 1: i32 num1 = 0, + 2: i32 num2, + 3: Operation op, + 4: optional string comment, +} + +/** + * Structs can also be exceptions, if they are nasty. + */ +exception InvalidOperation { + 1: i32 whatOp, + 2: string why +} + +/** + * Ahh, now onto the cool part, defining a service. Services just need a name + * and can optionally inherit from another service using the extends keyword. + */ +service Calculator extends shared.SharedService { + + /** + * A method definition looks like C code. It has a return type, arguments, + * and optionally a list of exceptions that it may throw. Note that argument + * lists and exception lists are specified using the exact same syntax as + * field lists in struct or exception definitions. + */ + + void ping(), + + i32 add(1:i32 num1, 2:i32 num2), + + i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), + + /** + * This method has a oneway modifier. That means the client only makes + * a request and does not listen for any response at all. Oneway methods + * must be void. + */ + oneway void zip() + +} + +/** + * That just about covers the basics. Take a look in the test/ folder for more + * detailed examples. After you run this file, your generated code shows up + * in folders with names gen-. The generated code isn't too scary + * to look at. It even has pretty indentation. + */ diff --git a/vendor/src/github.com/codahale/hdrhistogram/LICENSE b/vendor/src/github.com/codahale/hdrhistogram/LICENSE new file mode 100644 index 00000000..f9835c24 --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Coda Hale + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/github.com/codahale/hdrhistogram/README.md b/vendor/src/github.com/codahale/hdrhistogram/README.md new file mode 100644 index 00000000..614b197c --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/README.md @@ -0,0 +1,15 @@ +hdrhistogram +============ + +[![Build Status](https://travis-ci.org/codahale/hdrhistogram.png?branch=master)](https://travis-ci.org/codahale/hdrhistogram) + +A pure Go implementation of the [HDR Histogram](https://github.com/HdrHistogram/HdrHistogram). + +> A Histogram that supports recording and analyzing sampled data value counts +> across a configurable integer value range with configurable value precision +> within the range. Value precision is expressed as the number of significant +> digits in the value recording, and provides control over value quantization +> behavior across the value range and the subsequent value resolution at any +> given level. + +For documentation, check [godoc](http://godoc.org/github.com/codahale/hdrhistogram). diff --git a/vendor/src/github.com/codahale/hdrhistogram/hdr.go b/vendor/src/github.com/codahale/hdrhistogram/hdr.go new file mode 100644 index 00000000..c9784292 --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/hdr.go @@ -0,0 +1,564 @@ +// Package hdrhistogram provides an implementation of Gil Tene's HDR Histogram +// data structure. The HDR Histogram allows for fast and accurate analysis of +// the extreme ranges of data with non-normal distributions, like latency. +package hdrhistogram + +import ( + "fmt" + "math" +) + +// A Bracket is a part of a cumulative distribution. +type Bracket struct { + Quantile float64 + Count, ValueAt int64 +} + +// A Snapshot is an exported view of a Histogram, useful for serializing them. +// A Histogram can be constructed from it by passing it to Import. +type Snapshot struct { + LowestTrackableValue int64 + HighestTrackableValue int64 + SignificantFigures int64 + Counts []int64 +} + +// A Histogram is a lossy data structure used to record the distribution of +// non-normally distributed data (like latency) with a high degree of accuracy +// and a bounded degree of precision. +type Histogram struct { + lowestTrackableValue int64 + highestTrackableValue int64 + unitMagnitude int64 + significantFigures int64 + subBucketHalfCountMagnitude int32 + subBucketHalfCount int32 + subBucketMask int64 + subBucketCount int32 + bucketCount int32 + countsLen int32 + totalCount int64 + counts []int64 +} + +// New returns a new Histogram instance capable of tracking values in the given +// range and with the given amount of precision. +func New(minValue, maxValue int64, sigfigs int) *Histogram { + if sigfigs < 1 || 5 < sigfigs { + panic(fmt.Errorf("sigfigs must be [1,5] (was %d)", sigfigs)) + } + + largestValueWithSingleUnitResolution := 2 * math.Pow10(sigfigs) + subBucketCountMagnitude := int32(math.Ceil(math.Log2(float64(largestValueWithSingleUnitResolution)))) + + subBucketHalfCountMagnitude := subBucketCountMagnitude + if subBucketHalfCountMagnitude < 1 { + subBucketHalfCountMagnitude = 1 + } + subBucketHalfCountMagnitude-- + + unitMagnitude := int32(math.Floor(math.Log2(float64(minValue)))) + if unitMagnitude < 0 { + unitMagnitude = 0 + } + + subBucketCount := int32(math.Pow(2, float64(subBucketHalfCountMagnitude)+1)) + + subBucketHalfCount := subBucketCount / 2 + subBucketMask := int64(subBucketCount-1) << uint(unitMagnitude) + + // determine exponent range needed to support the trackable value with no + // overflow: + smallestUntrackableValue := int64(subBucketCount) << uint(unitMagnitude) + bucketsNeeded := int32(1) + for smallestUntrackableValue < maxValue { + smallestUntrackableValue <<= 1 + bucketsNeeded++ + } + + bucketCount := bucketsNeeded + countsLen := (bucketCount + 1) * (subBucketCount / 2) + + return &Histogram{ + lowestTrackableValue: minValue, + highestTrackableValue: maxValue, + unitMagnitude: int64(unitMagnitude), + significantFigures: int64(sigfigs), + subBucketHalfCountMagnitude: subBucketHalfCountMagnitude, + subBucketHalfCount: subBucketHalfCount, + subBucketMask: subBucketMask, + subBucketCount: subBucketCount, + bucketCount: bucketCount, + countsLen: countsLen, + totalCount: 0, + counts: make([]int64, countsLen), + } +} + +// ByteSize returns an estimate of the amount of memory allocated to the +// histogram in bytes. +// +// N.B.: This does not take into account the overhead for slices, which are +// small, constant, and specific to the compiler version. +func (h *Histogram) ByteSize() int { + return 6*8 + 5*4 + len(h.counts)*8 +} + +// Merge merges the data stored in the given histogram with the receiver, +// returning the number of recorded values which had to be dropped. +func (h *Histogram) Merge(from *Histogram) (dropped int64) { + i := from.rIterator() + for i.next() { + v := i.valueFromIdx + c := i.countAtIdx + + if h.RecordValues(v, c) != nil { + dropped += c + } + } + + return +} + +// TotalCount returns total number of values recorded. +func (h *Histogram) TotalCount() int64 { + return h.totalCount +} + +// Max returns the approximate maximum recorded value. +func (h *Histogram) Max() int64 { + var max int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + max = i.highestEquivalentValue + } + } + return h.highestEquivalentValue(max) +} + +// Min returns the approximate minimum recorded value. +func (h *Histogram) Min() int64 { + var min int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 && min == 0 { + min = i.highestEquivalentValue + break + } + } + return h.lowestEquivalentValue(min) +} + +// Mean returns the approximate arithmetic mean of the recorded values. +func (h *Histogram) Mean() float64 { + if h.totalCount == 0 { + return 0 + } + var total int64 + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + total += i.countAtIdx * h.medianEquivalentValue(i.valueFromIdx) + } + } + return float64(total) / float64(h.totalCount) +} + +// StdDev returns the approximate standard deviation of the recorded values. +func (h *Histogram) StdDev() float64 { + if h.totalCount == 0 { + return 0 + } + + mean := h.Mean() + geometricDevTotal := 0.0 + + i := h.iterator() + for i.next() { + if i.countAtIdx != 0 { + dev := float64(h.medianEquivalentValue(i.valueFromIdx)) - mean + geometricDevTotal += (dev * dev) * float64(i.countAtIdx) + } + } + + return math.Sqrt(geometricDevTotal / float64(h.totalCount)) +} + +// Reset deletes all recorded values and restores the histogram to its original +// state. +func (h *Histogram) Reset() { + h.totalCount = 0 + for i := range h.counts { + h.counts[i] = 0 + } +} + +// RecordValue records the given value, returning an error if the value is out +// of range. +func (h *Histogram) RecordValue(v int64) error { + return h.RecordValues(v, 1) +} + +// RecordCorrectedValue records the given value, correcting for stalls in the +// recording process. This only works for processes which are recording values +// at an expected interval (e.g., doing jitter analysis). Processes which are +// recording ad-hoc values (e.g., latency for incoming requests) can't take +// advantage of this. +func (h *Histogram) RecordCorrectedValue(v, expectedInterval int64) error { + if err := h.RecordValue(v); err != nil { + return err + } + + if expectedInterval <= 0 || v <= expectedInterval { + return nil + } + + missingValue := v - expectedInterval + for missingValue >= expectedInterval { + if err := h.RecordValue(missingValue); err != nil { + return err + } + missingValue -= expectedInterval + } + + return nil +} + +// RecordValues records n occurrences of the given value, returning an error if +// the value is out of range. +func (h *Histogram) RecordValues(v, n int64) error { + idx := h.countsIndexFor(v) + if idx < 0 || int(h.countsLen) <= idx { + return fmt.Errorf("value %d is too large to be recorded", v) + } + h.counts[idx] += n + h.totalCount += n + + return nil +} + +// ValueAtQuantile returns the recorded value at the given quantile (0..100). +func (h *Histogram) ValueAtQuantile(q float64) int64 { + if q > 100 { + q = 100 + } + + total := int64(0) + countAtPercentile := int64(((q / 100) * float64(h.totalCount)) + 0.5) + + i := h.iterator() + for i.next() { + total += i.countAtIdx + if total >= countAtPercentile { + return h.highestEquivalentValue(i.valueFromIdx) + } + } + + return 0 +} + +// CumulativeDistribution returns an ordered list of brackets of the +// distribution of recorded values. +func (h *Histogram) CumulativeDistribution() []Bracket { + var result []Bracket + + i := h.pIterator(1) + for i.next() { + result = append(result, Bracket{ + Quantile: i.percentile, + Count: i.countToIdx, + ValueAt: i.highestEquivalentValue, + }) + } + + return result +} + +// SignificantFigures returns the significant figures used to create the +// histogram +func (h *Histogram) SignificantFigures() int64 { + return h.significantFigures +} + +// LowestTrackableValue returns the lower bound on values that will be added +// to the histogram +func (h *Histogram) LowestTrackableValue() int64 { + return h.lowestTrackableValue +} + +// HighestTrackableValue returns the upper bound on values that will be added +// to the histogram +func (h *Histogram) HighestTrackableValue() int64 { + return h.highestTrackableValue +} + +// Histogram bar for plotting +type Bar struct { + From, To, Count int64 +} + +// Pretty print as csv for easy plotting +func (b Bar) String() string { + return fmt.Sprintf("%v, %v, %v\n", b.From, b.To, b.Count) +} + +// Distribution returns an ordered list of bars of the +// distribution of recorded values, counts can be normalized to a probability +func (h *Histogram) Distribution() (result []Bar) { + i := h.iterator() + for i.next() { + result = append(result, Bar{ + Count: i.countAtIdx, + From: h.lowestEquivalentValue(i.valueFromIdx), + To: i.highestEquivalentValue, + }) + } + + return result +} + +// Equals returns true if the two Histograms are equivalent, false if not. +func (h *Histogram) Equals(other *Histogram) bool { + switch { + case + h.lowestTrackableValue != other.lowestTrackableValue, + h.highestTrackableValue != other.highestTrackableValue, + h.unitMagnitude != other.unitMagnitude, + h.significantFigures != other.significantFigures, + h.subBucketHalfCountMagnitude != other.subBucketHalfCountMagnitude, + h.subBucketHalfCount != other.subBucketHalfCount, + h.subBucketMask != other.subBucketMask, + h.subBucketCount != other.subBucketCount, + h.bucketCount != other.bucketCount, + h.countsLen != other.countsLen, + h.totalCount != other.totalCount: + return false + default: + for i, c := range h.counts { + if c != other.counts[i] { + return false + } + } + } + return true +} + +// Export returns a snapshot view of the Histogram. This can be later passed to +// Import to construct a new Histogram with the same state. +func (h *Histogram) Export() *Snapshot { + return &Snapshot{ + LowestTrackableValue: h.lowestTrackableValue, + HighestTrackableValue: h.highestTrackableValue, + SignificantFigures: h.significantFigures, + Counts: append([]int64(nil), h.counts...), // copy + } +} + +// Import returns a new Histogram populated from the Snapshot data (which the +// caller must stop accessing). +func Import(s *Snapshot) *Histogram { + h := New(s.LowestTrackableValue, s.HighestTrackableValue, int(s.SignificantFigures)) + h.counts = s.Counts + totalCount := int64(0) + for i := int32(0); i < h.countsLen; i++ { + countAtIndex := h.counts[i] + if countAtIndex > 0 { + totalCount += countAtIndex + } + } + h.totalCount = totalCount + return h +} + +func (h *Histogram) iterator() *iterator { + return &iterator{ + h: h, + subBucketIdx: -1, + } +} + +func (h *Histogram) rIterator() *rIterator { + return &rIterator{ + iterator: iterator{ + h: h, + subBucketIdx: -1, + }, + } +} + +func (h *Histogram) pIterator(ticksPerHalfDistance int32) *pIterator { + return &pIterator{ + iterator: iterator{ + h: h, + subBucketIdx: -1, + }, + ticksPerHalfDistance: ticksPerHalfDistance, + } +} + +func (h *Histogram) sizeOfEquivalentValueRange(v int64) int64 { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + adjustedBucket := bucketIdx + if subBucketIdx >= h.subBucketCount { + adjustedBucket++ + } + return int64(1) << uint(h.unitMagnitude+int64(adjustedBucket)) +} + +func (h *Histogram) valueFromIndex(bucketIdx, subBucketIdx int32) int64 { + return int64(subBucketIdx) << uint(int64(bucketIdx)+h.unitMagnitude) +} + +func (h *Histogram) lowestEquivalentValue(v int64) int64 { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + return h.valueFromIndex(bucketIdx, subBucketIdx) +} + +func (h *Histogram) nextNonEquivalentValue(v int64) int64 { + return h.lowestEquivalentValue(v) + h.sizeOfEquivalentValueRange(v) +} + +func (h *Histogram) highestEquivalentValue(v int64) int64 { + return h.nextNonEquivalentValue(v) - 1 +} + +func (h *Histogram) medianEquivalentValue(v int64) int64 { + return h.lowestEquivalentValue(v) + (h.sizeOfEquivalentValueRange(v) >> 1) +} + +func (h *Histogram) getCountAtIndex(bucketIdx, subBucketIdx int32) int64 { + return h.counts[h.countsIndex(bucketIdx, subBucketIdx)] +} + +func (h *Histogram) countsIndex(bucketIdx, subBucketIdx int32) int32 { + bucketBaseIdx := (bucketIdx + 1) << uint(h.subBucketHalfCountMagnitude) + offsetInBucket := subBucketIdx - h.subBucketHalfCount + return bucketBaseIdx + offsetInBucket +} + +func (h *Histogram) getBucketIndex(v int64) int32 { + pow2Ceiling := bitLen(v | h.subBucketMask) + return int32(pow2Ceiling - int64(h.unitMagnitude) - + int64(h.subBucketHalfCountMagnitude+1)) +} + +func (h *Histogram) getSubBucketIdx(v int64, idx int32) int32 { + return int32(v >> uint(int64(idx)+int64(h.unitMagnitude))) +} + +func (h *Histogram) countsIndexFor(v int64) int { + bucketIdx := h.getBucketIndex(v) + subBucketIdx := h.getSubBucketIdx(v, bucketIdx) + return int(h.countsIndex(bucketIdx, subBucketIdx)) +} + +type iterator struct { + h *Histogram + bucketIdx, subBucketIdx int32 + countAtIdx, countToIdx, valueFromIdx int64 + highestEquivalentValue int64 +} + +func (i *iterator) next() bool { + if i.countToIdx >= i.h.totalCount { + return false + } + + // increment bucket + i.subBucketIdx++ + if i.subBucketIdx >= i.h.subBucketCount { + i.subBucketIdx = i.h.subBucketHalfCount + i.bucketIdx++ + } + + if i.bucketIdx >= i.h.bucketCount { + return false + } + + i.countAtIdx = i.h.getCountAtIndex(i.bucketIdx, i.subBucketIdx) + i.countToIdx += i.countAtIdx + i.valueFromIdx = i.h.valueFromIndex(i.bucketIdx, i.subBucketIdx) + i.highestEquivalentValue = i.h.highestEquivalentValue(i.valueFromIdx) + + return true +} + +type rIterator struct { + iterator + countAddedThisStep int64 +} + +func (r *rIterator) next() bool { + for r.iterator.next() { + if r.countAtIdx != 0 { + r.countAddedThisStep = r.countAtIdx + return true + } + } + return false +} + +type pIterator struct { + iterator + seenLastValue bool + ticksPerHalfDistance int32 + percentileToIteratorTo float64 + percentile float64 +} + +func (p *pIterator) next() bool { + if !(p.countToIdx < p.h.totalCount) { + if p.seenLastValue { + return false + } + + p.seenLastValue = true + p.percentile = 100 + + return true + } + + if p.subBucketIdx == -1 && !p.iterator.next() { + return false + } + + var done = false + for !done { + currentPercentile := (100.0 * float64(p.countToIdx)) / float64(p.h.totalCount) + if p.countAtIdx != 0 && p.percentileToIteratorTo <= currentPercentile { + p.percentile = p.percentileToIteratorTo + halfDistance := math.Trunc(math.Pow(2, math.Trunc(math.Log2(100.0/(100.0-p.percentileToIteratorTo)))+1)) + percentileReportingTicks := float64(p.ticksPerHalfDistance) * halfDistance + p.percentileToIteratorTo += 100.0 / percentileReportingTicks + return true + } + done = !p.iterator.next() + } + + return true +} + +func bitLen(x int64) (n int64) { + for ; x >= 0x8000; x >>= 16 { + n += 16 + } + if x >= 0x80 { + x >>= 8 + n += 8 + } + if x >= 0x8 { + x >>= 4 + n += 4 + } + if x >= 0x2 { + x >>= 2 + n += 2 + } + if x >= 0x1 { + n++ + } + return +} diff --git a/vendor/src/github.com/codahale/hdrhistogram/hdr_test.go b/vendor/src/github.com/codahale/hdrhistogram/hdr_test.go new file mode 100644 index 00000000..309f0ea8 --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/hdr_test.go @@ -0,0 +1,388 @@ +package hdrhistogram_test + +import ( + "math" + "reflect" + "testing" + + "github.com/codahale/hdrhistogram" +) + +func TestHighSigFig(t *testing.T) { + input := []int64{ + 459876, 669187, 711612, 816326, 931423, 1033197, 1131895, 2477317, + 3964974, 12718782, + } + + hist := hdrhistogram.New(459876, 12718782, 5) + for _, sample := range input { + hist.RecordValue(sample) + } + + if v, want := hist.ValueAtQuantile(50), int64(1048575); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func TestValueAtQuantile(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + data := []struct { + q float64 + v int64 + }{ + {q: 50, v: 500223}, + {q: 75, v: 750079}, + {q: 90, v: 900095}, + {q: 95, v: 950271}, + {q: 99, v: 990207}, + {q: 99.9, v: 999423}, + {q: 99.99, v: 999935}, + } + + for _, d := range data { + if v := h.ValueAtQuantile(d.q); v != d.v { + t.Errorf("P%v was %v, but expected %v", d.q, v, d.v) + } + } +} + +func TestMean(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Mean(), 500000.013312; v != want { + t.Errorf("Mean was %v, but expected %v", v, want) + } +} + +func TestStdDev(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.StdDev(), 288675.1403682715; v != want { + t.Errorf("StdDev was %v, but expected %v", v, want) + } +} + +func TestTotalCount(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + if v, want := h.TotalCount(), int64(i+1); v != want { + t.Errorf("TotalCount was %v, but expected %v", v, want) + } + } +} + +func TestMax(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Max(), int64(1000447); v != want { + t.Errorf("Max was %v, but expected %v", v, want) + } +} + +func TestReset(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h.Reset() + + if v, want := h.Max(), int64(0); v != want { + t.Errorf("Max was %v, but expected %v", v, want) + } +} + +func TestMerge(t *testing.T) { + h1 := hdrhistogram.New(1, 1000, 3) + h2 := hdrhistogram.New(1, 1000, 3) + + for i := 0; i < 100; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + for i := 100; i < 200; i++ { + if err := h2.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h1.Merge(h2) + + if v, want := h1.ValueAtQuantile(50), int64(99); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func TestMin(t *testing.T) { + h := hdrhistogram.New(1, 10000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if v, want := h.Min(), int64(0); v != want { + t.Errorf("Min was %v, but expected %v", v, want) + } +} + +func TestByteSize(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if v, want := h.ByteSize(), 65604; v != want { + t.Errorf("ByteSize was %v, but expected %d", v, want) + } +} + +func TestRecordCorrectedValue(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if err := h.RecordCorrectedValue(10, 100); err != nil { + t.Fatal(err) + } + + if v, want := h.ValueAtQuantile(75), int64(10); v != want { + t.Errorf("Corrected value was %v, but expected %v", v, want) + } +} + +func TestRecordCorrectedValueStall(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + + if err := h.RecordCorrectedValue(1000, 100); err != nil { + t.Fatal(err) + } + + if v, want := h.ValueAtQuantile(75), int64(800); v != want { + t.Errorf("Corrected value was %v, but expected %v", v, want) + } +} + +func TestCumulativeDistribution(t *testing.T) { + h := hdrhistogram.New(1, 100000000, 3) + + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + actual := h.CumulativeDistribution() + expected := []hdrhistogram.Bracket{ + hdrhistogram.Bracket{Quantile: 0, Count: 1, ValueAt: 0}, + hdrhistogram.Bracket{Quantile: 50, Count: 500224, ValueAt: 500223}, + hdrhistogram.Bracket{Quantile: 75, Count: 750080, ValueAt: 750079}, + hdrhistogram.Bracket{Quantile: 87.5, Count: 875008, ValueAt: 875007}, + hdrhistogram.Bracket{Quantile: 93.75, Count: 937984, ValueAt: 937983}, + hdrhistogram.Bracket{Quantile: 96.875, Count: 969216, ValueAt: 969215}, + hdrhistogram.Bracket{Quantile: 98.4375, Count: 984576, ValueAt: 984575}, + hdrhistogram.Bracket{Quantile: 99.21875, Count: 992256, ValueAt: 992255}, + hdrhistogram.Bracket{Quantile: 99.609375, Count: 996352, ValueAt: 996351}, + hdrhistogram.Bracket{Quantile: 99.8046875, Count: 998400, ValueAt: 998399}, + hdrhistogram.Bracket{Quantile: 99.90234375, Count: 999424, ValueAt: 999423}, + hdrhistogram.Bracket{Quantile: 99.951171875, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.9755859375, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.98779296875, Count: 999936, ValueAt: 999935}, + hdrhistogram.Bracket{Quantile: 99.993896484375, Count: 1000000, ValueAt: 1000447}, + hdrhistogram.Bracket{Quantile: 100, Count: 1000000, ValueAt: 1000447}, + } + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("CF was %#v, but expected %#v", actual, expected) + } +} + +func TestDistribution(t *testing.T) { + h := hdrhistogram.New(8, 1024, 3) + + for i := 0; i < 1024; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + actual := h.Distribution() + if len(actual) != 128 { + t.Errorf("Number of bars seen was %v, expected was 128", len(actual)) + } + for _, b := range actual { + if b.Count != 8 { + t.Errorf("Count per bar seen was %v, expected was 8", b.Count) + } + } +} + +func TestNaN(t *testing.T) { + h := hdrhistogram.New(1, 100000, 3) + if math.IsNaN(h.Mean()) { + t.Error("mean is NaN") + } + if math.IsNaN(h.StdDev()) { + t.Error("stddev is NaN") + } +} + +func TestSignificantFigures(t *testing.T) { + const sigFigs = 4 + h := hdrhistogram.New(1, 10, sigFigs) + if h.SignificantFigures() != sigFigs { + t.Errorf("Significant figures was %v, expected %d", h.SignificantFigures(), sigFigs) + } +} + +func TestLowestTrackableValue(t *testing.T) { + const minVal = 2 + h := hdrhistogram.New(minVal, 10, 3) + if h.LowestTrackableValue() != minVal { + t.Errorf("LowestTrackableValue figures was %v, expected %d", h.LowestTrackableValue(), minVal) + } +} + +func TestHighestTrackableValue(t *testing.T) { + const maxVal = 11 + h := hdrhistogram.New(1, maxVal, 3) + if h.HighestTrackableValue() != maxVal { + t.Errorf("HighestTrackableValue figures was %v, expected %d", h.HighestTrackableValue(), maxVal) + } +} + +func BenchmarkHistogramRecordValue(b *testing.B) { + h := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + b.Fatal(err) + } + } + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + h.RecordValue(100) + } +} + +func BenchmarkNew(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + hdrhistogram.New(1, 120000, 3) // this could track 1ms-2min + } +} + +func TestUnitMagnitudeOverflow(t *testing.T) { + h := hdrhistogram.New(0, 200, 4) + if err := h.RecordValue(11); err != nil { + t.Fatal(err) + } +} + +func TestSubBucketMaskOverflow(t *testing.T) { + hist := hdrhistogram.New(2e7, 1e8, 5) + for _, sample := range [...]int64{1e8, 2e7, 3e7} { + hist.RecordValue(sample) + } + + for q, want := range map[float64]int64{ + 50: 33554431, + 83.33: 33554431, + 83.34: 100663295, + 99: 100663295, + } { + if got := hist.ValueAtQuantile(q); got != want { + t.Errorf("got %d for %fth percentile. want: %d", got, q, want) + } + } +} + +func TestExportImport(t *testing.T) { + min := int64(1) + max := int64(10000000) + sigfigs := 3 + h := hdrhistogram.New(min, max, sigfigs) + for i := 0; i < 1000000; i++ { + if err := h.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + s := h.Export() + + if v := s.LowestTrackableValue; v != min { + t.Errorf("LowestTrackableValue was %v, but expected %v", v, min) + } + + if v := s.HighestTrackableValue; v != max { + t.Errorf("HighestTrackableValue was %v, but expected %v", v, max) + } + + if v := int(s.SignificantFigures); v != sigfigs { + t.Errorf("SignificantFigures was %v, but expected %v", v, sigfigs) + } + + if imported := hdrhistogram.Import(s); !imported.Equals(h) { + t.Error("Expected Histograms to be equivalent") + } + +} + +func TestEquals(t *testing.T) { + h1 := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 1000000; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + h2 := hdrhistogram.New(1, 10000000, 3) + for i := 0; i < 10000; i++ { + if err := h1.RecordValue(int64(i)); err != nil { + t.Fatal(err) + } + } + + if h1.Equals(h2) { + t.Error("Expected Histograms to not be equivalent") + } + + h1.Reset() + h2.Reset() + + if !h1.Equals(h2) { + t.Error("Expected Histograms to be equivalent") + } +} diff --git a/vendor/src/github.com/codahale/hdrhistogram/window.go b/vendor/src/github.com/codahale/hdrhistogram/window.go new file mode 100644 index 00000000..dc43612a --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/window.go @@ -0,0 +1,45 @@ +package hdrhistogram + +// A WindowedHistogram combines histograms to provide windowed statistics. +type WindowedHistogram struct { + idx int + h []Histogram + m *Histogram + + Current *Histogram +} + +// NewWindowed creates a new WindowedHistogram with N underlying histograms with +// the given parameters. +func NewWindowed(n int, minValue, maxValue int64, sigfigs int) *WindowedHistogram { + w := WindowedHistogram{ + idx: -1, + h: make([]Histogram, n), + m: New(minValue, maxValue, sigfigs), + } + + for i := range w.h { + w.h[i] = *New(minValue, maxValue, sigfigs) + } + w.Rotate() + + return &w +} + +// Merge returns a histogram which includes the recorded values from all the +// sections of the window. +func (w *WindowedHistogram) Merge() *Histogram { + w.m.Reset() + for _, h := range w.h { + w.m.Merge(&h) + } + return w.m +} + +// Rotate resets the oldest histogram and rotates it to be used as the current +// histogram. +func (w *WindowedHistogram) Rotate() { + w.idx++ + w.Current = &w.h[w.idx%len(w.h)] + w.Current.Reset() +} diff --git a/vendor/src/github.com/codahale/hdrhistogram/window_test.go b/vendor/src/github.com/codahale/hdrhistogram/window_test.go new file mode 100644 index 00000000..7e787588 --- /dev/null +++ b/vendor/src/github.com/codahale/hdrhistogram/window_test.go @@ -0,0 +1,64 @@ +package hdrhistogram_test + +import ( + "testing" + + "github.com/codahale/hdrhistogram" +) + +func TestWindowedHistogram(t *testing.T) { + w := hdrhistogram.NewWindowed(2, 1, 1000, 3) + + for i := 0; i < 100; i++ { + w.Current.RecordValue(int64(i)) + } + w.Rotate() + + for i := 100; i < 200; i++ { + w.Current.RecordValue(int64(i)) + } + w.Rotate() + + for i := 200; i < 300; i++ { + w.Current.RecordValue(int64(i)) + } + + if v, want := w.Merge().ValueAtQuantile(50), int64(199); v != want { + t.Errorf("Median was %v, but expected %v", v, want) + } +} + +func BenchmarkWindowedHistogramRecordAndRotate(b *testing.B) { + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3) + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if err := w.Current.RecordValue(100); err != nil { + b.Fatal(err) + } + + if i%100000 == 1 { + w.Rotate() + } + } +} + +func BenchmarkWindowedHistogramMerge(b *testing.B) { + w := hdrhistogram.NewWindowed(3, 1, 10000000, 3) + for i := 0; i < 10000000; i++ { + if err := w.Current.RecordValue(100); err != nil { + b.Fatal(err) + } + + if i%100000 == 1 { + w.Rotate() + } + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + w.Merge() + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/LICENSE b/vendor/src/github.com/crossdock/crossdock-go/LICENSE new file mode 100644 index 00000000..2a25a2cd --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/src/github.com/crossdock/crossdock-go/Makefile b/vendor/src/github.com/crossdock/crossdock-go/Makefile new file mode 100644 index 00000000..d46622cd --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/Makefile @@ -0,0 +1,43 @@ +PACKAGES := $(shell glide novendor) + +export GO15VENDOREXPERIMENT=1 + + +.PHONY: build +build: + go build $(PACKAGES) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.PHONY: test +test: + go test $(PACKAGES) + + +.PHONY: cover +cover: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + go tool cover -html=cover.out -o cover.html + + +.PHONY: install_ci +install_ci: install + go get -u -f github.com/golang/lint/golint + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + + +.PHONY: test_ci +test_ci: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + +.PHONY: lint_ci +lint_ci: + golint $(PACKAGES) + go vet $(PACKAGES) diff --git a/vendor/src/github.com/crossdock/crossdock-go/README.md b/vendor/src/github.com/crossdock/crossdock-go/README.md new file mode 100644 index 00000000..c0f0e36d --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/README.md @@ -0,0 +1,2 @@ +# crossdock-go [![Build Status](https://travis-ci.org/crossdock/crossdock-go.svg)](https://travis-ci.org/crossdock/crossdock-go) [![Coverage Status](https://coveralls.io/repos/github/crossdock/crossdock-go/badge.svg)](https://coveralls.io/github/crossdock/crossdock-go) +A Go client for Crossdock diff --git a/vendor/src/github.com/crossdock/crossdock-go/_codegen/imports/imports.go b/vendor/src/github.com/crossdock/crossdock-go/_codegen/imports/imports.go new file mode 100644 index 00000000..1777fc3a --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/_codegen/imports/imports.go @@ -0,0 +1,73 @@ +// The MIT License (MIT) +// +// Copyright (c) 2015 Ernesto Jiménez +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +package imports + +import "go/types" + +type Importer interface { + AddImportsFrom(t types.Type) + Imports() map[string]string +} + +// imports contains metadata about all the imports from a given package +type imports struct { + currentpkg string + imp map[string]string +} + +// AddImportsFrom adds imports used in the passed type +func (imp *imports) AddImportsFrom(t types.Type) { + switch el := t.(type) { + case *types.Basic: + case *types.Slice: + imp.AddImportsFrom(el.Elem()) + case *types.Pointer: + imp.AddImportsFrom(el.Elem()) + case *types.Named: + pkg := el.Obj().Pkg() + if pkg == nil { + return + } + if pkg.Name() == imp.currentpkg { + return + } + imp.imp[pkg.Path()] = pkg.Name() + case *types.Tuple: + for i := 0; i < el.Len(); i++ { + imp.AddImportsFrom(el.At(i).Type()) + } + default: + } +} + +// AddImportsFrom adds imports used in the passed type +func (imp *imports) Imports() map[string]string { + return imp.imp +} + +// New initializes a new structure to track packages imported by the currentpkg +func New(currentpkg string) Importer { + return &imports{ + currentpkg: currentpkg, + imp: make(map[string]string), + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/_codegen/main.go b/vendor/src/github.com/crossdock/crossdock-go/_codegen/main.go new file mode 100644 index 00000000..f9a0141b --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/_codegen/main.go @@ -0,0 +1,310 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// This program reads all assertion functions from the assert package and +// automatically generates the corersponding requires and forwarded assertions + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/build" + "go/doc" + "go/importer" + "go/parser" + "go/token" + "go/types" + "io" + "io/ioutil" + "log" + "os" + "path" + "strings" + "text/template" + + "github.com/crossdock/crossdock-go/_codegen/imports" +) + +var ( + pkg = flag.String("assert-path", "github.com/crossdock/crossdock-go/assert", "Path to the assert package") + outputPkg = flag.String("output-package", "", "package for the resulting code") + tmplFile = flag.String("template", "", "What file to load the function template from") + out = flag.String("out", "", "What file to write the source code to") +) + +func main() { + flag.Parse() + + scope, docs, err := parsePackageSource(*pkg) + if err != nil { + log.Fatal(err) + } + + importer, funcs, err := analyzeCode(scope, docs) + if err != nil { + log.Fatal(err) + } + + if err := generateCode(importer, funcs); err != nil { + log.Fatal(err) + } +} + +func generateCode(importer imports.Importer, funcs []testFunc) error { + buff := bytes.NewBuffer(nil) + + tmplHead, tmplFunc, err := parseTemplates() + if err != nil { + return err + } + + // Generate header + if err := tmplHead.Execute(buff, struct { + Name string + Imports map[string]string + }{ + *outputPkg, + importer.Imports(), + }); err != nil { + return err + } + + // Generate funcs + for _, fn := range funcs { + buff.Write([]byte("\n\n")) + if err := tmplFunc.Execute(buff, &fn); err != nil { + return err + } + } + + // Write file + output, err := outputFile() + if err != nil { + return err + } + defer output.Close() + _, err = io.Copy(output, buff) + return err +} + +func parseTemplates() (*template.Template, *template.Template, error) { + tmplHead, err := template.New("header").Parse(headerTemplate) + if err != nil { + return nil, nil, err + } + if *tmplFile != "" { + f, err := ioutil.ReadFile(*tmplFile) + if err != nil { + return nil, nil, err + } + funcTemplate = string(f) + } + tmpl, err := template.New("function").Parse(funcTemplate) + if err != nil { + return nil, nil, err + } + return tmplHead, tmpl, nil +} + +func outputFile() (*os.File, error) { + filename := *out + if filename == "-" || (filename == "" && *tmplFile == "") { + return os.Stdout, nil + } + if filename == "" { + filename = strings.TrimSuffix(strings.TrimSuffix(*tmplFile, ".tmpl"), ".go") + ".go" + } + return os.Create(filename) +} + +// analyzeCode takes the types scope and the docs and returns the import +// information and information about all the assertion functions. +func analyzeCode(scope *types.Scope, docs *doc.Package) (imports.Importer, []testFunc, error) { + testingT := scope.Lookup("TestingT").Type().Underlying().(*types.Interface) + + importer := imports.New(*outputPkg) + var funcs []testFunc + // Go through all the top level functions + for _, fdocs := range docs.Funcs { + // Find the function + obj := scope.Lookup(fdocs.Name) + + fn, ok := obj.(*types.Func) + if !ok { + continue + } + // Check function signatuer has at least two arguments + sig := fn.Type().(*types.Signature) + if sig.Params().Len() < 2 { + continue + } + // Check first argument is of type testingT + first, ok := sig.Params().At(0).Type().(*types.Named) + if !ok { + continue + } + firstType, ok := first.Underlying().(*types.Interface) + if !ok { + continue + } + if !types.Implements(firstType, testingT) { + continue + } + + funcs = append(funcs, testFunc{*outputPkg, fdocs, fn}) + importer.AddImportsFrom(sig.Params()) + } + return importer, funcs, nil +} + +// parsePackageSource returns the types scope and the package documentation from the pa +func parsePackageSource(pkg string) (*types.Scope, *doc.Package, error) { + pd, err := build.Import(pkg, ".", 0) + if err != nil { + return nil, nil, err + } + + fset := token.NewFileSet() + files := make(map[string]*ast.File) + fileList := make([]*ast.File, len(pd.GoFiles)) + for i, fname := range pd.GoFiles { + src, err := ioutil.ReadFile(path.Join(pd.SrcRoot, pd.ImportPath, fname)) + if err != nil { + return nil, nil, err + } + f, err := parser.ParseFile(fset, fname, src, parser.ParseComments|parser.AllErrors) + if err != nil { + return nil, nil, err + } + files[fname] = f + fileList[i] = f + } + + cfg := types.Config{ + Importer: importer.Default(), + } + info := types.Info{ + Defs: make(map[*ast.Ident]types.Object), + } + tp, err := cfg.Check(pkg, fset, fileList, &info) + if err != nil { + return nil, nil, err + } + + scope := tp.Scope() + + ap, _ := ast.NewPackage(fset, files, nil, nil) + docs := doc.New(ap, pkg, 0) + + return scope, docs, nil +} + +type testFunc struct { + CurrentPkg string + DocInfo *doc.Func + TypeInfo *types.Func +} + +func (f *testFunc) Qualifier(p *types.Package) string { + if p == nil || p.Name() == f.CurrentPkg { + return "" + } + return p.Name() +} + +func (f *testFunc) Params() string { + sig := f.TypeInfo.Type().(*types.Signature) + params := sig.Params() + p := "" + comma := "" + to := params.Len() + var i int + + if sig.Variadic() { + to-- + } + for i = 1; i < to; i++ { + param := params.At(i) + p += fmt.Sprintf("%s%s %s", comma, param.Name(), types.TypeString(param.Type(), f.Qualifier)) + comma = ", " + } + if sig.Variadic() { + param := params.At(params.Len() - 1) + p += fmt.Sprintf("%s%s ...%s", comma, param.Name(), types.TypeString(param.Type().(*types.Slice).Elem(), f.Qualifier)) + } + return p +} + +func (f *testFunc) ForwardedParams() string { + sig := f.TypeInfo.Type().(*types.Signature) + params := sig.Params() + p := "" + comma := "" + to := params.Len() + var i int + + if sig.Variadic() { + to-- + } + for i = 1; i < to; i++ { + param := params.At(i) + p += fmt.Sprintf("%s%s", comma, param.Name()) + comma = ", " + } + if sig.Variadic() { + param := params.At(params.Len() - 1) + p += fmt.Sprintf("%s%s...", comma, param.Name()) + } + return p +} + +func (f *testFunc) Comment() string { + return "// " + strings.Replace(strings.TrimSpace(f.DocInfo.Doc), "\n", "\n// ", -1) +} + +func (f *testFunc) CommentWithoutT(receiver string) string { + search := fmt.Sprintf("assert.%s(t, ", f.DocInfo.Name) + replace := fmt.Sprintf("%s.%s(", receiver, f.DocInfo.Name) + return strings.Replace(f.Comment(), search, replace, -1) +} + +var headerTemplate = `/* +* CODE GENERATED AUTOMATICALLY WITH github.com/crossdock/crossdock-go/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND +*/ + +package {{.Name}} + +import ( +{{range $path, $name := .Imports}} + {{$name}} "{{$path}}"{{end}} +) +` + +var funcTemplate = `{{.Comment}} +func (fwd *AssertionsForwarder) {{.DocInfo.Name}}({{.Params}}) bool { + return assert.{{.DocInfo.Name}}({{.ForwardedParams}}) +}` diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go b/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go new file mode 100644 index 00000000..ca06fe3d --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go @@ -0,0 +1,387 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/crossdock/crossdock-go/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND +*/ + +package assert + +import ( + + http "net/http" + url "net/url" + time "time" +) + + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { + return Condition(a.t, comp, msgAndArgs...) +} + + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") +// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + return Contains(a.t, s, contains, msgAndArgs...) +} + + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + return Empty(a.t, object, msgAndArgs...) +} + + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + return Equal(a.t, expected, actual, msgAndArgs...) +} + + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + return EqualError(a.t, theError, errString, msgAndArgs...) +} + + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + return EqualValues(a.t, expected, actual, msgAndArgs...) +} + + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { + return Error(a.t, err, msgAndArgs...) +} + + +// Exactly asserts that two objects are equal is value and type. +// +// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + return Exactly(a.t, expected, actual, msgAndArgs...) +} + + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + return Fail(a.t, failureMessage, msgAndArgs...) +} + + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + return FailNow(a.t, failureMessage, msgAndArgs...) +} + + +// False asserts that the specified value is false. +// +// a.False(myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { + return False(a.t, value, msgAndArgs...) +} + + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { + return HTTPBodyContains(a.t, handler, method, url, values, str) +} + + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { + return HTTPBodyNotContains(a.t, handler, method, url, values, str) +} + + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool { + return HTTPError(a.t, handler, method, url, values) +} + + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool { + return HTTPRedirect(a.t, handler, method, url, values) +} + + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool { + return HTTPSuccess(a.t, handler, method, url, values) +} + + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + return Implements(a.t, interfaceObject, object, msgAndArgs...) +} + + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + return InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + return IsType(a.t, expectedType, object, msgAndArgs...) +} + + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + return JSONEq(a.t, expected, actual, msgAndArgs...) +} + + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + return Len(a.t, object, length, msgAndArgs...) +} + + +// Nil asserts that the specified object is nil. +// +// a.Nil(err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + return Nil(a.t, object, msgAndArgs...) +} + + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { + return NoError(a.t, err, msgAndArgs...) +} + + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + return NotContains(a.t, s, contains, msgAndArgs...) +} + + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + return NotEmpty(a.t, object, msgAndArgs...) +} + + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + return NotEqual(a.t, expected, actual, msgAndArgs...) +} + + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + return NotNil(a.t, object, msgAndArgs...) +} + + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + return NotPanics(a.t, f, msgAndArgs...) +} + + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + return NotRegexp(a.t, rx, str, msgAndArgs...) +} + + +// NotZero asserts that i is not the zero value for its type and returns the truth. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + return NotZero(a.t, i, msgAndArgs...) +} + + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + return Panics(a.t, f, msgAndArgs...) +} + + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + return Regexp(a.t, rx, str, msgAndArgs...) +} + + +// True asserts that the specified value is true. +// +// a.True(myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { + return True(a.t, value, msgAndArgs...) +} + + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + + +// Zero asserts that i is the zero value for its type and returns the truth. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + return Zero(a.t, i, msgAndArgs...) +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go.tmpl b/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go.tmpl new file mode 100644 index 00000000..99f9acfb --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/assertion_forward.go.tmpl @@ -0,0 +1,4 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { + return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/assertions.go b/vendor/src/github.com/crossdock/crossdock-go/assert/assertions.go new file mode 100644 index 00000000..04660ee8 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/assertions.go @@ -0,0 +1,1072 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "math" + "reflect" + "regexp" + "runtime" + "strings" + "time" + "unicode" + "unicode/utf8" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" +) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) +} + +// Comparison a custom function that returns true on success and false on failure +type Comparison func() (success bool) + +/* + Helper functions +*/ + +// ObjectsAreEqual determines if two objects are considered equal. +// +// This function does no assertion of any kind. +func ObjectsAreEqual(expected, actual interface{}) bool { + + if expected == nil || actual == nil { + return expected == actual + } + + return reflect.DeepEqual(expected, actual) + +} + +// ObjectsAreEqualValues gets whether two objects are equal, or if their +// values are equal. +func ObjectsAreEqualValues(expected, actual interface{}) bool { + if ObjectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +/* CallerInfo is necessary because the assert functions use the testing object +internally, causing it to print the file:line of the assert method, rather than +where the problem actually occurred in calling code.*/ + +// CallerInfo returns an array of strings containing the file and line number +// of each stack frame leading from the assert call that failed back to the +// current test entry point. +func CallerInfo() []string { + // Our package name, we use this to find stacktraces boundaries. + const ourPackage = "github.com/crossdock/crossdock-go" + + // 16 is how many traces we want to collect, its the call stack between + // CallerInfo and up toward the program entry point. As there is about 7 + // frames above CallerInfo() until the usercode, this leaves about 8 frames + // for the returned stacktrace. The goal is to provide an informative error + // msg, not a scrolling game. + st := make([]uintptr, 16) + + // And here we already skip 2 stack frames, the Callers function frame and + // our own CallerInfo frame. + stDepth := runtime.Callers(2, st) + + var callers []string + stIndex := 0 + + // We walk up the stack until we are outside our package. + for ; stIndex < stDepth; stIndex++ { + pc := st[stIndex] + + f := runtime.FuncForPC(pc) + if f == nil { // can't do much without infos. + continue + } + fname := f.Name() + + // Ignoring this. + if fname == "" { + continue + } + + // As long as we are in our package, we continue. + if strings.Contains(fname, ourPackage) { + continue + } + + break + } + + // We walk up the stack until we reach the entry point of the current test, + // collecting traces on the way. + for ; stIndex < stDepth; stIndex++ { + pc := st[stIndex] + f := runtime.FuncForPC(pc) + if f == nil { // can't do much without infos. + continue + } + + fname := f.Name() + + // The program counter here is in fact the return pc right after the + // call. If we want the location of the call itself we have to back up + // the pc inside the boundary of the call instruction. The exception is + // if the previous pc (st[stIndex-1]) is the sigpanic function, in this + // case pc is the program counter of the faulting instruction, but we + // should not have this case here (and go doen't make it easy to get + // the address of the sigpanic function anyway). + if pc > f.Entry() { + pc-- + } + + file, line := f.FileLine(pc) + + // If we are back in our package, we stop, as we are most likely the + // entry point calling the user test function. + if strings.Contains(fname, ourPackage) { + break + } + + // We get rid of the fully qualified package name. + fname = fname[strings.LastIndexByte(fname, '.')+1:] + + // We can happily collect the trace. + callers = append(callers, fmt.Sprintf("%s() %s:%d", fname, file, line)) + + // If it looks like a test function, we stop the walk here. + if isTest(fname, "Test") || + isTest(fname, "Benchmark") || + isTest(fname, "Example") { + break + } + } + return callers +} + +// Stolen from the `go test` tool. +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +// getWhitespaceString returns a string that is long enough to overwrite the default +// output from the go testing framework. +func getWhitespaceString() string { + + _, file, line, ok := runtime.Caller(1) + if !ok { + return "" + } + parts := strings.Split(file, "/") + file = parts[len(parts)-1] + + return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) + +} + +func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { + if len(msgAndArgs) == 0 || msgAndArgs == nil { + return "" + } + if len(msgAndArgs) == 1 { + return msgAndArgs[0].(string) + } + if len(msgAndArgs) > 1 { + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } + return "" +} + +// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's +// test printing (see inner comment for specifics) +func indentMessageLines(message string, tabs int) string { + outBuf := new(bytes.Buffer) + + for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + if i != 0 { + outBuf.WriteRune('\n') + } + for ii := 0; ii < tabs; ii++ { + outBuf.WriteRune('\t') + // Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter + // by 1 prematurely. + if ii == 0 && i > 0 { + ii++ + } + } + outBuf.WriteString(scanner.Text()) + } + + return outBuf.String() +} + +type failNower interface { + FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + Fail(t, failureMessage, msgAndArgs...) + + // We cannot extend TestingT with FailNow() and + // maintain backwards compatibility, so we fallback + // to panicking when FailNow is not available in + // TestingT. + // See issue #263 + + if t, ok := t.(failNower); ok { + t.FailNow() + } else { + panic("test failed and t is missing `FailNow()`") + } + return false +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + + errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t") + if len(message) > 0 { + t.Errorf("\r%s\r\tError Trace:\t%s\n"+ + "\r\tError:%s\n"+ + "\r\tMessages:\t%s\n\r", + getWhitespaceString(), + errorTrace, + indentMessageLines(failureMessage, 2), + message) + } else { + t.Errorf("\r%s\r\tError Trace:\t%s\n"+ + "\r\tError:%s\n\r", + getWhitespaceString(), + errorTrace, + indentMessageLines(failureMessage, 2)) + } + + return false +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + + interfaceType := reflect.TypeOf(interfaceObject).Elem() + + if !reflect.TypeOf(object).Implements(interfaceType) { + return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) + } + + return true + +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { + return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) + } + + return true +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqual(expected, actual) { + diff := diff(expected, actual) + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)%s", expected, actual, diff), msgAndArgs...) + } + + return true + +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqualValues(expected, actual) { + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)", expected, actual), msgAndArgs...) + } + + return true + +} + +// Exactly asserts that two objects are equal is value and type. +// +// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...) + } + + return Equal(t, expected, actual, msgAndArgs...) + +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if !isNil(object) { + return true + } + return Fail(t, "Expected value not to be nil.", msgAndArgs...) +} + +// isNil checks if a specified object is nil or not, without Failing. +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + + return false +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if isNil(object) { + return true + } + return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) +} + +var numericZeros = []interface{}{ + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), +} + +// isEmpty gets whether the specified object is considered empty or not. +func isEmpty(object interface{}) bool { + + if object == nil { + return true + } else if object == "" { + return true + } else if object == false { + return true + } + + for _, v := range numericZeros { + if object == v { + return true + } + } + + objValue := reflect.ValueOf(object) + + switch objValue.Kind() { + case reflect.Map: + fallthrough + case reflect.Slice, reflect.Chan: + { + return (objValue.Len() == 0) + } + case reflect.Struct: + switch object.(type) { + case time.Time: + return object.(time.Time).IsZero() + } + case reflect.Ptr: + { + if objValue.IsNil() { + return true + } + switch object.(type) { + case *time.Time: + return object.(*time.Time).IsZero() + default: + return false + } + } + } + return false +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +// +// Returns whether the assertion was successful (true) or not (false). +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + + pass := isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + + pass := !isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// getLen try to get length of object. +// return (false, 0) if impossible. +func getLen(x interface{}) (ok bool, length int) { + v := reflect.ValueOf(x) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + return true, v.Len() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { + ok, l := getLen(object) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) + } + + if l != length { + return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + } + return true +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { + + if value != true { + return Fail(t, "Should be true", msgAndArgs...) + } + + return true + +} + +// False asserts that the specified value is false. +// +// assert.False(t, myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { + + if value != false { + return Fail(t, "Should be false", msgAndArgs...) + } + + return true + +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if ObjectsAreEqual(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + } + + return true + +} + +// containsElement try loop over the list check if the list includes the element. +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func includeElement(list interface{}, element interface{}) (ok, found bool) { + + listValue := reflect.ValueOf(list) + elementValue := reflect.ValueOf(element) + defer func() { + if e := recover(); e != nil { + ok = false + found = false + } + }() + + if reflect.TypeOf(list).Kind() == reflect.String { + return true, strings.Contains(listValue.String(), elementValue.String()) + } + + if reflect.TypeOf(list).Kind() == reflect.Map { + mapKeys := listValue.MapKeys() + for i := 0; i < len(mapKeys); i++ { + if ObjectsAreEqual(mapKeys[i].Interface(), element) { + return true, true + } + } + return true, false + } + + for i := 0; i < listValue.Len(); i++ { + if ObjectsAreEqual(listValue.Index(i).Interface(), element) { + return true, true + } + } + return true, false + +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") +// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if found { + return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { + result := comp() + if !result { + Fail(t, "Condition failed!", msgAndArgs...) + } + return result +} + +// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics +// methods, and represents a simple func that takes no arguments, and returns nothing. +type PanicTestFunc func() + +// didPanic returns true if the function passed to it panics. Otherwise, it returns false. +func didPanic(f PanicTestFunc) (bool, interface{}) { + + didPanic := false + var message interface{} + func() { + + defer func() { + if message = recover(); message != nil { + didPanic = true + } + }() + + // call the target function + f() + + }() + + return didPanic, message + +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + + if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + } + + return true +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + + if funcDidPanic, panicValue := didPanic(f); funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + } + + return true +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + + dt := expected.Sub(actual) + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +func toFloat(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + } + + if math.IsNaN(af) { + return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...) + } + + if math.IsNaN(bf) { + return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + } + + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) + if !result { + return result + } + } + + return true +} + +func calcRelativeError(expected, actual interface{}) (float64, error) { + af, aok := toFloat(expected) + if !aok { + return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + } + if af == 0 { + return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") + } + bf, bok := toFloat(actual) + if !bok { + return 0, fmt.Errorf("expected value %q cannot be converted to float", actual) + } + + return math.Abs(af-bf) / math.Abs(af), nil +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + actualEpsilon, err := calcRelativeError(expected, actual) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if actualEpsilon > epsilon { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...) + } + + return true +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) + if !result { + return result + } + } + + return true +} + +/* + Errors +*/ + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { + if err != nil { + return Fail(t, fmt.Sprintf("Received unexpected error %q", err), msgAndArgs...) + } + + return true +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + if err == nil { + return Fail(t, "An error is expected but got nil. %s", message) + } + + return true +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + if !NotNil(t, theError, "An error is expected but got nil. %s", message) { + return false + } + s := "An error with value \"%s\" is expected but got \"%s\". %s" + return Equal(t, errString, theError.Error(), + s, errString, theError.Error(), message) +} + +// matchRegexp return true if a specified regexp matches a string. +func matchRegexp(rx interface{}, str interface{}) bool { + + var r *regexp.Regexp + if rr, ok := rx.(*regexp.Regexp); ok { + r = rr + } else { + r = regexp.MustCompile(fmt.Sprint(rx)) + } + + return (r.FindStringIndex(fmt.Sprint(str)) != nil) + +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + + match := matchRegexp(rx, str) + + if !match { + Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) + } + + return match +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + match := matchRegexp(rx, str) + + if match { + Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) + } + + return !match + +} + +// Zero asserts that i is the zero value for its type and returns the truth. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// NotZero asserts that i is not the zero value for its type and returns the truth. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + var expectedJSONAsInterface, actualJSONAsInterface interface{} + + if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice or array. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { + return "" + } + + spew.Config.SortKeys = true + e := spew.Sdump(expected) + a := spew.Sdump(actual) + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return "\n\nDiff:\n" + diff +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/assertions_test.go b/vendor/src/github.com/crossdock/crossdock-go/assert/assertions_test.go new file mode 100644 index 00000000..8a98b5ca --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/assertions_test.go @@ -0,0 +1,1177 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "errors" + "io" + "math" + "os" + "reflect" + "regexp" + "testing" + "time" +) + +var ( + i interface{} + zeros = []interface{}{ + false, + byte(0), + complex64(0), + complex128(0), + float32(0), + float64(0), + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + rune(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + uintptr(0), + "", + [0]interface{}{}, + []interface{}(nil), + struct{ x int }{}, + (*interface{})(nil), + (func())(nil), + nil, + interface{}(nil), + map[interface{}]interface{}(nil), + (chan interface{})(nil), + (<-chan interface{})(nil), + (chan<- interface{})(nil), + } + nonZeros = []interface{}{ + true, + byte(1), + complex64(1), + complex128(1), + float32(1), + float64(1), + int(1), + int8(1), + int16(1), + int32(1), + int64(1), + rune(1), + uint(1), + uint8(1), + uint16(1), + uint32(1), + uint64(1), + uintptr(1), + "s", + [1]interface{}{1}, + []interface{}{}, + struct{ x int }{1}, + (*interface{})(&i), + (func())(func() {}), + interface{}(1), + map[interface{}]interface{}{}, + (chan interface{})(make(chan interface{})), + (<-chan interface{})(make(chan interface{})), + (chan<- interface{})(make(chan interface{})), + } +) + +// AssertionTesterInterface defines an interface to be used for testing assertion methods +type AssertionTesterInterface interface { + TestMethod() +} + +// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface +type AssertionTesterConformingObject struct { +} + +func (a *AssertionTesterConformingObject) TestMethod() { +} + +// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface +type AssertionTesterNonConformingObject struct { +} + +func TestObjectsAreEqual(t *testing.T) { + + if !ObjectsAreEqual("Hello World", "Hello World") { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(123, 123) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(123.5, 123.5) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual([]byte("Hello World"), []byte("Hello World")) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(nil, nil) { + t.Error("objectsAreEqual should return true") + } + if ObjectsAreEqual(map[int]int{5: 10}, map[int]int{10: 20}) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual('x', "x") { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual("x", 'x') { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(0, 0.1) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(0.1, 0) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(uint32(10), int32(10)) { + t.Error("objectsAreEqual should return false") + } + if !ObjectsAreEqualValues(uint32(10), int32(10)) { + t.Error("ObjectsAreEqualValues should return true") + } + if ObjectsAreEqualValues(0, nil) { + t.Fail() + } + if ObjectsAreEqualValues(nil, 0) { + t.Fail() + } + +} + +func TestImplements(t *testing.T) { + + mockT := new(testing.T) + + if !Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } + +} + +func TestIsType(t *testing.T) { + + mockT := new(testing.T) + + if !IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqual(t *testing.T) { + + mockT := new(testing.T) + + if !Equal(mockT, "Hello World", "Hello World") { + t.Error("Equal should return true") + } + if !Equal(mockT, 123, 123) { + t.Error("Equal should return true") + } + if !Equal(mockT, 123.5, 123.5) { + t.Error("Equal should return true") + } + if !Equal(mockT, []byte("Hello World"), []byte("Hello World")) { + t.Error("Equal should return true") + } + if !Equal(mockT, nil, nil) { + t.Error("Equal should return true") + } + if !Equal(mockT, int32(123), int32(123)) { + t.Error("Equal should return true") + } + if !Equal(mockT, uint64(123), uint64(123)) { + t.Error("Equal should return true") + } + +} + +func TestNotNil(t *testing.T) { + + mockT := new(testing.T) + + if !NotNil(mockT, new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if NotNil(mockT, nil) { + t.Error("NotNil should return false: object is nil") + } + if NotNil(mockT, (*struct{})(nil)) { + t.Error("NotNil should return false: object is (*struct{})(nil)") + } + +} + +func TestNil(t *testing.T) { + + mockT := new(testing.T) + + if !Nil(mockT, nil) { + t.Error("Nil should return true: object is nil") + } + if !Nil(mockT, (*struct{})(nil)) { + t.Error("Nil should return true: object is (*struct{})(nil)") + } + if Nil(mockT, new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrue(t *testing.T) { + + mockT := new(testing.T) + + if !True(mockT, true) { + t.Error("True should return true") + } + if True(mockT, false) { + t.Error("True should return false") + } + +} + +func TestFalse(t *testing.T) { + + mockT := new(testing.T) + + if !False(mockT, false) { + t.Error("False should return true") + } + if False(mockT, true) { + t.Error("False should return false") + } + +} + +func TestExactly(t *testing.T) { + + mockT := new(testing.T) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + + if Exactly(mockT, a, b) { + t.Error("Exactly should return false") + } + if Exactly(mockT, a, d) { + t.Error("Exactly should return false") + } + if !Exactly(mockT, a, c) { + t.Error("Exactly should return true") + } + + if Exactly(mockT, nil, a) { + t.Error("Exactly should return false") + } + if Exactly(mockT, a, nil) { + t.Error("Exactly should return false") + } + +} + +func TestNotEqual(t *testing.T) { + + mockT := new(testing.T) + + if !NotEqual(mockT, "Hello World", "Hello World!") { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, 123, 1234) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, 123.5, 123.55) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, []byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return true") + } + funcA := func() int { return 23 } + funcB := func() int { return 42 } + if !NotEqual(mockT, funcA, funcB) { + t.Error("NotEqual should return true") + } + + if NotEqual(mockT, "Hello World", "Hello World") { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, 123, 123) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, 123.5, 123.5) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, []byte("Hello World"), []byte("Hello World")) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return false") + } +} + +type A struct { + Name, Value string +} + +func TestContains(t *testing.T) { + + mockT := new(testing.T) + list := []string{"Foo", "Bar"} + complexList := []*A{ + {"b", "c"}, + {"d", "e"}, + {"g", "h"}, + {"j", "k"}, + } + simpleMap := map[interface{}]interface{}{"Foo": "Bar"} + + if !Contains(mockT, "Hello World", "Hello") { + t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") + } + if Contains(mockT, "Hello World", "Salut") { + t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") + } + + if !Contains(mockT, list, "Bar") { + t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Bar\"") + } + if Contains(mockT, list, "Salut") { + t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") + } + if !Contains(mockT, complexList, &A{"g", "h"}) { + t.Error("Contains should return true: complexList contains {\"g\", \"h\"}") + } + if Contains(mockT, complexList, &A{"g", "e"}) { + t.Error("Contains should return false: complexList contains {\"g\", \"e\"}") + } + if Contains(mockT, complexList, &A{"g", "e"}) { + t.Error("Contains should return false: complexList contains {\"g\", \"e\"}") + } + if !Contains(mockT, simpleMap, "Foo") { + t.Error("Contains should return true: \"{\"Foo\": \"Bar\"}\" contains \"Foo\"") + } + if Contains(mockT, simpleMap, "Bar") { + t.Error("Contains should return false: \"{\"Foo\": \"Bar\"}\" does not contains \"Bar\"") + } +} + +func TestNotContains(t *testing.T) { + + mockT := new(testing.T) + list := []string{"Foo", "Bar"} + simpleMap := map[interface{}]interface{}{"Foo": "Bar"} + + if !NotContains(mockT, "Hello World", "Hello!") { + t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") + } + if NotContains(mockT, "Hello World", "Hello") { + t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") + } + + if !NotContains(mockT, list, "Foo!") { + t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") + } + if NotContains(mockT, list, "Foo") { + t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + if NotContains(mockT, simpleMap, "Foo") { + t.Error("Contains should return true: \"{\"Foo\": \"Bar\"}\" contains \"Foo\"") + } + if !NotContains(mockT, simpleMap, "Bar") { + t.Error("Contains should return false: \"{\"Foo\": \"Bar\"}\" does not contains \"Bar\"") + } +} + +func Test_includeElement(t *testing.T) { + + list1 := []string{"Foo", "Bar"} + list2 := []int{1, 2} + simpleMap := map[interface{}]interface{}{"Foo": "Bar"} + + ok, found := includeElement("Hello World", "World") + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Foo") + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Bar") + True(t, ok) + True(t, found) + + ok, found = includeElement(list2, 1) + True(t, ok) + True(t, found) + + ok, found = includeElement(list2, 2) + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Foo!") + True(t, ok) + False(t, found) + + ok, found = includeElement(list2, 3) + True(t, ok) + False(t, found) + + ok, found = includeElement(list2, "1") + True(t, ok) + False(t, found) + + ok, found = includeElement(simpleMap, "Foo") + True(t, ok) + True(t, found) + + ok, found = includeElement(simpleMap, "Bar") + True(t, ok) + False(t, found) + + ok, found = includeElement(1433, "1") + False(t, ok) + False(t, found) +} + +func TestCondition(t *testing.T) { + mockT := new(testing.T) + + if !Condition(mockT, func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if Condition(mockT, func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanic(t *testing.T) { + + if funcDidPanic, _ := didPanic(func() { + panic("Panic!") + }); !funcDidPanic { + t.Error("didPanic should return true") + } + + if funcDidPanic, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanics(t *testing.T) { + + mockT := new(testing.T) + + if !Panics(mockT, func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if Panics(mockT, func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestNotPanics(t *testing.T) { + + mockT := new(testing.T) + + if !NotPanics(mockT, func() { + }) { + t.Error("NotPanics should return true") + } + + if NotPanics(mockT, func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + True(t, NoError(mockT, err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("some error") + + False(t, NoError(mockT, err), "NoError with error should return False") + + // returning an empty error interface + err = func() error { + var err *customError + if err != nil { + t.Fatal("err should be nil here") + } + return err + }() + + if err == nil { // err is not nil here! + t.Error("Error should be nil due to empty interface", err) + } + + False(t, NoError(mockT, err), "NoError should fail with empty error interface") +} + +type customError struct{} + +func (*customError) Error() string { return "fail" } + +func TestError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + False(t, Error(mockT, err), "Error should return False for nil arg") + + // now set an error + err = errors.New("some error") + + True(t, Error(mockT, err), "Error with error should return True") + + // returning an empty error interface + err = func() error { + var err *customError + if err != nil { + t.Fatal("err should be nil here") + } + return err + }() + + if err == nil { // err is not nil here! + t.Error("Error should be nil due to empty interface", err) + } + + True(t, Error(mockT, err), "Error should pass with empty error interface") +} + +func TestEqualError(t *testing.T) { + mockT := new(testing.T) + + // start with a nil error + var err error + False(t, EqualError(mockT, err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + False(t, EqualError(mockT, err, "Not some error"), + "EqualError should return false for different error string") + True(t, EqualError(mockT, err, "some error"), + "EqualError should return true") +} + +func Test_isEmpty(t *testing.T) { + + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + True(t, isEmpty("")) + True(t, isEmpty(nil)) + True(t, isEmpty([]string{})) + True(t, isEmpty(0)) + True(t, isEmpty(int32(0))) + True(t, isEmpty(int64(0))) + True(t, isEmpty(false)) + True(t, isEmpty(map[string]string{})) + True(t, isEmpty(new(time.Time))) + True(t, isEmpty(time.Time{})) + True(t, isEmpty(make(chan struct{}))) + False(t, isEmpty("something")) + False(t, isEmpty(errors.New("something"))) + False(t, isEmpty([]string{"something"})) + False(t, isEmpty(1)) + False(t, isEmpty(true)) + False(t, isEmpty(map[string]string{"Hello": "World"})) + False(t, isEmpty(chWithValue)) + +} + +func TestEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + var tiP *time.Time + var tiNP time.Time + var s *string + var f *os.File + + True(t, Empty(mockT, ""), "Empty string is empty") + True(t, Empty(mockT, nil), "Nil is empty") + True(t, Empty(mockT, []string{}), "Empty string array is empty") + True(t, Empty(mockT, 0), "Zero int value is empty") + True(t, Empty(mockT, false), "False value is empty") + True(t, Empty(mockT, make(chan struct{})), "Channel without values is empty") + True(t, Empty(mockT, s), "Nil string pointer is empty") + True(t, Empty(mockT, f), "Nil os.File pointer is empty") + True(t, Empty(mockT, tiP), "Nil time.Time pointer is empty") + True(t, Empty(mockT, tiNP), "time.Time is empty") + + False(t, Empty(mockT, "something"), "Non Empty string is not empty") + False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty") + False(t, Empty(mockT, []string{"something"}), "Non empty string array is not empty") + False(t, Empty(mockT, 1), "Non-zero int value is not empty") + False(t, Empty(mockT, true), "True value is not empty") + False(t, Empty(mockT, chWithValue), "Channel with values is not empty") +} + +func TestNotEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + False(t, NotEmpty(mockT, ""), "Empty string is empty") + False(t, NotEmpty(mockT, nil), "Nil is empty") + False(t, NotEmpty(mockT, []string{}), "Empty string array is empty") + False(t, NotEmpty(mockT, 0), "Zero int value is empty") + False(t, NotEmpty(mockT, false), "False value is empty") + False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty") + + True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty") + True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty") + True(t, NotEmpty(mockT, []string{"something"}), "Non empty string array is not empty") + True(t, NotEmpty(mockT, 1), "Non-zero int value is not empty") + True(t, NotEmpty(mockT, true), "True value is not empty") + True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") +} + +func Test_getLen(t *testing.T) { + falseCases := []interface{}{ + nil, + 0, + true, + false, + 'A', + struct{}{}, + } + for _, v := range falseCases { + ok, l := getLen(v) + False(t, ok, "Expected getLen fail to get length of %#v", v) + Equal(t, 0, l, "getLen should return 0 for %#v", v) + } + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + trueCases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range trueCases { + ok, l := getLen(c.v) + True(t, ok, "Expected getLen success to get length of %#v", c.v) + Equal(t, c.l, l) + } +} + +func TestLen(t *testing.T) { + mockT := new(testing.T) + + False(t, Len(mockT, nil, 0), "nil does not have length") + False(t, Len(mockT, 0, 0), "int does not have length") + False(t, Len(mockT, true, 0), "true does not have length") + False(t, Len(mockT, false, 0), "false does not have length") + False(t, Len(mockT, 'A', 0), "Rune does not have length") + False(t, Len(mockT, struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + True(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } + + cases = []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 4}, + {[...]int{1, 2, 3}, 2}, + {"ABC", 2}, + {map[int]int{1: 2, 2: 4, 3: 6}, 4}, + {ch, 2}, + + {[]int{}, 1}, + {map[int]int{}, 1}, + {make(chan int), 1}, + + {[]int(nil), 1}, + {map[int]int(nil), 1}, + {(chan int)(nil), 1}, + } + + for _, c := range cases { + False(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDuration(t *testing.T) { + + mockT := new(testing.T) + a := time.Now() + b := a.Add(10 * time.Second) + + True(t, WithinDuration(mockT, a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + True(t, WithinDuration(mockT, b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + False(t, WithinDuration(mockT, a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestInDelta(t *testing.T) { + mockT := new(testing.T) + + True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDelta(mockT, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDelta(mockT, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDelta(mockT, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDelta(mockT, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail") + False(t, InDelta(mockT, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InDelta(mockT, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, InDelta(mockT, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInDeltaSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InDeltaSlice(mockT, + []float64{1.001, 0.999}, + []float64{1, 1}, + 0.1), "{1.001, 0.009} is element-wise close to {1, 1} in delta=0.1") + + True(t, InDeltaSlice(mockT, + []float64{1, 2}, + []float64{0, 3}, + 1), "{1, 2} is element-wise close to {0, 3} in delta=1") + + False(t, InDeltaSlice(mockT, + []float64{1, 2}, + []float64{0, 3}, + 0.1), "{1, 2} is not element-wise close to {0, 3} in delta=0.1") + + False(t, InDeltaSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestInEpsilon(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + {0.1, 0, 2}, + } + + for _, tc := range cases { + True(t, InEpsilon(t, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon), "test: %q", tc) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + {0, 0.1, 2}, // expected must be different to zero + } + + for _, tc := range cases { + False(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + +} + +func TestInEpsilonSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InEpsilonSlice(mockT, + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.06), "{2.2, 2.0} is element-wise close to {2.1, 2.1} in espilon=0.06") + + False(t, InEpsilonSlice(mockT, + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.04), "{2.2, 2.0} is not element-wise close to {2.1, 2.1} in espilon=0.04") + + False(t, InEpsilonSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestRegexp(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, Regexp(mockT, tc.rx, tc.str)) + True(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + False(t, NotRegexp(mockT, tc.rx, tc.str)) + False(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, Regexp(mockT, tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + True(t, NotRegexp(mockT, tc.rx, tc.str)) + True(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } +} + +func testAutogeneratedFunction() { + defer func() { + if err := recover(); err == nil { + panic("did not panic") + } + CallerInfo() + }() + t := struct { + io.Closer + }{} + var c io.Closer + c = t + c.Close() +} + +func TestCallerInfoWithAutogeneratedFunctions(t *testing.T) { + NotPanics(t, func() { + testAutogeneratedFunction() + }) +} + +func TestZero(t *testing.T) { + mockT := new(testing.T) + + for _, test := range zeros { + True(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } + + for _, test := range nonZeros { + False(t, Zero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } +} + +func TestNotZero(t *testing.T) { + mockT := new(testing.T) + + for _, test := range zeros { + False(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } + + for _, test := range nonZeros { + True(t, NotZero(mockT, test, "%#v is not the %v zero value", test, reflect.TypeOf(test))) + } +} + +func TestJSONEq_EqualSONString(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} + +func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}")) +} + +func TestJSONEq_Array(t *testing.T) { + mockT := new(testing.T) + True(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) +} + +func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) +} + +func TestJSONEq_HashesNotEquivalent(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_ActualIsNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `{"foo": "bar"}`, "Not JSON")) +} + +func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`)) +} + +func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, "Not JSON", "Not JSON")) +} + +func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(testing.T) + False(t, JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) +} + +func TestDiff(t *testing.T) { + expected := ` + +Diff: +--- Expected ++++ Actual +@@ -1,3 +1,3 @@ + (struct { foo string }) { +- foo: (string) (len=5) "hello" ++ foo: (string) (len=3) "bar" + } +` + actual := diff( + struct{ foo string }{"hello"}, + struct{ foo string }{"bar"}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -2,5 +2,5 @@ + (int) 1, +- (int) 2, + (int) 3, +- (int) 4 ++ (int) 5, ++ (int) 7 + } +` + actual = diff( + []int{1, 2, 3, 4}, + []int{1, 3, 5, 7}, + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -2,4 +2,4 @@ + (int) 1, +- (int) 2, +- (int) 3 ++ (int) 3, ++ (int) 5 + } +` + actual = diff( + []int{1, 2, 3, 4}[0:3], + []int{1, 3, 5, 7}[0:3], + ) + Equal(t, expected, actual) + + expected = ` + +Diff: +--- Expected ++++ Actual +@@ -1,6 +1,6 @@ + (map[string]int) (len=4) { +- (string) (len=4) "four": (int) 4, ++ (string) (len=4) "five": (int) 5, + (string) (len=3) "one": (int) 1, +- (string) (len=5) "three": (int) 3, +- (string) (len=3) "two": (int) 2 ++ (string) (len=5) "seven": (int) 7, ++ (string) (len=5) "three": (int) 3 + } +` + + actual = diff( + map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}, + map[string]int{"one": 1, "three": 3, "five": 5, "seven": 7}, + ) + Equal(t, expected, actual) +} + +func TestDiffEmptyCases(t *testing.T) { + Equal(t, "", diff(nil, nil)) + Equal(t, "", diff(struct{ foo string }{}, nil)) + Equal(t, "", diff(nil, struct{ foo string }{})) + Equal(t, "", diff(1, 2)) + Equal(t, "", diff(1, 2)) + Equal(t, "", diff([]int{1}, []bool{true})) +} + +type mockTestingT struct { +} + +func (m *mockTestingT) Errorf(format string, args ...interface{}) {} + +func TestFailNowWithPlainTestingT(t *testing.T) { + mockT := &mockTestingT{} + + Panics(t, func() { + FailNow(mockT, "failed") + }, "should panic since mockT is missing FailNow()") +} + +type mockFailNowTestingT struct { +} + +func (m *mockFailNowTestingT) Errorf(format string, args ...interface{}) {} + +func (m *mockFailNowTestingT) FailNow() {} + +func TestFailNowWithFullTestingT(t *testing.T) { + mockT := &mockFailNowTestingT{} + + NotPanics(t, func() { + FailNow(mockT, "failed") + }, "should call mockT.FailNow() rather than panicking") +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/doc.go b/vendor/src/github.com/crossdock/crossdock-go/assert/doc.go new file mode 100644 index 00000000..a3081bcb --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/doc.go @@ -0,0 +1,68 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. +// +// Example Usage +// +// The following is a complete example using assert in a standard test function: +// import ( +// "testing" +// "github.com/crossdock/crossdock-go/assert" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// if you assert many times, use the format below: +// +// import ( +// "testing" +// "github.com/crossdock/crossdock-go/assert" +// ) +// +// func TestSomething(t *testing.T) { +// assert := assert.New(t) +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(a, b, "The two words should be the same.") +// } +// +// Assertions +// +// Assertions allow you to easily write test code, and are global funcs in the `assert` package. +// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// testing framework. This allows the assertion funcs to write the failings and other details to +// the correct place. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package assert diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/errors.go b/vendor/src/github.com/crossdock/crossdock-go/assert/errors.go new file mode 100644 index 00000000..fbd62a26 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/errors.go @@ -0,0 +1,33 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "errors" +) + +// AnError is an error instance useful for testing. If the code does not care +// about error specifics, and only needs to return the error for example, this +// error should be used to make the test code more readable. +var AnError = errors.New("assert.AnError general error for testing") diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions.go b/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions.go new file mode 100644 index 00000000..369aeee5 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions_test.go b/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions_test.go new file mode 100644 index 00000000..77105c91 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/forward_assertions_test.go @@ -0,0 +1,634 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "errors" + "regexp" + "testing" + "time" +) + +func TestImplementsWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } +} + +func TestIsTypeWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqualWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Equal("Hello World", "Hello World") { + t.Error("Equal should return true") + } + if !assert.Equal(123, 123) { + t.Error("Equal should return true") + } + if !assert.Equal(123.5, 123.5) { + t.Error("Equal should return true") + } + if !assert.Equal([]byte("Hello World"), []byte("Hello World")) { + t.Error("Equal should return true") + } + if !assert.Equal(nil, nil) { + t.Error("Equal should return true") + } +} + +func TestEqualValuesWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.EqualValues(uint32(10), int32(10)) { + t.Error("EqualValues should return true") + } +} + +func TestNotNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.NotNil(new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if assert.NotNil(nil) { + t.Error("NotNil should return false: object is nil") + } + +} + +func TestNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Nil(nil) { + t.Error("Nil should return true: object is nil") + } + if assert.Nil(new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrueWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.True(true) { + t.Error("True should return true") + } + if assert.True(false) { + t.Error("True should return false") + } + +} + +func TestFalseWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.False(false) { + t.Error("False should return true") + } + if assert.False(true) { + t.Error("False should return false") + } + +} + +func TestExactlyWrapper(t *testing.T) { + assert := New(new(testing.T)) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + + if assert.Exactly(a, b) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, d) { + t.Error("Exactly should return false") + } + if !assert.Exactly(a, c) { + t.Error("Exactly should return true") + } + + if assert.Exactly(nil, a) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, nil) { + t.Error("Exactly should return false") + } + +} + +func TestNotEqualWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotEqual("Hello World", "Hello World!") { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123, 1234) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123.5, 123.55) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return true") + } +} + +func TestContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.Contains("Hello World", "Hello") { + t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") + } + if assert.Contains("Hello World", "Salut") { + t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") + } + + if !assert.Contains(list, "Foo") { + t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + if assert.Contains(list, "Salut") { + t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") + } + +} + +func TestNotContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.NotContains("Hello World", "Hello!") { + t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") + } + if assert.NotContains("Hello World", "Hello") { + t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") + } + + if !assert.NotContains(list, "Foo!") { + t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") + } + if assert.NotContains(list, "Foo") { + t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + +} + +func TestConditionWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Condition(func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if assert.Condition(func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanicWrapper(t *testing.T) { + + if funcDidPanic, _ := didPanic(func() { + panic("Panic!") + }); !funcDidPanic { + t.Error("didPanic should return true") + } + + if funcDidPanic, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Panics(func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if assert.Panics(func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestNotPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotPanics(func() { + }) { + t.Error("NotPanics should return true") + } + + if assert.NotPanics(func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.True(mockAssert.NoError(err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.False(mockAssert.NoError(err), "NoError with error should return False") + +} + +func TestErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.False(mockAssert.Error(err), "Error should return False for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.True(mockAssert.Error(err), "Error with error should return True") + +} + +func TestEqualErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + assert.False(mockAssert.EqualError(err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + assert.False(mockAssert.EqualError(err, "Not some error"), + "EqualError should return false for different error string") + assert.True(mockAssert.EqualError(err, "some error"), + "EqualError should return true") +} + +func TestEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.Empty(""), "Empty string is empty") + assert.True(mockAssert.Empty(nil), "Nil is empty") + assert.True(mockAssert.Empty([]string{}), "Empty string array is empty") + assert.True(mockAssert.Empty(0), "Zero int value is empty") + assert.True(mockAssert.Empty(false), "False value is empty") + + assert.False(mockAssert.Empty("something"), "Non Empty string is not empty") + assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty") + assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty") + assert.False(mockAssert.Empty(1), "Non-zero int value is not empty") + assert.False(mockAssert.Empty(true), "True value is not empty") + +} + +func TestNotEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.NotEmpty(""), "Empty string is empty") + assert.False(mockAssert.NotEmpty(nil), "Nil is empty") + assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty") + assert.False(mockAssert.NotEmpty(0), "Zero int value is empty") + assert.False(mockAssert.NotEmpty(false), "False value is empty") + + assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty") + assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty") + assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty") + assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty") + assert.True(mockAssert.NotEmpty(true), "True value is not empty") + +} + +func TestLenWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.Len(nil, 0), "nil does not have length") + assert.False(mockAssert.Len(0, 0), "int does not have length") + assert.False(mockAssert.Len(true, 0), "true does not have length") + assert.False(mockAssert.Len(false, 0), "false does not have length") + assert.False(mockAssert.Len('A', 0), "Rune does not have length") + assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDurationWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + a := time.Now() + b := a.Add(10 * time.Second) + + assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestInDeltaWrapper(t *testing.T) { + assert := New(new(testing.T)) + + True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1") + False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInEpsilonWrapper(t *testing.T) { + assert := New(new(testing.T)) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + } + + for _, tc := range cases { + True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + } + + for _, tc := range cases { + False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } +} + +func TestRegexpWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, assert.Regexp(tc.rx, tc.str)) + True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + False(t, assert.NotRegexp(tc.rx, tc.str)) + False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + True(t, assert.NotRegexp(tc.rx, tc.str)) + True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } +} + +func TestZeroWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + for _, test := range zeros { + assert.True(mockAssert.Zero(test), "Zero should return true for %v", test) + } + + for _, test := range nonZeros { + assert.False(mockAssert.Zero(test), "Zero should return false for %v", test) + } +} + +func TestNotZeroWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + for _, test := range zeros { + assert.False(mockAssert.NotZero(test), "Zero should return true for %v", test) + } + + for _, test := range nonZeros { + assert.True(mockAssert.NotZero(test), "Zero should return false for %v", test) + } +} + +func TestJSONEqWrapper_EqualSONString(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") { + t.Error("JSONEq should return true") + } +} + +func TestJSONEqWrapper_Array(t *testing.T) { + assert := New(new(testing.T)) + if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { + t.Error("JSONEq should return true") + } + +} + +func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq("Not JSON", "Not JSON") { + t.Error("JSONEq should return false") + } +} + +func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + assert := New(new(testing.T)) + if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { + t.Error("JSONEq should return false") + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions.go b/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions.go new file mode 100644 index 00000000..799bd0ea --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions.go @@ -0,0 +1,129 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" +) + +// httpCode is a helper that returns HTTP code of the response. It returns -1 +// if building a new request fails. +func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return -1 + } + handler(w, req) + return w.Code +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusOK && code <= http.StatusPartialContent +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusBadRequest +} + +// HTTPBody is a helper that returns HTTP body of the response. It returns +// empty string if building a new request fails. +func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return "" + } + handler(w, req) + return w.Body.String() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if !contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return contains +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if contains { + Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body) + } + + return !contains +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions_test.go b/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions_test.go new file mode 100644 index 00000000..fa80a1e4 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/assert/http_assertions_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package assert + +import ( + "fmt" + "net/http" + "net/url" + "testing" +) + +func httpOK(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func httpRedirect(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTemporaryRedirect) +} + +func httpError(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) +} + +func TestHTTPStatuses(t *testing.T) { + assert := New(t) + mockT := new(testing.T) + + assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true) + assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false) + assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false) + + assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false) + assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true) + assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false) + + assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false) + assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false) + assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true) +} + +func TestHTTPStatusesWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true) +} + +func httpHelloName(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + w.Write([]byte(fmt.Sprintf("Hello, %s!", name))) +} + +func TestHttpBody(t *testing.T) { + assert := New(t) + mockT := new(testing.T) + + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) +} + +func TestHttpBodyWrappers(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/call.go b/vendor/src/github.com/crossdock/crossdock-go/call.go new file mode 100644 index 00000000..7d6369d2 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/call.go @@ -0,0 +1,65 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "encoding/json" + "log" + "net/url" + "testing" + "time" + + "github.com/crossdock/crossdock-go/require" + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" +) + +// Call makes a GET request to the client +func Call(t *testing.T, clientURL string, behavior string, args url.Values) { + args.Set("behavior", behavior) + u, err := url.Parse(clientURL) + require.NoError(t, err, "failed to parse URL") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + u.RawQuery = args.Encode() + log.Println("GET", u.String()) + res, err := ctxhttp.Get(ctx, nil, u.String()) + + require.NoError(t, err, "request %v failed", args) + defer res.Body.Close() + + var results []Entry + require.NoError(t, json.NewDecoder(res.Body).Decode(&results), + "failed to decode response for %v", args) + + for _, result := range results { + if result.Status() != Passed && result.Status() != Skipped { + output := result.Output() + + delete(result, statusKey) + delete(result, outputKey) + + t.Errorf("request %v failed: tags: %v; output: %s", args, result, output) + } + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/client.go b/vendor/src/github.com/crossdock/crossdock-go/client.go new file mode 100644 index 00000000..2fcbc358 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/client.go @@ -0,0 +1,91 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "encoding/json" + "log" + "net/http" + "net/url" +) + +// BehaviorFunc executes a set of tests against T +type BehaviorFunc func(t T) + +// Behaviors is a map of BehaviorFuncs to dispatch to +type Behaviors map[string]BehaviorFunc + +// BehaviorParam is the url param representing the test to run +const BehaviorParam = "behavior" + +// Start begins a blocking Crossdock client +func Start(behaviors Behaviors) { + http.Handle("/", Handler(behaviors, false)) + log.Fatal(http.ListenAndServe(":8080", nil)) +} + +// Handler returns an http.Handler that dispatches to functions defined in behaviors map. +// If failOnUnknown is true, a behavior that is not in the behaviors map will cause the +// handler to return an error status. Otherwise, the handler returns skipped status. +func Handler(behaviors Behaviors, failOnUnknown bool) http.Handler { + return requestHandler{behaviors: behaviors, failOnUnknown: failOnUnknown} +} + +type requestHandler struct { + behaviors Behaviors + failOnUnknown bool +} + +func (h requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Add("Content-Length", "0") + return + } + params := extractParams(r.URL.Query()) + entries := Run(params, func(t T) { + behavior := h.behaviors[t.Behavior()] + if behavior == nil { + if h.failOnUnknown { + t.Errorf("unknown behavior %q", t.Behavior()) + } else { + t.Skipf("unknown behavior %q", t.Behavior()) + } + return + } + behavior(t) + }) + + enc := json.NewEncoder(w) + if err := enc.Encode(entries); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +// extractParams returns a map of params from url values +func extractParams(p url.Values) Params { + params := Params{} + for k, l := range p { + for _, v := range l { + params[k] = v + } + } + return params +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/client_test.go b/vendor/src/github.com/crossdock/crossdock-go/client_test.go new file mode 100644 index 00000000..d3e2d611 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/client_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/crossdock/crossdock-go/assert" + "github.com/crossdock/crossdock-go/require" +) + +func TestHandler(t *testing.T) { + tests := []struct { + failOnUnknown bool + status string + }{ + {false, "skipped"}, + {true, "failed"}, + } + + for _, test := range tests { + testHandler(t, test.failOnUnknown, test.status) + } +} + +func testHandler(t *testing.T, failOnUnknown bool, status string) { + behaviors := Behaviors{ + "b1": func(t T) { + t.Successf("ok") + }, + } + + server := httptest.NewServer(Handler(behaviors, failOnUnknown)) + defer server.Close() + + runTestCase(t, server.URL, "b1", map[string]string{ + "status": "passed", + "output": "ok", + }) + + runTestCase(t, server.URL, "b2", map[string]string{ + "status": status, + "output": `unknown behavior "b2"`, + }) +} + +func runTestCase(t *testing.T, url string, behavior string, expectation map[string]string) { + res, err := http.Get(fmt.Sprintf("%s/?behavior=%s", url, behavior)) + require.NoError(t, err) + + defer res.Body.Close() + + var answer []map[string]string + err = json.NewDecoder(res.Body).Decode(&answer) + require.NoError(t, err) + + assert.Equal(t, []map[string]string{expectation}, answer) +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/combinations.go b/vendor/src/github.com/crossdock/crossdock-go/combinations.go new file mode 100644 index 00000000..f3d8b118 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/combinations.go @@ -0,0 +1,76 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import "sort" + +// Combinations takes a map from axis name to list of values for that axis and +// returns a collection of entries which contain all combinations of each axis +// value with every other axis' values. +func Combinations(axes map[string][]string) []map[string]string { + // sort the names to get deterministic ordering + names := make([]string, 0, len(axes)) + for name := range axes { + names = append(names, name) + } + sort.Strings(names) + + var xs []axis + for _, name := range names { + xs = append(xs, axis{name, axes[name]}) + } + return axisCombinations(xs) +} + +func axisCombinations(axes []axis) []map[string]string { + if len(axes) == 0 { + return nil + } + + if len(axes) == 1 { + return axes[0].entries() + } + + var entries []map[string]string + for _, remaining := range axisCombinations(axes[1:]) { + for _, entry := range axes[0].entries() { + for k, v := range remaining { + entry[k] = v + } + entries = append(entries, entry) + } + } + + return entries +} + +type axis struct { + name string + values []string +} + +func (x axis) entries() []map[string]string { + items := make([]map[string]string, len(x.values)) + for i, value := range x.values { + items[i] = map[string]string{x.name: value} + } + return items +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/combinations_test.go b/vendor/src/github.com/crossdock/crossdock-go/combinations_test.go new file mode 100644 index 00000000..bdecf20a --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/combinations_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "testing" + + "github.com/crossdock/crossdock-go/assert" +) + +func TestCombinations(t *testing.T) { + tests := []struct { + axes map[string][]string + want []map[string]string + }{ + { + axes: nil, + want: nil, + }, + { + axes: map[string][]string{}, + want: nil, + }, + { + axes: map[string][]string{ + "x": {"1", "2"}, + }, + want: []map[string]string{ + {"x": "1"}, + {"x": "2"}, + }, + }, + { + axes: map[string][]string{ + "x": {"1", "2"}, + "y": {"3", "4"}, + }, + want: []map[string]string{ + {"x": "1", "y": "3"}, + {"x": "2", "y": "3"}, + {"x": "1", "y": "4"}, + {"x": "2", "y": "4"}, + }, + }, + { + axes: map[string][]string{ + "x": {"1", "2"}, + "y": {"3", "4"}, + "z": {"5", "6"}, + }, + want: []map[string]string{ + {"x": "1", "y": "3", "z": "5"}, + {"x": "2", "y": "3", "z": "5"}, + {"x": "1", "y": "4", "z": "5"}, + {"x": "2", "y": "4", "z": "5"}, + {"x": "1", "y": "3", "z": "6"}, + {"x": "2", "y": "3", "z": "6"}, + {"x": "1", "y": "4", "z": "6"}, + {"x": "2", "y": "4", "z": "6"}, + }, + }, + } + + for _, tt := range tests { + assert.Equal(t, tt.want, Combinations(tt.axes)) + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/doc.go b/vendor/src/github.com/crossdock/crossdock-go/doc.go new file mode 100644 index 00000000..c6d71606 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/doc.go @@ -0,0 +1,37 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package crossdock implements the machinery for writing behaviors in a way +// similar to unit tests. +// +// func MyBehavior(s behavior.Sink, p behavior.Params) { +// if p.Param("something") != "foo" { +// behavior.Failf(s, "expected foo, got %v", p.Param("something")) +// } +// behavior.Successf(s, "success") +// } +// +// Sinks can be obtained using the Run function. Sinks are not valid outside +// the Run context. +// +// entries := behavior.Run(func(s Sink) { +// MyBehavior(s, someParams) +// }) +package crossdock diff --git a/vendor/src/github.com/crossdock/crossdock-go/entry.go b/vendor/src/github.com/crossdock/crossdock-go/entry.go new file mode 100644 index 00000000..779ecbd3 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/entry.go @@ -0,0 +1,62 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import "fmt" + +// Status represents the result of running a behavior. +type Status string + +// Different valid Statuses. +const ( + Passed Status = "passed" + Skipped Status = "skipped" + Failed Status = "failed" +) + +const ( + statusKey = "status" + outputKey = "output" +) + +// Entry is the most basic form of a test result. +type Entry map[string]interface{} + +// Status returns the Status stored in the Entry. +func (e Entry) Status() Status { + switch v := e[statusKey].(type) { + case string: + return Status(v) + case Status: + return v + default: + panic(fmt.Sprintf("invalid status: %v", v)) + } +} + +// Output returns the output attached to the entry. +func (e Entry) Output() string { + s, ok := e[outputKey].(string) + if ok { + return s + } + return "" +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/glide.lock b/vendor/src/github.com/crossdock/crossdock-go/glide.lock new file mode 100644 index 00000000..51e6e7ab --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/glide.lock @@ -0,0 +1,17 @@ +hash: a970fa7a49e831327ca2bfda44d071afa1641d718a1a884d2aa62b9fca73fed0 +updated: 2016-07-28T11:01:21.702887088-07:00 +imports: +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: 792786c7400a136282c1664665ae0a8db921c6c2 + subpackages: + - difflib +- name: golang.org/x/net + version: 6a513affb38dc9788b449d59ffed099b8de18fa0 + subpackages: + - context + - context/ctxhttp +testImports: [] diff --git a/vendor/src/github.com/crossdock/crossdock-go/glide.yaml b/vendor/src/github.com/crossdock/crossdock-go/glide.yaml new file mode 100644 index 00000000..8c2f0366 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/glide.yaml @@ -0,0 +1,9 @@ +package: github.com/crossdock/crossdock-go +import: +- package: golang.org/x/net + subpackages: + - context + - context/ctxhttp +- package: github.com/davecgh/go-spew + subpackages: + - spew diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/doc.go b/vendor/src/github.com/crossdock/crossdock-go/require/doc.go new file mode 100644 index 00000000..0da254c9 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/doc.go @@ -0,0 +1,51 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Package require implements the same assertions as the `assert` package but +// stops test execution when a test fails. +// +// Example Usage +// +// The following is a complete example using require in a standard test function: +// import ( +// "testing" +// "github.com/crossdock/crossdock-go/require" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// require.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// Assertions +// +// The `require` package have same global functions as in the `assert` package, +// but instead of returning a boolean result they call `t.FailNow()`. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package require diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements.go b/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements.go new file mode 100644 index 00000000..3a25b246 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package require + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements_test.go b/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements_test.go new file mode 100644 index 00000000..838cc591 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/forward_requirements_test.go @@ -0,0 +1,408 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package require + +import ( + "errors" + "testing" + "time" +) + +func TestImplementsWrapper(t *testing.T) { + require := New(t) + + require.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestIsTypeWrapper(t *testing.T) { + require := New(t) + require.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualWrapper(t *testing.T) { + require := New(t) + require.Equal(1, 1) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Equal(1, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEqualWrapper(t *testing.T) { + require := New(t) + require.NotEqual(1, 2) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotEqual(2, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestExactlyWrapper(t *testing.T) { + require := New(t) + + a := float32(1) + b := float32(1) + c := float64(1) + + require.Exactly(a, b) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Exactly(a, c) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotNilWrapper(t *testing.T) { + require := New(t) + require.NotNil(t, new(AssertionTesterConformingObject)) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotNil(nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNilWrapper(t *testing.T) { + require := New(t) + require.Nil(nil) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Nil(new(AssertionTesterConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestTrueWrapper(t *testing.T) { + require := New(t) + require.True(true) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.True(false) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestFalseWrapper(t *testing.T) { + require := New(t) + require.False(false) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.False(true) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestContainsWrapper(t *testing.T) { + require := New(t) + require.Contains("Hello World", "Hello") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Contains("Hello World", "Salut") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotContainsWrapper(t *testing.T) { + require := New(t) + require.NotContains("Hello World", "Hello!") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotContains("Hello World", "Hello") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestPanicsWrapper(t *testing.T) { + require := New(t) + require.Panics(func() { + panic("Panic!") + }) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Panics(func() {}) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotPanicsWrapper(t *testing.T) { + require := New(t) + require.NotPanics(func() {}) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotPanics(func() { + panic("Panic!") + }) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNoErrorWrapper(t *testing.T) { + require := New(t) + require.NoError(nil) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NoError(errors.New("some error")) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestErrorWrapper(t *testing.T) { + require := New(t) + require.Error(errors.New("some error")) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Error(nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualErrorWrapper(t *testing.T) { + require := New(t) + require.EqualError(errors.New("some error"), "some error") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.EqualError(errors.New("some error"), "Not some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEmptyWrapper(t *testing.T) { + require := New(t) + require.Empty("") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Empty("x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEmptyWrapper(t *testing.T) { + require := New(t) + require.NotEmpty("x") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotEmpty("") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestWithinDurationWrapper(t *testing.T) { + require := New(t) + a := time.Now() + b := a.Add(10 * time.Second) + + require.WithinDuration(a, b, 15*time.Second) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.WithinDuration(a, b, 5*time.Second) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestInDeltaWrapper(t *testing.T) { + require := New(t) + require.InDelta(1.001, 1, 0.01) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.InDelta(1, 2, 0.5) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestZeroWrapper(t *testing.T) { + require := New(t) + require.Zero(0) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.Zero(1) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotZeroWrapper(t *testing.T) { + require := New(t) + require.NotZero(1) + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.NotZero(0) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_EqualSONString(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_Array(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`{"foo": "bar"}`, "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq("Not JSON", "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + mockRequire := New(mockT) + + mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/require.go b/vendor/src/github.com/crossdock/crossdock-go/require/require.go new file mode 100644 index 00000000..e099ec2c --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/require.go @@ -0,0 +1,1338 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/crossdock/crossdock-go/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND +*/ + +package require + +import ( + + assert "github.com/crossdock/crossdock-go/assert" + http "net/http" + url "net/url" + time "time" +) + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { + if !assert.Condition(t, comp, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") +// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if !assert.Contains(t, s, contains, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +// +// Returns whether the assertion was successful (true) or not (false). +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if !assert.Empty(t, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.Equal(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { + if !assert.EqualError(t, theError, errString, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.EqualValues(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func Error(t TestingT, err error, msgAndArgs ...interface{}) { + if !assert.Error(t, err, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Exactly asserts that two objects are equal is value and type. +// +// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.Exactly(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if !assert.Fail(t, failureMessage, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if !assert.FailNow(t, failureMessage, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// False asserts that the specified value is false. +// +// assert.False(t, myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func False(t TestingT, value bool, msgAndArgs ...interface{}) { + if !assert.False(t, value, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { + if !assert.HTTPBodyContains(t, handler, method, url, values, str) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { + if !assert.HTTPBodyNotContains(t, handler, method, url, values, str) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { + if !assert.HTTPError(t, handler, method, url, values) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { + if !assert.HTTPRedirect(t, handler, method, url, values) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { + if !assert.HTTPSuccess(t, handler, method, url, values) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if !assert.Implements(t, interfaceObject, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if !assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if !assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if !assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if !assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if !assert.IsType(t, expectedType, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if !assert.JSONEq(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { + if !assert.Len(t, object, length, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if !assert.Nil(t, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NoError(t TestingT, err error, msgAndArgs ...interface{}) { + if !assert.NoError(t, err, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if !assert.NotContains(t, s, contains, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if !assert.NotEmpty(t, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.NotEqual(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { + if !assert.NotNil(t, object, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if !assert.NotPanics(t, f, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if !assert.NotRegexp(t, rx, str, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// NotZero asserts that i is not the zero value for its type and returns the truth. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if !assert.NotZero(t, i, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { + if !assert.Panics(t, f, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { + if !assert.Regexp(t, rx, str, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// True asserts that the specified value is true. +// +// assert.True(t, myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func True(t TestingT, value bool, msgAndArgs ...interface{}) { + if !assert.True(t, value, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if !assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + + +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Zero asserts that i is the zero value for its type and returns the truth. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if !assert.Zero(t, i, msgAndArgs...) { + t.FailNow() + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/require.go.tmpl b/vendor/src/github.com/crossdock/crossdock-go/require/require.go.tmpl new file mode 100644 index 00000000..d2e75757 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/require.go.tmpl @@ -0,0 +1,29 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +{{.Comment}} +func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { + if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { + t.FailNow() + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go b/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go new file mode 100644 index 00000000..80097062 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go @@ -0,0 +1,388 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/crossdock/crossdock-go/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND +*/ + +package require + +import ( + + assert "github.com/crossdock/crossdock-go/assert" + http "net/http" + url "net/url" + time "time" +) + + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { + Condition(a.t, comp, msgAndArgs...) +} + + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") +// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + Contains(a.t, s, contains, msgAndArgs...) +} + + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { + Empty(a.t, object, msgAndArgs...) +} + + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + Equal(a.t, expected, actual, msgAndArgs...) +} + + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { + EqualError(a.t, theError, errString, msgAndArgs...) +} + + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + EqualValues(a.t, expected, actual, msgAndArgs...) +} + + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { + Error(a.t, err, msgAndArgs...) +} + + +// Exactly asserts that two objects are equal is value and type. +// +// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + Exactly(a.t, expected, actual, msgAndArgs...) +} + + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { + Fail(a.t, failureMessage, msgAndArgs...) +} + + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { + FailNow(a.t, failureMessage, msgAndArgs...) +} + + +// False asserts that the specified value is false. +// +// a.False(myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { + False(a.t, value, msgAndArgs...) +} + + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { + HTTPBodyContains(a.t, handler, method, url, values, str) +} + + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { + HTTPBodyNotContains(a.t, handler, method, url, values, str) +} + + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) { + HTTPError(a.t, handler, method, url, values) +} + + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) { + HTTPRedirect(a.t, handler, method, url, values) +} + + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) { + HTTPSuccess(a.t, handler, method, url, values) +} + + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + Implements(a.t, interfaceObject, object, msgAndArgs...) +} + + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + IsType(a.t, expectedType, object, msgAndArgs...) +} + + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { + JSONEq(a.t, expected, actual, msgAndArgs...) +} + + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { + Len(a.t, object, length, msgAndArgs...) +} + + +// Nil asserts that the specified object is nil. +// +// a.Nil(err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { + Nil(a.t, object, msgAndArgs...) +} + + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { + NoError(a.t, err, msgAndArgs...) +} + + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + NotContains(a.t, s, contains, msgAndArgs...) +} + + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { + NotEmpty(a.t, object, msgAndArgs...) +} + + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + NotEqual(a.t, expected, actual, msgAndArgs...) +} + + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { + NotNil(a.t, object, msgAndArgs...) +} + + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + NotPanics(a.t, f, msgAndArgs...) +} + + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + NotRegexp(a.t, rx, str, msgAndArgs...) +} + + +// NotZero asserts that i is not the zero value for its type and returns the truth. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { + NotZero(a.t, i, msgAndArgs...) +} + + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + Panics(a.t, f, msgAndArgs...) +} + + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { + Regexp(a.t, rx, str, msgAndArgs...) +} + + +// True asserts that the specified value is true. +// +// a.True(myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { + True(a.t, value, msgAndArgs...) +} + + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + + +// Zero asserts that i is the zero value for its type and returns the truth. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { + Zero(a.t, i, msgAndArgs...) +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go.tmpl b/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go.tmpl new file mode 100644 index 00000000..b93569e0 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/require_forward.go.tmpl @@ -0,0 +1,4 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { + {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/requirements.go b/vendor/src/github.com/crossdock/crossdock-go/require/requirements.go new file mode 100644 index 00000000..09c482fa --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/requirements.go @@ -0,0 +1,32 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package require + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) + FailNow() +} + +//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl diff --git a/vendor/src/github.com/crossdock/crossdock-go/require/requirements_test.go b/vendor/src/github.com/crossdock/crossdock-go/require/requirements_test.go new file mode 100644 index 00000000..e593883e --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/require/requirements_test.go @@ -0,0 +1,392 @@ +// Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +// +// Please consider promoting this project if you find it useful. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +package require + +import ( + "errors" + "testing" + "time" +) + +// AssertionTesterInterface defines an interface to be used for testing assertion methods +type AssertionTesterInterface interface { + TestMethod() +} + +// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface +type AssertionTesterConformingObject struct { +} + +func (a *AssertionTesterConformingObject) TestMethod() { +} + +// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface +type AssertionTesterNonConformingObject struct { +} + +type MockT struct { + Failed bool +} + +func (t *MockT) FailNow() { + t.Failed = true +} + +func (t *MockT) Errorf(format string, args ...interface{}) { + _, _ = format, args +} + +func TestImplements(t *testing.T) { + + Implements(t, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestIsType(t *testing.T) { + + IsType(t, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) + + mockT := new(MockT) + IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqual(t *testing.T) { + + Equal(t, 1, 1) + + mockT := new(MockT) + Equal(mockT, 1, 2) + if !mockT.Failed { + t.Error("Check should fail") + } + +} + +func TestNotEqual(t *testing.T) { + + NotEqual(t, 1, 2) + mockT := new(MockT) + NotEqual(mockT, 2, 2) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestExactly(t *testing.T) { + + a := float32(1) + b := float32(1) + c := float64(1) + + Exactly(t, a, b) + + mockT := new(MockT) + Exactly(mockT, a, c) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotNil(t *testing.T) { + + NotNil(t, new(AssertionTesterConformingObject)) + + mockT := new(MockT) + NotNil(mockT, nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNil(t *testing.T) { + + Nil(t, nil) + + mockT := new(MockT) + Nil(mockT, new(AssertionTesterConformingObject)) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestTrue(t *testing.T) { + + True(t, true) + + mockT := new(MockT) + True(mockT, false) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestFalse(t *testing.T) { + + False(t, false) + + mockT := new(MockT) + False(mockT, true) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestContains(t *testing.T) { + + Contains(t, "Hello World", "Hello") + + mockT := new(MockT) + Contains(mockT, "Hello World", "Salut") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotContains(t *testing.T) { + + NotContains(t, "Hello World", "Hello!") + + mockT := new(MockT) + NotContains(mockT, "Hello World", "Hello") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestPanics(t *testing.T) { + + Panics(t, func() { + panic("Panic!") + }) + + mockT := new(MockT) + Panics(mockT, func() {}) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotPanics(t *testing.T) { + + NotPanics(t, func() {}) + + mockT := new(MockT) + NotPanics(mockT, func() { + panic("Panic!") + }) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNoError(t *testing.T) { + + NoError(t, nil) + + mockT := new(MockT) + NoError(mockT, errors.New("some error")) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestError(t *testing.T) { + + Error(t, errors.New("some error")) + + mockT := new(MockT) + Error(mockT, nil) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEqualError(t *testing.T) { + + EqualError(t, errors.New("some error"), "some error") + + mockT := new(MockT) + EqualError(mockT, errors.New("some error"), "Not some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestEmpty(t *testing.T) { + + Empty(t, "") + + mockT := new(MockT) + Empty(mockT, "x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotEmpty(t *testing.T) { + + NotEmpty(t, "x") + + mockT := new(MockT) + NotEmpty(mockT, "") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestWithinDuration(t *testing.T) { + + a := time.Now() + b := a.Add(10 * time.Second) + + WithinDuration(t, a, b, 15*time.Second) + + mockT := new(MockT) + WithinDuration(mockT, a, b, 5*time.Second) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestInDelta(t *testing.T) { + + InDelta(t, 1.001, 1, 0.01) + + mockT := new(MockT) + InDelta(mockT, 1, 2, 0.5) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestZero(t *testing.T) { + + Zero(t, "") + + mockT := new(MockT) + Zero(mockT, "x") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestNotZero(t *testing.T) { + + NotZero(t, "x") + + mockT := new(MockT) + NotZero(mockT, "") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_EqualSONString(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_EquivalentButNotEqual(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_HashOfArraysAndHashes(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", + "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_Array(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mockT.Failed { + t.Error("Check should pass") + } +} + +func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_HashesNotEquivalent(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ActualIsNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `{"foo": "bar"}`, "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ExpectedIsNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`) + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, "Not JSON", "Not JSON") + if !mockT.Failed { + t.Error("Check should fail") + } +} + +func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) { + mockT := new(MockT) + JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mockT.Failed { + t.Error("Check should fail") + } +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/run.go b/vendor/src/github.com/crossdock/crossdock-go/run.go new file mode 100644 index 00000000..bd4ff334 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/run.go @@ -0,0 +1,80 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "runtime" + "runtime/debug" +) + +// Run the given function inside a behavior context and return the entries +// logged by it. +// +// Functions like Fatalf won't work if the behavior is not executed inside a +// Run context. +func Run(params Params, f func(T)) []Entry { + behavior := params[BehaviorParam] + delete(params, BehaviorParam) + t := entryT{ + params: params, + behavior: behavior, + } + + done := make(chan struct{}) + + // We run the function inside a goroutine so that Fatalf can simply call + // runtime.Goexit to stop execution. + go func() { + defer func() { + if err := recover(); err != nil { + t.Errorf("%v\n%s", err, string(debug.Stack())) + } + close(done) + }() + + if runtime.Version() == "go1.5" { + // Gnarly workaround for https://github.com/golang/go/issues/12253 + // + // In short: A bug in Go 1.5 causes a specific form of comparison + // (runtime.assertE2T2) to leave an invalid pointer on the stack instead + // of zeroing it out. Usually, this isn't a problem because the function + // returns afterwards. However, we're consistently hitting a case where + // another function call is causing the stack to be grown while the + // pointer is still invalid. The scanner responsible for copying the + // stack as part of growing it runs into the invalid pointer and + // crashes. + // + // To work around this, we grow the stack significantly beforehand to + // reduce the likelihood of another growth attempt while the pointer is + // invalid. + growStack([1024]int64{}) + } + + f(&t) + }() + + <-done + return t.entries +} + +func growStack([1024]int64) { + // Nothing to do +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/run_test.go b/vendor/src/github.com/crossdock/crossdock-go/run_test.go new file mode 100644 index 00000000..88a6d584 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/run_test.go @@ -0,0 +1,52 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "testing" + + "github.com/crossdock/crossdock-go/assert" +) + +func TestRunRecordsFailureOnFatal(t *testing.T) { + tests := []struct { + f func(T) + output string + }{ + { + func(ct T) { ct.Fatalf("great sadness") }, + "great sadness", + }, + { + func(s T) { panic("aaaahh") }, + "aaaahh", + }, + } + + for _, tt := range tests { + entries := Run(nil, tt.f) + assert.Len(t, entries, 1) + entry := entries[0] + assert.Equal(t, Failed, entry.Status()) + assert.Contains(t, entry.Output(), tt.output) + } + +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/scripts/cover.sh b/vendor/src/github.com/crossdock/crossdock-go/scripts/cover.sh new file mode 100644 index 00000000..3d33d48a --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/scripts/cover.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +COVER=cover +ROOT_PKG=github.com/crossdock/crossdock-go/ + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | map(select(startswith("'"$ROOT_PKG"'"))) + | map(select(contains("/vendor/") | not)) + | . + ["'"$pkg"'"] + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + go test \ + -coverprofile "$COVER/cover.${i}.out" -coverpkg "$coverpkg" \ + -v "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/github.com/crossdock/crossdock-go/t.go b/vendor/src/github.com/crossdock/crossdock-go/t.go new file mode 100644 index 00000000..db05b190 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/t.go @@ -0,0 +1,145 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "fmt" + "runtime" +) + +// T records the result of calling different behaviors. +type T interface { + Behavior() string + + // Look up a behavior parameter. + Param(key string) string + + // Tag adds the given key-value pair to all entries emitted by this T from + // this point onwards. + // + // If a key with the same name already exists, it will be overwritten. + // If value is empty, the tag will be deleted from all entries that follow. + // + // key MUST NOT be "stauts" or "output". + Tag(key, value string) + + // Log a failure and continue running the behavior. + Errorf(format string, args ...interface{}) + + // Log a skipped test and continue running the behavior. + Skipf(format string, args ...interface{}) + + // Log a success and continue running the behavior. + Successf(format string, args ...interface{}) + + // Log a failure and stop executing this behavior immediately. + Fatalf(format string, args ...interface{}) + + // Stop executing this behavior immediately. + FailNow() + + // Put logs an entry with the given status and output. Usually, you'll want + // to use Errorf, Skipf, Successf or Fatalf instead. + Put(status Status, output string) +} + +// Params represents args to a test +type Params map[string]string + +// entryT is a sink that keeps track of entries in-order +type entryT struct { + behavior string + params Params + tags map[string]string + entries []Entry +} + +// Behavior returns the test to dispatch on +func (t entryT) Behavior() string { + return t.behavior +} + +// Param gets a key out of the params map +func (t entryT) Param(key string) string { + return t.params[key] +} + +func (t *entryT) Tag(key, value string) { + if key == statusKey || key == outputKey { + panic(fmt.Sprintf("tag %q is reserved", key)) + } + + if t.tags == nil { + t.tags = make(map[string]string) + } + + if value == "" { + delete(t.tags, key) + } else { + t.tags[key] = value + } +} + +func (t *entryT) Put(status Status, output string) { + e := make(Entry) + e[statusKey] = status + e[outputKey] = output + for k, v := range t.tags { + e[k] = v + } + + t.entries = append(t.entries, e) +} + +func (*entryT) FailNow() { + // Exit this goroutine and call any deferred functions + runtime.Goexit() +} + +// Skipf records a skipped test. +// +// This may be called multiple times if multiple tests inside a behavior were +// skipped. +func (t *entryT) Skipf(format string, args ...interface{}) { + t.Put(Skipped, fmt.Sprintf(format, args...)) +} + +// Errorf records a failed test. +// +// This may be called multiple times if multiple tests inside a behavior +// failed. +func (t *entryT) Errorf(format string, args ...interface{}) { + t.Put(Failed, fmt.Sprintf(format, args...)) +} + +// Successf records a successful test. +// +// This may be called multiple times for multiple successful tests inside a +// behavior. +func (t *entryT) Successf(format string, args ...interface{}) { + t.Put(Passed, fmt.Sprintf(format, args...)) +} + +// Fatalf records a failed test and fails immediately +func (t *entryT) Fatalf(format string, args ...interface{}) { + t.Errorf(format, args...) + t.FailNow() +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/testify.go b/vendor/src/github.com/crossdock/crossdock-go/testify.go new file mode 100644 index 00000000..7d6f9e1a --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/testify.go @@ -0,0 +1,640 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "fmt" + "time" + + "github.com/crossdock/crossdock-go/assert" + "github.com/crossdock/crossdock-go/require" +) + +// Assert builds an Assertions object that logs success or failure for all +// operations to the given T. The behavior will continue executing in case of +// failure. +// +// The following will log exactly len(tests) entries. +// +// assert := Assert(t) +// for _, tt := range tests { +// assert.Equals(tt.want, f(tt.give), "expected f(%v) == %v", tt.give, tt.want) +// } +func Assert(t T) Assertions { + return sinkAssertions{t, assert.New(sinkTestingT{t})} +} + +// Checks builds an Assertions object that logs only failures to the given T. +// The behavior will continue executing in case of failure. +// +// The following will log only as many entries as invalid test cases. +// +// checks := Checks(t) +// for _, tt := range tests { +// checks.Equals(tt.want, f(tt.give), "expected f(%v) == %v", tt.give, tt.want) +// } +func Checks(t T) Assertions { + return assert.New(sinkTestingT{t}) +} + +// Require builds an Assertions object that logs success or failure for all +// operations to the given T. Execution of the behavior will be terminated +// immediately on the first failing assertion. +// +// The following will log one entry for each successful test case starting at +// the first one and the first failure that is encountered. +// +// require := Require(t) +// for _, tt := range tests { +// require.Equals(tt.want, f(tt.give), "expected f(%v) == %v", tt.give, tt.want) +// } +func Require(t T) Assertions { + return sinkAssertions{t, requireAssertions{require.New(sinkTestingT{t})}} +} + +// Fatals builds an Assertions object that logs only failures to the given +// T. Execution of the behavior will be terminated immediately on the first +// failing assertion. +// +// The following will log the first failure encountered or nothing if all test +// cases were succesful. +// +// fatals := Fatals(t) +// for _, tt := range tests { +// fatals.Equals(tt.want, f(tt.give), "expected f(%v) == %v", tt.give, tt.want) +// } +func Fatals(t T) Assertions { + return requireAssertions{require.New(sinkTestingT{t})} +} + +// sinkTestingT adapts a crossdock.T into an {require,assert}.TestingT +type sinkTestingT struct{ t T } + +func (st sinkTestingT) FailNow() { st.t.FailNow() } + +func (st sinkTestingT) Errorf(format string, args ...interface{}) { + // We need to prepend a newline because the error message from testify + // always includes a \r at the start. + st.t.Errorf("\n"+format, args...) +} + +////////////////////////////////////////////////////////////////////////////// +// Assertions + +// Assertions provides helpers to assert conditions in crossdock behaviors. +// +// All assertions can include informative error messages formatted using +// fmt.Sprintf style, +// +// assert := Assert(t) +// assert.Contains(foo, "bar", "expected to find 'bar' in %q", foo) +// +// All assert operations return true if the condition was met and false +// otherwise. This allows gating operations that would otherwise panic behind +// preconditions. +// +// if assert.Error(t, err, "expected failure") { +// assert.Contains(t, err.Error(), "something went wrong", "error message mismatch") +// } +// +// Additionally, in case of failure, all Assertions make an attempt to +// provide a stack trace in the error message. +// +// Four kinds of Assertions objects are offered via the corresponding +// functions: +// +// Assert(T): All asserts will result in a success or failure being logged to the +// crossdock.T. Execution will continue on failure. +// +// Checks(T): Only failures will be logged to crossdock.T. Execution will +// continue on failure. +// +// Require(T): All asserts will result in a success or failure being logged to +// the crossdock.T. Execution of the behavior will be terminated immediately +// on failure. +// +// Fatals(t): Only failures will be logged to crossdock.T. Execution of the +// behavior will be temrinated immediately on failure. +// +// +--------+--------+---------+--------+ +// | Assert | Checks | Require | Fatals | +// +--------------------+--------+--------+---------+--------+ +// | Log on success | Yes | No | Yes | No | +// +--------------------+--------+--------+---------+--------+ +// | Continue execution | Yes | Yes | No | No | +// | on failure | | | | | +// +--------------------+--------+--------+---------+--------+ +// +type Assertions interface { + Condition(comp assert.Comparison, msgAndArgs ...interface{}) bool + Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool + Empty(object interface{}, msgAndArgs ...interface{}) bool + Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool + EqualError(theError error, errString string, msgAndArgs ...interface{}) bool + EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool + Error(err error, msgAndArgs ...interface{}) bool + Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool + Fail(failureMessage string, msgAndArgs ...interface{}) bool + FailNow(failureMessage string, msgAndArgs ...interface{}) bool + False(value bool, msgAndArgs ...interface{}) bool + Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool + InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool + InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool + InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool + InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool + IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool + JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool + Len(object interface{}, length int, msgAndArgs ...interface{}) bool + Nil(object interface{}, msgAndArgs ...interface{}) bool + NoError(err error, msgAndArgs ...interface{}) bool + NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool + NotEmpty(object interface{}, msgAndArgs ...interface{}) bool + NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool + NotNil(object interface{}, msgAndArgs ...interface{}) bool + NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool + NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool + NotZero(i interface{}, msgAndArgs ...interface{}) bool + Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool + Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool + True(value bool, msgAndArgs ...interface{}) bool + WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool + Zero(i interface{}, msgAndArgs ...interface{}) bool +} + +////////////////////////////////////////////////////////////////////////////// +// T => TestingT + +func formatMsgAndArgs(msgAndArgs []interface{}) string { + if len(msgAndArgs) == 0 { + return "" + } + if len(msgAndArgs) == 1 { + return msgAndArgs[0].(string) + } + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) +} + +type sinkAssertions struct { + // We need to wrap assert rather than using it as-is because we need to + // log success messages. + t T + a Assertions +} + +var _ Assertions = (*sinkAssertions)(nil) + +func (sa sinkAssertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) bool { + if sa.a.Condition(comp, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Contains(s, contains, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Empty(object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Equal(expected, actual, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + if sa.a.EqualError(theError, errString, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if sa.a.EqualValues(expected, actual, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Error(err error, msgAndArgs ...interface{}) bool { + if sa.a.Error(err, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Exactly(expected, actual, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + if sa.a.Fail(failureMessage, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + if sa.a.FailNow(failureMessage, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) False(value bool, msgAndArgs ...interface{}) bool { + if sa.a.False(value, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Implements(interfaceObject, object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if sa.a.InDelta(expected, actual, delta, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if sa.a.InDeltaSlice(expected, actual, delta, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if sa.a.InEpsilon(expected, actual, epsilon, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if sa.a.InEpsilonSlice(expected, actual, delta, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.IsType(expectedType, object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if sa.a.JSONEq(expected, actual, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + if sa.a.Len(object, length, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Nil(object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NoError(err error, msgAndArgs ...interface{}) bool { + if sa.a.NoError(err, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotContains(s, contains, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotEmpty(object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotEqual(expected, actual, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotNil(object, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool { + if sa.a.NotPanics(f, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotRegexp(rx, str, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + if sa.a.NotZero(i, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool { + if sa.a.Panics(f, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Regexp(rx, str, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) True(value bool, msgAndArgs ...interface{}) bool { + if sa.a.True(value, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if sa.a.WithinDuration(expected, actual, delta, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +func (sa sinkAssertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + if sa.a.Zero(i, msgAndArgs...) { + sa.t.Successf(formatMsgAndArgs(msgAndArgs)) + return true + } + return false +} + +////////////////////////////////////////////////////////////////////////////// +// Require + +// Adapts a require.Assertions into an Assertions. This simply returns true +// for all cases because execution just stops in case of failure. +type requireAssertions struct{ r *require.Assertions } + +var _ Assertions = (*requireAssertions)(nil) + +func (r requireAssertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) bool { + r.r.Condition(comp, msgAndArgs...) + return true +} + +func (r requireAssertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + r.r.Contains(s, contains, msgAndArgs...) + return true +} + +func (r requireAssertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + r.r.Empty(object, msgAndArgs...) + return true +} + +func (r requireAssertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + r.r.Equal(expected, actual, msgAndArgs...) + return true +} + +func (r requireAssertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + r.r.EqualError(theError, errString, msgAndArgs...) + return true +} + +func (r requireAssertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + r.r.EqualValues(expected, actual, msgAndArgs...) + return true +} + +func (r requireAssertions) Error(err error, msgAndArgs ...interface{}) bool { + r.r.Error(err, msgAndArgs...) + return true +} + +func (r requireAssertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + r.r.Exactly(expected, actual, msgAndArgs...) + return true +} + +func (r requireAssertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + r.r.Fail(failureMessage, msgAndArgs...) + return true +} + +func (r requireAssertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + r.r.FailNow(failureMessage, msgAndArgs...) + return true +} + +func (r requireAssertions) False(value bool, msgAndArgs ...interface{}) bool { + r.r.False(value, msgAndArgs...) + return true +} + +func (r requireAssertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + r.r.Implements(interfaceObject, object, msgAndArgs...) + return true +} + +func (r requireAssertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + r.r.InDelta(expected, actual, delta, msgAndArgs...) + return true +} + +func (r requireAssertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + r.r.InDeltaSlice(expected, actual, delta, msgAndArgs...) + return true +} + +func (r requireAssertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + r.r.InEpsilon(expected, actual, epsilon, msgAndArgs...) + return true +} + +func (r requireAssertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + r.r.InEpsilonSlice(expected, actual, delta, msgAndArgs...) + return true +} + +func (r requireAssertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + r.r.IsType(expectedType, object, msgAndArgs...) + return true +} + +func (r requireAssertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + r.r.JSONEq(expected, actual, msgAndArgs...) + return true +} + +func (r requireAssertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + r.r.Len(object, length, msgAndArgs...) + return true +} + +func (r requireAssertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + r.r.Nil(object, msgAndArgs...) + return true +} + +func (r requireAssertions) NoError(err error, msgAndArgs ...interface{}) bool { + r.r.NoError(err, msgAndArgs...) + return true +} + +func (r requireAssertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + r.r.NotContains(s, contains, msgAndArgs...) + return true +} + +func (r requireAssertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + r.r.NotEmpty(object, msgAndArgs...) + return true +} + +func (r requireAssertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + r.r.NotEqual(expected, actual, msgAndArgs...) + return true +} + +func (r requireAssertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + r.r.NotNil(object, msgAndArgs...) + return true +} + +func (r requireAssertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool { + r.r.NotPanics(f, msgAndArgs...) + return true +} + +func (r requireAssertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + r.r.NotRegexp(rx, str, msgAndArgs...) + return true +} + +func (r requireAssertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + r.r.NotZero(i, msgAndArgs...) + return true +} + +func (r requireAssertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) bool { + r.r.Panics(f, msgAndArgs...) + return true +} + +func (r requireAssertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + r.r.Regexp(rx, str, msgAndArgs...) + return true +} + +func (r requireAssertions) True(value bool, msgAndArgs ...interface{}) bool { + r.r.True(value, msgAndArgs...) + return true +} + +func (r requireAssertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + r.r.WithinDuration(expected, actual, delta, msgAndArgs...) + return true +} + +func (r requireAssertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + r.r.Zero(i, msgAndArgs...) + return true +} diff --git a/vendor/src/github.com/crossdock/crossdock-go/wait.go b/vendor/src/github.com/crossdock/crossdock-go/wait.go new file mode 100644 index 00000000..e9f48100 --- /dev/null +++ b/vendor/src/github.com/crossdock/crossdock-go/wait.go @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package crossdock + +import ( + "log" + "testing" + "time" + + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" +) + +// Wait sends attempts HEAD requests to url +func Wait(t *testing.T, url string, attempts int) { + ctx := context.Background() + + for a := 0; a < attempts; a++ { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + log.Println("HEAD", url) + _, err := ctxhttp.Head(ctx, nil, url) + if err == nil { + log.Println("Client is ready, beginning test...") + return + } + + sleepFor := 100 * time.Millisecond + log.Println(err, "- sleeping for", sleepFor) + time.Sleep(sleepFor) + } + + t.Fatalf("could not talk to client in %d attempts", attempts) +} diff --git a/vendor/src/github.com/uber-go/atomic/LICENSE.txt b/vendor/src/github.com/uber-go/atomic/LICENSE.txt new file mode 100644 index 00000000..8765c9fb --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/github.com/uber-go/atomic/Makefile b/vendor/src/github.com/uber-go/atomic/Makefile new file mode 100644 index 00000000..dfc63d9d --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/Makefile @@ -0,0 +1,64 @@ +PACKAGES := $(shell glide nv) +# Many Go tools take file globs or directories as arguments instead of packages. +PACKAGE_FILES ?= *.go + + +# The linting tools evolve with each Go version, so run them only on the latest +# stable release. +GO_VERSION := $(shell go version | cut -d " " -f 3) +GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION))) +LINTABLE_MINOR_VERSIONS := 7 8 +ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),) +SHOULD_LINT := true +endif + + +export GO15VENDOREXPERIMENT=1 + + +.PHONY: build +build: + go build -i $(PACKAGES) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.PHONY: test +test: + go test -cover -race $(PACKAGES) + + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover +ifdef SHOULD_LINT + go get github.com/golang/lint/golint +endif + +.PHONY: lint +lint: +ifdef SHOULD_LINT + @rm -rf lint.log + @echo "Checking formatting..." + @gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log + @echo "Checking vet..." + @$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking lint..." + @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking for unresolved FIXMEs..." + @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log + @[ ! -s lint.log ] +else + @echo "Skipping linters on" $(GO_VERSION) +endif + + +.PHONY: test_ci +test_ci: install_ci build + ./scripts/cover.sh $(shell go list $(PACKAGES)) diff --git a/vendor/src/github.com/uber-go/atomic/README.md b/vendor/src/github.com/uber-go/atomic/README.md new file mode 100644 index 00000000..6505abf6 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/README.md @@ -0,0 +1,36 @@ +# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard] + +Simple wrappers for primitive types to enforce atomic access. + +## Installation +`go get -u go.uber.org/atomic` + +## Usage +The standard library's `sync/atomic` is powerful, but it's easy to forget which +variables must be accessed atomically. `go.uber.org/atomic` preserves all the +functionality of the standard library, but wraps the primitive types to +provide a safer, more convenient API. + +```go +var atom atomic.Uint32 +atom.Store(42) +atom.Sub(2) +atom.CAS(40, 11) +``` + +See the [documentation][doc] for a complete API specification. + +## Development Status +Stable. + +
+Released under the [MIT License](LICENSE.txt). + +[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg +[doc]: https://godoc.org/go.uber.org/atomic +[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master +[ci]: https://travis-ci.org/uber-go/atomic +[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/atomic +[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic +[reportcard]: https://goreportcard.com/report/go.uber.org/atomic diff --git a/vendor/src/github.com/uber-go/atomic/atomic.go b/vendor/src/github.com/uber-go/atomic/atomic.go new file mode 100644 index 00000000..1ca50dc3 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/atomic.go @@ -0,0 +1,309 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package atomic provides simple wrappers around numerics to enforce atomic +// access. +package atomic + +import ( + "math" + "sync/atomic" +) + +// Int32 is an atomic wrapper around an int32. +type Int32 struct{ v int32 } + +// NewInt32 creates an Int32. +func NewInt32(i int32) *Int32 { + return &Int32{i} +} + +// Load atomically loads the wrapped value. +func (i *Int32) Load() int32 { + return atomic.LoadInt32(&i.v) +} + +// Add atomically adds to the wrapped int32 and returns the new value. +func (i *Int32) Add(n int32) int32 { + return atomic.AddInt32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int32 and returns the new value. +func (i *Int32) Sub(n int32) int32 { + return atomic.AddInt32(&i.v, -n) +} + +// Inc atomically increments the wrapped int32 and returns the new value. +func (i *Int32) Inc() int32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Int32) Dec() int32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int32) CAS(old, new int32) bool { + return atomic.CompareAndSwapInt32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int32) Store(n int32) { + atomic.StoreInt32(&i.v, n) +} + +// Swap atomically swaps the wrapped int32 and returns the old value. +func (i *Int32) Swap(n int32) int32 { + return atomic.SwapInt32(&i.v, n) +} + +// Int64 is an atomic wrapper around an int64. +type Int64 struct{ v int64 } + +// NewInt64 creates an Int64. +func NewInt64(i int64) *Int64 { + return &Int64{i} +} + +// Load atomically loads the wrapped value. +func (i *Int64) Load() int64 { + return atomic.LoadInt64(&i.v) +} + +// Add atomically adds to the wrapped int64 and returns the new value. +func (i *Int64) Add(n int64) int64 { + return atomic.AddInt64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int64 and returns the new value. +func (i *Int64) Sub(n int64) int64 { + return atomic.AddInt64(&i.v, -n) +} + +// Inc atomically increments the wrapped int64 and returns the new value. +func (i *Int64) Inc() int64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int64 and returns the new value. +func (i *Int64) Dec() int64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int64) CAS(old, new int64) bool { + return atomic.CompareAndSwapInt64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int64) Store(n int64) { + atomic.StoreInt64(&i.v, n) +} + +// Swap atomically swaps the wrapped int64 and returns the old value. +func (i *Int64) Swap(n int64) int64 { + return atomic.SwapInt64(&i.v, n) +} + +// Uint32 is an atomic wrapper around an uint32. +type Uint32 struct{ v uint32 } + +// NewUint32 creates a Uint32. +func NewUint32(i uint32) *Uint32 { + return &Uint32{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint32) Load() uint32 { + return atomic.LoadUint32(&i.v) +} + +// Add atomically adds to the wrapped uint32 and returns the new value. +func (i *Uint32) Add(n uint32) uint32 { + return atomic.AddUint32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint32 and returns the new value. +func (i *Uint32) Sub(n uint32) uint32 { + return atomic.AddUint32(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint32 and returns the new value. +func (i *Uint32) Inc() uint32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Uint32) Dec() uint32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint32) CAS(old, new uint32) bool { + return atomic.CompareAndSwapUint32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint32) Store(n uint32) { + atomic.StoreUint32(&i.v, n) +} + +// Swap atomically swaps the wrapped uint32 and returns the old value. +func (i *Uint32) Swap(n uint32) uint32 { + return atomic.SwapUint32(&i.v, n) +} + +// Uint64 is an atomic wrapper around a uint64. +type Uint64 struct{ v uint64 } + +// NewUint64 creates a Uint64. +func NewUint64(i uint64) *Uint64 { + return &Uint64{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint64) Load() uint64 { + return atomic.LoadUint64(&i.v) +} + +// Add atomically adds to the wrapped uint64 and returns the new value. +func (i *Uint64) Add(n uint64) uint64 { + return atomic.AddUint64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint64 and returns the new value. +func (i *Uint64) Sub(n uint64) uint64 { + return atomic.AddUint64(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint64 and returns the new value. +func (i *Uint64) Inc() uint64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint64 and returns the new value. +func (i *Uint64) Dec() uint64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint64) CAS(old, new uint64) bool { + return atomic.CompareAndSwapUint64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint64) Store(n uint64) { + atomic.StoreUint64(&i.v, n) +} + +// Swap atomically swaps the wrapped uint64 and returns the old value. +func (i *Uint64) Swap(n uint64) uint64 { + return atomic.SwapUint64(&i.v, n) +} + +// Bool is an atomic Boolean. +type Bool struct{ v uint32 } + +// NewBool creates a Bool. +func NewBool(initial bool) *Bool { + return &Bool{boolToInt(initial)} +} + +// Load atomically loads the Boolean. +func (b *Bool) Load() bool { + return truthy(atomic.LoadUint32(&b.v)) +} + +// CAS is an atomic compare-and-swap. +func (b *Bool) CAS(old, new bool) bool { + return atomic.CompareAndSwapUint32(&b.v, boolToInt(old), boolToInt(new)) +} + +// Store atomically stores the passed value. +func (b *Bool) Store(new bool) { + atomic.StoreUint32(&b.v, boolToInt(new)) +} + +// Swap sets the given value and returns the previous value. +func (b *Bool) Swap(new bool) bool { + return truthy(atomic.SwapUint32(&b.v, boolToInt(new))) +} + +// Toggle atomically negates the Boolean and returns the previous value. +func (b *Bool) Toggle() bool { + return truthy(atomic.AddUint32(&b.v, 1) - 1) +} + +func truthy(n uint32) bool { + return n&1 == 1 +} + +func boolToInt(b bool) uint32 { + if b { + return 1 + } + return 0 +} + +// Float64 is an atomic wrapper around float64. +type Float64 struct { + v uint64 +} + +// NewFloat64 creates a Float64. +func NewFloat64(f float64) *Float64 { + return &Float64{math.Float64bits(f)} +} + +// Load atomically loads the wrapped value. +func (f *Float64) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&f.v)) +} + +// Store atomically stores the passed value. +func (f *Float64) Store(s float64) { + atomic.StoreUint64(&f.v, math.Float64bits(s)) +} + +// Add atomically adds to the wrapped float64 and returns the new value. +func (f *Float64) Add(s float64) float64 { + for { + old := f.Load() + new := old + s + if f.CAS(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float64 and returns the new value. +func (f *Float64) Sub(s float64) float64 { + return f.Add(-s) +} + +// CAS is an atomic compare-and-swap. +func (f *Float64) CAS(old, new float64) bool { + return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new)) +} + +// Value shadows the type of the same name from sync/atomic +// https://godoc.org/sync/atomic#Value +type Value struct{ atomic.Value } diff --git a/vendor/src/github.com/uber-go/atomic/atomic_test.go b/vendor/src/github.com/uber-go/atomic/atomic_test.go new file mode 100644 index 00000000..9f293b7d --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/atomic_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInt32(t *testing.T) { + atom := NewInt32(42) + + require.Equal(t, int32(42), atom.Load(), "Load didn't work.") + require.Equal(t, int32(46), atom.Add(4), "Add didn't work.") + require.Equal(t, int32(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestInt64(t *testing.T) { + atom := NewInt64(42) + + require.Equal(t, int64(42), atom.Load(), "Load didn't work.") + require.Equal(t, int64(46), atom.Add(4), "Add didn't work.") + require.Equal(t, int64(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint32(t *testing.T) { + atom := NewUint32(42) + + require.Equal(t, uint32(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint32(46), atom.Add(4), "Add didn't work.") + require.Equal(t, uint32(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint64(t *testing.T) { + atom := NewUint64(42) + + require.Equal(t, uint64(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint64(46), atom.Add(4), "Add didn't work.") + require.Equal(t, uint64(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestBool(t *testing.T) { + atom := NewBool(false) + require.False(t, atom.Toggle(), "Expected swap to return previous value.") + require.True(t, atom.Load(), "Unexpected state after swap.") + + require.True(t, atom.CAS(true, true), "CAS should swap when old matches") + require.True(t, atom.Load(), "CAS should have no effect") + require.True(t, atom.CAS(true, false), "CAS should swap when old matches") + require.False(t, atom.Load(), "CAS should have modified the value") + require.False(t, atom.CAS(true, false), "CAS should fail on old mismatch") + require.False(t, atom.Load(), "CAS should not have modified the value") + + atom.Store(false) + require.False(t, atom.Load(), "Unexpected state after store.") + + prev := atom.Swap(false) + require.False(t, prev, "Expected Swap to return previous value.") + + prev = atom.Swap(true) + require.False(t, prev, "Expected Swap to return previous value.") +} + +func TestFloat64(t *testing.T) { + atom := NewFloat64(4.2) + + require.Equal(t, float64(4.2), atom.Load(), "Load didn't work.") + + require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.") + require.Equal(t, float64(0.5), atom.Load(), "CAS didn't set the correct value.") + require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.") + + atom.Store(42.0) + require.Equal(t, float64(42.0), atom.Load(), "Store didn't set the correct value.") + require.Equal(t, float64(42.5), atom.Add(0.5), "Add didn't work.") + require.Equal(t, float64(42.0), atom.Sub(0.5), "Sub didn't work.") +} + +func TestValue(t *testing.T) { + var v Value + assert.Nil(t, v.Load(), "initial Value is not nil") + + v.Store(42) + assert.Equal(t, 42, v.Load()) + + v.Store(84) + assert.Equal(t, 84, v.Load()) + + assert.Panics(t, func() { v.Store("foo") }) +} diff --git a/vendor/src/github.com/uber-go/atomic/example_test.go b/vendor/src/github.com/uber-go/atomic/example_test.go new file mode 100644 index 00000000..806e11c4 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/example_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic_test + +import ( + "fmt" + + "go.uber.org/atomic" +) + +func Example() { + // Uint32 is a thin wrapper around the primitive uint32 type. + var atom atomic.Uint32 + + // The wrapper ensures that all operations are atomic. + atom.Store(42) + fmt.Println(atom.Inc()) + fmt.Println(atom.CAS(43, 0)) + fmt.Println(atom.Load()) + + // Output: + // 43 + // true + // 0 +} diff --git a/vendor/src/github.com/uber-go/atomic/glide.lock b/vendor/src/github.com/uber-go/atomic/glide.lock new file mode 100644 index 00000000..3c72c599 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/glide.lock @@ -0,0 +1,17 @@ +hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53 +updated: 2016-10-27T00:10:51.16960137-07:00 +imports: [] +testImports: +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: d77da356e56a7428ad25149ca77381849a6a5232 + subpackages: + - assert + - require diff --git a/vendor/src/github.com/uber-go/atomic/glide.yaml b/vendor/src/github.com/uber-go/atomic/glide.yaml new file mode 100644 index 00000000..4cf608ec --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/glide.yaml @@ -0,0 +1,6 @@ +package: go.uber.org/atomic +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert + - require diff --git a/vendor/src/github.com/uber-go/atomic/scripts/cover.sh b/vendor/src/github.com/uber-go/atomic/scripts/cover.sh new file mode 100644 index 00000000..5dfb65e4 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/scripts/cover.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +COVER=cover +ROOT_PKG=go.uber.org/atomic + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | map(select(startswith("'"$ROOT_PKG"'"))) + | map(select(contains("/vendor/") | not)) + | . + ["'"$pkg"'"] + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + go test \ + -coverprofile "$COVER/cover.${i}.out" -coverpkg "$coverpkg" \ + -v "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/github.com/uber-go/atomic/scripts/test-ubergo.sh b/vendor/src/github.com/uber-go/atomic/scripts/test-ubergo.sh new file mode 100644 index 00000000..9bc526de --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/scripts/test-ubergo.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euox pipefail +IFS=$'\n\t' + +# This script creates a fake GOPATH, symlinks in the current +# directory as uber-go/atomic and verifies that tests still pass. + +WORK_DIR=`mktemp -d` +function cleanup { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + + +export GOPATH="$WORK_DIR" +PKG_PARENT="$WORK_DIR/src/github.com/uber-go" +PKG_DIR="$PKG_PARENT/atomic" + +mkdir -p "$PKG_PARENT" +cp -R `pwd` "$PKG_DIR" +cd "$PKG_DIR" + +# The example imports go.uber.org, fix the import. +sed -e 's/go.uber.org\/atomic/github.com\/uber-go\/atomic/' -i="" example_test.go + +make test diff --git a/vendor/src/github.com/uber-go/atomic/stress_test.go b/vendor/src/github.com/uber-go/atomic/stress_test.go new file mode 100644 index 00000000..72a65bd4 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/stress_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "runtime" + "sync" + "testing" +) + +const ( + _parallelism = 4 + _iterations = 1000 +) + +var _stressTests = map[string]func(){ + "i32": stressInt32, + "i64": stressInt64, + "u32": stressUint32, + "u64": stressUint64, + "f64": stressFloat64, + "bool": stressBool, + "string": stressString, +} + +func TestStress(t *testing.T) { + for name, f := range _stressTests { + t.Run(name, func(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + + start := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(_parallelism) + for i := 0; i < _parallelism; i++ { + go func() { + defer wg.Done() + <-start + for j := 0; j < _iterations; j++ { + f() + } + }() + } + close(start) + wg.Wait() + }) + } +} + +func BenchmarkStress(b *testing.B) { + for name, f := range _stressTests { + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + f() + } + }) + } +} + +func stressInt32() { + var atom Int32 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressInt64() { + var atom Int64 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressUint32() { + var atom Uint32 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressUint64() { + var atom Uint64 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressFloat64() { + var atom Float64 + atom.Load() + atom.CAS(1.0, 0.1) + atom.Add(1.1) + atom.Sub(0.2) + atom.Store(1.0) +} + +func stressBool() { + var atom Bool + atom.Load() + atom.Store(false) + atom.Swap(true) + atom.CAS(true, false) + atom.CAS(true, false) + atom.Load() + atom.Toggle() + atom.Toggle() +} + +func stressString() { + var atom String + atom.Load() + atom.Store("abc") + atom.Load() + atom.Store("def") + atom.Load() + atom.Store("") +} diff --git a/vendor/src/github.com/uber-go/atomic/string.go b/vendor/src/github.com/uber-go/atomic/string.go new file mode 100644 index 00000000..329fb123 --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/string.go @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// String is an atomic type-safe wrapper around Value for strings. +type String struct{ v Value } + +// NewString creates a String. +func NewString(str string) *String { + s := &String{} + if str != "" { + s.Store(str) + } + return s +} + +// Load atomically loads the wrapped string. +func (s *String) Load() string { + v := s.v.Load() + if v == nil { + return "" + } + return v.(string) +} + +// Store atomically stores the passed string. +// Note: Converting the string to an interface{} to store in the Value +// requires an allocation. +func (s *String) Store(str string) { + if str == "" { + s.v = Value{} + return + } + s.v.Store(str) +} diff --git a/vendor/src/github.com/uber-go/atomic/string_test.go b/vendor/src/github.com/uber-go/atomic/string_test.go new file mode 100644 index 00000000..bf72505d --- /dev/null +++ b/vendor/src/github.com/uber-go/atomic/string_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStringNoInitialValue(t *testing.T) { + atom := &String{} + require.Equal(t, "", atom.Load(), "Initial value should be blank string") +} + +func TestString(t *testing.T) { + atom := NewString("") + require.Equal(t, "", atom.Load(), "Expected Load to return initialized value") + + atom.Store("abc") + require.Equal(t, "abc", atom.Load(), "Unexpected value after Store") + + atom = NewString("bcd") + require.Equal(t, "bcd", atom.Load(), "Expected Load to return initialized value") + + atom.Store("") + require.Equal(t, "", atom.Load(), "Expected Load to return empty value") +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/CHANGELOG.md b/vendor/src/github.com/uber/jaeger-client-go/CHANGELOG.md new file mode 100644 index 00000000..17014701 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/CHANGELOG.md @@ -0,0 +1,131 @@ +Changes by Version +================== + +2.9.1 (unreleased) +------------------ + +- nothing yet + + +2.9.0 (2017-07-29) +------------------ + +- Pin thrift <= 0.10 (#179) +- Introduce a parallel interface ContribObserver (#159) + + +2.8.0 (2017-07-05) +------------------ + +- Drop `jaeger.` prefix from `jaeger.hostname` process-level tag +- Add options to set tracer tags + + +2.7.0 (2017-06-21) +------------------ + +- Fix rate limiter balance [#135](https://github.com/uber/jaeger-client-go/pull/135) [#140](https://github.com/uber/jaeger-client-go/pull/140) +- Default client to send Jaeger.thrift [#147](https://github.com/uber/jaeger-client-go/pull/147) +- Save baggage in span [#153](https://github.com/uber/jaeger-client-go/pull/153) +- Move reporter.queueLength to the top of the struct to guarantee 64bit alignment [#158](https://github.com/uber/jaeger-client-go/pull/158) +- Support HTTP transport with jaeger.thrift [#161](https://github.com/uber/jaeger-client-go/pull/161) + + +2.6.0 (2017-03-28) +------------------ + +- Add config option to initialize RPC Metrics feature + + +2.5.0 (2017-03-23) +------------------ + +- Split request latency metric by success/failure [#123](https://github.com/uber/jaeger-client-go/pull/123) +- Add mutex to adaptive sampler and fix race condition [#124](https://github.com/uber/jaeger-client-go/pull/124) +- Fix rate limiter panic [#125](https://github.com/uber/jaeger-client-go/pull/125) + + +2.4.0 (2017-03-21) +------------------ + +- Remove `_ms` suffix from request latency metric name [#121](https://github.com/uber/jaeger-client-go/pull/121) +- Rename all metrics to "request" and "http_request" and use tags for other dimensions [#121](https://github.com/uber/jaeger-client-go/pull/121) + + +2.3.0 (2017-03-20) +------------------ + +- Make Span type public to allow access to non-std methods for testing [#117](https://github.com/uber/jaeger-client-go/pull/117) +- Add a structured way to extract traces for logging with zap [#118](https://github.com/uber/jaeger-client-go/pull/118) + + +2.2.1 (2017-03-14) +------------------ + +- Fix panic caused by updating the remote sampler from adaptive sampler to any other sampler type (https://github.com/uber/jaeger-client-go/pull/111) + + +2.2.0 (2017-03-10) +------------------ + +- Introduce Observer and SpanObserver (https://github.com/uber/jaeger-client-go/pull/94) +- Add RPC metrics emitter as Observer/SpanObserver (https://github.com/uber/jaeger-client-go/pull/103) + + +2.1.2 (2017-02-27) +------------------- + +- Fix leaky bucket bug (https://github.com/uber/jaeger-client-go/pull/99) +- Fix zap logger Infof (https://github.com/uber/jaeger-client-go/pull/100) +- Add tracer initialization godoc examples + + +2.1.1 (2017-02-21) +------------------- + +- Fix inefficient usage of zap.Logger + + +2.1.0 (2017-02-17) +------------------- + +- Add adapter for zap.Logger (https://github.com/uber-go/zap) +- Move logging API to ./log/ package + + +2.0.0 (2017-02-08) +------------------- + +- Support Adaptive Sampling +- Support 128bit Trace IDs +- Change trace/span IDs from uint64 to strong types TraceID and SpanID +- Add Zipkin HTTP B3 Propagation format support #72 +- Rip out existing metrics and use github.com/uber/jaeger-lib/metrics +- Change API for tracer, reporter, sampler initialization + + +1.6.0 (2016-10-14) +------------------- + +- Add Zipkin HTTP transport +- Support external baggage via jaeger-baggage header +- Unpin Thrift version, keep to master + + +1.5.1 (2016-09-27) +------------------- + +- Relax dependency on opentracing to ^1 + + +1.5.0 (2016-09-27) +------------------- + +- Upgrade to opentracing-go 1.0 +- Support KV logging for Spans + + +1.4.0 (2016-09-14) +------------------- + +- Support debug traces via HTTP header "jaeger-debug-id" diff --git a/vendor/src/github.com/uber/jaeger-client-go/CONTRIBUTING.md b/vendor/src/github.com/uber/jaeger-client-go/CONTRIBUTING.md new file mode 100644 index 00000000..bb46b5cb --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing to `jaeger-client-go` + +We'd love your help! If you would like to contribute code you can do so through GitHub +by forking the repository and sending a pull request into the `master` branch. + +## Getting Started + +This library uses [glide](https://github.com/Masterminds/glide) to manage dependencies. + +The library's import path is `github.com/uber/jaeger-client-go`. + +To get started: + +```bash +git submodule update --init --recursive +glide install +make test +``` + +## Making A Change + +*Before making any significant changes, please [open an +issue](https://github.com/uber/jaeger-client-go/issues).* Discussing your proposed +changes ahead of time will make the contribution process smooth for everyone. + +Once we've discussed your changes and you've got your code ready, make sure +that tests are passing (`make test` or `make cover`) and open your PR! Your +pull request is most likely to be accepted if it: + +* Includes tests for new functionality. +* Follows the guidelines in [Effective + Go](https://golang.org/doc/effective_go.html) and the [Go team's common code + review comments](https://github.com/golang/go/wiki/CodeReviewComments). +* Has a [good commit + message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Cutting a Release + +See [RELEASE.md](./RELEASE.md) + +## License + +By contributing your code, you agree to license your contribution under the terms of the [Apache 2.0 License](LICENSE). + +If you are adding a new file it should have a header like below. The easiest way to add such header is to run `make fmt`. + +``` +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +``` + diff --git a/vendor/src/github.com/uber/jaeger-client-go/LICENSE b/vendor/src/github.com/uber/jaeger-client-go/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/src/github.com/uber/jaeger-client-go/Makefile b/vendor/src/github.com/uber/jaeger-client-go/Makefile new file mode 100644 index 00000000..c8b0925b --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/Makefile @@ -0,0 +1,104 @@ +PROJECT_ROOT=github.com/uber/jaeger-client-go +PACKAGES := $(shell glide novendor | grep -v ./thrift-gen/...) +# all .go files that don't exist in hidden directories +ALL_SRC := $(shell find . -name "*.go" | grep -v -e vendor -e thrift-gen \ + -e ".*/\..*" \ + -e ".*/_.*" \ + -e ".*/mocks.*") + +-include crossdock/rules.mk + +export GO15VENDOREXPERIMENT=1 + +RACE=-race +GOTEST=go test -v $(RACE) +GOLINT=golint +GOVET=go vet +GOFMT=gofmt +FMT_LOG=fmt.log +LINT_LOG=lint.log + +THRIFT_VER=0.9.3 +THRIFT_IMG=thrift:$(THRIFT_VER) +THRIFT=docker run -v "${PWD}:/data" $(THRIFT_IMG) thrift +THRIFT_GO_ARGS=thrift_import="github.com/apache/thrift/lib/go/thrift" +THRIFT_GEN_DIR=thrift-gen + +PASS=$(shell printf "\033[32mPASS\033[0m") +FAIL=$(shell printf "\033[31mFAIL\033[0m") +COLORIZE=sed ''/PASS/s//$(PASS)/'' | sed ''/FAIL/s//$(FAIL)/'' + +.DEFAULT_GOAL := test-and-lint + +.PHONY: test-and-lint +test-and-lint: test fmt lint + +.PHONY: test +test: + bash -c "set -e; set -o pipefail; $(GOTEST) $(PACKAGES) | $(COLORIZE)" + +.PHONY: fmt +fmt: + $(GOFMT) -e -s -l -w $(ALL_SRC) + ./scripts/updateLicenses.sh + +.PHONY: lint +lint: + $(GOVET) $(PACKAGES) + @cat /dev/null > $(LINT_LOG) + @$(foreach pkg, $(PACKAGES), $(GOLINT) $(pkg) | grep -v crossdock/thrift >> $(LINT_LOG) || true;) + @[ ! -s "$(LINT_LOG)" ] || (echo "Lint Failures" | cat - $(LINT_LOG) && false) + @$(GOFMT) -e -s -l $(ALL_SRC) > $(FMT_LOG) + @[ ! -s "$(FMT_LOG)" ] || (echo "Go Fmt Failures, run 'make fmt'" | cat - $(FMT_LOG) && false) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.PHONY: cover +cover: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + go tool cover -html=cover.out -o cover.html + + +# This is not part of the regular test target because we don't want to slow it +# down. +.PHONY: test-examples +test-examples: + make -C examples + +# TODO at the moment we're not generating tchan_*.go files +thrift: idl-submodule thrift-image + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/agent.thrift + sed -i '' 's|"zipkincore"|"$(PROJECT_ROOT)/thrift-gen/zipkincore"|g' $(THRIFT_GEN_DIR)/agent/*.go + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/sampling.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/jaeger.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/zipkincore.thrift + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/$(THRIFT_GEN_DIR) /data/idl/thrift/baggage.thrift + rm -rf thrift-gen/*/*-remote + $(THRIFT) -o /data --gen go:$(THRIFT_GO_ARGS) --out /data/crossdock/thrift/ /data/idl/thrift/crossdock/tracetest.thrift + rm -rf crossdock/thrift/*/*-remote + +idl-submodule: + git submodule init + git submodule update + +thrift-image: + $(THRIFT) -version + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + go get github.com/golang/lint/golint + + +.PHONY: test_ci +test_ci: + @./scripts/cover.sh $(shell go list $(PACKAGES)) + make lint + diff --git a/vendor/src/github.com/uber/jaeger-client-go/README.md b/vendor/src/github.com/uber/jaeger-client-go/README.md new file mode 100644 index 00000000..3b8abb2d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/README.md @@ -0,0 +1,226 @@ +[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![OpenTracing 1.0 Enabled][ot-img]][ot-url] + +# Jaeger Bindings for Go OpenTracing API + +This is a client side library that implements an +[OpenTracing](http://opentracing.io) Tracer, +with Zipkin-compatible data model. + +## Installation + +We recommended using a dependency manager like [glide](https://github.com/Masterminds/glide) +and [semantic versioning](http://semver.org/) when including this library into an application. +For example, Jaeger backend imports this library like this: + +```yaml +- package: github.com/uber/jaeger-client-go + version: ^2.7.0 +``` + +If you instead want to use the latest version in `master`, you can pull it via `go get`. +Note that during `go get` you may see build errors due to incompatible dependencies, which is why +we recommend using semantic versions for dependencioes. The error may be fixed by running +`make install` (it will install `glide` if you don't have it): + +```shell +go get -u github.com/uber/jaeger-client-go/ +cd $GOPATH/src/github.com/uber/jaeger-client-go/ +git submodule update --init --recursive +make install +``` + +## Initialization + +See tracer initialization examples in [godoc](https://godoc.org/github.com/uber/jaeger-client-go/config#pkg-examples) +and [config/example_test.go](./config/example_test.go). + +### Closing the tracer via `io.Closer` + +The constructor functions for Jaeger Tracer return the tracer itself and an `io.Closer` instance. +It is recommended to structure your `main()` so that it calls the `Close()` function on the closer +before exiting, e.g. + +```go +tracer, closer, err := cfg.New(...) +defer closer.Close() +``` + +This is especially useful for command-line tools that enable tracing, as well as +for the long-running apps that support graceful shutdown. For example, if your deployment +system sends SIGTERM instead of killing the process and you trap that signal to do a graceful +exit, then having `defer closer.Closer()` ensures that all buffered spans are flushed. + +### Metrics & Monitoring + +The tracer emits a number of different metrics, defined in +[metrics.go](metrics.go). The monitoring backend is expected to support +tag-based metric names, e.g. instead of `statsd`-style string names +like `counters.my-service.jaeger.spans.started.sampled`, the metrics +are defined by a short name and a collection of key/value tags, for +example: `name:traces, state:started, sampled:true`. + +The monitoring backend is represented by the +[StatsReporter](stats_reporter.go) interface. An implementation +of that interface should be passed to the `New` method during +tracer initialization: + +```go + stats := // create StatsReporter implementation + tracer := config.Tracing.New("your-service-name", stats) +``` + +By default, a no-op `NullStatsReporter` is used. + +### Logging + +The tracer can be configured with an optional logger, which will be +used to log communication errors, or log spans if a logging reporter +option is specified in the configuration. The logging API is abstracted +by the [Logger](logger.go) interface. A logger instance implementing +this interface can be set on the `Config` object before calling the +`New` method. + +Besides the [zap](https://github.com/uber-go/zap) implementation +bundled with this package there is also a [go-kit](https://github.com/go-kit/kit) +one in the [jaeger-lib](https://github.com/uber/jaeger-lib) repository. + +## Instrumentation for Tracing + +Since this tracer is fully compliant with OpenTracing API 1.0, +all code instrumentation should only use the API itself, as described +in the [opentracing-go] +(https://github.com/opentracing/opentracing-go) documentation. + +## Features + +### Reporters + +A "reporter" is a component receives the finished spans and reports +them to somewhere. Under normal circumstances, the Tracer +should use the default `RemoteReporter`, which sends the spans out of +process via configurable "transport". For testing purposes, one can +use an `InMemoryReporter` that accumulates spans in a buffer and +allows to retrieve them for later verification. Also available are +`NullReporter`, a no-op reporter that does nothing, a `LoggingReporter` +which logs all finished spans using their `String()` method, and a +`CompositeReporter` that can be used to combine more than one reporter +into one, e.g. to attach a logging reporter to the main remote reporter. + +### Span Reporting Transports + +The remote reporter uses "transports" to actually send the spans out +of process. Currently two supported transports are Thrift over UDP +and Thrift over TChannel. More transports will be added in the future. + +The only data format currently used is Zipkin Thrift 1.x span format, +which allows easy integration of the tracer with Zipkin backend. + +### Sampling + +The tracer does not record all spans, but only those that have the +sampling bit set in the `flags`. When a new trace is started and a new +unique ID is generated, a sampling decision is made whether this trace +should be sampled. The sampling decision is propagated to all downstream +calls via the `flags` field of the trace context. The following samplers +are available: + 1. `RemotelyControlledSampler` uses one of the other simpler samplers + and periodically updates it by polling an external server. This + allows dynamic control of the sampling strategies. + 1. `ConstSampler` always makes the same sampling decision for all + trace IDs. it can be configured to either sample all traces, or + to sample none. + 1. `ProbabilisticSampler` uses a fixed sampling rate as a probability + for a given trace to be sampled. The actual decision is made by + comparing the trace ID with a random number multiplied by the + sampling rate. + 1. `RateLimitingSampler` can be used to allow only a certain fixed + number of traces to be sampled per second. + +### Baggage Injection + +The OpenTracing spec allows for [baggage](https://github.com/opentracing/specification/blob/master/specification.md#set-a-baggage-item), +which are key value pairs that are added to the span context and propagated +throughout the trace. +An external process can inject baggage by setting the special +HTTP Header `jaeger-baggage` on a request + +```sh +curl -H "jaeger-baggage: key1=value1, key2=value2" http://myhost.com +``` + +Baggage can also be programatically set inside your service by doing +the following + +```go +if span := opentracing.SpanFromContext(ctx); span != nil { + span.SetBaggageItem("key", "value") +} +``` + +Another service downstream of that can retrieve the baggage in a similar way: + +```go +if span := opentracing.SpanFromContext(ctx); span != nil { + val := span.BaggageItem("key") + println(val) +} +``` + +### Debug Traces (Forced Sampling) + +#### Programmatically + +The OpenTracing API defines a `sampling.priority` standard tag that +can be used to affect the sampling of a span and its children: + +```go +import ( + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" +) + +span := opentracing.SpanFromContext(ctx) +ext.SamplingPriority.Set(span, 1) +``` + +#### Via HTTP Headers + +Jaeger Tracer also understands a special HTTP Header `jaeger-debug-id`, +which can be set in the incoming request, e.g. + +```sh +curl -H "jaeger-debug-id: some-correlation-id" http://myhost.com +``` + +When Jaeger sees this header in the request that otherwise has no +tracing context, it ensures that the new trace started for this +request will be sampled in the "debug" mode (meaning it should survive +all downsampling that might happen in the collection pipeline), and the +root span will have a tag as if this statement was executed: + +```go +span.SetTag("jaeger-debug-id", "some-correlation-id") +``` + +This allows using Jaeger UI to find the trace by this tag. + +### Zipkin HTTP B3 compatible header propagation + +Jaeger Tracer supports Zipkin B3 Propagation HTTP headers, which are used +by a lot of Zipkin tracers. This means that you can use Jaeger in conjunction with e.g. [these OpenZipkin tracers](https://github.com/openzipkin). + +However it is not the default propagation format, see [here](zipkin/README.md#NewZipkinB3HTTPHeaderPropagator) how to set it up. + +## License + +[Apache 2.0 License](LICENSE). + + +[doc-img]: https://godoc.org/github.com/uber/jaeger-client-go?status.svg +[doc]: https://godoc.org/github.com/uber/jaeger-client-go +[ci-img]: https://travis-ci.org/uber/jaeger-client-go.svg?branch=master +[ci]: https://travis-ci.org/uber/jaeger-client-go +[cov-img]: https://coveralls.io/repos/uber/jaeger-client-go/badge.svg?branch=master&service=github +[cov]: https://coveralls.io/github/uber/jaeger-client-go?branch=master +[ot-img]: https://img.shields.io/badge/OpenTracing--1.0-enabled-blue.svg +[ot-url]: http://opentracing.io diff --git a/vendor/src/github.com/uber/jaeger-client-go/RELEASE.md b/vendor/src/github.com/uber/jaeger-client-go/RELEASE.md new file mode 100644 index 00000000..115e49ab --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/RELEASE.md @@ -0,0 +1,11 @@ +# Release Process + +1. Create a PR "Preparing for release X.Y.Z" against master branch + * Alter CHANGELOG.md from ` (unreleased)` to ` (YYYY-MM-DD)` + * Update `JaegerClientVersion` in constants.go to `Go-X.Y.Z` +2. Create a release "Release X.Y.Z" on Github + * Create Tag `vX.Y.Z` + * Copy CHANGELOG.md into the release notes +3. Create a PR "Back to development" against master branch + * Add ` (unreleased)` to CHANGELOG.md + * Update `JaegerClientVersion` in constants.go to `Go-dev` diff --git a/vendor/src/github.com/uber/jaeger-client-go/baggage_setter.go b/vendor/src/github.com/uber/jaeger-client-go/baggage_setter.go new file mode 100644 index 00000000..1037ca0e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/baggage_setter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go/log" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +// baggageSetter is an actor that can set a baggage value on a Span given certain +// restrictions (eg. maxValueLength). +type baggageSetter struct { + restrictionManager baggage.RestrictionManager + metrics *Metrics +} + +func newBaggageSetter(restrictionManager baggage.RestrictionManager, metrics *Metrics) *baggageSetter { + return &baggageSetter{ + restrictionManager: restrictionManager, + metrics: metrics, + } +} + +// (NB) span should hold the lock before making this call +func (s *baggageSetter) setBaggage(span *Span, key, value string) { + var truncated bool + var prevItem string + restriction := s.restrictionManager.GetRestriction(span.serviceName(), key) + if !restriction.KeyAllowed() { + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + s.metrics.BaggageUpdateFailure.Inc(1) + return + } + if len(value) > restriction.MaxValueLength() { + truncated = true + value = value[:restriction.MaxValueLength()] + s.metrics.BaggageTruncate.Inc(1) + } + prevItem = span.context.baggage[key] + s.logFields(span, key, value, prevItem, truncated, restriction.KeyAllowed()) + span.context = span.context.WithBaggageItem(key, value) + s.metrics.BaggageUpdateSuccess.Inc(1) +} + +func (s *baggageSetter) logFields(span *Span, key, value, prevItem string, truncated, valid bool) { + if !span.context.IsSampled() { + return + } + fields := []log.Field{ + log.String("event", "baggage"), + log.String("key", key), + log.String("value", value), + } + if prevItem != "" { + fields = append(fields, log.String("override", "true")) + } + if truncated { + fields = append(fields, log.String("truncated", "true")) + } + if !valid { + fields = append(fields, log.String("invalid", "true")) + } + span.logFieldsNoLocking(fields...) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/baggage_setter_test.go b/vendor/src/github.com/uber/jaeger-client-go/baggage_setter_test.go new file mode 100644 index 00000000..0e48b6d9 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/baggage_setter_test.go @@ -0,0 +1,126 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +func withTracerAndMetrics(f func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory)) { + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, nil) + + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + f(tracer.(*Tracer), m, factory) +} + +func TestTruncateBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(5), metrics) + key := "key" + value := "01234567890" + expected := "01234" + + parent := tracer.StartSpan("parent").(*Span) + parent.context = parent.context.WithBaggageItem(key, value) + span := tracer.StartSpan("child", opentracing.ChildOf(parent.Context())).(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, expected, true, true, false) + assert.Equal(t, expected, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-truncate", + Value: 1, + }, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-update", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +type keyNotAllowedBaggageRestrictionManager struct{} + +func (m *keyNotAllowedBaggageRestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + return baggage.NewRestriction(false, 0) +} + +func TestInvalidBaggage(t *testing.T) { + withTracerAndMetrics(func(tracer *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + setter := newBaggageSetter(&keyNotAllowedBaggageRestrictionManager{}, metrics) + key := "key" + value := "value" + + span := tracer.StartSpan("span").(*Span) + + setter.setBaggage(span, key, value) + assertBaggageFields(t, span, key, value, false, false, true) + assert.Empty(t, span.context.baggage[key]) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-update", + Tags: map[string]string{"result": "err"}, + Value: 1, + }, + ) + }) +} + +func TestNotSampled(t *testing.T) { + withTracerAndMetrics(func(_ *Tracer, metrics *Metrics, factory *metrics.LocalFactory) { + tracer, closer := NewTracer("svc", NewConstSampler(false), NewNullReporter()) + defer closer.Close() + + setter := newBaggageSetter(baggage.NewDefaultRestrictionManager(10), metrics) + span := tracer.StartSpan("span").(*Span) + setter.setBaggage(span, "key", "value") + assert.Empty(t, span.logs, "No baggage fields should be created if span is not sampled") + }) +} + +func assertBaggageFields(t *testing.T, sp *Span, key, value string, override, truncated, invalid bool) { + require.Len(t, sp.logs, 1) + keys := map[string]struct{}{} + for _, field := range sp.logs[0].Fields { + keys[field.String()] = struct{}{} + } + assert.Contains(t, keys, "event:baggage") + assert.Contains(t, keys, "key:"+key) + assert.Contains(t, keys, "value:"+value) + if invalid { + assert.Contains(t, keys, "invalid:true") + } + if override { + assert.Contains(t, keys, "override:true") + } + if truncated { + assert.Contains(t, keys, "truncated:true") + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/config/config.go b/vendor/src/github.com/uber/jaeger-client-go/config/config.go new file mode 100644 index 00000000..10fa1c00 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/config/config.go @@ -0,0 +1,287 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "errors" + "fmt" + "io" + "strings" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage/remote" + "github.com/uber/jaeger-client-go/rpcmetrics" +) + +const defaultSamplingProbability = 0.001 + +// Configuration configures and creates Jaeger Tracer +type Configuration struct { + Disabled bool `yaml:"disabled"` + Sampler *SamplerConfig `yaml:"sampler"` + Reporter *ReporterConfig `yaml:"reporter"` + Headers *jaeger.HeadersConfig `yaml:"headers"` + RPCMetrics bool `yaml:"rpc_metrics"` + BaggageRestrictions *BaggageRestrictionsConfig `yaml:"baggage_restrictions"` +} + +// SamplerConfig allows initializing a non-default sampler. All fields are optional. +type SamplerConfig struct { + // Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote + Type string `yaml:"type"` + + // Param is a value passed to the sampler. + // Valid values for Param field are: + // - for "const" sampler, 0 or 1 for always false/true respectively + // - for "probabilistic" sampler, a probability between 0 and 1 + // - for "rateLimiting" sampler, the number of spans per second + // - for "remote" sampler, param is the same as for "probabilistic" + // and indicates the initial sampling rate before the actual one + // is received from the mothership + Param float64 `yaml:"param"` + + // SamplingServerURL is the address of jaeger-agent's HTTP sampling server + SamplingServerURL string `yaml:"samplingServerURL"` + + // MaxOperations is the maximum number of operations that the sampler + // will keep track of. If an operation is not tracked, a default probabilistic + // sampler will be used rather than the per operation specific sampler. + MaxOperations int `yaml:"maxOperations"` + + // SamplingRefreshInterval controls how often the remotely controlled sampler will poll + // jaeger-agent for the appropriate sampling strategy. + SamplingRefreshInterval time.Duration `yaml:"samplingRefreshInterval"` +} + +// ReporterConfig configures the reporter. All fields are optional. +type ReporterConfig struct { + // QueueSize controls how many spans the reporter can keep in memory before it starts dropping + // new spans. The queue is continuously drained by a background go-routine, as fast as spans + // can be sent out of process. + QueueSize int `yaml:"queueSize"` + + // BufferFlushInterval controls how often the buffer is force-flushed, even if it's not full. + // It is generally not useful, as it only matters for very low traffic services. + BufferFlushInterval time.Duration + + // LogSpans, when true, enables LoggingReporter that runs in parallel with the main reporter + // and logs all submitted spans. Main Configuration.Logger must be initialized in the code + // for this option to have any effect. + LogSpans bool `yaml:"logSpans"` + + // LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address + LocalAgentHostPort string `yaml:"localAgentHostPort"` +} + +// BaggageRestrictionsConfig configures the baggage restrictions manager which can be used to whitelist +// certain baggage keys. All fields are optional. +type BaggageRestrictionsConfig struct { + // DenyBaggageOnInitializationFailure controls the startup failure mode of the baggage restriction + // manager. If true, the manager will not allow any baggage to be written until baggage restrictions have + // been retrieved from jaeger-agent. If false, the manager wil allow any baggage to be written until baggage + // restrictions have been retrieved from jaeger-agent. + DenyBaggageOnInitializationFailure bool `yaml:"denyBaggageOnInitializationFailure"` + + // HostPort is the hostPort of jaeger-agent's baggage restrictions server + HostPort string `yaml:"hostPort"` + + // RefreshInterval controls how often the baggage restriction manager will poll + // jaeger-agent for the most recent baggage restrictions. + RefreshInterval time.Duration `yaml:"refreshInterval"` +} + +type nullCloser struct{} + +func (*nullCloser) Close() error { return nil } + +// New creates a new Jaeger Tracer, and a closer func that can be used to flush buffers +// before shutdown. +func (c Configuration) New( + serviceName string, + options ...Option, +) (opentracing.Tracer, io.Closer, error) { + if serviceName == "" { + return nil, nil, errors.New("no service name provided") + } + if c.Disabled { + return &opentracing.NoopTracer{}, &nullCloser{}, nil + } + opts := applyOptions(options...) + tracerMetrics := jaeger.NewMetrics(opts.metrics, nil) + if c.RPCMetrics { + Observer( + rpcmetrics.NewObserver( + opts.metrics.Namespace("jaeger-rpc", map[string]string{"component": "jaeger"}), + rpcmetrics.DefaultNameNormalizer, + ), + )(&opts) // adds to c.observers + } + if c.Sampler == nil { + c.Sampler = &SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: defaultSamplingProbability, + } + } + if c.Reporter == nil { + c.Reporter = &ReporterConfig{} + } + + sampler, err := c.Sampler.NewSampler(serviceName, tracerMetrics) + if err != nil { + return nil, nil, err + } + + reporter := opts.reporter + if reporter == nil { + r, err := c.Reporter.NewReporter(serviceName, tracerMetrics, opts.logger) + if err != nil { + return nil, nil, err + } + reporter = r + } + + tracerOptions := []jaeger.TracerOption{ + jaeger.TracerOptions.Metrics(tracerMetrics), + jaeger.TracerOptions.Logger(opts.logger), + jaeger.TracerOptions.CustomHeaderKeys(c.Headers), + jaeger.TracerOptions.Gen128Bit(opts.gen128Bit), + jaeger.TracerOptions.ZipkinSharedRPCSpan(opts.zipkinSharedRPCSpan), + } + + for _, tag := range opts.tags { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Tag(tag.Key, tag.Value)) + } + + for _, obs := range opts.observers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.Observer(obs)) + } + + for _, cobs := range opts.contribObservers { + tracerOptions = append(tracerOptions, jaeger.TracerOptions.ContribObserver(cobs)) + } + + if c.BaggageRestrictions != nil { + mgr := remote.NewRestrictionManager( + serviceName, + remote.Options.Metrics(tracerMetrics), + remote.Options.Logger(opts.logger), + remote.Options.HostPort(c.BaggageRestrictions.HostPort), + remote.Options.RefreshInterval(c.BaggageRestrictions.RefreshInterval), + remote.Options.DenyBaggageOnInitializationFailure( + c.BaggageRestrictions.DenyBaggageOnInitializationFailure, + ), + ) + tracerOptions = append(tracerOptions, jaeger.TracerOptions.BaggageRestrictionManager(mgr)) + } + + tracer, closer := jaeger.NewTracer( + serviceName, + sampler, + reporter, + tracerOptions...) + + return tracer, closer, nil +} + +// InitGlobalTracer creates a new Jaeger Tracer, and sets it as global OpenTracing Tracer. +// It returns a closer func that can be used to flush buffers before shutdown. +func (c Configuration) InitGlobalTracer( + serviceName string, + options ...Option, +) (io.Closer, error) { + if c.Disabled { + return &nullCloser{}, nil + } + tracer, closer, err := c.New(serviceName, options...) + if err != nil { + return nil, err + } + opentracing.InitGlobalTracer(tracer) + return closer, nil +} + +// NewSampler creates a new sampler based on the configuration +func (sc *SamplerConfig) NewSampler( + serviceName string, + metrics *jaeger.Metrics, +) (jaeger.Sampler, error) { + samplerType := strings.ToLower(sc.Type) + if samplerType == jaeger.SamplerTypeConst { + return jaeger.NewConstSampler(sc.Param != 0), nil + } + if samplerType == jaeger.SamplerTypeProbabilistic { + if sc.Param >= 0 && sc.Param <= 1.0 { + return jaeger.NewProbabilisticSampler(sc.Param) + } + return nil, fmt.Errorf( + "Invalid Param for probabilistic sampler: %v. Expecting value between 0 and 1", + sc.Param, + ) + } + if samplerType == jaeger.SamplerTypeRateLimiting { + return jaeger.NewRateLimitingSampler(sc.Param), nil + } + if samplerType == jaeger.SamplerTypeRemote || sc.Type == "" { + sc2 := *sc + sc2.Type = jaeger.SamplerTypeProbabilistic + initSampler, err := sc2.NewSampler(serviceName, nil) + if err != nil { + return nil, err + } + options := []jaeger.SamplerOption{ + jaeger.SamplerOptions.Metrics(metrics), + jaeger.SamplerOptions.InitialSampler(initSampler), + jaeger.SamplerOptions.SamplingServerURL(sc.SamplingServerURL), + } + if sc.MaxOperations != 0 { + options = append(options, jaeger.SamplerOptions.MaxOperations(sc.MaxOperations)) + } + if sc.SamplingRefreshInterval != 0 { + options = append(options, jaeger.SamplerOptions.SamplingRefreshInterval(sc.SamplingRefreshInterval)) + } + return jaeger.NewRemotelyControlledSampler(serviceName, options...), nil + } + return nil, fmt.Errorf("Unknown sampler type %v", sc.Type) +} + +// NewReporter instantiates a new reporter that submits spans to tcollector +func (rc *ReporterConfig) NewReporter( + serviceName string, + metrics *jaeger.Metrics, + logger jaeger.Logger, +) (jaeger.Reporter, error) { + sender, err := rc.newTransport() + if err != nil { + return nil, err + } + reporter := jaeger.NewRemoteReporter( + sender, + jaeger.ReporterOptions.QueueSize(rc.QueueSize), + jaeger.ReporterOptions.BufferFlushInterval(rc.BufferFlushInterval), + jaeger.ReporterOptions.Logger(logger), + jaeger.ReporterOptions.Metrics(metrics)) + if rc.LogSpans && logger != nil { + logger.Infof("Initializing logging reporter\n") + reporter = jaeger.NewCompositeReporter(jaeger.NewLoggingReporter(logger), reporter) + } + return reporter, err +} + +func (rc *ReporterConfig) newTransport() (jaeger.Transport, error) { + return jaeger.NewUDPTransport(rc.LocalAgentHostPort, 0) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/config/config_test.go b/vendor/src/github.com/uber/jaeger-client-go/config/config_test.go new file mode 100644 index 00000000..4ad9afda --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/config/config_test.go @@ -0,0 +1,259 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" +) + +func TestNewSamplerConst(t *testing.T) { + constTests := []struct { + param float64 + decision bool + }{{1, true}, {0, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeConst, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + require.NoError(t, err) + s1, ok := s.(*jaeger.ConstSampler) + require.True(t, ok, "converted to constSampler") + require.Equal(t, tst.decision, s1.Decision, "decision") + } +} + +func TestNewSamplerProbabilistic(t *testing.T) { + constTests := []struct { + param float64 + error bool + }{{1.5, true}, {0.5, false}} + for _, tst := range constTests { + cfg := &SamplerConfig{Type: jaeger.SamplerTypeProbabilistic, Param: tst.param} + s, err := cfg.NewSampler("x", nil) + if tst.error { + require.Error(t, err) + } else { + require.NoError(t, err) + _, ok := s.(*jaeger.ProbabilisticSampler) + require.True(t, ok, "converted to ProbabilisticSampler") + } + } +} + +func TestDefaultSampler(t *testing.T) { + cfg := Configuration{ + Sampler: &SamplerConfig{Type: "InvalidType"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestInvalidSamplerType(t *testing.T) { + cfg := &SamplerConfig{MaxOperations: 10} + s, err := cfg.NewSampler("x", jaeger.NewNullMetrics()) + require.NoError(t, err) + rcs, ok := s.(*jaeger.RemotelyControlledSampler) + require.True(t, ok, "converted to RemotelyControlledSampler") + rcs.Close() +} + +func TestDefaultConfig(t *testing.T) { + cfg := Configuration{} + _, _, err := cfg.New("", Metrics(metrics.NullFactory), Logger(log.NullLogger)) + require.EqualError(t, err, "no service name provided") + + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestDisabledFlag(t *testing.T) { + cfg := Configuration{Disabled: true} + _, closer, err := cfg.New("testService") + defer closer.Close() + require.NoError(t, err) +} + +func TestNewReporterError(t *testing.T) { + cfg := Configuration{ + Reporter: &ReporterConfig{LocalAgentHostPort: "bad_local_agent"}, + } + _, _, err := cfg.New("testService") + require.Error(t, err) +} + +func TestInitGlobalTracer(t *testing.T) { + // Save the existing GlobalTracer and replace after finishing function + prevTracer := opentracing.GlobalTracer() + defer opentracing.InitGlobalTracer(prevTracer) + noopTracer := opentracing.NoopTracer{} + + tests := []struct { + cfg Configuration + shouldErr bool + tracerChanged bool + }{ + { + cfg: Configuration{Disabled: true}, + shouldErr: false, + tracerChanged: false, + }, + { + cfg: Configuration{Sampler: &SamplerConfig{Type: "InvalidType"}}, + shouldErr: true, + tracerChanged: false, + }, + { + cfg: Configuration{ + Sampler: &SamplerConfig{ + Type: "remote", + SamplingRefreshInterval: 1, + }, + }, + shouldErr: false, + tracerChanged: true, + }, + { + cfg: Configuration{}, + shouldErr: false, + tracerChanged: true, + }, + } + for _, test := range tests { + opentracing.InitGlobalTracer(noopTracer) + _, err := test.cfg.InitGlobalTracer("testService") + if test.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if test.tracerChanged { + require.NotEqual(t, noopTracer, opentracing.GlobalTracer()) + } else { + require.Equal(t, noopTracer, opentracing.GlobalTracer()) + } + } +} + +func TestConfigWithReporter(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New("test", Reporter(r)) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test").Finish() + assert.Len(t, r.GetSpans(), 1) +} + +func TestConfigWithRPCMetrics(t *testing.T) { + metrics := metrics.NewLocalFactory(0) + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + r := jaeger.NewInMemoryReporter() + tracer, closer, err := c.New( + "test", + Reporter(r), + Metrics(metrics), + ContribObserver(fakeContribObserver{}), + ) + require.NoError(t, err) + defer closer.Close() + + tracer.StartSpan("test", ext.SpanKindRPCServer).Finish() + + testutils.AssertCounterMetrics(t, metrics, + testutils.ExpectedMetric{ + Name: "jaeger-rpc.requests", + Tags: map[string]string{"component": "jaeger", "endpoint": "test", "error": "false"}, + Value: 1, + }, + ) +} + +func TestBaggageRestrictionsConfig(t *testing.T) { + m := metrics.NewLocalFactory(0) + c := Configuration{ + BaggageRestrictions: &BaggageRestrictionsConfig{ + HostPort: "not:1929213", + RefreshInterval: time.Minute, + }, + } + _, closer, err := c.New( + "test", + Metrics(m), + ) + require.NoError(t, err) + defer closer.Close() + + metricName := "jaeger.baggage-restrictions-update" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := m.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, m, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) +} + +func TestConfigWithGen128Bit(t *testing.T) { + c := Configuration{ + Sampler: &SamplerConfig{ + Type: "const", + Param: 1, + }, + RPCMetrics: true, + } + tracer, closer, err := c.New("test", Gen128Bit(true)) + require.NoError(t, err) + defer closer.Close() + + span := tracer.StartSpan("test") + defer span.Finish() + traceID := span.Context().(jaeger.SpanContext).TraceID() + require.True(t, traceID.High != 0) + require.True(t, traceID.Low != 0) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/config/example_test.go b/vendor/src/github.com/uber/jaeger-client-go/config/example_test.go new file mode 100644 index 00000000..befaae8d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/config/example_test.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config_test + +import ( + "log" + + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" + jaegercfg "github.com/uber/jaeger-client-go/config" + jaegerlog "github.com/uber/jaeger-client-go/log" +) + +func ExampleConfiguration_InitGlobalTracer_testing() { + // Sample configuration for testing. Use constant sampling to sample every trace + // and enable LogSpan to log every span via configured Logger. + cfg := jaegercfg.Configuration{ + Sampler: &jaegercfg.SamplerConfig{ + Type: jaeger.SamplerTypeConst, + Param: 1, + }, + Reporter: &jaegercfg.ReporterConfig{ + LogSpans: true, + }, + } + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} + +func ExampleConfiguration_InitGlobalTracer_production() { + // Recommended configuration for production. + cfg := jaegercfg.Configuration{} + + // Example logger and metrics factory. Use github.com/uber/jaeger-client-go/log + // and github.com/uber/jaeger-lib/metrics respectively to bind to real logging and metrics + // frameworks. + jLogger := jaegerlog.StdLogger + jMetricsFactory := metrics.NullFactory + + // Initialize tracer with a logger and a metrics factory + closer, err := cfg.InitGlobalTracer( + "serviceName", + jaegercfg.Logger(jLogger), + jaegercfg.Metrics(jMetricsFactory), + ) + if err != nil { + log.Printf("Could not initialize jaeger tracer: %s", err.Error()) + return + } + defer closer.Close() + + // continue main() +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/config/options.go b/vendor/src/github.com/uber/jaeger-client-go/config/options.go new file mode 100644 index 00000000..76486440 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/config/options.go @@ -0,0 +1,113 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +// Option is a function that sets some option on the client. +type Option func(c *Options) + +// Options control behavior of the client. +type Options struct { + metrics metrics.Factory + logger jaeger.Logger + reporter jaeger.Reporter + contribObservers []jaeger.ContribObserver + observers []jaeger.Observer + gen128Bit bool + zipkinSharedRPCSpan bool + tags []opentracing.Tag +} + +// Metrics creates an Option that initializes Metrics in the tracer, +// which is used to emit statistics about spans. +func Metrics(factory metrics.Factory) Option { + return func(c *Options) { + c.metrics = factory + } +} + +// Logger can be provided to log Reporter errors, as well as to log spans +// if Reporter.LogSpans is set to true. +func Logger(logger jaeger.Logger) Option { + return func(c *Options) { + c.logger = logger + } +} + +// Reporter can be provided explicitly to override the configuration. +// Useful for testing, e.g. by passing InMemoryReporter. +func Reporter(reporter jaeger.Reporter) Option { + return func(c *Options) { + c.reporter = reporter + } +} + +// Observer can be registered with the Tracer to receive notifications about new Spans. +func Observer(observer jaeger.Observer) Option { + return func(c *Options) { + c.observers = append(c.observers, observer) + } +} + +// ContribObserver can be registered with the Tracer to recieve notifications +// about new spans. +func ContribObserver(observer jaeger.ContribObserver) Option { + return func(c *Options) { + c.contribObservers = append(c.contribObservers, observer) + } +} + +// Gen128Bit specifies whether to generate 128bit trace IDs. +func Gen128Bit(gen128Bit bool) Option { + return func(c *Options) { + c.gen128Bit = gen128Bit + } +} + +// ZipkinSharedRPCSpan creates an option that enables sharing span ID between client +// and server spans a la zipkin. If false, client and server spans will be assigned +// different IDs. +func ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) Option { + return func(c *Options) { + c.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +// Tag creates an option that adds a tracer-level tag. +func Tag(key string, value interface{}) Option { + return func(c *Options) { + c.tags = append(c.tags, opentracing.Tag{Key: key, Value: value}) + } +} + +func applyOptions(options ...Option) Options { + opts := Options{} + for _, option := range options { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = metrics.NullFactory + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + return opts +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/config/options_test.go b/vendor/src/github.com/uber/jaeger-client-go/config/options_test.go new file mode 100644 index 00000000..01f33802 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/config/options_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package config + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + + "github.com/uber/jaeger-client-go" +) + +func TestApplyOptions(t *testing.T) { + metricsFactory := metrics.NewLocalFactory(0) + observer := fakeObserver{} + contribObserver := fakeContribObserver{} + opts := applyOptions( + Metrics(metricsFactory), + Logger(jaeger.StdLogger), + Observer(observer), + ContribObserver(contribObserver), + Gen128Bit(true), + ZipkinSharedRPCSpan(true), + ) + assert.Equal(t, jaeger.StdLogger, opts.logger) + assert.Equal(t, metricsFactory, opts.metrics) + assert.Equal(t, []jaeger.Observer{observer}, opts.observers) + assert.Equal(t, []jaeger.ContribObserver{contribObserver}, opts.contribObservers) + assert.True(t, opts.gen128Bit) + assert.True(t, opts.zipkinSharedRPCSpan) +} + +func TestTraceTagOption(t *testing.T) { + c := Configuration{} + tracer, closer, err := c.New("test-service", Tag("tag-key", "tag-value")) + require.NoError(t, err) + defer closer.Close() + assert.Equal(t, opentracing.Tag{Key: "tag-key", Value: "tag-value"}, tracer.(*jaeger.Tracer).Tags()[0]) +} + +func TestApplyOptionsDefaults(t *testing.T) { + opts := applyOptions() + assert.Equal(t, jaeger.NullLogger, opts.logger) + assert.Equal(t, metrics.NullFactory, opts.metrics) +} + +type fakeObserver struct{} + +func (o fakeObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) jaeger.SpanObserver { + return nil +} + +type fakeContribObserver struct{} + +func (o fakeContribObserver) OnStartSpan(span opentracing.Span, operationName string, options opentracing.StartSpanOptions) (jaeger.ContribSpanObserver, bool) { + return nil, false +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/constants.go b/vendor/src/github.com/uber/jaeger-client-go/constants.go new file mode 100644 index 00000000..fda2daa8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/constants.go @@ -0,0 +1,76 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +const ( + // JaegerClientVersion is the version of the client library reported as Span tag. + JaegerClientVersion = "Go-2.9.1dev" + + // JaegerClientVersionTagKey is the name of the tag used to report client version. + JaegerClientVersionTagKey = "jaeger.version" + + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader = "jaeger-debug-id" + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader = "jaeger-baggage" + + // TracerHostnameTagKey used to report host name of the process. + TracerHostnameTagKey = "hostname" + + // TracerIPTagKey used to report ip of the process. + TracerIPTagKey = "ip" + + // SamplerTypeTagKey reports which sampler was used on the root span. + SamplerTypeTagKey = "sampler.type" + + // SamplerParamTagKey reports the parameter of the sampler, like sampling probability. + SamplerParamTagKey = "sampler.param" + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName = "uber-trace-id" + + // TracerStateHeaderName is deprecated. + // Deprecated: use TraceContextHeaderName + TracerStateHeaderName = TraceContextHeaderName + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix = "uberctx-" + + // SamplerTypeConst is the type of sampler that always makes the same decision. + SamplerTypeConst = "const" + + // SamplerTypeRemote is the type of sampler that polls Jaeger agent for sampling strategy. + SamplerTypeRemote = "remote" + + // SamplerTypeProbabilistic is the type of sampler that samples traces + // with a certain fixed probability. + SamplerTypeProbabilistic = "probabilistic" + + // SamplerTypeRateLimiting is the type of sampler that samples + // only up to a fixed number of traces per second. + SamplerTypeRateLimiting = "ratelimiting" + + // SamplerTypeLowerBound is the type of sampler that samples + // only up to a fixed number of traces per second. + SamplerTypeLowerBound = "lowerbound" +) diff --git a/vendor/src/github.com/uber/jaeger-client-go/constants_test.go b/vendor/src/github.com/uber/jaeger-client-go/constants_test.go new file mode 100644 index 00000000..0beae191 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/constants_test.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "strings" + "testing" +) + +func TestHeaderConstants(t *testing.T) { + if TraceContextHeaderName != strings.ToLower(TraceContextHeaderName) { + t.Errorf("TraceContextHeaderName is not lower-case: %+v", TraceContextHeaderName) + } + if TraceBaggageHeaderPrefix != strings.ToLower(TraceBaggageHeaderPrefix) { + t.Errorf("TraceBaggageHeaderPrefix is not lower-case: %+v", TraceBaggageHeaderPrefix) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/context.go b/vendor/src/github.com/uber/jaeger-client-go/context.go new file mode 100644 index 00000000..8b06173d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/context.go @@ -0,0 +1,258 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + flagSampled = byte(1) + flagDebug = byte(2) +) + +var ( + errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state") + errMalformedTracerStateString = errors.New("String does not match tracer state format") + + emptyContext = SpanContext{} +) + +// TraceID represents unique 128bit identifier of a trace +type TraceID struct { + High, Low uint64 +} + +// SpanID represents unique 64bit identifier of a span +type SpanID uint64 + +// SpanContext represents propagated span identity and state +type SpanContext struct { + // traceID represents globally unique ID of the trace. + // Usually generated as a random number. + traceID TraceID + + // spanID represents span ID that must be unique within its trace, + // but does not have to be globally unique. + spanID SpanID + + // parentID refers to the ID of the parent span. + // Should be 0 if the current span is a root span. + parentID SpanID + + // flags is a bitmap containing such bits as 'sampled' and 'debug'. + flags byte + + // Distributed Context baggage. The is a snapshot in time. + baggage map[string]string + + // debugID can be set to some correlation ID when the context is being + // extracted from a TextMap carrier. + // + // See JaegerDebugHeader in constants.go + debugID string +} + +// ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext +func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) { + for k, v := range c.baggage { + if !handler(k, v) { + break + } + } +} + +// IsSampled returns whether this trace was chosen for permanent storage +// by the sampling mechanism of the tracer. +func (c SpanContext) IsSampled() bool { + return (c.flags & flagSampled) == flagSampled +} + +// IsDebug indicates whether sampling was explicitly requested by the service. +func (c SpanContext) IsDebug() bool { + return (c.flags & flagDebug) == flagDebug +} + +// IsValid indicates whether this context actually represents a valid trace. +func (c SpanContext) IsValid() bool { + return c.traceID.IsValid() && c.spanID != 0 +} + +func (c SpanContext) String() string { + if c.traceID.High == 0 { + return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) + } + return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags) +} + +// ContextFromString reconstructs the Context encoded in a string +func ContextFromString(value string) (SpanContext, error) { + var context SpanContext + if value == "" { + return emptyContext, errEmptyTracerStateString + } + parts := strings.Split(value, ":") + if len(parts) != 4 { + return emptyContext, errMalformedTracerStateString + } + var err error + if context.traceID, err = TraceIDFromString(parts[0]); err != nil { + return emptyContext, err + } + if context.spanID, err = SpanIDFromString(parts[1]); err != nil { + return emptyContext, err + } + if context.parentID, err = SpanIDFromString(parts[2]); err != nil { + return emptyContext, err + } + flags, err := strconv.ParseUint(parts[3], 10, 8) + if err != nil { + return emptyContext, err + } + context.flags = byte(flags) + return context, nil +} + +// TraceID returns the trace ID of this span context +func (c SpanContext) TraceID() TraceID { + return c.traceID +} + +// SpanID returns the span ID of this span context +func (c SpanContext) SpanID() SpanID { + return c.spanID +} + +// ParentID returns the parent span ID of this span context +func (c SpanContext) ParentID() SpanID { + return c.parentID +} + +// NewSpanContext creates a new instance of SpanContext +func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext { + flags := byte(0) + if sampled { + flags = flagSampled + } + return SpanContext{ + traceID: traceID, + spanID: spanID, + parentID: parentID, + flags: flags, + baggage: baggage} +} + +// CopyFrom copies data from ctx into this context, including span identity and baggage. +// TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing. +func (c *SpanContext) CopyFrom(ctx *SpanContext) { + c.traceID = ctx.traceID + c.spanID = ctx.spanID + c.parentID = ctx.parentID + c.flags = ctx.flags + if l := len(ctx.baggage); l > 0 { + c.baggage = make(map[string]string, l) + for k, v := range ctx.baggage { + c.baggage[k] = v + } + } else { + c.baggage = nil + } +} + +// WithBaggageItem creates a new context with an extra baggage item. +func (c SpanContext) WithBaggageItem(key, value string) SpanContext { + var newBaggage map[string]string + if c.baggage == nil { + newBaggage = map[string]string{key: value} + } else { + newBaggage = make(map[string]string, len(c.baggage)+1) + for k, v := range c.baggage { + newBaggage[k] = v + } + newBaggage[key] = value + } + // Use positional parameters so the compiler will help catch new fields. + return SpanContext{c.traceID, c.spanID, c.parentID, c.flags, newBaggage, ""} +} + +// isDebugIDContainerOnly returns true when the instance of the context is only +// used to return the debug/correlation ID from extract() method. This happens +// in the situation when "jaeger-debug-id" header is passed in the carrier to +// the extract() method, but the request otherwise has no span context in it. +// Previously this would've returned opentracing.ErrSpanContextNotFound from the +// extract method, but now it returns a dummy context with only debugID filled in. +// +// See JaegerDebugHeader in constants.go +// See textMapPropagator#Extract +func (c *SpanContext) isDebugIDContainerOnly() bool { + return !c.traceID.IsValid() && c.debugID != "" +} + +// ------- TraceID ------- + +func (t TraceID) String() string { + if t.High == 0 { + return fmt.Sprintf("%x", t.Low) + } + return fmt.Sprintf("%x%016x", t.High, t.Low) +} + +// TraceIDFromString creates a TraceID from a hexadecimal string +func TraceIDFromString(s string) (TraceID, error) { + var hi, lo uint64 + var err error + if len(s) > 32 { + return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s) + } else if len(s) > 16 { + hiLen := len(s) - 16 + if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil { + return TraceID{}, err + } + if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil { + return TraceID{}, err + } + } else { + if lo, err = strconv.ParseUint(s, 16, 64); err != nil { + return TraceID{}, err + } + } + return TraceID{High: hi, Low: lo}, nil +} + +// IsValid checks if the trace ID is valid, i.e. not zero. +func (t TraceID) IsValid() bool { + return t.High != 0 || t.Low != 0 +} + +// ------- SpanID ------- + +func (s SpanID) String() string { + return fmt.Sprintf("%x", uint64(s)) +} + +// SpanIDFromString creates a SpanID from a hexadecimal string +func SpanIDFromString(s string) (SpanID, error) { + if len(s) > 16 { + return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s) + } + id, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return SpanID(0), err + } + return SpanID(id), nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/context_test.go b/vendor/src/github.com/uber/jaeger-client-go/context_test.go new file mode 100644 index 00000000..34dfc745 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/context_test.go @@ -0,0 +1,110 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestContextFromString(t *testing.T) { + var err error + _, err = ContextFromString("") + assert.Error(t, err) + _, err = ContextFromString("abcd") + assert.Error(t, err) + _, err = ContextFromString("x:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:x:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:x:1") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("1:1:1:x") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901234:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("01234567890123456789012345678901:1:1:1") + assert.NoError(t, err) + _, err = ContextFromString("01234_67890123456789012345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("0123456789012345678901_345678901:1:1:1") + assert.Error(t, err) + _, err = ContextFromString("1:0123456789012345:1:1") + assert.NoError(t, err) + _, err = ContextFromString("1:01234567890123456:1:1") + assert.Error(t, err) + ctx, err := ContextFromString("10000000000000001:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{High: 1, Low: 1}, ctx.traceID) + ctx, err = ContextFromString("1:1:1:1") + assert.NoError(t, err) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + ctx = NewSpanContext(TraceID{Low: 1}, 1, 1, true, nil) + assert.EqualValues(t, TraceID{Low: 1}, ctx.traceID) + assert.EqualValues(t, 1, ctx.spanID) + assert.EqualValues(t, 1, ctx.parentID) + assert.EqualValues(t, 1, ctx.flags) + assert.Equal(t, "ff", SpanID(255).String()) + assert.Equal(t, "ff", TraceID{Low: 255}.String()) + assert.Equal(t, "ff00000000000000ff", TraceID{High: 255, Low: 255}.String()) + ctx = NewSpanContext(TraceID{High: 255, Low: 255}, SpanID(1), SpanID(1), false, nil) + assert.Equal(t, "ff00000000000000ff:1:1:0", ctx.String()) +} + +func TestSpanContext_WithBaggageItem(t *testing.T) { + var ctx SpanContext + ctx = ctx.WithBaggageItem("some-KEY", "Some-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Value"}, ctx.baggage) + ctx = ctx.WithBaggageItem("some-KEY", "Some-Other-Value") + assert.Equal(t, map[string]string{"some-KEY": "Some-Other-Value"}, ctx.baggage) +} + +func TestSpanContext_SampledDebug(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:3") + require.NoError(t, err) + assert.True(t, ctx.IsSampled()) + assert.True(t, ctx.IsDebug()) + + ctx, err = ContextFromString("1:1:1:0") + require.NoError(t, err) + assert.False(t, ctx.IsSampled()) + assert.False(t, ctx.IsDebug()) +} + +func TestSpanContext_CopyFrom(t *testing.T) { + ctx, err := ContextFromString("1:1:1:1") + require.NoError(t, err) + ctx2 := SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + // with baggage + ctx = ctx.WithBaggageItem("x", "y") + ctx2 = SpanContext{} + ctx2.CopyFrom(&ctx) + assert.Equal(t, ctx, ctx2) + assert.Equal(t, "y", ctx2.baggage["x"]) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/contrib_observer.go b/vendor/src/github.com/uber/jaeger-client-go/contrib_observer.go new file mode 100644 index 00000000..4ce1881f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/contrib_observer.go @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + opentracing "github.com/opentracing/opentracing-go" +) + +// ContribObserver can be registered with the Tracer to receive notifications +// about new Spans. Modelled after github.com/opentracing-contrib/go-observer. +type ContribObserver interface { + // Create and return a span observer. Called when a span starts. + // If the Observer is not interested in the given span, it must return (nil, false). + // E.g : + // func StartSpan(opName string, opts ...opentracing.StartSpanOption) { + // var sp opentracing.Span + // sso := opentracing.StartSpanOptions{} + // if spanObserver, ok := Observer.OnStartSpan(span, opName, sso); ok { + // // we have a valid SpanObserver + // } + // ... + // } + OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) +} + +// ContribSpanObserver is created by the Observer and receives notifications +// about other Span events. This interface is meant to match +// github.com/opentracing-contrib/go-observer, via duck typing, without +// directly importing the go-observer package. +type ContribSpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// wrapper observer for the old observers (see observer.go) +type oldObserver struct { + obs Observer +} + +func (o *oldObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) (ContribSpanObserver, bool) { + spanObserver := o.obs.OnStartSpan(operationName, options) + return spanObserver, spanObserver != nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/Dockerfile b/vendor/src/github.com/uber/jaeger-client-go/crossdock/Dockerfile new file mode 100644 index 00000000..ae8284f1 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/Dockerfile @@ -0,0 +1,4 @@ +FROM scratch +ADD crossdock / +CMD ["/crossdock"] +EXPOSE 8080-8082 diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/README.md b/vendor/src/github.com/uber/jaeger-client-go/crossdock/README.md new file mode 100644 index 00000000..2faf91ad --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/README.md @@ -0,0 +1,78 @@ +# Crossdock-based Integration Test Suite + +This package implements integration test suite for testing +interoperability between different Jaeger client libraries. + +## Actors + +There are five actors participating in any given test case, +the crossdock driver itself, a Client, and three Servers, S1-S3. + +### Driver + +The crossdock driver reads axis and test definitions from the YAML file, +generates permutations based on values listed for each axis, and +makes an HTTP request to the Client, passing it the selected value +for each axis. + +### Client + +Client runs as part of the `jaeger-client/go` image and orchestrates +the actual test case with the servers S1-S3. The incoming request +from the driver is expected to have parameters defined in +[client/constants.go](client/constants.go), which specify + + 1. The type of test to execute (only `trace` is currently supported) + 1. Whether the trace should be sampled or not + 1. For each of the servers S1-S3: + * the name of the server (same as docker image name, same as host name) + * the transport to send request to that server (http or TChannel) + * the type of client to use (e.g. in Python, `urllib2` vs. `requests`) + +The Client translates the parameters into a "call tree" instruction set, +and calls S1, which in turn calls S2 with the sub-set of instructions, +and so on. Upon receiving the response from S1, the Client validates the +response against the conditions of the test, and returns a summary result +to the crossdock driver, indicating a success of a failure of the test. +For the `trace` test type, the success conditions are that at all levels +the observed tracing spans have the same trace ID, the same sampling flag +equal to the input `sampled` parameter, and the same value of a baggage +item. The baggage item value is randomly selected by the client at the +start of each test. + +### Servers + +Servers represent examples of business services with Jaeger tracing enabled. +Servers must be implemented for each supported language, and potentially +multiple times for a given language depending on the framework used to build +the service, such as Flask vs. Tornado in Python. Each implementation of the +server may act as any of the S1-S3 servers in the test. Each server must +implement the `TracedService` interface from +[thrift/tracetest.thrift](thrift/tracetest.thrift): + + service TracedService { + TraceResponse startTrace(1: StartTraceRequest request) + TraceResponse joinTrace(1: JoinTraceRequest request) + } + + * In `startTrace` the server is supposed to ignore any trace it may have + received via inbound request and start a brand new trace, with the + sampling flag set appropriately, using `sampling.priority` tag, + see [Go server implementation](server/trace.go) for example. + * In `joinTrace` the server is supposed to respect the trace in the + inbound request and propagate it to the outbound downstream request. + +The response from the server contains the information about the current +tracing span it has observed (or started), including trace ID, sampling +flag, and the value of a baggage item. For S1 and S2 the response also +includes the response of the downstream server. + +## Running the tests + +The intended setup is that every commit to master branch of each of the client +libraries results in a build of a new docker image (or images, e.g. in Python). +When a new pull request is tested against one of the libraries, it will build +a new image from the modified version of the library, and use the existing +images for the other languages. The `docker-compose.yaml` file refers to those +images by name. + diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client.go new file mode 100644 index 00000000..16a9148a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client.go @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "fmt" + "net" + "net/http" + "sync" + + "github.com/crossdock/crossdock-go" + + "github.com/uber/jaeger-client-go/crossdock/common" +) + +// Client is a controller for the tests +type Client struct { + ClientHostPort string + ServerPortHTTP string + ServerPortTChannel string + listener net.Listener + hostMapper func(service string) string +} + +// Start begins a blocking Crossdock client +func (c *Client) Start() error { + if err := c.Listen(); err != nil { + return err + } + return c.Serve() +} + +// AsyncStart begins a Crossdock client in the background, +// but does not return until it started serving. +func (c *Client) AsyncStart() error { + if err := c.Listen(); err != nil { + return err + } + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + c.Serve() + }() + started.Wait() + return nil +} + +// Listen initializes the server +func (c *Client) Listen() error { + c.setDefaultPort(&c.ClientHostPort, ":"+common.DefaultClientPortHTTP) + c.setDefaultPort(&c.ServerPortHTTP, common.DefaultServerPortHTTP) + c.setDefaultPort(&c.ServerPortTChannel, common.DefaultServerPortTChannel) + + behaviors := crossdock.Behaviors{ + behaviorTrace: c.trace, + } + + http.Handle("/", crossdock.Handler(behaviors, true)) + + listener, err := net.Listen("tcp", c.ClientHostPort) + if err != nil { + return err + } + c.listener = listener + c.ClientHostPort = listener.Addr().String() + return nil +} + +// Serve starts service crossdock traffic. This is a blocking call. +func (c *Client) Serve() error { + return http.Serve(c.listener, nil) +} + +// Close stops the client +func (c *Client) Close() error { + return c.listener.Close() +} + +// URL returns a URL that the client can be accessed on +func (c *Client) URL() string { + return fmt.Sprintf("http://%s/", c.ClientHostPort) +} + +func (c *Client) setDefaultPort(port *string, defaultPort string) { + if *port == "" { + *port = defaultPort + } +} + +func (c *Client) mapServiceToHost(service string) string { + mapper := c.hostMapper + if mapper == nil { + return service + } + return mapper(service) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client_test.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client_test.go new file mode 100644 index 00000000..dad89e2f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/client_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "net/url" + "testing" + + "github.com/crossdock/crossdock-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func TestCrossdock(t *testing.T) { + log.Enabled = false // enable when debugging tests + log.Printf("Starting crossdock test") + + var reporter jaeger.Reporter + if log.Enabled { + reporter = jaeger.NewLoggingReporter(jlog.StdLogger) + } else { + reporter = jaeger.NewNullReporter() + } + + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + reporter) + defer tCloser.Close() + + s := &server.Server{ + HostPortHTTP: "127.0.0.1:0", + HostPortTChannel: "127.0.0.1:0", + Tracer: tracer, + } + err := s.Start() + require.NoError(t, err) + defer s.Close() + + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: s.GetPortHTTP(), + ServerPortTChannel: s.GetPortTChannel(), + hostMapper: func(server string) string { return "localhost" }, + } + err = c.AsyncStart() + require.NoError(t, err) + defer c.Close() + + crossdock.Wait(t, s.URL(), 10) + crossdock.Wait(t, c.URL(), 10) + + behaviors := []struct { + name string + axes map[string][]string + }{ + { + name: behaviorTrace, + axes: map[string][]string{ + server1NameParam: {common.DefaultServiceName}, + sampledParam: {"true", "false"}, + server2NameParam: {common.DefaultServiceName}, + server2TransportParam: {transportHTTP, transportTChannel, transportDummy}, + server3NameParam: {common.DefaultServiceName}, + server3TransportParam: {transportHTTP, transportTChannel}, + }, + }, + } + + for _, bb := range behaviors { + for _, entry := range crossdock.Combinations(bb.axes) { + entryArgs := url.Values{} + for k, v := range entry { + entryArgs.Set(k, v) + } + // test via real HTTP call + crossdock.Call(t, c.URL(), bb.name, entryArgs) + } + } +} + +func TestHostMapper(t *testing.T) { + c := &Client{ + ClientHostPort: "127.0.0.1:0", + ServerPortHTTP: "8080", + ServerPortTChannel: "8081", + } + assert.Equal(t, "go", c.mapServiceToHost("go")) + c.hostMapper = func(server string) string { return "localhost" } + assert.Equal(t, "localhost", c.mapServiceToHost("go")) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/constants.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/constants.go new file mode 100644 index 00000000..52d73f54 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/constants.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +// Different parameter keys and values used by the system +const ( + // S1 instructions + sampledParam = "sampled" + server1NameParam = "s1name" + // S1->S2 instructions + server2NameParam = "s2name" + server2TransportParam = "s2transport" + // S2->S3 instructions + server3NameParam = "s3name" + server3TransportParam = "s3transport" + + transportHTTP = "http" + transportTChannel = "tchannel" + transportDummy = "dummy" + + behaviorTrace = "trace" + + // RoleS1 is the name of the role for server S1 + RoleS1 = "S1" + + // RoleS2 is the name of the role for server S2 + RoleS2 = "S2" + + // RoleS3 is the name of the role for server S3 + RoleS3 = "S3" +) diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/trace.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/trace.go new file mode 100644 index 00000000..cfb72416 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/client/trace.go @@ -0,0 +1,167 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package client + +import ( + "fmt" + "time" + + "github.com/crossdock/crossdock-go" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" +) + +func (c *Client) trace(t crossdock.T) { + sampled := str2bool(t.Param(sampledParam)) + baggage := randomBaggage() + + level1 := tracetest.NewStartTraceRequest() + level1.ServerRole = RoleS1 + level1.Sampled = sampled + level1.Baggage = baggage + server1 := t.Param(server1NameParam) + + level2 := tracetest.NewDownstream() + level2.ServiceName = t.Param(server2NameParam) + level2.ServerRole = RoleS2 + level2.Host = c.mapServiceToHost(level2.ServiceName) + level2.Port = c.transport2port(t.Param(server2TransportParam)) + level2.Transport = transport2transport(t.Param(server2TransportParam)) + level1.Downstream = level2 + + level3 := tracetest.NewDownstream() + level3.ServiceName = t.Param(server3NameParam) + level3.ServerRole = RoleS3 + level3.Host = c.mapServiceToHost(level3.ServiceName) + level3.Port = c.transport2port(t.Param(server3TransportParam)) + level3.Transport = transport2transport(t.Param(server3TransportParam)) + level2.Downstream = level3 + + server1host := c.mapServiceToHost(server1) + url := fmt.Sprintf("http://%s:%s/start_trace", server1host, c.ServerPortHTTP) + resp, err := common.PostJSON(context.Background(), url, level1) + if err != nil { + t.Errorf(err.Error()) + return + } + + for r := resp; r != nil; r = r.Downstream { + if r.NotImplementedError != "" { + t.Skipf(r.NotImplementedError) + log.Printf("SKIP: %s", r.NotImplementedError) + return + } + } + + traceID := resp.Span.TraceId + if traceID == "" { + t.Errorf("Trace ID is empty in S1(%s)", server1) + return + } + + success := validateTrace(t, level1.Downstream, resp, server1, 1, traceID, sampled, baggage) + if success { + t.Successf("trace checks out") + log.Printf("PASS") + } +} + +func validateTrace( + t crossdock.T, + target *tracetest.Downstream, + resp *tracetest.TraceResponse, + service string, + level int, + traceID string, + sampled bool, + baggage string) bool { + + success := true + if traceID != resp.Span.TraceId { + t.Errorf("Trace ID mismatch in S%d(%s): expected %s, received %s", + level, service, traceID, resp.Span.TraceId) + success = false + } + if baggage != resp.Span.Baggage { + t.Errorf("Baggage mismatch in S%d(%s): expected %s, received %s", + level, service, baggage, resp.Span.Baggage) + success = false + } + if sampled != resp.Span.Sampled { + t.Errorf("Sampled mismatch in S%d(%s): expected %t, received %t", + level, service, sampled, resp.Span.Sampled) + success = false + } + if target != nil { + if resp.Downstream == nil { + t.Errorf("Missing downstream in S%d(%s)", level, service) + success = false + } else { + success = validateTrace(t, target.Downstream, resp.Downstream, + target.Host, level+1, traceID, sampled, baggage) && success + } + } else if resp.Downstream != nil { + t.Errorf("Unexpected downstream in S%d(%s)", level, service) + success = false + } + return success +} + +func randomBaggage() string { + r := utils.NewRand(time.Now().UnixNano()) + n := uint64(r.Int63()) + return fmt.Sprintf("%x", n) +} + +func str2bool(v string) bool { + switch v { + case "true": + return true + case "false": + return false + default: + panic(v + " is not a Boolean") + } +} + +func (c *Client) transport2port(v string) string { + switch v { + case transportHTTP: + return c.ServerPortHTTP + case transportTChannel: + return c.ServerPortTChannel + case transportDummy: + return "9999" + default: + panic("Unknown protocol " + v) + } +} + +func transport2transport(v string) tracetest.Transport { + switch v { + case transportHTTP: + return tracetest.Transport_HTTP + case transportTChannel: + return tracetest.Transport_TCHANNEL + case transportDummy: + return tracetest.Transport_DUMMY + default: + panic("Unknown protocol " + v) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/constants.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/constants.go new file mode 100644 index 00000000..f3c2837f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/constants.go @@ -0,0 +1,32 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package common + +const ( + // DefaultClientPortHTTP is the port where the client (controller) runs + DefaultClientPortHTTP = "8080" + + // DefaultServerPortHTTP is the port where HTTP server runs + DefaultServerPortHTTP = "8081" + + // DefaultServerPortTChannel is the port where TChannel server runs + DefaultServerPortTChannel = "8082" + + // DefaultServiceName is the service name used by TChannel server + DefaultServiceName = "go" + + // DefaultTracerServiceName is the service name used by the tracer + DefaultTracerServiceName = "crossdock-go" +) diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/json.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/json.go new file mode 100644 index 00000000..1b93544c --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/common/json.go @@ -0,0 +1,73 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package common + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" + "github.com/uber/jaeger-client-go/utils" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" +) + +// PostJSON sends a POST request to `url` with body containing JSON-serialized `req`. +// It injects tracing span into the headers (if found in the context). +// It returns parsed TraceResponse, or error. +func PostJSON(ctx context.Context, url string, req interface{}) (*tracetest.TraceResponse, error) { + data, err := json.Marshal(req) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/json") + + span, err := injectSpan(ctx, httpReq) + if span != nil { + defer span.Finish() + } + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(httpReq) + if err != nil { + return nil, err + } + + var result tracetest.TraceResponse + err = utils.ReadJSON(resp, &result) + return &result, err +} + +func injectSpan(ctx context.Context, req *http.Request) (opentracing.Span, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, nil + } + span = span.Tracer().StartSpan("post", opentracing.ChildOf(span.Context())) + ext.SpanKindRPCClient.Set(span) + c := opentracing.HTTPHeadersCarrier(req.Header) + err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, c) + return span, err +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml b/vendor/src/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml new file mode 100644 index 00000000..bc0c85af --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/docker-compose.yml @@ -0,0 +1,71 @@ +version: '2' + +services: + crossdock: + image: crossdock/crossdock + links: + - test_driver + - go + - java + - python + + environment: + - WAIT_FOR=test_driver,go,java,python + - WAIT_FOR_TIMEOUT=60s + + - CALL_TIMEOUT=60s + + - AXIS_CLIENT=go + - AXIS_S1NAME=go,java,python + - AXIS_SAMPLED=true,false + - AXIS_S2NAME=go,java,python + - AXIS_S2TRANSPORT=http,tchannel + - AXIS_S3NAME=go,java,python + - AXIS_S3TRANSPORT=http,tchannel + + - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2transport,s3name,s3transport + + - AXIS_TESTDRIVER=test_driver + - AXIS_SERVICES=go + + - BEHAVIOR_ENDTOEND=testdriver,services + + - REPORT=compact + go: + build: . + ports: + - "8080-8082" + + java: + image: jaegertracing/xdock-java + ports: + - "8080-8082" + + python: + image: jaegertracing/xdock-py + ports: + - "8080:8082" + + cassandra: + image: "cassandra:3.9" + + test_driver: + image: jaegertracing/test-driver + links: + - cassandra + depends_on: + - cassandra + ports: + - "8080" + +# node: +# image: yarpc/yarpc-node +# ports: +# - "8080-8082" +# +# python-sync: +# image: yarpc/yarpc-python +# ports: +# - 8080 +# environment: +# - SYNC=1 diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go new file mode 100644 index 00000000..837c3235 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler.go @@ -0,0 +1,137 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package endtoend + +import ( + "encoding/json" + "fmt" + "net/http" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" +) + +var ( + defaultSamplerType = jaeger.SamplerTypeRemote + + endToEndConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: defaultSamplerType, + Param: 1.0, + SamplingServerURL: "http://test_driver:5778/sampling", + SamplingRefreshInterval: 5 * time.Second, + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + LocalAgentHostPort: "test_driver:5775", + }, + } +) + +/*Handler handles creating traces from a http request. + * + * json: { + * "type": "remote", + * "operation": "operationName", + * "count": 2, + * "tags": { + * "key": "value" + * } + * } + * + * Given the above json payload, the handler will use a tracer with the RemotelyControlledSampler + * to create 2 traces for "operationName" operation with the tags: {"key":"value"}. These traces + * are reported to the agent with the hostname "test_driver". + */ +type Handler struct { + sync.RWMutex + + tracers map[string]opentracing.Tracer +} + +type traceRequest struct { + Type string `json:"type"` + Operation string `json:"operation"` + Tags map[string]string `json:"tags"` + Count int `json:"count"` +} + +// NewHandler returns a Handler. +func NewHandler() *Handler { + return &Handler{ + tracers: make(map[string]opentracing.Tracer), + } +} + +// init initializes the handler with a tracer +func (h *Handler) init(cfg config.Configuration) error { + tracer, _, err := cfg.New(common.DefaultTracerServiceName) + if err != nil { + return err + } + h.tracers[cfg.Sampler.Type] = tracer + return nil +} + +func (h *Handler) getTracer(samplerType string) opentracing.Tracer { + if samplerType == "" { + samplerType = defaultSamplerType + } + h.Lock() + defer h.Unlock() + tracer, ok := h.tracers[samplerType] + if !ok { + endToEndConfig.Sampler.Type = samplerType + if err := h.init(endToEndConfig); err != nil { + log.Printf("Failed to create tracer: %s", err.Error()) + return nil + } + tracer, _ = h.tracers[samplerType] + } + return tracer +} + +// GenerateTraces creates traces given the parameters in the request. +func (h *Handler) GenerateTraces(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var req traceRequest + if err := decoder.Decode(&req); err != nil { + http.Error(w, fmt.Sprintf("JSON payload is invalid: %s", err.Error()), http.StatusBadRequest) + return + } + tracer := h.getTracer(req.Type) + if tracer == nil { + http.Error(w, "Tracer is not initialized", http.StatusInternalServerError) + return + } + generateTraces(tracer, &req) +} + +func generateTraces(tracer opentracing.Tracer, r *traceRequest) { + for i := 0; i < r.Count; i++ { + span := tracer.StartSpan(r.Operation) + for k, v := range r.Tags { + span.SetTag(k, v) + } + span.Finish() + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go new file mode 100644 index 00000000..48b4068f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/endtoend/handler_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package endtoend + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" + "github.com/uber/jaeger-client-go/log" +) + +var ( + testOperation = "testOperation" + testService = "testService" + + testConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: jaeger.SamplerTypeRemote, + Param: 1.0, + SamplingServerURL: "http://localhost:5778/sampling", + }, + Reporter: &config.ReporterConfig{ + BufferFlushInterval: time.Second, + LocalAgentHostPort: "localhost:5775", + }, + } + + badConfig = config.Configuration{ + Disabled: false, + Sampler: &config.SamplerConfig{ + Type: "INVALID_TYPE", + }, + } + + testTraceRequest = traceRequest{ + Type: jaeger.SamplerTypeConst, + Operation: testOperation, + Tags: map[string]string{ + "key": "value", + }, + Count: 2, + } + + testInvalidJSON = `bad_json` + + testTraceJSONRequest = ` + { + "type": "const", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` + + testInvalidTypeJSONRequest = ` + { + "type": "INVALID_TYPE", + "operation": "testOperation", + "tags": { + "key": "value" + }, + "count": 2 + } + ` +) + +func newInMemoryTracer() (opentracing.Tracer, *jaeger.InMemoryReporter) { + inMemoryReporter := jaeger.NewInMemoryReporter() + tracer, _ := jaeger.NewTracer( + testService, + jaeger.NewConstSampler(true), + inMemoryReporter, + jaeger.TracerOptions.Metrics(jaeger.NewNullMetrics()), + jaeger.TracerOptions.Logger(log.NullLogger)) + return tracer, inMemoryReporter +} + +func TestInit(t *testing.T) { + handler := NewHandler() + err := handler.init(testConfig) + assert.NoError(t, err) +} + +func TestInitBadConfig(t *testing.T) { + handler := NewHandler() + err := handler.init(badConfig) + assert.Error(t, err) +} + +func TestGetTracer(t *testing.T) { + iTracer, _ := newInMemoryTracer() + handler := &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: iTracer}} + tracer := handler.getTracer(jaeger.SamplerTypeConst) + assert.Equal(t, iTracer, tracer) +} + +func TestGetTracerError(t *testing.T) { + handler := NewHandler() + tracer := handler.getTracer("INVALID_TYPE") + assert.Nil(t, tracer) +} + +func TestGenerateTraces(t *testing.T) { + tracer, _ := newInMemoryTracer() + + tests := []struct { + expectedCode int + json string + handler *Handler + }{ + {http.StatusOK, testTraceJSONRequest, &Handler{tracers: map[string]opentracing.Tracer{jaeger.SamplerTypeConst: tracer}}}, + {http.StatusBadRequest, testInvalidJSON, NewHandler()}, + {http.StatusInternalServerError, testInvalidTypeJSONRequest, NewHandler()}, // Tracer failed to initialize + } + + for _, test := range tests { + req, err := http.NewRequest("POST", "/create_spans", bytes.NewBuffer([]byte(test.json))) + assert.NoError(t, err, "Failed to initialize request") + recorder := httptest.NewRecorder() + handlerFunc := http.HandlerFunc(test.handler.GenerateTraces) + handlerFunc.ServeHTTP(recorder, req) + + assert.Equal(t, test.expectedCode, recorder.Code) + } +} + +func TestGenerateTracesInternal(t *testing.T) { + tracer, reporter := newInMemoryTracer() + generateTraces(tracer, &testTraceRequest) + assert.Equal(t, 2, reporter.SpansSubmitted()) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/log/logger.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/log/logger.go new file mode 100644 index 00000000..44fd9255 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/log/logger.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import ( + real_log "log" +) + +// Enabled controls logging from crossdock tests. It is enabled in main.go, but off in unit tests. +var Enabled bool + +// Printf delegates to log.Printf if Enabled == true +func Printf(msg string, args ...interface{}) { + if Enabled { + real_log.Printf(msg, args) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/main.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/main.go new file mode 100644 index 00000000..6bf47708 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/main.go @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package main + +import ( + "io" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/client" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/server" + jlog "github.com/uber/jaeger-client-go/log" +) + +func main() { + log.Enabled = true + + tracer, tCloser := initTracer() + defer tCloser.Close() + + s := &server.Server{Tracer: tracer} + if err := s.Start(); err != nil { + panic(err.Error()) + } else { + defer s.Close() + } + client := &client.Client{} + if err := client.Start(); err != nil { + panic(err.Error()) + } +} + +func initTracer() (opentracing.Tracer, io.Closer) { + t, c := jaeger.NewTracer( + common.DefaultTracerServiceName, + jaeger.NewConstSampler(false), + jaeger.NewLoggingReporter(jlog.StdLogger)) + return t, c +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/rules.mk b/vendor/src/github.com/uber/jaeger-client-go/crossdock/rules.mk new file mode 100644 index 00000000..30ddfe47 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/rules.mk @@ -0,0 +1,25 @@ +XDOCK_YAML=crossdock/docker-compose.yml + +.PHONY: crossdock-linux-bin +crossdock-linux-bin: + CGO_ENABLED=0 GOOS=linux time go build -a -installsuffix cgo -o crossdock/crossdock ./crossdock + +.PHONY: crossdock +crossdock: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill go + docker-compose -f $(XDOCK_YAML) rm -f go + docker-compose -f $(XDOCK_YAML) build go + docker-compose -f $(XDOCK_YAML) run crossdock 2>&1 | tee run-crossdock.log + grep 'Tests passed!' run-crossdock.log + +.PHONY: crossdock-fresh +crossdock-fresh: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill + docker-compose -f $(XDOCK_YAML) rm --force + docker-compose -f $(XDOCK_YAML) pull + docker-compose -f $(XDOCK_YAML) build + docker-compose -f $(XDOCK_YAML) run crossdock + +.PHONE: crossdock-logs +crossdock-logs: + docker-compose -f $(XDOCK_YAML) logs diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/constants.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/constants.go new file mode 100644 index 00000000..481263c8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/constants.go @@ -0,0 +1,26 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import "errors" + +// BaggageKey is the key used to pass baggage item +const BaggageKey = "crossdock-baggage-key" + +var ( + errNoSpanObserved = errors.New("no span found in Context") + errUnrecognizedProtocol = errors.New("unrecognized protocol for downstream call") + errCannotStartInTChannel = errors.New("cannot start new trace in tchannel server") +) diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server.go new file mode 100644 index 00000000..a25b49d6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server.go @@ -0,0 +1,164 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "strings" + "sync" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/tchannel-go" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/endtoend" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +// Server implements S1-S3 servers +type Server struct { + HostPortHTTP string + HostPortTChannel string + Tracer opentracing.Tracer + listener net.Listener + channel *tchannel.Channel + eHandler *endtoend.Handler +} + +// Start starts the test server called by the Client and other upstream servers. +func (s *Server) Start() error { + if s.HostPortHTTP == "" { + s.HostPortHTTP = ":" + common.DefaultServerPortHTTP + } + if s.HostPortTChannel == "" { + s.HostPortTChannel = ":" + common.DefaultServerPortTChannel + } + + if err := s.startTChannelServer(s.Tracer); err != nil { + return err + } + s.eHandler = endtoend.NewHandler() + + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { return }) // health check + mux.HandleFunc("/start_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewStartTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doStartTrace(req.(*tracetest.StartTraceRequest)) + }) + }) + mux.HandleFunc("/join_trace", func(w http.ResponseWriter, r *http.Request) { + s.handleJSON(w, r, func() interface{} { + return tracetest.NewJoinTraceRequest() + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return s.doJoinTrace(ctx, req.(*tracetest.JoinTraceRequest)) + }) + }) + mux.HandleFunc("/create_traces", s.eHandler.GenerateTraces) + + listener, err := net.Listen("tcp", s.HostPortHTTP) + if err != nil { + return err + } + s.listener = listener + s.HostPortHTTP = listener.Addr().String() + + var started sync.WaitGroup + started.Add(1) + go func() { + started.Done() + http.Serve(listener, mux) + }() + started.Wait() + log.Printf("Started http server at %s\n", s.HostPortHTTP) + return nil +} + +// URL returns URL of the HTTP server +func (s *Server) URL() string { + return fmt.Sprintf("http://%s/", s.HostPortHTTP) +} + +// Close stops the server +func (s *Server) Close() error { + return s.listener.Close() +} + +// GetPortHTTP returns the network port the server listens to. +func (s *Server) GetPortHTTP() string { + hostPort := s.HostPortHTTP + hostPortSplit := strings.Split(hostPort, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} + +// GetPortTChannel returns the actual port the server listens to +func (s *Server) GetPortTChannel() string { + hostPortSplit := strings.Split(s.HostPortTChannel, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} + +func (s *Server) handleJSON( + w http.ResponseWriter, + r *http.Request, + newReq func() interface{}, + handle func(ctx context.Context, req interface{}) (interface{}, error), +) { + spanCtx, err := s.Tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) + if err != nil && err != opentracing.ErrSpanContextNotFound { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusBadRequest) + return + } + span := s.Tracer.StartSpan("post", ext.RPCServerOption(spanCtx)) + ctx := opentracing.ContextWithSpan(context.Background(), span) + defer span.Finish() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot read request body: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server request: %s", string(body)) + req := newReq() + if err := json.Unmarshal(body, req); err != nil { + http.Error(w, fmt.Sprintf("Cannot parse request JSON: %+v. body=[%s]", err, string(body)), http.StatusBadRequest) + return + } + resp, err := handle(ctx, req) + if err != nil { + log.Printf("Handle error: %s", err.Error()) + http.Error(w, fmt.Sprintf("Execution error: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, fmt.Sprintf("Cannot marshall response to JSON: %+v", err), http.StatusInternalServerError) + return + } + log.Printf("Server response: %s", string(json)) + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server_test.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server_test.go new file mode 100644 index 00000000..247008a5 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/server_test.go @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "fmt" + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func TestServerJSON(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + jaeger.NewNullReporter()) + defer tCloser.Close() + + s := &Server{HostPortHTTP: "127.0.0.1:0", HostPortTChannel: "127.0.0.1:0", Tracer: tracer} + err := s.Start() + require.NoError(t, err) + defer s.Close() + + req := tracetest.NewStartTraceRequest() + req.Sampled = true + req.Baggage = "Zoidberg" + req.Downstream = &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortHTTP(), + Transport: tracetest.Transport_HTTP, + Downstream: &tracetest.Downstream{ + ServiceName: "go", + Host: "localhost", + Port: s.GetPortTChannel(), + Transport: tracetest.Transport_TCHANNEL, + }, + } + + url := fmt.Sprintf("http://%s/start_trace", s.HostPortHTTP) + result, err := common.PostJSON(context.Background(), url, req) + + require.NoError(t, err) + log.Printf("response=%+v", &result) +} + +func TestObserveSpan(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(true), + jaeger.NewNullReporter()) + defer tCloser.Close() + + _, err := observeSpan(context.Background(), tracer) + assert.Error(t, err) + + span := tracer.StartSpan("hi") + span.SetBaggageItem(BaggageKey, "xyz") + ctx := opentracing.ContextWithSpan(context.Background(), span) + + s, err := observeSpan(ctx, tracer) + assert.NoError(t, err) + assert.True(t, s.Sampled) + traceID := span.Context().(jaeger.SpanContext).TraceID().String() + assert.Equal(t, traceID, s.TraceId) + assert.Equal(t, "xyz", s.Baggage) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/tchannel.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/tchannel.go new file mode 100644 index 00000000..a8b11963 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/tchannel.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "fmt" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/thrift" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func (s *Server) startTChannelServer(tracer opentracing.Tracer) error { + channelOpts := &tchannel.ChannelOptions{ + Tracer: tracer, + } + ch, err := tchannel.NewChannel("go", channelOpts) + if err != nil { + return err + } + server := thrift.NewServer(ch) + + s.channel = ch + + handler := tracetest.NewTChanTracedServiceServer(s) + server.Register(handler) + + if err := ch.ListenAndServe(s.HostPortTChannel); err != nil { + return err + } + s.HostPortTChannel = ch.PeerInfo().HostPort + log.Printf("Started tchannel server at %s\n", s.HostPortTChannel) + return nil +} + +// StartTrace implements StartTrace() of TChanTracedService +func (s *Server) StartTrace(ctx thrift.Context, request *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) { + return nil, errCannotStartInTChannel +} + +// JoinTrace implements JoinTrace() of TChanTracedService +func (s *Server) JoinTrace(ctx thrift.Context, request *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) { + log.Printf("tchannel server handling JoinTrace") + return s.prepareResponse(ctx, request.ServerRole, request.Downstream) +} + +func (s *Server) callDownstreamTChannel(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) { + req := &tracetest.JoinTraceRequest{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + + hostPort := fmt.Sprintf("%s:%s", target.Host, target.Port) + log.Printf("calling downstream '%s' over tchannel:%s", target.ServiceName, hostPort) + + channelOpts := &tchannel.ChannelOptions{ + Tracer: s.Tracer, + } + ch, err := tchannel.NewChannel("tchannel-client", channelOpts) + if err != nil { + return nil, err + } + + opts := &thrift.ClientOptions{HostPort: hostPort} + thriftClient := thrift.NewClient(ch, target.ServiceName, opts) + + client := tracetest.NewTChanTracedServiceClient(thriftClient) + ctx, cx := context.WithTimeout(ctx, time.Second) + defer cx() + return client.JoinTrace(thrift.Wrap(ctx), req) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/trace.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/trace.go new file mode 100644 index 00000000..7ff6e143 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/server/trace.go @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package server + +import ( + "fmt" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/crossdock/common" + "github.com/uber/jaeger-client-go/crossdock/log" + "github.com/uber/jaeger-client-go/crossdock/thrift/tracetest" +) + +func (s *Server) doStartTrace(req *tracetest.StartTraceRequest) (*tracetest.TraceResponse, error) { + span := s.Tracer.StartSpan(req.ServerRole) + if req.Sampled { + ext.SamplingPriority.Set(span, 1) + } + span.SetBaggageItem(BaggageKey, req.Baggage) + defer span.Finish() + + ctx := opentracing.ContextWithSpan(context.Background(), span) + + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) doJoinTrace(ctx context.Context, req *tracetest.JoinTraceRequest) (*tracetest.TraceResponse, error) { + return s.prepareResponse(ctx, req.ServerRole, req.Downstream) +} + +func (s *Server) prepareResponse(ctx context.Context, role string, reqDwn *tracetest.Downstream) (*tracetest.TraceResponse, error) { + observedSpan, err := observeSpan(ctx, s.Tracer) + if err != nil { + return nil, err + } + + resp := tracetest.NewTraceResponse() + resp.Span = observedSpan + + if reqDwn != nil { + downstreamResp, err := s.callDownstream(ctx, role, reqDwn) + if err != nil { + return nil, err + } + resp.Downstream = downstreamResp + } + + return resp, nil +} + +func (s *Server) callDownstream(ctx context.Context, role string, downstream *tracetest.Downstream) (*tracetest.TraceResponse, error) { + switch downstream.Transport { + case tracetest.Transport_HTTP: + return s.callDownstreamHTTP(ctx, downstream) + case tracetest.Transport_TCHANNEL: + return s.callDownstreamTChannel(ctx, downstream) + case tracetest.Transport_DUMMY: + return &tracetest.TraceResponse{NotImplementedError: "DUMMY transport not implemented"}, nil + default: + return nil, errUnrecognizedProtocol + } +} + +func (s *Server) callDownstreamHTTP(ctx context.Context, target *tracetest.Downstream) (*tracetest.TraceResponse, error) { + req := &tracetest.JoinTraceRequest{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + url := fmt.Sprintf("http://%s:%s/join_trace", target.Host, target.Port) + log.Printf("Calling downstream service '%s' at %s", target.ServiceName, url) + return common.PostJSON(ctx, url, req) +} + +func observeSpan(ctx context.Context, tracer opentracing.Tracer) (*tracetest.ObservedSpan, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, errNoSpanObserved + } + sc := span.Context().(jaeger.SpanContext) + observedSpan := tracetest.NewObservedSpan() + observedSpan.TraceId = sc.TraceID().String() + observedSpan.Sampled = sc.IsSampled() + observedSpan.Baggage = span.BaggageItem(BaggageKey) + return observedSpan, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go new file mode 100644 index 00000000..06fee409 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go new file mode 100644 index 00000000..2ec9bbda --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tchan-tracetest.go @@ -0,0 +1,137 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package tracetest is generated code used to make or handle TChannel calls using Thrift. +package tracetest + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanTracedService is the interface that defines the server handler and client interface. +type TChanTracedService interface { + JoinTrace(ctx thrift.Context, request *JoinTraceRequest) (*TraceResponse, error) + StartTrace(ctx thrift.Context, request *StartTraceRequest) (*TraceResponse, error) +} + +// Implementation of a client and service handler. + +type tchanTracedServiceClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanTracedServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanTracedServiceClient { + return &tchanTracedServiceClient{ + thriftService, + client, + } +} + +// NewTChanTracedServiceClient creates a client that can be used to make remote calls. +func NewTChanTracedServiceClient(client thrift.TChanClient) TChanTracedService { + return NewTChanTracedServiceInheritedClient("TracedService", client) +} + +func (c *tchanTracedServiceClient) JoinTrace(ctx thrift.Context, request *JoinTraceRequest) (*TraceResponse, error) { + var resp TracedServiceJoinTraceResult + args := TracedServiceJoinTraceArgs{ + Request: request, + } + success, err := c.client.Call(ctx, c.thriftService, "joinTrace", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +func (c *tchanTracedServiceClient) StartTrace(ctx thrift.Context, request *StartTraceRequest) (*TraceResponse, error) { + var resp TracedServiceStartTraceResult + args := TracedServiceStartTraceArgs{ + Request: request, + } + success, err := c.client.Call(ctx, c.thriftService, "startTrace", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +type tchanTracedServiceServer struct { + handler TChanTracedService +} + +// NewTChanTracedServiceServer wraps a handler for TChanTracedService so it can be +// registered with a thrift.Server. +func NewTChanTracedServiceServer(handler TChanTracedService) thrift.TChanServer { + return &tchanTracedServiceServer{ + handler, + } +} + +func (s *tchanTracedServiceServer) Service() string { + return "TracedService" +} + +func (s *tchanTracedServiceServer) Methods() []string { + return []string{ + "joinTrace", + "startTrace", + } +} + +func (s *tchanTracedServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "joinTrace": + return s.handleJoinTrace(ctx, protocol) + case "startTrace": + return s.handleStartTrace(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanTracedServiceServer) handleJoinTrace(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req TracedServiceJoinTraceArgs + var res TracedServiceJoinTraceResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.JoinTrace(ctx, req.Request) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} + +func (s *tchanTracedServiceServer) handleStartTrace(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req TracedServiceStartTraceArgs + var res TracedServiceStartTraceResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.StartTrace(ctx, req.Request) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go new file mode 100644 index 00000000..c2a1e54a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/tracedservice.go @@ -0,0 +1,747 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type TracedService interface { + // Parameters: + // - Request + StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) + // Parameters: + // - Request + JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) +} + +type TracedServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewTracedServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewTracedServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *TracedServiceClient { + return &TracedServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Request +func (p *TracedServiceClient) StartTrace(request *StartTraceRequest) (r *TraceResponse, err error) { + if err = p.sendStartTrace(request); err != nil { + return + } + return p.recvStartTrace() +} + +func (p *TracedServiceClient) sendStartTrace(request *StartTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("startTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceStartTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvStartTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "startTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "startTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "startTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "startTrace failed: invalid message type") + return + } + result := TracedServiceStartTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +// Parameters: +// - Request +func (p *TracedServiceClient) JoinTrace(request *JoinTraceRequest) (r *TraceResponse, err error) { + if err = p.sendJoinTrace(request); err != nil { + return + } + return p.recvJoinTrace() +} + +func (p *TracedServiceClient) sendJoinTrace(request *JoinTraceRequest) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("joinTrace", thrift.CALL, p.SeqId); err != nil { + return + } + args := TracedServiceJoinTraceArgs{ + Request: request, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *TracedServiceClient) recvJoinTrace() (value *TraceResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "joinTrace" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "joinTrace failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "joinTrace failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "joinTrace failed: invalid message type") + return + } + result := TracedServiceJoinTraceResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type TracedServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler TracedService +} + +func (p *TracedServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *TracedServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *TracedServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewTracedServiceProcessor(handler TracedService) *TracedServiceProcessor { + + self4 := &TracedServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["startTrace"] = &tracedServiceProcessorStartTrace{handler: handler} + self4.processorMap["joinTrace"] = &tracedServiceProcessorJoinTrace{handler: handler} + return self4 +} + +func (p *TracedServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type tracedServiceProcessorStartTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorStartTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceStartTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceStartTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.StartTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing startTrace: "+err2.Error()) + oprot.WriteMessageBegin("startTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("startTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type tracedServiceProcessorJoinTrace struct { + handler TracedService +} + +func (p *tracedServiceProcessorJoinTrace) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := TracedServiceJoinTraceArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := TracedServiceJoinTraceResult{} + var retval *TraceResponse + var err2 error + if retval, err2 = p.handler.JoinTrace(args.Request); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing joinTrace: "+err2.Error()) + oprot.WriteMessageBegin("joinTrace", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("joinTrace", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Request +type TracedServiceStartTraceArgs struct { + Request *StartTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceStartTraceArgs() *TracedServiceStartTraceArgs { + return &TracedServiceStartTraceArgs{} +} + +var TracedServiceStartTraceArgs_Request_DEFAULT *StartTraceRequest + +func (p *TracedServiceStartTraceArgs) GetRequest() *StartTraceRequest { + if !p.IsSetRequest() { + return TracedServiceStartTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceStartTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceStartTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &StartTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceStartTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceStartTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceStartTraceResult() *TracedServiceStartTraceResult { + return &TracedServiceStartTraceResult{} +} + +var TracedServiceStartTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceStartTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceStartTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceStartTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceStartTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("startTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceStartTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceStartTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceStartTraceResult(%+v)", *p) +} + +// Attributes: +// - Request +type TracedServiceJoinTraceArgs struct { + Request *JoinTraceRequest `thrift:"request,1" json:"request"` +} + +func NewTracedServiceJoinTraceArgs() *TracedServiceJoinTraceArgs { + return &TracedServiceJoinTraceArgs{} +} + +var TracedServiceJoinTraceArgs_Request_DEFAULT *JoinTraceRequest + +func (p *TracedServiceJoinTraceArgs) GetRequest() *JoinTraceRequest { + if !p.IsSetRequest() { + return TracedServiceJoinTraceArgs_Request_DEFAULT + } + return p.Request +} +func (p *TracedServiceJoinTraceArgs) IsSetRequest() bool { + return p.Request != nil +} + +func (p *TracedServiceJoinTraceArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) readField1(iprot thrift.TProtocol) error { + p.Request = &JoinTraceRequest{} + if err := p.Request.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Request), err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("request", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:request: ", p), err) + } + if err := p.Request.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Request), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:request: ", p), err) + } + return err +} + +func (p *TracedServiceJoinTraceArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceArgs(%+v)", *p) +} + +// Attributes: +// - Success +type TracedServiceJoinTraceResult struct { + Success *TraceResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewTracedServiceJoinTraceResult() *TracedServiceJoinTraceResult { + return &TracedServiceJoinTraceResult{} +} + +var TracedServiceJoinTraceResult_Success_DEFAULT *TraceResponse + +func (p *TracedServiceJoinTraceResult) GetSuccess() *TraceResponse { + if !p.IsSetSuccess() { + return TracedServiceJoinTraceResult_Success_DEFAULT + } + return p.Success +} +func (p *TracedServiceJoinTraceResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *TracedServiceJoinTraceResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) readField0(iprot thrift.TProtocol) error { + p.Success = &TraceResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("joinTrace_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TracedServiceJoinTraceResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *TracedServiceJoinTraceResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TracedServiceJoinTraceResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go new file mode 100644 index 00000000..267fa18d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/crossdock/thrift/tracetest/ttypes.go @@ -0,0 +1,1103 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package tracetest + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type Transport int64 + +const ( + Transport_HTTP Transport = 0 + Transport_TCHANNEL Transport = 1 + Transport_DUMMY Transport = 2 +) + +func (p Transport) String() string { + switch p { + case Transport_HTTP: + return "HTTP" + case Transport_TCHANNEL: + return "TCHANNEL" + case Transport_DUMMY: + return "DUMMY" + } + return "" +} + +func TransportFromString(s string) (Transport, error) { + switch s { + case "HTTP": + return Transport_HTTP, nil + case "TCHANNEL": + return Transport_TCHANNEL, nil + case "DUMMY": + return Transport_DUMMY, nil + } + return Transport(0), fmt.Errorf("not a valid Transport string") +} + +func TransportPtr(v Transport) *Transport { return &v } + +func (p Transport) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *Transport) UnmarshalText(text []byte) error { + q, err := TransportFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - ServiceName +// - ServerRole +// - Host +// - Port +// - Transport +// - Downstream +type Downstream struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + ServerRole string `thrift:"serverRole,2,required" json:"serverRole"` + Host string `thrift:"host,3,required" json:"host"` + Port string `thrift:"port,4,required" json:"port"` + Transport Transport `thrift:"transport,5,required" json:"transport"` + Downstream *Downstream `thrift:"downstream,6" json:"downstream,omitempty"` +} + +func NewDownstream() *Downstream { + return &Downstream{} +} + +func (p *Downstream) GetServiceName() string { + return p.ServiceName +} + +func (p *Downstream) GetServerRole() string { + return p.ServerRole +} + +func (p *Downstream) GetHost() string { + return p.Host +} + +func (p *Downstream) GetPort() string { + return p.Port +} + +func (p *Downstream) GetTransport() Transport { + return p.Transport +} + +var Downstream_Downstream_DEFAULT Downstream + +func (p *Downstream) GetDownstream() Downstream { + if !p.IsSetDownstream() { + return Downstream_Downstream_DEFAULT + } + return *p.Downstream +} +func (p *Downstream) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *Downstream) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + var issetServerRole bool = false + var issetHost bool = false + var issetPort bool = false + var issetTransport bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetServerRole = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetHost = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetPort = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetTransport = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetHost { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Host is not set")) + } + if !issetPort { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Port is not set")) + } + if !issetTransport { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Transport is not set")) + } + return nil +} + +func (p *Downstream) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Downstream) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *Downstream) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Host = v + } + return nil +} + +func (p *Downstream) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Downstream) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + temp := Transport(v) + p.Transport = temp + } + return nil +} + +func (p *Downstream) readField6(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *Downstream) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Downstream"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Downstream) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Downstream) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:serverRole: ", p), err) + } + return err +} + +func (p *Downstream) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("host", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := oprot.WriteString(string(p.Host)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.host (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + return err +} + +func (p *Downstream) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.STRING, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:port: ", p), err) + } + if err := oprot.WriteString(string(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:port: ", p), err) + } + return err +} + +func (p *Downstream) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("transport", thrift.I32, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:transport: ", p), err) + } + if err := oprot.WriteI32(int32(p.Transport)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.transport (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:transport: ", p), err) + } + return err +} + +func (p *Downstream) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:downstream: ", p), err) + } + } + return err +} + +func (p *Downstream) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Downstream(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Sampled +// - Baggage +// - Downstream +type StartTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` + Downstream *Downstream `thrift:"downstream,4,required" json:"downstream"` +} + +func NewStartTraceRequest() *StartTraceRequest { + return &StartTraceRequest{} +} + +func (p *StartTraceRequest) GetServerRole() string { + return p.ServerRole +} + +func (p *StartTraceRequest) GetSampled() bool { + return p.Sampled +} + +func (p *StartTraceRequest) GetBaggage() string { + return p.Baggage +} + +var StartTraceRequest_Downstream_DEFAULT *Downstream + +func (p *StartTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return StartTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *StartTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *StartTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + var issetSampled bool = false + var issetBaggage bool = false + var issetDownstream bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetDownstream = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + if !issetDownstream { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Downstream is not set")) + } + return nil +} + +func (p *StartTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *StartTraceRequest) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *StartTraceRequest) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *StartTraceRequest) readField4(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *StartTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("StartTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *StartTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *StartTraceRequest) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:downstream: ", p), err) + } + return err +} + +func (p *StartTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("StartTraceRequest(%+v)", *p) +} + +// Attributes: +// - ServerRole +// - Downstream +type JoinTraceRequest struct { + ServerRole string `thrift:"serverRole,1,required" json:"serverRole"` + Downstream *Downstream `thrift:"downstream,2" json:"downstream,omitempty"` +} + +func NewJoinTraceRequest() *JoinTraceRequest { + return &JoinTraceRequest{} +} + +func (p *JoinTraceRequest) GetServerRole() string { + return p.ServerRole +} + +var JoinTraceRequest_Downstream_DEFAULT *Downstream + +func (p *JoinTraceRequest) GetDownstream() *Downstream { + if !p.IsSetDownstream() { + return JoinTraceRequest_Downstream_DEFAULT + } + return p.Downstream +} +func (p *JoinTraceRequest) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *JoinTraceRequest) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServerRole bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServerRole = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServerRole { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServerRole is not set")) + } + return nil +} + +func (p *JoinTraceRequest) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServerRole = v + } + return nil +} + +func (p *JoinTraceRequest) readField2(iprot thrift.TProtocol) error { + p.Downstream = &Downstream{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *JoinTraceRequest) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("JoinTraceRequest"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *JoinTraceRequest) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serverRole", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serverRole: ", p), err) + } + if err := oprot.WriteString(string(p.ServerRole)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serverRole (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serverRole: ", p), err) + } + return err +} + +func (p *JoinTraceRequest) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *JoinTraceRequest) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("JoinTraceRequest(%+v)", *p) +} + +// Attributes: +// - TraceId +// - Sampled +// - Baggage +type ObservedSpan struct { + TraceId string `thrift:"traceId,1,required" json:"traceId"` + Sampled bool `thrift:"sampled,2,required" json:"sampled"` + Baggage string `thrift:"baggage,3,required" json:"baggage"` +} + +func NewObservedSpan() *ObservedSpan { + return &ObservedSpan{} +} + +func (p *ObservedSpan) GetTraceId() string { + return p.TraceId +} + +func (p *ObservedSpan) GetSampled() bool { + return p.Sampled +} + +func (p *ObservedSpan) GetBaggage() string { + return p.Baggage +} +func (p *ObservedSpan) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceId bool = false + var issetSampled bool = false + var issetBaggage bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceId = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSampled = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetBaggage = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceId is not set")) + } + if !issetSampled { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Sampled is not set")) + } + if !issetBaggage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Baggage is not set")) + } + return nil +} + +func (p *ObservedSpan) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceId = v + } + return nil +} + +func (p *ObservedSpan) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Sampled = v + } + return nil +} + +func (p *ObservedSpan) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Baggage = v + } + return nil +} + +func (p *ObservedSpan) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ObservedSpan"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ObservedSpan) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceId", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceId: ", p), err) + } + if err := oprot.WriteString(string(p.TraceId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceId (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceId: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("sampled", thrift.BOOL, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:sampled: ", p), err) + } + if err := oprot.WriteBool(bool(p.Sampled)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.sampled (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:sampled: ", p), err) + } + return err +} + +func (p *ObservedSpan) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggage", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:baggage: ", p), err) + } + if err := oprot.WriteString(string(p.Baggage)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggage (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:baggage: ", p), err) + } + return err +} + +func (p *ObservedSpan) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ObservedSpan(%+v)", *p) +} + +// Each server must include the information about the span it observed. +// It can only be omitted from the response if notImplementedError field is not empty. +// If the server was instructed to make a downstream call, it must embed the +// downstream response in its own response. +// +// Attributes: +// - Span +// - Downstream +// - NotImplementedError +type TraceResponse struct { + Span *ObservedSpan `thrift:"span,1" json:"span,omitempty"` + Downstream *TraceResponse `thrift:"downstream,2" json:"downstream,omitempty"` + NotImplementedError string `thrift:"notImplementedError,3,required" json:"notImplementedError"` +} + +func NewTraceResponse() *TraceResponse { + return &TraceResponse{} +} + +var TraceResponse_Span_DEFAULT *ObservedSpan + +func (p *TraceResponse) GetSpan() *ObservedSpan { + if !p.IsSetSpan() { + return TraceResponse_Span_DEFAULT + } + return p.Span +} + +var TraceResponse_Downstream_DEFAULT TraceResponse + +func (p *TraceResponse) GetDownstream() TraceResponse { + if !p.IsSetDownstream() { + return TraceResponse_Downstream_DEFAULT + } + return *p.Downstream +} + +func (p *TraceResponse) GetNotImplementedError() string { + return p.NotImplementedError +} +func (p *TraceResponse) IsSetSpan() bool { + return p.Span != nil +} + +func (p *TraceResponse) IsSetDownstream() bool { + return p.Downstream != nil +} + +func (p *TraceResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetNotImplementedError bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetNotImplementedError = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetNotImplementedError { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field NotImplementedError is not set")) + } + return nil +} + +func (p *TraceResponse) readField1(iprot thrift.TProtocol) error { + p.Span = &ObservedSpan{} + if err := p.Span.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Span), err) + } + return nil +} + +func (p *TraceResponse) readField2(iprot thrift.TProtocol) error { + p.Downstream = &TraceResponse{} + if err := p.Downstream.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Downstream), err) + } + return nil +} + +func (p *TraceResponse) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.NotImplementedError = v + } + return nil +} + +func (p *TraceResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("TraceResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *TraceResponse) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetSpan() { + if err := oprot.WriteFieldBegin("span", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:span: ", p), err) + } + if err := p.Span.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Span), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:span: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetDownstream() { + if err := oprot.WriteFieldBegin("downstream", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:downstream: ", p), err) + } + if err := p.Downstream.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Downstream), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:downstream: ", p), err) + } + } + return err +} + +func (p *TraceResponse) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("notImplementedError", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:notImplementedError: ", p), err) + } + if err := oprot.WriteString(string(p.NotImplementedError)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.notImplementedError (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:notImplementedError: ", p), err) + } + return err +} + +func (p *TraceResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("TraceResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/doc.go b/vendor/src/github.com/uber/jaeger-client-go/doc.go new file mode 100644 index 00000000..4f554903 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +/* +Package jaeger implements an OpenTracing (http://opentracing.io) Tracer. +It is currently using Zipkin-compatible data model and can be directly +itegrated with Zipkin backend (http://zipkin.io). + +For integration instructions please refer to the README: + +https://github.com/uber/jaeger-client-go/blob/master/README.md +*/ +package jaeger diff --git a/vendor/src/github.com/uber/jaeger-client-go/glide.lock b/vendor/src/github.com/uber/jaeger-client-go/glide.lock new file mode 100644 index 00000000..912d2c54 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/glide.lock @@ -0,0 +1,75 @@ +hash: 4abcf83a509c003112c27a131eafde7d4fa11aeb17decb43ac0746593890ff85 +updated: 2017-08-22T14:21:39.649697305-04:00 +imports: +- name: github.com/apache/thrift + version: b2a4d4ae21c789b689dd162deb819665567f481c + subpackages: + - lib/go/thrift +- name: github.com/codahale/hdrhistogram + version: f8ad88b59a584afeee9d334eff879b104439117b +- name: github.com/crossdock/crossdock-go + version: 049aabb0122b03bc9bd30cab8f3f91fb60166361 + subpackages: + - assert + - require +- name: github.com/davecgh/go-spew + version: adab96458c51a58dc1783b3335dcce5461522e75 + subpackages: + - spew +- name: github.com/opentracing/opentracing-go + version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 + subpackages: + - ext + - log +- name: github.com/pmezard/go-difflib + version: 792786c7400a136282c1664665ae0a8db921c6c2 + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require + - suite +- name: github.com/uber-go/atomic + version: 0c9e689d64f004564b79d9a663634756df322902 +- name: github.com/uber/jaeger-lib + version: 575c678b2873b62aaadd0fa5817a2aeb3155dc4d + subpackages: + - metrics + - metrics/testutils +- name: github.com/uber/tchannel-go + version: a7ad9ecb640b5f10a0395b38d6319175172b3ab2 + subpackages: + - atomic + - internal/argreader + - relay + - thrift + - thrift/gen-go/meta + - tnet + - tos + - trace/thrift/gen-go/tcollector + - trand + - typed +- name: go.uber.org/atomic + version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf +- name: go.uber.org/zap + version: 9cabc84638b70e564c3dab2766efcb1ded2aac9f + subpackages: + - buffer + - internal/bufferpool + - internal/color + - internal/exit + - internal/multierror + - zapcore +- name: golang.org/x/net + version: f5079bd7f6f74e23c4d65efa0f4ce14cbd6a3c0f + subpackages: + - bpf + - context + - context/ctxhttp + - internal/iana + - internal/socket + - ipv4 + - ipv6 +testImports: [] diff --git a/vendor/src/github.com/uber/jaeger-client-go/glide.yaml b/vendor/src/github.com/uber/jaeger-client-go/glide.yaml new file mode 100644 index 00000000..fa30bded --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/glide.yaml @@ -0,0 +1,34 @@ +package: github.com/uber/jaeger-client-go +import: +- package: github.com/apache/thrift + version: ">=0.9.3, <0.11.0" + subpackages: + - lib/go/thrift +- package: github.com/opentracing/opentracing-go + version: ^1 + subpackages: + - ext + - log +- package: golang.org/x/net + subpackages: + - context +- package: github.com/uber/tchannel-go + version: ^1.1.0 + subpackages: + - atomic + - thrift + - thrift/gen-go/meta + - tnet + - trace/thrift/gen-go/tcollector + - typed +- package: github.com/stretchr/testify + version: ^1.1.3 + subpackages: + - assert + - require + - suite +- package: github.com/crossdock/crossdock-go +- package: github.com/uber/jaeger-lib + version: ^1.0.0 + subpackages: + - metrics diff --git a/vendor/src/github.com/uber/jaeger-client-go/header.go b/vendor/src/github.com/uber/jaeger-client-go/header.go new file mode 100644 index 00000000..19c2c055 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/header.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +// HeadersConfig contains the values for the header keys that Jaeger will use. +// These values may be either custom or default depending on whether custom +// values were provided via a configuration. +type HeadersConfig struct { + // JaegerDebugHeader is the name of HTTP header or a TextMap carrier key which, + // if found in the carrier, forces the trace to be sampled as "debug" trace. + // The value of the header is recorded as the tag on the root span, so that the + // trace can be found in the UI using this value as a correlation ID. + JaegerDebugHeader string `yaml:"jaegerDebugHeader"` + + // JaegerBaggageHeader is the name of the HTTP header that is used to submit baggage. + // It differs from TraceBaggageHeaderPrefix in that it can be used only in cases where + // a root span does not exist. + JaegerBaggageHeader string `yaml:"jaegerBaggageHeader"` + + // TraceContextHeaderName is the http header name used to propagate tracing context. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceContextHeaderName string `yaml:"TraceContextHeaderName"` + + // TraceBaggageHeaderPrefix is the prefix for http headers used to propagate baggage. + // This must be in lower-case to avoid mismatches when decoding incoming headers. + TraceBaggageHeaderPrefix string `yaml:"traceBaggageHeaderPrefix"` +} + +func (c *HeadersConfig) applyDefaults() *HeadersConfig { + if c.JaegerBaggageHeader == "" { + c.JaegerBaggageHeader = JaegerBaggageHeader + } + if c.JaegerDebugHeader == "" { + c.JaegerDebugHeader = JaegerDebugHeader + } + if c.TraceBaggageHeaderPrefix == "" { + c.TraceBaggageHeaderPrefix = TraceBaggageHeaderPrefix + } + if c.TraceContextHeaderName == "" { + c.TraceContextHeaderName = TraceContextHeaderName + } + return c +} + +func getDefaultHeadersConfig() *HeadersConfig { + return &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/header_test.go b/vendor/src/github.com/uber/jaeger-client-go/header_test.go new file mode 100644 index 00000000..d6a481a8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/header_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetDefaultOrCustom(t *testing.T) { + assert.Equal(t, (&HeadersConfig{}).applyDefaults(), getDefaultHeadersConfig()) + assert.Equal(t, (&HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + }).applyDefaults(), &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) + + customHeaders := &HeadersConfig{ + JaegerDebugHeader: "custom-jaeger-debug-header", + JaegerBaggageHeader: "custom-jaeger-baggage-header", + TraceContextHeaderName: "custom-tracer-state-header-name", + TraceBaggageHeaderPrefix: "custom-tracer-baggage-header-prefix", + } + assert.Equal(t, customHeaders.applyDefaults(), customHeaders) +} + +func TestGetDefaultHeadersConfig(t *testing.T) { + assert.Equal(t, getDefaultHeadersConfig(), &HeadersConfig{ + JaegerDebugHeader: JaegerDebugHeader, + JaegerBaggageHeader: JaegerBaggageHeader, + TraceContextHeaderName: TraceContextHeaderName, + TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix, + }) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go new file mode 100644 index 00000000..74572931 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/options.go @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "time" + + "github.com/uber/jaeger-client-go" +) + +const ( + defaultMaxValueLength = 2048 + defaultRefreshInterval = time.Minute + defaultHostPort = "localhost:5778" +) + +// Option is a function that sets some option on the RestrictionManager +type Option func(options *options) + +// Options is a factory for all available options +var Options options + +type options struct { + denyBaggageOnInitializationFailure bool + metrics *jaeger.Metrics + logger jaeger.Logger + hostPort string + refreshInterval time.Duration +} + +// DenyBaggageOnInitializationFailure creates an Option that determines the startup failure mode of RestrictionManager. +// If DenyBaggageOnInitializationFailure is true, RestrictionManager will not allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +// If DenyBaggageOnInitializationFailure is false, RestrictionManager will allow any baggage to be written until baggage +// restrictions have been retrieved from agent. +func (options) DenyBaggageOnInitializationFailure(b bool) Option { + return func(o *options) { + o.denyBaggageOnInitializationFailure = b + } +} + +// Metrics creates an Option that initializes Metrics on the RestrictionManager, which is used to emit statistics. +func (options) Metrics(m *jaeger.Metrics) Option { + return func(o *options) { + o.metrics = m + } +} + +// Logger creates an Option that sets the logger used by the RestrictionManager. +func (options) Logger(logger jaeger.Logger) Option { + return func(o *options) { + o.logger = logger + } +} + +// HostPort creates an Option that sets the hostPort of the local agent that contains the baggage restrictions. +func (options) HostPort(hostPort string) Option { + return func(o *options) { + o.hostPort = hostPort + } +} + +// RefreshInterval creates an Option that sets how often the RestrictionManager will poll local agent for +// the baggage restrictions. +func (options) RefreshInterval(refreshInterval time.Duration) Option { + return func(o *options) { + o.refreshInterval = refreshInterval + } +} + +func applyOptions(o ...Option) options { + opts := options{} + for _, option := range o { + option(&opts) + } + if opts.metrics == nil { + opts.metrics = jaeger.NewNullMetrics() + } + if opts.logger == nil { + opts.logger = jaeger.NullLogger + } + if opts.hostPort == "" { + opts.hostPort = defaultHostPort + } + if opts.refreshInterval == 0 { + opts.refreshInterval = defaultRefreshInterval + } + return opts +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go new file mode 100644 index 00000000..a56515ac --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager.go @@ -0,0 +1,157 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "fmt" + "net/url" + "sync" + "time" + + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" + "github.com/uber/jaeger-client-go/utils" +) + +type httpBaggageRestrictionManagerProxy struct { + url string +} + +func newHTTPBaggageRestrictionManagerProxy(hostPort, serviceName string) *httpBaggageRestrictionManagerProxy { + v := url.Values{} + v.Set("service", serviceName) + return &httpBaggageRestrictionManagerProxy{ + url: fmt.Sprintf("http://%s/baggageRestrictions?%s", hostPort, v.Encode()), + } +} + +func (s *httpBaggageRestrictionManagerProxy) GetBaggageRestrictions(serviceName string) ([]*thrift.BaggageRestriction, error) { + var out []*thrift.BaggageRestriction + if err := utils.GetJSON(s.url, &out); err != nil { + return nil, err + } + return out, nil +} + +// RestrictionManager manages baggage restrictions by retrieving baggage restrictions from agent +type RestrictionManager struct { + options + + mux sync.RWMutex + serviceName string + restrictions map[string]*baggage.Restriction + thriftProxy thrift.BaggageRestrictionManager + pollStopped sync.WaitGroup + stopPoll chan struct{} + invalidRestriction *baggage.Restriction + validRestriction *baggage.Restriction + + // Determines if the manager has successfully retrieved baggage restrictions from agent + initialized bool +} + +// NewRestrictionManager returns a BaggageRestrictionManager that polls the agent for the latest +// baggage restrictions. +func NewRestrictionManager(serviceName string, options ...Option) *RestrictionManager { + // TODO there is a developing use case where a single tracer can generate traces on behalf of many services. + // restrictionsMap will need to exist per service + opts := applyOptions(options...) + m := &RestrictionManager{ + serviceName: serviceName, + options: opts, + restrictions: make(map[string]*baggage.Restriction), + thriftProxy: newHTTPBaggageRestrictionManagerProxy(opts.hostPort, serviceName), + stopPoll: make(chan struct{}), + invalidRestriction: baggage.NewRestriction(false, 0), + validRestriction: baggage.NewRestriction(true, defaultMaxValueLength), + } + m.pollStopped.Add(1) + go m.pollManager() + return m +} + +// isReady returns true if the manager has retrieved baggage restrictions from the remote source. +func (m *RestrictionManager) isReady() bool { + m.mux.RLock() + defer m.mux.RUnlock() + return m.initialized +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *RestrictionManager) GetRestriction(service, key string) *baggage.Restriction { + m.mux.RLock() + defer m.mux.RUnlock() + if !m.initialized { + if m.denyBaggageOnInitializationFailure { + return m.invalidRestriction + } + return m.validRestriction + } + if restriction, ok := m.restrictions[key]; ok { + return restriction + } + return m.invalidRestriction +} + +// Close stops remote polling and closes the RemoteRestrictionManager. +func (m *RestrictionManager) Close() error { + close(m.stopPoll) + m.pollStopped.Wait() + return nil +} + +func (m *RestrictionManager) pollManager() { + defer m.pollStopped.Done() + // attempt to initialize baggage restrictions + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to initialize baggage restrictions: %s", err.Error())) + } + ticker := time.NewTicker(m.refreshInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := m.updateRestrictions(); err != nil { + m.logger.Error(fmt.Sprintf("Failed to update baggage restrictions: %s", err.Error())) + } + case <-m.stopPoll: + return + } + } +} + +func (m *RestrictionManager) updateRestrictions() error { + restrictions, err := m.thriftProxy.GetBaggageRestrictions(m.serviceName) + if err != nil { + m.metrics.BaggageRestrictionsUpdateFailure.Inc(1) + return err + } + newRestrictions := m.parseRestrictions(restrictions) + m.metrics.BaggageRestrictionsUpdateSuccess.Inc(1) + m.mux.Lock() + defer m.mux.Unlock() + m.initialized = true + m.restrictions = newRestrictions + return nil +} + +func (m *RestrictionManager) parseRestrictions(restrictions []*thrift.BaggageRestriction) map[string]*baggage.Restriction { + setters := make(map[string]*baggage.Restriction, len(restrictions)) + for _, restriction := range restrictions { + setters[restriction.BaggageKey] = baggage.NewRestriction(true, int(restriction.MaxValueLength)) + } + return setters +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go new file mode 100644 index 00000000..6d5b865f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/remote/restriction_manager_test.go @@ -0,0 +1,220 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package remote + +import ( + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/internal/baggage" + thrift "github.com/uber/jaeger-client-go/thrift-gen/baggage" +) + +const ( + service = "svc" + expectedKey = "key" + expectedSize = 10 +) + +var ( + testRestrictions = []*thrift.BaggageRestriction{ + {BaggageKey: expectedKey, MaxValueLength: int32(expectedSize)}, + } +) + +var _ io.Closer = new(RestrictionManager) // API check + +type baggageHandler struct { + returnError *atomic.Bool + restrictions []*thrift.BaggageRestriction +} + +func (h *baggageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if h.returnError.Load() { + w.WriteHeader(http.StatusInternalServerError) + } else { + bytes, _ := json.Marshal(h.restrictions) + w.Header().Add("Content-Type", "application/json") + w.Write(bytes) + } +} + +func (h *baggageHandler) setReturnError(b bool) { + h.returnError.Store(b) +} + +func withHTTPServer( + restrictions []*thrift.BaggageRestriction, + f func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ), +) { + factory := metrics.NewLocalFactory(0) + m := jaeger.NewMetrics(factory, nil) + + handler := &baggageHandler{returnError: atomic.NewBool(true), restrictions: restrictions} + server := httptest.NewServer(handler) + defer server.Close() + + f(m, factory, handler, server) +} + +func TestNewRemoteRestrictionManager(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + handler.setReturnError(false) + mgr := NewRestrictionManager( + service, + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + defer mgr.Close() + + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + + badKey := "bad-key" + restriction = mgr.GetRestriction(service, badKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.baggage-restrictions-update", + Tags: map[string]string{"result": "ok"}, + Value: 1, + }, + ) + }) +} + +func TestDenyBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + m *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.DenyBaggageOnInitializationFailure(true), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(m), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + metricName := "jaeger.baggage-restrictions-update" + metricTags := map[string]string{"result": "err"} + key := metrics.GetKey(metricName, metricTags, "|", "=") + for i := 0; i < 100; i++ { + // wait until the async initialization call is complete + counters, _ := factory.Snapshot() + if _, ok := counters[key]; ok { + break + } + time.Sleep(time.Millisecond) + } + + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: metricName, + Tags: metricTags, + Value: 1, + }, + ) + + // DenyBaggageOnInitializationFailure should not allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(false, 0), restriction) + + // have the http server return restrictions + handler.setReturnError(false) + mgr.updateRestrictions() + + // Wait until manager retrieves baggage restrictions + for i := 0; i < 100; i++ { + if mgr.isReady() { + break + } + time.Sleep(time.Millisecond) + } + require.True(t, mgr.isReady()) + + restriction = mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, expectedSize), restriction) + }) +} + +func TestAllowBaggageOnInitializationFailure(t *testing.T) { + withHTTPServer( + testRestrictions, + func( + metrics *jaeger.Metrics, + factory *metrics.LocalFactory, + handler *baggageHandler, + server *httptest.Server, + ) { + mgr := NewRestrictionManager( + service, + Options.RefreshInterval(time.Millisecond), + Options.HostPort(getHostPort(t, server.URL)), + Options.Metrics(metrics), + Options.Logger(jaeger.NullLogger), + ) + require.False(t, mgr.isReady()) + + // AllowBaggageOnInitializationFailure should allow any key to be written + restriction := mgr.GetRestriction(service, expectedKey) + assert.EqualValues(t, baggage.NewRestriction(true, 2048), restriction) + }) +} + +func getHostPort(t *testing.T, s string) string { + u, err := url.Parse(s) + require.NoError(t, err, "Failed to parse url") + return u.Host +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go new file mode 100644 index 00000000..c16a5c56 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager.go @@ -0,0 +1,71 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package baggage + +const ( + defaultMaxValueLength = 2048 +) + +// Restriction determines whether a baggage key is allowed and contains any restrictions on the baggage value. +type Restriction struct { + keyAllowed bool + maxValueLength int +} + +// NewRestriction returns a new Restriction. +func NewRestriction(keyAllowed bool, maxValueLength int) *Restriction { + return &Restriction{ + keyAllowed: keyAllowed, + maxValueLength: maxValueLength, + } +} + +// KeyAllowed returns whether the baggage key for this restriction is allowed. +func (r *Restriction) KeyAllowed() bool { + return r.keyAllowed +} + +// MaxValueLength returns the max length for the baggage value. +func (r *Restriction) MaxValueLength() int { + return r.maxValueLength +} + +// RestrictionManager keeps track of valid baggage keys and their restrictions. The manager +// will return a Restriction for a specific baggage key which will determine whether the baggage +// key is allowed for the current service and any other applicable restrictions on the baggage +// value. +type RestrictionManager interface { + GetRestriction(service, key string) *Restriction +} + +// DefaultRestrictionManager allows any baggage key. +type DefaultRestrictionManager struct { + defaultRestriction *Restriction +} + +// NewDefaultRestrictionManager returns a DefaultRestrictionManager. +func NewDefaultRestrictionManager(maxValueLength int) *DefaultRestrictionManager { + if maxValueLength == 0 { + maxValueLength = defaultMaxValueLength + } + return &DefaultRestrictionManager{ + defaultRestriction: &Restriction{keyAllowed: true, maxValueLength: maxValueLength}, + } +} + +// GetRestriction implements RestrictionManager#GetRestriction. +func (m *DefaultRestrictionManager) GetRestriction(service, key string) *Restriction { + return m.defaultRestriction +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go new file mode 100644 index 00000000..b91d866d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/baggage/restriction_manager_test.go @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package baggage + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var _ RestrictionManager = &DefaultRestrictionManager{} + +func TestDefaultRestrictionManager(t *testing.T) { + mgr := NewDefaultRestrictionManager(0) + restriction := mgr.GetRestriction("svc", "key") + assert.EqualValues(t, NewRestriction(true, 2048), restriction) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/internal/spanlog/json.go b/vendor/src/github.com/uber/jaeger-client-go/internal/spanlog/json.go new file mode 100644 index 00000000..0e10b8a5 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/internal/spanlog/json.go @@ -0,0 +1,81 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package spanlog + +import ( + "encoding/json" + "fmt" + + "github.com/opentracing/opentracing-go/log" +) + +type fieldsAsMap map[string]string + +// MaterializeWithJSON converts log Fields into JSON string +// TODO refactor into pluggable materializer +func MaterializeWithJSON(logFields []log.Field) ([]byte, error) { + fields := fieldsAsMap(make(map[string]string, len(logFields))) + for _, field := range logFields { + field.Marshal(fields) + } + if event, ok := fields["event"]; ok && len(fields) == 1 { + return []byte(event), nil + } + return json.Marshal(fields) +} + +func (ml fieldsAsMap) EmitString(key, value string) { + ml[key] = value +} + +func (ml fieldsAsMap) EmitBool(key string, value bool) { + ml[key] = fmt.Sprintf("%t", value) +} + +func (ml fieldsAsMap) EmitInt(key string, value int) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt32(key string, value int32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitInt64(key string, value int64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint32(key string, value uint32) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitUint64(key string, value uint64) { + ml[key] = fmt.Sprintf("%d", value) +} + +func (ml fieldsAsMap) EmitFloat32(key string, value float32) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitFloat64(key string, value float64) { + ml[key] = fmt.Sprintf("%f", value) +} + +func (ml fieldsAsMap) EmitObject(key string, value interface{}) { + ml[key] = fmt.Sprintf("%+v", value) +} + +func (ml fieldsAsMap) EmitLazyLogger(value log.LazyLogger) { + value(ml) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/interop.go b/vendor/src/github.com/uber/jaeger-client-go/interop.go new file mode 100644 index 00000000..8402d087 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/interop.go @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// TODO this file should not be needed after TChannel PR. + +type formatKey int + +// SpanContextFormat is a constant used as OpenTracing Format. +// Requires *SpanContext as carrier. +// This format is intended for interop with TChannel or other Zipkin-like tracers. +const SpanContextFormat formatKey = iota + +type jaegerTraceContextPropagator struct { + tracer *Tracer +} + +func (p *jaegerTraceContextPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.CopyFrom(&ctx) + return nil +} + +func (p *jaegerTraceContextPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(*SpanContext) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + ctx := new(SpanContext) + ctx.CopyFrom(carrier) + return *ctx, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/jaeger_tag.go b/vendor/src/github.com/uber/jaeger-client-go/jaeger_tag.go new file mode 100644 index 00000000..868b2a5b --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/jaeger_tag.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + + "github.com/opentracing/opentracing-go/log" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type tags []*j.Tag + +// ConvertLogsToJaegerTags converts log Fields into jaeger tags. +func ConvertLogsToJaegerTags(logFields []log.Field) []*j.Tag { + fields := tags(make([]*j.Tag, 0, len(logFields))) + for _, field := range logFields { + field.Marshal(&fields) + } + return fields +} + +func (t *tags) EmitString(key, value string) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &value}) +} + +func (t *tags) EmitBool(key string, value bool) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_BOOL, VBool: &value}) +} + +func (t *tags) EmitInt(key string, value int) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt32(key string, value int32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitInt64(key string, value int64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &value}) +} + +func (t *tags) EmitUint32(key string, value uint32) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitUint64(key string, value uint64) { + vLong := int64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_LONG, VLong: &vLong}) +} + +func (t *tags) EmitFloat32(key string, value float32) { + vDouble := float64(value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &vDouble}) +} + +func (t *tags) EmitFloat64(key string, value float64) { + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_DOUBLE, VDouble: &value}) +} + +func (t *tags) EmitObject(key string, value interface{}) { + vStr := fmt.Sprintf("%+v", value) + *t = append(*t, &j.Tag{Key: key, VType: j.TagType_STRING, VStr: &vStr}) +} + +func (t *tags) EmitLazyLogger(value log.LazyLogger) { + value(t) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span.go b/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span.go new file mode 100644 index 00000000..338a3458 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span.go @@ -0,0 +1,172 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// BuildJaegerThrift builds jaeger span based on internal span. +func BuildJaegerThrift(span *Span) *j.Span { + startTime := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime) + duration := span.duration.Nanoseconds() / int64(time.Microsecond) + jaegerSpan := &j.Span{ + TraceIdLow: int64(span.context.traceID.Low), + TraceIdHigh: int64(span.context.traceID.High), + SpanId: int64(span.context.spanID), + ParentSpanId: int64(span.context.parentID), + OperationName: span.operationName, + Flags: int32(span.context.flags), + StartTime: startTime, + Duration: duration, + Tags: buildTags(span.tags), + Logs: buildLogs(span.logs), + References: buildReferences(span.references), + } + return jaegerSpan +} + +// BuildJaegerProcessThrift creates a thrift Process type. +func BuildJaegerProcessThrift(span *Span) *j.Process { + return buildJaegerProcessThrift(span.tracer) +} + +func buildJaegerProcessThrift(tracer *Tracer) *j.Process { + process := &j.Process{ + ServiceName: tracer.serviceName, + Tags: buildTags(tracer.tags), + } + return process +} + +func buildTags(tags []Tag) []*j.Tag { + jTags := make([]*j.Tag, 0, len(tags)) + for _, tag := range tags { + jTag := buildTag(&tag) + jTags = append(jTags, jTag) + } + return jTags +} + +func buildLogs(logs []opentracing.LogRecord) []*j.Log { + jLogs := make([]*j.Log, 0, len(logs)) + for _, log := range logs { + jLog := &j.Log{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Fields: ConvertLogsToJaegerTags(log.Fields), + } + jLogs = append(jLogs, jLog) + } + return jLogs +} + +func buildTag(tag *Tag) *j.Tag { + jTag := &j.Tag{Key: tag.key} + switch value := tag.value.(type) { + case string: + vStr := truncateString(value) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + case []byte: + if len(value) > maxAnnotationLength { + value = value[:maxAnnotationLength] + } + jTag.VBinary = value + jTag.VType = j.TagType_BINARY + case int: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint8: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint16: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint32: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case int64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case uint64: + vLong := int64(value) + jTag.VLong = &vLong + jTag.VType = j.TagType_LONG + case float32: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case float64: + vDouble := float64(value) + jTag.VDouble = &vDouble + jTag.VType = j.TagType_DOUBLE + case bool: + vBool := value + jTag.VBool = &vBool + jTag.VType = j.TagType_BOOL + default: + vStr := truncateString(stringify(value)) + jTag.VStr = &vStr + jTag.VType = j.TagType_STRING + } + return jTag +} + +func buildReferences(references []Reference) []*j.SpanRef { + retMe := make([]*j.SpanRef, 0, len(references)) + for _, ref := range references { + if ref.Type == opentracing.ChildOfRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_CHILD_OF)) + } else if ref.Type == opentracing.FollowsFromRef { + retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_FOLLOWS_FROM)) + } + } + return retMe +} + +func spanRef(ctx SpanContext, refType j.SpanRefType) *j.SpanRef { + return &j.SpanRef{ + RefType: refType, + TraceIdLow: int64(ctx.traceID.Low), + TraceIdHigh: int64(ctx.traceID.High), + SpanId: int64(ctx.spanID), + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go b/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go new file mode 100644 index 00000000..995d0ddb --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/jaeger_thrift_span_test.go @@ -0,0 +1,388 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +var ( + someString = "str" + someBool = true + someLong = int64(123) + someDouble = float64(123) + someBinary = []byte("hello") + someSlice = []string{"a"} + someSliceString = "[a]" +) + +func TestBuildJaegerThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("sp1").(*Span) + ext.SpanKindRPCServer.Set(sp1) + ext.PeerService.Set(sp1, "svc") + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + ext.SpanKindRPCClient.Set(sp2) + sp2.Finish() + sp1.Finish() + + jaegerSpan1 := BuildJaegerThrift(sp1) + jaegerSpan2 := BuildJaegerThrift(sp2) + assert.Equal(t, "sp1", jaegerSpan1.OperationName) + assert.Equal(t, "sp2", jaegerSpan2.OperationName) + assert.EqualValues(t, 0, jaegerSpan1.ParentSpanId) + assert.Equal(t, jaegerSpan1.SpanId, jaegerSpan2.ParentSpanId) + assert.Len(t, jaegerSpan1.Tags, 4) + tag := findTag(jaegerSpan1, SamplerTypeTagKey) + assert.Equal(t, SamplerTypeConst, *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCServerEnum), *tag.VStr) + tag = findTag(jaegerSpan1, string(ext.PeerService)) + assert.Equal(t, "svc", *tag.VStr) + assert.Empty(t, jaegerSpan1.References) + + assert.Len(t, jaegerSpan2.References, 1) + assert.Equal(t, j.SpanRefType_CHILD_OF, jaegerSpan2.References[0].RefType) + assert.EqualValues(t, jaegerSpan1.TraceIdLow, jaegerSpan2.References[0].TraceIdLow) + assert.EqualValues(t, jaegerSpan1.TraceIdHigh, jaegerSpan2.References[0].TraceIdHigh) + assert.EqualValues(t, jaegerSpan1.SpanId, jaegerSpan2.References[0].SpanId) + tag = findTag(jaegerSpan2, string(ext.SpanKind)) + assert.Equal(t, string(ext.SpanKindRPCClientEnum), *tag.VStr) +} + +func TestBuildJaegerProcessThrift(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("sp1").(*Span) + sp.Finish() + + process := BuildJaegerProcessThrift(sp) + assert.Equal(t, process.ServiceName, "DOOP") + require.Len(t, process.Tags, 3) + assert.NotNil(t, findJaegerTag("jaeger.version", process.Tags)) + assert.NotNil(t, findJaegerTag("hostname", process.Tags)) + assert.NotNil(t, findJaegerTag("ip", process.Tags)) +} + +func TestBuildLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + errString := "error" + + tests := []struct { + field log.Field + logFunc func(sp opentracing.Span) + expected []*j.Tag + expectedTimestamp int64 + disableSampling bool + }{ + {field: log.String("event", someString), expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.String("k", someString), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someString}}}, + {field: log.Bool("k", someBool), expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}}, + {field: log.Int("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Int64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Uint64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}}, + {field: log.Float32("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Float64("k", 123), expected: []*j.Tag{{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}}, + {field: log.Error(errors.New(errString)), expected: []*j.Tag{{Key: "error", VType: j.TagType_STRING, VStr: &errString}}}, + {field: log.Object("k", someSlice), expected: []*j.Tag{{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}}, + { + field: log.Lazy(func(fv log.Encoder) { + fv.EmitBool("k", someBool) + }), + expected: []*j.Tag{{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: []*j.Tag{ + {Key: "error", VType: j.TagType_STRING, VStr: getStringPtr("non-even keyValues len: 1")}, + {Key: "function", VType: j.TagType_STRING, VStr: getStringPtr("LogKV")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent(someString) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload(someString, "payload") + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString}) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: someString, Payload: "payload"}) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: []log.Field{log.String("event", someString)}, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + }, + }, + }) + }, + expected: []*j.Tag{{Key: "event", VType: j.TagType_STRING, VStr: &someString}}, + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: someString, + Payload: "payload", + }, + }, + }) + }, + expected: []*j.Tag{ + {Key: "event", VType: j.TagType_STRING, VStr: &someString}, + {Key: "payload", VType: j.TagType_STRING, VStr: getStringPtr("payload")}, + }, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + field: log.String("event", someString), + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", someString) + }, + }, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if test.field != (log.Field{}) { + sp.LogFields(test.field) + } + jaegerSpan := BuildJaegerThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(jaegerSpan.Logs), testName) + continue + } + assert.Equal(t, 1, len(jaegerSpan.Logs), testName) + compareTagSlices(t, test.expected, jaegerSpan.Logs[0].GetFields(), testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, jaegerSpan.Logs[0].Timestamp, testName) + } + } +} + +func TestBuildTags(t *testing.T) { + tests := []struct { + tag Tag + expected *j.Tag + }{ + {tag: Tag{key: "k", value: someString}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someString}}, + {tag: Tag{key: "k", value: int(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint8(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint16(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: int64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: uint64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_LONG, VLong: &someLong}}, + {tag: Tag{key: "k", value: float32(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: float64(123)}, expected: &j.Tag{Key: "k", VType: j.TagType_DOUBLE, VDouble: &someDouble}}, + {tag: Tag{key: "k", value: someBool}, expected: &j.Tag{Key: "k", VType: j.TagType_BOOL, VBool: &someBool}}, + {tag: Tag{key: "k", value: someBinary}, expected: &j.Tag{Key: "k", VType: j.TagType_BINARY, VBinary: someBinary}}, + {tag: Tag{key: "k", value: someSlice}, expected: &j.Tag{Key: "k", VType: j.TagType_STRING, VStr: &someSliceString}}, + } + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + actual := buildTags([]Tag{test.tag}) + assert.Len(t, actual, 1) + compareTags(t, test.expected, actual[0], testName) + } +} + +func TestBuildReferences(t *testing.T) { + references := []Reference{ + {Type: opentracing.ChildOfRef, Context: SpanContext{traceID: TraceID{High: 1, Low: 1}, spanID: SpanID(1)}}, + {Type: opentracing.FollowsFromRef, Context: SpanContext{traceID: TraceID{High: 2, Low: 2}, spanID: SpanID(2)}}, + } + spanRefs := buildReferences(references) + assert.Len(t, spanRefs, 2) + assert.Equal(t, j.SpanRefType_CHILD_OF, spanRefs[0].RefType) + assert.EqualValues(t, 1, spanRefs[0].SpanId) + assert.EqualValues(t, 1, spanRefs[0].TraceIdHigh) + assert.EqualValues(t, 1, spanRefs[0].TraceIdLow) + + assert.Equal(t, j.SpanRefType_FOLLOWS_FROM, spanRefs[1].RefType) + assert.EqualValues(t, 2, spanRefs[1].SpanId) + assert.EqualValues(t, 2, spanRefs[1].TraceIdHigh) + assert.EqualValues(t, 2, spanRefs[1].TraceIdLow) +} + +func TestJaegerSpanBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + jaegerSpan := BuildJaegerThrift(sp) + require.Len(t, jaegerSpan.Logs, 1) + fields := jaegerSpan.Logs[0].Fields + require.Len(t, fields, 3) + assertJaegerTag(t, fields, "event", "baggage") + assertJaegerTag(t, fields, "key", "auth.token") + assertJaegerTag(t, fields, "value", "token") +} + +func assertJaegerTag(t *testing.T, tags []*j.Tag, key string, value string) { + tag := findJaegerTag(key, tags) + require.NotNil(t, tag) + assert.Equal(t, value, tag.GetVStr()) +} + +func getStringPtr(s string) *string { + return &s +} + +func findTag(span *j.Span, key string) *j.Tag { + for _, s := range span.Tags { + if s.Key == key { + return s + } + } + return nil +} + +func findJaegerTag(key string, tags []*j.Tag) *j.Tag { + for _, tag := range tags { + if tag.Key == key { + return tag + } + } + return nil +} + +func compareTagSlices(t *testing.T, expectedTags, actualTags []*j.Tag, testName string) { + assert.Equal(t, len(expectedTags), len(actualTags)) + for _, expectedTag := range expectedTags { + actualTag := findJaegerTag(expectedTag.Key, actualTags) + compareTags(t, expectedTag, actualTag, testName) + } +} + +func compareTags(t *testing.T, expected, actual *j.Tag, testName string) { + if expected == nil && actual == nil { + return + } + if expected == nil || actual == nil { + assert.Fail(t, "one of the tags is nil", testName) + return + } + assert.Equal(t, expected.Key, actual.Key, testName) + assert.Equal(t, expected.VType, actual.VType, testName) + switch expected.VType { + case j.TagType_STRING: + assert.Equal(t, *expected.VStr, *actual.VStr, testName) + case j.TagType_LONG: + assert.Equal(t, *expected.VLong, *actual.VLong, testName) + case j.TagType_DOUBLE: + assert.Equal(t, *expected.VDouble, *actual.VDouble, testName) + case j.TagType_BOOL: + assert.Equal(t, *expected.VBool, *actual.VBool, testName) + case j.TagType_BINARY: + assert.Equal(t, expected.VBinary, actual.VBinary, testName) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/logger.go b/vendor/src/github.com/uber/jaeger-client-go/log/logger.go new file mode 100644 index 00000000..3e2d9886 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/logger.go @@ -0,0 +1,51 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import "log" + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that delegates to default `log` package +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/logger_test.go b/vendor/src/github.com/uber/jaeger-client-go/log/logger_test.go new file mode 100644 index 00000000..a20580a6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/logger_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package log + +import ( + "testing" +) + +func TestLogger(t *testing.T) { + for _, logger := range []Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/zap/field.go b/vendor/src/github.com/uber/jaeger-client-go/log/zap/field.go new file mode 100644 index 00000000..a7f6683a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/zap/field.go @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "context" + "fmt" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Trace creates a field that extracts tracing information from a context and +// includes it under the "trace" key. +// +// Because the opentracing APIs don't expose this information, the returned +// zap.Field is a no-op for contexts that don't contain a span or contain a +// non-Jaeger span. +func Trace(ctx context.Context) zapcore.Field { + if ctx == nil { + return zap.Skip() + } + return zap.Object("trace", trace{ctx}) +} + +type trace struct { + ctx context.Context +} + +func (t trace) MarshalLogObject(enc zapcore.ObjectEncoder) error { + span := opentracing.SpanFromContext(t.ctx) + if span == nil { + return nil + } + j, ok := span.Context().(jaeger.SpanContext) + if !ok { + return nil + } + if !j.IsValid() { + return fmt.Errorf("invalid span: %v", j.SpanID()) + } + enc.AddString("span", j.SpanID().String()) + enc.AddString("trace", j.TraceID().String()) + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/zap/field_test.go b/vendor/src/github.com/uber/jaeger-client-go/log/zap/field_test.go new file mode 100644 index 00000000..ee367b47 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/zap/field_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "context" + "testing" + + jaeger "github.com/uber/jaeger-client-go" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestTraceField(t *testing.T) { + assert.Equal(t, zap.Skip(), Trace(nil), "Expected Trace of a nil context to be a no-op.") + + withTracedContext(func(ctx context.Context) { + enc := zapcore.NewMapObjectEncoder() + Trace(ctx).AddTo(enc) + + logged, ok := enc.Fields["trace"].(map[string]interface{}) + require.True(t, ok, "Expected trace to be a map.") + + // We could extract the span from the context and assert specific IDs, + // but that just copies the production code. Instead, just assert that + // the keys we expect are present. + keys := make(map[string]struct{}, len(logged)) + for k := range logged { + keys[k] = struct{}{} + } + assert.Equal( + t, + map[string]struct{}{"span": {}, "trace": {}}, + keys, + "Expected to log span and trace IDs.", + ) + }) +} + +func withTracedContext(f func(ctx context.Context)) { + tracer, closer := jaeger.NewTracer( + "serviceName", jaeger.NewConstSampler(true), jaeger.NewNullReporter(), + ) + defer closer.Close() + + ctx := opentracing.ContextWithSpan(context.Background(), tracer.StartSpan("test")) + f(ctx) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger.go b/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger.go new file mode 100644 index 00000000..f05e568e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger.go @@ -0,0 +1,39 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "go.uber.org/zap" +) + +// Logger is an adapter from zap Logger to jaeger-lib Logger. +type Logger struct { + logger *zap.SugaredLogger +} + +// NewLogger creates a new Logger. +func NewLogger(logger *zap.Logger) *Logger { + return &Logger{logger: logger.Sugar()} +} + +// Error logs a message at error priority +func (l *Logger) Error(msg string) { + l.logger.Error(msg) +} + +// Infof logs a message at info priority +func (l *Logger) Infof(msg string, args ...interface{}) { + l.logger.Infof(msg, args...) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger_test.go b/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger_test.go new file mode 100644 index 00000000..325ea1ca --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/log/zap/logger_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zap + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func TestLogger(t *testing.T) { + buf := &bytes.Buffer{} + encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "key"}) + logger := NewLogger(zap.New(zapcore.NewCore(encoder, zapcore.AddSync(buf), zapcore.InfoLevel))) + logger.Infof("Hi %s %d", "there", 5) + assert.Equal(t, buf.String(), "Hi there 5\n") + buf.Reset() + logger.Error("Bad wolf") + assert.Equal(t, buf.String(), "Bad wolf\n") +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/logger.go b/vendor/src/github.com/uber/jaeger-client-go/logger.go new file mode 100644 index 00000000..d4f0b501 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/logger.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import "log" + +// NB This will be deprecated in 3.0.0, please use jaeger-client-go/log/logger instead. + +// Logger provides an abstract interface for logging from Reporters. +// Applications can provide their own implementation of this interface to adapt +// reporters logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). +type Logger interface { + // Error logs a message at error priority + Error(msg string) + + // Infof logs a message at info priority + Infof(msg string, args ...interface{}) +} + +// StdLogger is implementation of the Logger interface that delegates to default `log` package +var StdLogger = &stdLogger{} + +type stdLogger struct{} + +func (l *stdLogger) Error(msg string) { + log.Printf("ERROR: %s", msg) +} + +// Infof logs a message at info priority +func (l *stdLogger) Infof(msg string, args ...interface{}) { + log.Printf(msg, args...) +} + +// NullLogger is implementation of the Logger interface that delegates to default `log` package +var NullLogger = &nullLogger{} + +type nullLogger struct{} + +func (l *nullLogger) Error(msg string) {} +func (l *nullLogger) Infof(msg string, args ...interface{}) {} diff --git a/vendor/src/github.com/uber/jaeger-client-go/logger_test.go b/vendor/src/github.com/uber/jaeger-client-go/logger_test.go new file mode 100644 index 00000000..519c86a8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/logger_test.go @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/uber/jaeger-client-go/log" +) + +func TestLogger(t *testing.T) { + for _, logger := range []Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} + +func TestCompatibility(t *testing.T) { + for _, logger := range []log.Logger{StdLogger, NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } + + for _, logger := range []Logger{log.StdLogger, log.NullLogger} { + logger.Infof("Hi %s", "there") + logger.Error("Bad wolf") + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/metrics.go b/vendor/src/github.com/uber/jaeger-client-go/metrics.go new file mode 100644 index 00000000..93f24478 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/metrics.go @@ -0,0 +1,100 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/uber/jaeger-lib/metrics" +) + +// Metrics is a container of all stats emitted by Jaeger tracer. +type Metrics struct { + // Number of traces started by this tracer as sampled + TracesStartedSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=y"` + + // Number of traces started by this tracer as not sampled + TracesStartedNotSampled metrics.Counter `metric:"traces" tags:"state=started,sampled=n"` + + // Number of externally started sampled traces this tracer joined + TracesJoinedSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=y"` + + // Number of externally started not-sampled traces this tracer joined + TracesJoinedNotSampled metrics.Counter `metric:"traces" tags:"state=joined,sampled=n"` + + // Number of sampled spans started by this tracer + SpansStarted metrics.Counter `metric:"spans" tags:"group=lifecycle,state=started"` + + // Number of sampled spans finished by this tracer + SpansFinished metrics.Counter `metric:"spans" tags:"group=lifecycle,state=finished"` + + // Number of sampled spans started by this tracer + SpansSampled metrics.Counter `metric:"spans" tags:"group=sampling,sampled=y"` + + // Number of not-sampled spans started by this tracer + SpansNotSampled metrics.Counter `metric:"spans" tags:"group=sampling,sampled=n"` + + // Number of errors decoding tracing context + DecodingErrors metrics.Counter `metric:"decoding-errors"` + + // Number of spans successfully reported + ReporterSuccess metrics.Counter `metric:"reporter-spans" tags:"state=success"` + + // Number of spans in failed attempts to report + ReporterFailure metrics.Counter `metric:"reporter-spans" tags:"state=failure"` + + // Number of spans dropped due to internal queue overflow + ReporterDropped metrics.Counter `metric:"reporter-spans" tags:"state=dropped"` + + // Current number of spans in the reporter queue + ReporterQueueLength metrics.Gauge `metric:"reporter-queue"` + + // Number of times the Sampler succeeded to retrieve sampling strategy + SamplerRetrieved metrics.Counter `metric:"sampler" tags:"state=retrieved"` + + // Number of times the Sampler succeeded to retrieve and update sampling strategy + SamplerUpdated metrics.Counter `metric:"sampler" tags:"state=updated"` + + // Number of times the Sampler failed to update sampling strategy + SamplerUpdateFailure metrics.Counter `metric:"sampler" tags:"state=failure,phase=updating"` + + // Number of times the Sampler failed to retrieve sampling strategy + SamplerQueryFailure metrics.Counter `metric:"sampler" tags:"state=failure,phase=query"` + + // Number of times baggage was successfully written or updated on spans. + BaggageUpdateSuccess metrics.Counter `metric:"baggage-update" tags:"result=ok"` + + // Number of times baggage failed to write or update on spans. + BaggageUpdateFailure metrics.Counter `metric:"baggage-update" tags:"result=err"` + + // Number of times baggage was truncated as per baggage restrictions. + BaggageTruncate metrics.Counter `metric:"baggage-truncate"` + + // Number of times baggage restrictions were successfully updated. + BaggageRestrictionsUpdateSuccess metrics.Counter `metric:"baggage-restrictions-update" tags:"result=ok"` + + // Number of times baggage restrictions failed to update. + BaggageRestrictionsUpdateFailure metrics.Counter `metric:"baggage-restrictions-update" tags:"result=err"` +} + +// NewMetrics creates a new Metrics struct and initializes it. +func NewMetrics(factory metrics.Factory, globalTags map[string]string) *Metrics { + m := &Metrics{} + metrics.Init(m, factory.Namespace("jaeger", nil), globalTags) + return m +} + +// NewNullMetrics creates a new Metrics struct that won't report any metrics. +func NewNullMetrics() *Metrics { + return NewMetrics(metrics.NullFactory, nil) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/metrics_test.go b/vendor/src/github.com/uber/jaeger-client-go/metrics_test.go new file mode 100644 index 00000000..1a0274eb --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/metrics_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func TestNewMetrics(t *testing.T) { + tags := map[string]string{"lib": "jaeger"} + + factory := metrics.NewLocalFactory(0) + m := NewMetrics(factory, tags) + + require.NotNil(t, m.SpansSampled, "counter not initialized") + require.NotNil(t, m.ReporterQueueLength, "gauge not initialized") + + m.SpansSampled.Inc(1) + m.ReporterQueueLength.Update(11) + testutils.AssertCounterMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.spans", + Tags: map[string]string{"group": "sampling", "lib": "jaeger", "sampled": "y"}, + Value: 1, + }, + ) + testutils.AssertGaugeMetrics(t, factory, + testutils.ExpectedMetric{ + Name: "jaeger.reporter-queue", + Tags: map[string]string{"lib": "jaeger"}, + Value: 11, + }, + ) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/observer.go b/vendor/src/github.com/uber/jaeger-client-go/observer.go new file mode 100644 index 00000000..7bbd0288 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/observer.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import opentracing "github.com/opentracing/opentracing-go" + +// Observer can be registered with the Tracer to receive notifications about +// new Spans. +// +// Deprecated: use jaeger.ContribObserver instead. +type Observer interface { + OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver +} + +// SpanObserver is created by the Observer and receives notifications about +// other Span events. +// +// Deprecated: use jaeger.ContribSpanObserver instead. +type SpanObserver interface { + OnSetOperationName(operationName string) + OnSetTag(key string, value interface{}) + OnFinish(options opentracing.FinishOptions) +} + +// compositeObserver is a dispatcher to other observers +type compositeObserver struct { + observers []ContribObserver +} + +// compositeSpanObserver is a dispatcher to other span observers +type compositeSpanObserver struct { + observers []ContribSpanObserver +} + +// noopSpanObserver is used when there are no observers registered +// on the Tracer or none of them returns span observers from OnStartSpan. +var noopSpanObserver = &compositeSpanObserver{} + +func (o *compositeObserver) append(contribObserver ContribObserver) { + o.observers = append(o.observers, contribObserver) +} + +func (o *compositeObserver) OnStartSpan(sp opentracing.Span, operationName string, options opentracing.StartSpanOptions) ContribSpanObserver { + var spanObservers []ContribSpanObserver + for _, obs := range o.observers { + spanObs, ok := obs.OnStartSpan(sp, operationName, options) + if ok { + if spanObservers == nil { + spanObservers = make([]ContribSpanObserver, 0, len(o.observers)) + } + spanObservers = append(spanObservers, spanObs) + } + } + if len(spanObservers) == 0 { + return noopSpanObserver + } + return &compositeSpanObserver{observers: spanObservers} +} + +func (o *compositeSpanObserver) OnSetOperationName(operationName string) { + for _, obs := range o.observers { + obs.OnSetOperationName(operationName) + } +} + +func (o *compositeSpanObserver) OnSetTag(key string, value interface{}) { + for _, obs := range o.observers { + obs.OnSetTag(key, value) + } +} + +func (o *compositeSpanObserver) OnFinish(options opentracing.FinishOptions) { + for _, obs := range o.observers { + obs.OnFinish(options) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/observer_test.go b/vendor/src/github.com/uber/jaeger-client-go/observer_test.go new file mode 100644 index 00000000..84c8d0b9 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/observer_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestEmptyObserver(t *testing.T) { + tracer, closer := NewTracer("test", NewConstSampler(true), NewInMemoryReporter()) + defer closer.Close() + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + s.Finish() + assert.Equal(t, s.(*Span).observer, noopSpanObserver) +} + +func TestObservers(t *testing.T) { + tracer, closer := NewTracer( + "test", + NewConstSampler(true), + NewInMemoryReporter(), + TracerOptions.Observer(testObserver{}), + TracerOptions.Observer(testObserver{}), + ) + defer closer.Close() + + s := tracer.StartSpan("test", ext.RPCServerOption(nil)) + + forEachObs := func(f func(so *testSpanObserver)) { + observers := s.(*Span).observer.(*compositeSpanObserver).observers + assert.Len(t, observers, 2) + for _, so := range observers { + f(so.(*testSpanObserver)) + } + } + + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + }, + }, *so) + }) + + s.SetOperationName("test2") + s.SetTag("bender", "rodriguez") + forEachObs(func(so *testSpanObserver) { + assert.Equal(t, testSpanObserver{ + operationName: "test2", + tags: map[string]interface{}{ + "span.kind": ext.SpanKindRPCServerEnum, + "bender": "rodriguez", + }, + }, *so) + }) + + s.Finish() + forEachObs(func(so *testSpanObserver) { + assert.True(t, so.finished) + }) +} + +type testObserver struct{} + +type testSpanObserver struct { + operationName string + tags map[string]interface{} + finished bool +} + +func (o testObserver) OnStartSpan(operationName string, options opentracing.StartSpanOptions) SpanObserver { + tags := make(map[string]interface{}) + for k, v := range options.Tags { + tags[k] = v + } + return &testSpanObserver{ + operationName: operationName, + tags: tags, + } +} + +func (o *testSpanObserver) OnSetOperationName(operationName string) { + o.operationName = operationName +} + +func (o *testSpanObserver) OnSetTag(key string, value interface{}) { + o.tags[key] = value +} + +func (o *testSpanObserver) OnFinish(options opentracing.FinishOptions) { + o.finished = true +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/propagation.go b/vendor/src/github.com/uber/jaeger-client-go/propagation.go new file mode 100644 index 00000000..abca67a3 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/propagation.go @@ -0,0 +1,300 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "net/url" + "strings" + "sync" + + opentracing "github.com/opentracing/opentracing-go" +) + +// Injector is responsible for injecting SpanContext instances in a manner suitable +// for propagation via a format-specific "carrier" object. Typically the +// injection will take place across an RPC boundary, but message queues and +// other IPC mechanisms are also reasonable places to use an Injector. +type Injector interface { + // Inject takes `SpanContext` and injects it into `carrier`. The actual type + // of `carrier` depends on the `format` passed to `Tracer.Inject()`. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if injection fails. + Inject(ctx SpanContext, carrier interface{}) error +} + +// Extractor is responsible for extracting SpanContext instances from a +// format-specific "carrier" object. Typically the extraction will take place +// on the server side of an RPC boundary, but message queues and other IPC +// mechanisms are also reasonable places to use an Extractor. +type Extractor interface { + // Extract decodes a SpanContext instance from the given `carrier`, + // or (nil, opentracing.ErrSpanContextNotFound) if no context could + // be found in the `carrier`. + Extract(carrier interface{}) (SpanContext, error) +} + +type textMapPropagator struct { + headerKeys *HeadersConfig + metrics Metrics + encodeValue func(string) string + decodeValue func(string) string +} + +func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return val + }, + decodeValue: func(val string) string { + return val + }, + } +} + +func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator { + return &textMapPropagator{ + headerKeys: headerKeys, + metrics: metrics, + encodeValue: func(val string) string { + return url.QueryEscape(val) + }, + decodeValue: func(val string) string { + // ignore decoding errors, cannot do anything about them + if v, err := url.QueryUnescape(val); err == nil { + return v + } + return val + }, + } +} + +type binaryPropagator struct { + tracer *Tracer + buffers sync.Pool +} + +func newBinaryPropagator(tracer *Tracer) *binaryPropagator { + return &binaryPropagator{ + tracer: tracer, + buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}, + } +} + +func (p *textMapPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Do not encode the string with trace context to avoid accidental double-encoding + // if people are using opentracing < 0.10.0. Our colon-separated representation + // of the trace context is already safe for HTTP headers. + textMapWriter.Set(p.headerKeys.TraceContextHeaderName, sc.String()) + for k, v := range sc.baggage { + safeKey := p.addBaggageKeyPrefix(k) + safeVal := p.encodeValue(v) + textMapWriter.Set(safeKey, safeVal) + } + return nil +} + +func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + var baggage map[string]string + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + if key == p.headerKeys.TraceContextHeaderName { + var err error + safeVal := p.decodeValue(value) + if ctx, err = ContextFromString(safeVal); err != nil { + return err + } + } else if key == p.headerKeys.JaegerDebugHeader { + ctx.debugID = p.decodeValue(value) + } else if key == p.headerKeys.JaegerBaggageHeader { + if baggage == nil { + baggage = make(map[string]string) + } + for k, v := range p.parseCommaSeparatedMap(value) { + baggage[k] = v + } + } else if strings.HasPrefix(key, p.headerKeys.TraceBaggageHeaderPrefix) { + if baggage == nil { + baggage = make(map[string]string) + } + safeKey := p.removeBaggageKeyPrefix(key) + safeVal := p.decodeValue(value) + baggage[safeKey] = safeVal + } + return nil + }) + if err != nil { + p.metrics.DecodingErrors.Inc(1) + return emptyContext, err + } + if !ctx.traceID.IsValid() && ctx.debugID == "" && len(baggage) == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + ctx.baggage = baggage + return ctx, nil +} + +func (p *binaryPropagator) Inject( + sc SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(io.Writer) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // Handle the tracer context + if err := binary.Write(carrier, binary.BigEndian, sc.traceID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.spanID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.parentID); err != nil { + return err + } + if err := binary.Write(carrier, binary.BigEndian, sc.flags); err != nil { + return err + } + + // Handle the baggage items + if err := binary.Write(carrier, binary.BigEndian, int32(len(sc.baggage))); err != nil { + return err + } + for k, v := range sc.baggage { + if err := binary.Write(carrier, binary.BigEndian, int32(len(k))); err != nil { + return err + } + io.WriteString(carrier, k) + if err := binary.Write(carrier, binary.BigEndian, int32(len(v))); err != nil { + return err + } + io.WriteString(carrier, v) + } + + return nil +} + +func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(io.Reader) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + var ctx SpanContext + + if err := binary.Read(carrier, binary.BigEndian, &ctx.traceID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.spanID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.parentID); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if err := binary.Read(carrier, binary.BigEndian, &ctx.flags); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + + // Handle the baggage items + var numBaggage int32 + if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + if iNumBaggage := int(numBaggage); iNumBaggage > 0 { + ctx.baggage = make(map[string]string, iNumBaggage) + buf := p.buffers.Get().(*bytes.Buffer) + defer p.buffers.Put(buf) + + var keyLen, valLen int32 + for i := 0; i < iNumBaggage; i++ { + if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(keyLen)) + if n, err := io.CopyN(buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + key := buf.String() + + if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + buf.Reset() + buf.Grow(int(valLen)) + if n, err := io.CopyN(buf, carrier, int64(valLen)); err != nil || int32(n) != valLen { + return emptyContext, opentracing.ErrSpanContextCorrupted + } + ctx.baggage[key] = buf.String() + } + } + + return ctx, nil +} + +// Converts a comma separated key value pair list into a map +// e.g. key1=value1, key2=value2, key3 = value3 +// is converted to map[string]string { "key1" : "value1", +// "key2" : "value2", +// "key3" : "value3" } +func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]string { + baggage := make(map[string]string) + value, err := url.QueryUnescape(value) + if err != nil { + log.Printf("Unable to unescape %s, %v", value, err) + return baggage + } + for _, kvpair := range strings.Split(value, ",") { + kv := strings.Split(strings.TrimSpace(kvpair), "=") + if len(kv) == 2 { + baggage[kv[0]] = kv[1] + } else { + log.Printf("Malformed value passed in for %s", p.headerKeys.JaegerBaggageHeader) + } + } + return baggage +} + +// Converts a baggage item key into an http header format, +// by prepending TraceBaggageHeaderPrefix and encoding the key string +func (p *textMapPropagator) addBaggageKeyPrefix(key string) string { + // TODO encodeBaggageKeyAsHeader add caching and escaping + return fmt.Sprintf("%v%v", p.headerKeys.TraceBaggageHeaderPrefix, key) +} + +func (p *textMapPropagator) removeBaggageKeyPrefix(key string) string { + // TODO decodeBaggageHeaderKey add caching and escaping + return key[len(p.headerKeys.TraceBaggageHeaderPrefix):] +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/propagation_test.go b/vendor/src/github.com/uber/jaeger-client-go/propagation_test.go new file mode 100644 index 00000000..0cc62753 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/propagation_test.go @@ -0,0 +1,268 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "bytes" + "net/http" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +func initMetrics() (*metrics.LocalFactory, *Metrics) { + factory := metrics.NewLocalFactory(0) + return factory, NewMetrics(factory, nil) +} + +func TestSpanPropagator(t *testing.T) { + const op = "test" + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics), TracerOptions.ZipkinSharedRPCSpan(true)) + + mapc := opentracing.TextMapCarrier(make(map[string]string)) + httpc := opentracing.HTTPHeadersCarrier(http.Header{}) + tests := []struct { + format, carrier, formatName interface{} + }{ + {SpanContextFormat, new(SpanContext), "TraceContextFormat"}, + {opentracing.Binary, new(bytes.Buffer), "Binary"}, + {opentracing.TextMap, mapc, "TextMap"}, + {opentracing.HTTPHeaders, httpc, "HTTPHeaders"}, + } + + sp := tracer.StartSpan(op) + sp.SetTag("x", "y") // to avoid later comparing nil vs. [] + sp.SetBaggageItem("foo", "bar") + for _, test := range tests { + // starting normal child to extract its serialized context + child := tracer.StartSpan(op, opentracing.ChildOf(sp.Context())) + err := tracer.Inject(child.Context(), test.format, test.carrier) + assert.NoError(t, err) + // Note: we're not finishing the above span + childCtx, err := tracer.Extract(test.format, test.carrier) + assert.NoError(t, err) + child = tracer.StartSpan(test.formatName.(string), ext.RPCServerOption(childCtx)) + child.SetTag("x", "y") // to avoid later comparing nil vs. [] + child.Finish() + } + sp.Finish() + closer.Close() + + otSpans := reporter.GetSpans() + require.Equal(t, len(tests)+1, len(otSpans), "unexpected number of spans reporter") + + spans := make([]*Span, len(otSpans)) + for i, s := range otSpans { + spans[i] = s.(*Span) + } + + // The last span is the original one. + exp, spans := spans[len(spans)-1], spans[:len(spans)-1] + exp.duration = time.Duration(123) + exp.startTime = time.Time{}.Add(1) + require.Len(t, exp.logs, 1) // The parent span should have baggage logs + fields := exp.logs[0].Fields + require.Len(t, fields, 3) + require.Equal(t, "event", fields[0].Key()) + require.Equal(t, "baggage", fields[0].Value().(string)) + require.Equal(t, "key", fields[1].Key()) + require.Equal(t, "foo", fields[1].Value().(string)) + require.Equal(t, "value", fields[2].Key()) + require.Equal(t, "bar", fields[2].Value().(string)) + + if exp.context.ParentID() != 0 { + t.Fatalf("Root span's ParentID %d is not 0", exp.context.ParentID()) + } + + expTags := exp.tags[2:] // skip two sampler.xxx tags + for i, sp := range spans { + formatName := sp.operationName + if a, e := sp.context.ParentID(), exp.context.SpanID(); a != e { + t.Fatalf("%d: ParentID %d does not match expectation %d", i, a, e) + } else { + // Prepare for comparison. + sp.context.spanID, sp.context.parentID = exp.context.SpanID(), 0 + sp.duration, sp.startTime = exp.duration, exp.startTime + } + assert.Equal(t, exp.context, sp.context, formatName) + assert.Equal(t, "span.kind", sp.tags[0].key) + assert.Equal(t, expTags, sp.tags[1:] /*skip span.kind tag*/, formatName) + assert.Empty(t, sp.logs, formatName) + // Override collections to avoid tripping comparison on different pointers + sp.context = exp.context + sp.tags = exp.tags + sp.logs = exp.logs + sp.operationName = op + sp.references = exp.references + // Compare the rest of the fields + assert.Equal(t, exp, sp, formatName) + } + + testutils.AssertCounterMetrics(t, metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 1 + 2*len(tests)}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 1 + 2*len(tests)}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 1 + len(tests)}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"state": "joined", "sampled": "y"}, Value: len(tests)}, + }...) +} + +func TestSpanIntegrityAfterSerialize(t *testing.T) { + serializedString := "f6c385a2c57ed8d7:b04a90b7723bdc:76c385a2c57ed8d7:1" + + context, err := ContextFromString(serializedString) + require.NoError(t, err) + require.True(t, context.traceID.Low > (uint64(1)<<63)) + require.True(t, int64(context.traceID.Low) < 0) + + newSerializedString := context.String() + require.Equal(t, serializedString, newSerializedString) +} + +func TestDecodingError(t *testing.T) { + reporter := NewInMemoryReporter() + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("x", NewConstSampler(true), reporter, TracerOptions.Metrics(metrics)) + defer closer.Close() + + badHeader := "x.x.x.x" + httpHeader := http.Header{} + httpHeader.Add(TraceContextHeaderName, badHeader) + tmc := opentracing.HTTPHeadersCarrier(httpHeader) + _, err := tracer.Extract(opentracing.HTTPHeaders, tmc) + assert.Error(t, err) + + testutils.AssertCounterMetrics(t, metricsFactory, testutils.ExpectedMetric{Name: "jaeger.decoding-errors", Value: 1}) +} + +func TestBaggagePropagationHTTP(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + assert.Equal(t, "12345", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + sp1.SetBaggageItem("Some_Key", "98:765") + assert.Equal(t, "98:765", sp1.BaggageItem("Some_Key"), "baggage: %+v", sp1.context.baggage) + assert.Empty(t, sp1.BaggageItem("some-KEY"), "baggage: %+v", sp1.context.baggage) + + h := http.Header{} + h.Add("header1", "value1") // make sure this does not get unmarshalled as baggage + err := tracer.Inject(sp1.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + // check that colon : was encoded as %3A + assert.Equal(t, "98%3A765", h.Get(TraceBaggageHeaderPrefix+"Some_Key"), "headers: %+v", h) + + sp2, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.Equal(t, map[string]string{"some_key": "98:765"}, sp2.(SpanContext).baggage) +} + +func TestJaegerBaggageHeader(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + h.Add(JaegerBaggageHeader, "key1=value1, key 2=value two") + + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + + sp := tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + + assert.Equal(t, "value1", sp.BaggageItem("key1")) + assert.Equal(t, "value two", sp.BaggageItem("key 2")) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) +} + +func TestParseCommaSeperatedMap(t *testing.T) { + var testcases = []struct { + in string + out map[string]string + }{ + {"hobbit=Bilbo Baggins", map[string]string{"hobbit": "Bilbo Baggins"}}, + {"hobbit=Bilbo Baggins, dwarf= Thrain", map[string]string{"hobbit": "Bilbo Baggins", "dwarf": " Thrain"}}, + {"kevin spacey=actor", map[string]string{"kevin spacey": "actor"}}, + {"kevin%20spacey=se7en%3Aactor", map[string]string{"kevin spacey": "se7en:actor"}}, + {"key1=, key2=", map[string]string{"key1": "", "key2": ""}}, + {"malformed", map[string]string{}}, + {"malformed, string", map[string]string{}}, + {"another malformed string", map[string]string{}}, + } + + for _, testcase := range testcases { + m := (&textMapPropagator{ + headerKeys: getDefaultHeadersConfig(), + }).parseCommaSeparatedMap(testcase.in) + assert.Equal(t, testcase.out, m) + } +} + +func TestDebugCorrelationID(t *testing.T) { + metricsFactory, metrics := initMetrics() + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + ) + defer closer.Close() + + h := http.Header{} + h.Add(JaegerDebugHeader, "value1") + ctx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(h)) + require.NoError(t, err) + assert.EqualValues(t, 0, ctx.(SpanContext).parentID) + assert.EqualValues(t, "value1", ctx.(SpanContext).debugID) + sp := tracer.StartSpan("root", opentracing.ChildOf(ctx)).(*Span) + assert.EqualValues(t, 0, sp.context.parentID) + assert.True(t, sp.context.traceID.IsValid()) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + tagFound := false + for _, tag := range sp.tags { + if tag.key == JaegerDebugHeader { + assert.Equal(t, "value1", tag.value) + tagFound = true + } + } + assert.True(t, tagFound) + + // ensure that traces.started counter is incremented, not traces.joined + testutils.AssertCounterMetrics(t, metricsFactory, + testutils.ExpectedMetric{ + Name: "jaeger.traces", Tags: map[string]string{"state": "started", "sampled": "y"}, Value: 1, + }, + ) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/reference.go b/vendor/src/github.com/uber/jaeger-client-go/reference.go new file mode 100644 index 00000000..5646e78b --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/reference.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import "github.com/opentracing/opentracing-go" + +// Reference represents a causal reference to other Spans (via their SpanContext). +type Reference struct { + Type opentracing.SpanReferenceType + Context SpanContext +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/reporter.go b/vendor/src/github.com/uber/jaeger-client-go/reporter.go new file mode 100644 index 00000000..3acc4be7 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/reporter.go @@ -0,0 +1,262 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/log" +) + +// Reporter is called by the tracer when a span is completed to report the span to the tracing collector. +type Reporter interface { + // Report submits a new span to collectors, possibly asynchronously and/or with buffering. + Report(span *Span) + + // Close does a clean shutdown of the reporter, flushing any traces that may be buffered in memory. + Close() +} + +// ------------------------------ + +type nullReporter struct{} + +// NewNullReporter creates a no-op reporter that ignores all reported spans. +func NewNullReporter() Reporter { + return &nullReporter{} +} + +// Report implements Report() method of Reporter by doing nothing. +func (r *nullReporter) Report(span *Span) { + // no-op +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *nullReporter) Close() { + // no-op +} + +// ------------------------------ + +type loggingReporter struct { + logger Logger +} + +// NewLoggingReporter creates a reporter that logs all reported spans to provided logger. +func NewLoggingReporter(logger Logger) Reporter { + return &loggingReporter{logger} +} + +// Report implements Report() method of Reporter by logging the span to the logger. +func (r *loggingReporter) Report(span *Span) { + r.logger.Infof("Reporting span %+v", span) +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *loggingReporter) Close() { + // no-op +} + +// ------------------------------ + +// InMemoryReporter is used for testing, and simply collects spans in memory. +type InMemoryReporter struct { + spans []opentracing.Span + lock sync.Mutex +} + +// NewInMemoryReporter creates a reporter that stores spans in memory. +// NOTE: the Tracer should be created with options.PoolSpans = false. +func NewInMemoryReporter() *InMemoryReporter { + return &InMemoryReporter{ + spans: make([]opentracing.Span, 0, 10), + } +} + +// Report implements Report() method of Reporter by storing the span in the buffer. +func (r *InMemoryReporter) Report(span *Span) { + r.lock.Lock() + r.spans = append(r.spans, span) + r.lock.Unlock() +} + +// Close implements Close() method of Reporter by doing nothing. +func (r *InMemoryReporter) Close() { + // no-op +} + +// SpansSubmitted returns the number of spans accumulated in the buffer. +func (r *InMemoryReporter) SpansSubmitted() int { + r.lock.Lock() + defer r.lock.Unlock() + return len(r.spans) +} + +// GetSpans returns accumulated spans as a copy of the buffer. +func (r *InMemoryReporter) GetSpans() []opentracing.Span { + r.lock.Lock() + defer r.lock.Unlock() + copied := make([]opentracing.Span, len(r.spans)) + copy(copied, r.spans) + return copied +} + +// Reset clears all accumulated spans. +func (r *InMemoryReporter) Reset() { + r.lock.Lock() + defer r.lock.Unlock() + r.spans = nil +} + +// ------------------------------ + +type compositeReporter struct { + reporters []Reporter +} + +// NewCompositeReporter creates a reporter that ignores all reported spans. +func NewCompositeReporter(reporters ...Reporter) Reporter { + return &compositeReporter{reporters: reporters} +} + +// Report implements Report() method of Reporter by delegating to each underlying reporter. +func (r *compositeReporter) Report(span *Span) { + for _, reporter := range r.reporters { + reporter.Report(span) + } +} + +// Close implements Close() method of Reporter by closing each underlying reporter. +func (r *compositeReporter) Close() { + for _, reporter := range r.reporters { + reporter.Close() + } +} + +// ------------------------------ + +const ( + defaultQueueSize = 100 + defaultBufferFlushInterval = 10 * time.Second +) + +type remoteReporter struct { + // must be first in the struct because `sync/atomic` expects 64-bit alignment. + // Cf. https://github.com/uber/jaeger-client-go/issues/155, https://goo.gl/zW7dgq + queueLength int64 + + reporterOptions + sender Transport + queue chan *Span + queueDrained sync.WaitGroup + flushSignal chan *sync.WaitGroup +} + +// NewRemoteReporter creates a new reporter that sends spans out of process by means of Sender +func NewRemoteReporter(sender Transport, opts ...ReporterOption) Reporter { + options := reporterOptions{} + for _, option := range opts { + option(&options) + } + if options.bufferFlushInterval <= 0 { + options.bufferFlushInterval = defaultBufferFlushInterval + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.queueSize <= 0 { + options.queueSize = defaultQueueSize + } + reporter := &remoteReporter{ + reporterOptions: options, + sender: sender, + flushSignal: make(chan *sync.WaitGroup), + queue: make(chan *Span, options.queueSize), + } + go reporter.processQueue() + return reporter +} + +// Report implements Report() method of Reporter. +// It passes the span to a background go-routine for submission to Jaeger. +func (r *remoteReporter) Report(span *Span) { + select { + case r.queue <- span: + atomic.AddInt64(&r.queueLength, 1) + default: + r.metrics.ReporterDropped.Inc(1) + } +} + +// Close implements Close() method of Reporter by waiting for the queue to be drained. +func (r *remoteReporter) Close() { + r.queueDrained.Add(1) + close(r.queue) + r.queueDrained.Wait() + r.sender.Close() +} + +// processQueue reads spans from the queue, converts them to Thrift, and stores them in an internal buffer. +// When the buffer length reaches batchSize, it is flushed by submitting the accumulated spans to Jaeger. +// Buffer also gets flushed automatically every batchFlushInterval seconds, just in case the tracer stopped +// reporting new spans. +func (r *remoteReporter) processQueue() { + timer := time.NewTicker(r.bufferFlushInterval) + for { + select { + case span, ok := <-r.queue: + if ok { + atomic.AddInt64(&r.queueLength, -1) + if flushed, err := r.sender.Append(span); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(fmt.Sprintf("error reporting span %q: %s", span.OperationName(), err.Error())) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + // to reduce the number of gauge stats, we only emit queue length on flush + r.metrics.ReporterQueueLength.Update(atomic.LoadInt64(&r.queueLength)) + } + } else { + // queue closed + timer.Stop() + r.flush() + r.queueDrained.Done() + return + } + case <-timer.C: + r.flush() + case wg := <-r.flushSignal: // for testing + r.flush() + wg.Done() + } + } +} + +// flush causes the Sender to flush its accumulated spans and clear the buffer +func (r *remoteReporter) flush() { + if flushed, err := r.sender.Flush(); err != nil { + r.metrics.ReporterFailure.Inc(int64(flushed)) + r.logger.Error(err.Error()) + } else if flushed > 0 { + r.metrics.ReporterSuccess.Inc(int64(flushed)) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/reporter_options.go b/vendor/src/github.com/uber/jaeger-client-go/reporter_options.go new file mode 100644 index 00000000..65012d70 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/reporter_options.go @@ -0,0 +1,69 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" +) + +// ReporterOption is a function that sets some option on the reporter. +type ReporterOption func(c *reporterOptions) + +// ReporterOptions is a factory for all available ReporterOption's +var ReporterOptions reporterOptions + +// reporterOptions control behavior of the reporter. +type reporterOptions struct { + // queueSize is the size of internal queue where reported spans are stored before they are processed in the background + queueSize int + // bufferFlushInterval is how often the buffer is force-flushed, even if it's not full + bufferFlushInterval time.Duration + // logger is used to log errors of span submissions + logger Logger + // metrics is used to record runtime stats + metrics *Metrics +} + +// QueueSize creates a ReporterOption that sets the size of the internal queue where +// spans are stored before they are processed. +func (reporterOptions) QueueSize(queueSize int) ReporterOption { + return func(r *reporterOptions) { + r.queueSize = queueSize + } +} + +// Metrics creates a ReporterOption that initializes Metrics in the reporter, +// which is used to record runtime statistics. +func (reporterOptions) Metrics(metrics *Metrics) ReporterOption { + return func(r *reporterOptions) { + r.metrics = metrics + } +} + +// BufferFlushInterval creates a ReporterOption that sets how often the queue +// is force-flushed. +func (reporterOptions) BufferFlushInterval(bufferFlushInterval time.Duration) ReporterOption { + return func(r *reporterOptions) { + r.bufferFlushInterval = bufferFlushInterval + } +} + +// Logger creates a ReporterOption that initializes the logger used to log +// errors of span submissions. +func (reporterOptions) Logger(logger Logger) ReporterOption { + return func(r *reporterOptions) { + r.logger = logger + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/reporter_test.go b/vendor/src/github.com/uber/jaeger-client-go/reporter_test.go new file mode 100644 index 00000000..77ce0f86 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/reporter_test.go @@ -0,0 +1,272 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "io" + "sort" + "strings" + "sync" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/testutils" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +type reporterSuite struct { + suite.Suite + tracer opentracing.Tracer + closer io.Closer + serviceName string + reporter *remoteReporter + collector *fakeSender + metricsFactory *metrics.LocalFactory +} + +func (s *reporterSuite) SetupTest() { + s.metricsFactory = metrics.NewLocalFactory(0) + metrics := NewMetrics(s.metricsFactory, nil) + s.serviceName = "DOOP" + s.collector = &fakeSender{} + s.reporter = NewRemoteReporter( + s.collector, ReporterOptions.Metrics(metrics), + ).(*remoteReporter) + + s.tracer, s.closer = NewTracer( + "reporter-test-service", + NewConstSampler(true), + s.reporter, + TracerOptions.Metrics(metrics)) + s.NotNil(s.tracer) +} + +func (s *reporterSuite) TearDownTest() { + s.closer.Close() + s.tracer = nil + s.reporter = nil + s.collector = nil +} + +func TestReporter(t *testing.T) { + suite.Run(t, new(reporterSuite)) +} + +func (s *reporterSuite) flushReporter() { + // Wait for reporter queue to add spans to buffer. We could've called reporter.Close(), + // but then it fails when the test suite calls close on it again (via tracer's Closer). + time.Sleep(5 * time.Millisecond) + + var wg sync.WaitGroup + wg.Add(1) + s.reporter.flushSignal <- &wg + wg.Wait() +} + +func (s *reporterSuite) TestRootSpanTags() { + s.metricsFactory.Clear() + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, s.serviceName) + sp.Finish() + s.flushReporter() + s.Equal(1, len(s.collector.Spans())) + span := s.collector.Spans()[0] + s.Len(span.tags, 4) + s.EqualValues("server", span.tags[2].value, "span.kind should be server") + + mTestutils.AssertCounterMetrics(s.T(), s.metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter-spans", + Tags: map[string]string{"state": "success"}, + Value: 1, + }, + ) +} + +func (s *reporterSuite) TestClientSpan() { + s.metricsFactory.Clear() + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, s.serviceName) + sp2 := s.tracer.StartSpan("get_last_name", opentracing.ChildOf(sp.Context())) + ext.SpanKindRPCClient.Set(sp2) + ext.PeerService.Set(sp2, s.serviceName) + sp2.Finish() + sp.Finish() + s.flushReporter() + s.Equal(2, len(s.collector.Spans())) + span := s.collector.Spans()[0] // child span is reported first + s.EqualValues(span.context.spanID, sp2.(*Span).context.spanID) + s.Len(span.tags, 2) + s.EqualValues("client", span.tags[0].value, "span.kind should be client") + + mTestutils.AssertCounterMetrics(s.T(), s.metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.reporter-spans", + Tags: map[string]string{"state": "success"}, + Value: 2, + }, + ) +} + +func (s *reporterSuite) TestTagsAndEvents() { + sp := s.tracer.StartSpan("get_name") + sp.LogEvent("hello") + sp.LogEvent(strings.Repeat("long event ", 30)) + expected := []string{"long", "ping", "awake", "awake", "one", "two", "three", "bite me", + SamplerParamTagKey, SamplerTypeTagKey, "does not compute"} + sp.SetTag("long", strings.Repeat("x", 300)) + sp.SetTag("ping", "pong") + sp.SetTag("awake", true) + sp.SetTag("awake", false) + sp.SetTag("one", 1) + sp.SetTag("two", int32(2)) + sp.SetTag("three", int64(3)) + sp.SetTag("bite me", []byte{1}) + sp.SetTag("does not compute", sp) // should be converted to string + sp.Finish() + s.flushReporter() + s.Equal(1, len(s.collector.Spans())) + span := s.collector.Spans()[0] + s.Equal(2, len(span.logs), "expecting two logs") + s.Equal(len(expected), len(span.tags), + "expecting %d tags", len(expected)) + tags := []string{} + for _, tag := range span.tags { + tags = append(tags, string(tag.key)) + } + sort.Strings(expected) + sort.Strings(tags) + s.Equal(expected, tags, "expecting %d tags", len(expected)) + + s.NotNil(findDomainLog(span, "hello"), "expecting 'hello' log: %+v", span.logs) +} + +func TestUDPReporter(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + testRemoteReporter(t, + func(m *Metrics) (Transport, error) { + return NewUDPTransport(agent.SpanServerAddr(), 0) + }, + func() []*j.Batch { + return agent.GetJaegerBatches() + }) +} + +func testRemoteReporter( + t *testing.T, + factory func(m *Metrics) (Transport, error), + getBatches func() []*j.Batch, +) { + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + sender, err := factory(metrics) + require.NoError(t, err) + reporter := NewRemoteReporter(sender, ReporterOptions.Metrics(metrics)).(*remoteReporter) + + tracer, closer := NewTracer( + "reporter-test-service", + NewConstSampler(true), + reporter, + TracerOptions.Metrics(metrics)) + + span := tracer.StartSpan("leela") + ext.SpanKindRPCClient.Set(span) + ext.PeerService.Set(span, "downstream") + span.Finish() + closer.Close() // close the tracer, which also closes and flushes the reporter + // however, in case of UDP reporter it's fire and forget, so we need to wait a bit + time.Sleep(5 * time.Millisecond) + + batches := getBatches() + require.Equal(t, 1, len(batches)) + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, "leela", batches[0].Spans[0].OperationName) + assert.Equal(t, "reporter-test-service", batches[0].Process.ServiceName) + tag := findJaegerTag("peer.service", batches[0].Spans[0].Tags) + assert.NotNil(t, tag) + assert.Equal(t, "downstream", *tag.VStr) + + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.reporter-spans", Tags: map[string]string{"state": "success"}, Value: 1}, + {Name: "jaeger.reporter-spans", Tags: map[string]string{"state": "failure"}, Value: 0}, + }...) +} + +func (s *reporterSuite) TestMemoryReporterReport() { + sp := s.tracer.StartSpan("leela") + ext.PeerService.Set(sp, s.serviceName) + reporter := NewInMemoryReporter() + reporter.Report(sp.(*Span)) + s.Equal(1, reporter.SpansSubmitted(), "expected number of spans submitted") + reporter.Close() +} + +type fakeSender struct { + spans []*Span + mutex sync.Mutex +} + +func (s *fakeSender) Append(span *Span) (int, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.spans = append(s.spans, span) + return 1, nil +} + +func (s *fakeSender) Flush() (int, error) { + return 0, nil +} + +func (s *fakeSender) Close() error { return nil } + +func (s *fakeSender) Spans() []*Span { + s.mutex.Lock() + defer s.mutex.Unlock() + res := make([]*Span, len(s.spans)) + copy(res, s.spans) + return res +} + +func findDomainLog(span *Span, key string) *opentracing.LogRecord { + for _, log := range span.logs { + if log.Fields[0].Value().(string) == key { + return &log + } + } + return nil +} + +func findDomainTag(span *Span, key string) *Tag { + for _, tag := range span.tags { + if tag.key == key { + return &tag + } + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/README.md b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/README.md new file mode 100644 index 00000000..879948e9 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/README.md @@ -0,0 +1,5 @@ +An Observer that can be used to emit RPC metrics +================================================ + +It can be attached to the tracer during tracer construction. +See `ExampleObserver` function in [observer_test.go](./observer_test.go). diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/doc.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/doc.go new file mode 100644 index 00000000..51aa11b3 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package rpcmetrics implements an Observer that can be used to emit RPC metrics. +package rpcmetrics diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go new file mode 100644 index 00000000..30555243 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints.go @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import "sync" + +// normalizedEndpoints is a cache for endpointName -> safeName mappings. +type normalizedEndpoints struct { + names map[string]string + maxSize int + defaultName string + normalizer NameNormalizer + mux sync.RWMutex +} + +func newNormalizedEndpoints(maxSize int, normalizer NameNormalizer) *normalizedEndpoints { + return &normalizedEndpoints{ + maxSize: maxSize, + normalizer: normalizer, + names: make(map[string]string, maxSize), + } +} + +// normalize looks up the name in the cache, if not found it uses normalizer +// to convert the name to a safe name. If called with more than maxSize unique +// names it returns "" for all other names beyond those already cached. +func (n *normalizedEndpoints) normalize(name string) string { + n.mux.RLock() + norm, ok := n.names[name] + l := len(n.names) + n.mux.RUnlock() + if ok { + return norm + } + if l >= n.maxSize { + return "" + } + return n.normalizeWithLock(name) +} + +func (n *normalizedEndpoints) normalizeWithLock(name string) string { + norm := n.normalizer.Normalize(name) + n.mux.Lock() + defer n.mux.Unlock() + // cache may have grown while we were not holding the lock + if len(n.names) >= n.maxSize { + return "" + } + n.names[name] = norm + return norm +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go new file mode 100644 index 00000000..8a5b4e53 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/endpoints_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalizedEndpoints(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + + assertLen := func(l int) { + n.mux.RLock() + defer n.mux.RUnlock() + assert.Len(t, n.names, l) + } + + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "one translation") + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "cache hit") + assertLen(1) + assert.Equal(t, "", n.normalize("xys"), "cache overflow") + assertLen(1) +} + +func TestNormalizedEndpointsDoubleLocking(t *testing.T) { + n := newNormalizedEndpoints(1, DefaultNameNormalizer) + assert.Equal(t, "ab-cd", n.normalize("ab^cd"), "fill out the cache") + assert.Equal(t, "", n.normalizeWithLock("xys"), "cache overflow") +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go new file mode 100644 index 00000000..ab8d74c2 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics.go @@ -0,0 +1,124 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "sync" + + "github.com/uber/jaeger-lib/metrics" +) + +const ( + otherEndpointsPlaceholder = "other" + endpointNameMetricTag = "endpoint" +) + +// Metrics is a collection of metrics for an endpoint describing +// throughput, success, errors, and performance. +type Metrics struct { + // RequestCountSuccess is a counter of the total number of successes. + RequestCountSuccess metrics.Counter `metric:"requests" tags:"error=false"` + + // RequestCountFailures is a counter of the number of times any failure has been observed. + RequestCountFailures metrics.Counter `metric:"requests" tags:"error=true"` + + // RequestLatencySuccess is a latency histogram of succesful requests. + RequestLatencySuccess metrics.Timer `metric:"request_latency" tags:"error=false"` + + // RequestLatencyFailures is a latency histogram of failed requests. + RequestLatencyFailures metrics.Timer `metric:"request_latency" tags:"error=true"` + + // HTTPStatusCode2xx is a counter of the total number of requests with HTTP status code 200-299 + HTTPStatusCode2xx metrics.Counter `metric:"http_requests" tags:"status_code=2xx"` + + // HTTPStatusCode3xx is a counter of the total number of requests with HTTP status code 300-399 + HTTPStatusCode3xx metrics.Counter `metric:"http_requests" tags:"status_code=3xx"` + + // HTTPStatusCode4xx is a counter of the total number of requests with HTTP status code 400-499 + HTTPStatusCode4xx metrics.Counter `metric:"http_requests" tags:"status_code=4xx"` + + // HTTPStatusCode5xx is a counter of the total number of requests with HTTP status code 500-599 + HTTPStatusCode5xx metrics.Counter `metric:"http_requests" tags:"status_code=5xx"` +} + +func (m *Metrics) recordHTTPStatusCode(statusCode uint16) { + if statusCode >= 200 && statusCode < 300 { + m.HTTPStatusCode2xx.Inc(1) + } else if statusCode >= 300 && statusCode < 400 { + m.HTTPStatusCode3xx.Inc(1) + } else if statusCode >= 400 && statusCode < 500 { + m.HTTPStatusCode4xx.Inc(1) + } else if statusCode >= 500 && statusCode < 600 { + m.HTTPStatusCode5xx.Inc(1) + } +} + +// MetricsByEndpoint is a registry/cache of metrics for each unique endpoint name. +// Only maxNumberOfEndpoints Metrics are stored, all other endpoint names are mapped +// to a generic endpoint name "other". +type MetricsByEndpoint struct { + metricsFactory metrics.Factory + endpoints *normalizedEndpoints + metricsByEndpoint map[string]*Metrics + mux sync.RWMutex +} + +func newMetricsByEndpoint( + metricsFactory metrics.Factory, + normalizer NameNormalizer, + maxNumberOfEndpoints int, +) *MetricsByEndpoint { + return &MetricsByEndpoint{ + metricsFactory: metricsFactory, + endpoints: newNormalizedEndpoints(maxNumberOfEndpoints, normalizer), + metricsByEndpoint: make(map[string]*Metrics, maxNumberOfEndpoints+1), // +1 for "other" + } +} + +func (m *MetricsByEndpoint) get(endpoint string) *Metrics { + safeName := m.endpoints.normalize(endpoint) + if safeName == "" { + safeName = otherEndpointsPlaceholder + } + m.mux.RLock() + met := m.metricsByEndpoint[safeName] + m.mux.RUnlock() + if met != nil { + return met + } + + return m.getWithWriteLock(safeName) +} + +// split to make easier to test +func (m *MetricsByEndpoint) getWithWriteLock(safeName string) *Metrics { + m.mux.Lock() + defer m.mux.Unlock() + + // it is possible that the name has been already registered after we released + // the read lock and before we grabbed the write lock, so check for that. + if met, ok := m.metricsByEndpoint[safeName]; ok { + return met + } + + // it would be nice to create the struct before locking, since Init() is somewhat + // expensive, however some metrics backends (e.g. expvar) may not like duplicate metrics. + met := &Metrics{} + tags := map[string]string{endpointNameMetricTag: safeName} + metrics.Init(met, m.metricsFactory, tags) + + m.metricsByEndpoint[safeName] = met + return met +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go new file mode 100644 index 00000000..292ec943 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/metrics_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +// E.g. tags("key", "value", "key", "value") +func tags(kv ...string) map[string]string { + m := make(map[string]string) + for i := 0; i < len(kv)-1; i += 2 { + m[kv[i]] = kv[i+1] + } + return m +} + +func endpointTags(endpoint string, kv ...string) map[string]string { + return tags(append([]string{"endpoint", endpoint}, kv...)...) +} + +func TestMetricsByEndpoint(t *testing.T) { + met := metrics.NewLocalFactory(0) + mbe := newMetricsByEndpoint(met, DefaultNameNormalizer, 2) + + m1 := mbe.get("abc1") + m2 := mbe.get("abc1") // from cache + m2a := mbe.getWithWriteLock("abc1") // from cache in double-checked lock + assert.Equal(t, m1, m2) + assert.Equal(t, m1, m2a) + + m3 := mbe.get("abc3") + m4 := mbe.get("overflow") + m5 := mbe.get("overflow2") + + for _, m := range []*Metrics{m1, m2, m2a, m3, m4, m5} { + m.RequestCountSuccess.Inc(1) + } + + testutils.AssertCounterMetrics(t, met, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc1", "error", "false"), Value: 3}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("abc3", "error", "false"), Value: 1}, + testutils.ExpectedMetric{Name: "requests", Tags: endpointTags("other", "error", "false"), Value: 2}, + ) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go new file mode 100644 index 00000000..148d84b3 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer.go @@ -0,0 +1,101 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +// NameNormalizer is used to convert the endpoint names to strings +// that can be safely used as tags in the metrics. +type NameNormalizer interface { + Normalize(name string) string +} + +// DefaultNameNormalizer converts endpoint names so that they contain only characters +// from the safe charset [a-zA-Z0-9-./_]. All other characters are replaced with '-'. +var DefaultNameNormalizer = &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Range{From: 'A', To: 'Z'}, + &Range{From: '0', To: '9'}, + &Char{'-'}, + &Char{'_'}, + &Char{'/'}, + &Char{'.'}, + }, + Replacement: '-', +} + +// SimpleNameNormalizer uses a set of safe character sets. +type SimpleNameNormalizer struct { + SafeSets []SafeCharacterSet + Replacement byte +} + +// SafeCharacterSet determines if the given character is "safe" +type SafeCharacterSet interface { + IsSafe(c byte) bool +} + +// Range implements SafeCharacterSet +type Range struct { + From, To byte +} + +// IsSafe implements SafeCharacterSet +func (r *Range) IsSafe(c byte) bool { + return c >= r.From && c <= r.To +} + +// Char implements SafeCharacterSet +type Char struct { + Val byte +} + +// IsSafe implements SafeCharacterSet +func (ch *Char) IsSafe(c byte) bool { + return c == ch.Val +} + +// Normalize checks each character in the string against SafeSets, +// and if it's not safe substitutes it with Replacement. +func (n *SimpleNameNormalizer) Normalize(name string) string { + var retMe []byte + nameBytes := []byte(name) + for i, b := range nameBytes { + if n.safeByte(b) { + if retMe != nil { + retMe[i] = b + } + } else { + if retMe == nil { + retMe = make([]byte, len(nameBytes)) + copy(retMe[0:i], nameBytes[0:i]) + } + retMe[i] = n.Replacement + } + } + if retMe == nil { + return name + } + return string(retMe) +} + +// safeByte checks if b against all safe charsets. +func (n *SimpleNameNormalizer) safeByte(b byte) bool { + for i := range n.SafeSets { + if n.SafeSets[i].IsSafe(b) { + return true + } + } + return false +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go new file mode 100644 index 00000000..a93c1716 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/normalizer_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleNameNormalizer(t *testing.T) { + n := &SimpleNameNormalizer{ + SafeSets: []SafeCharacterSet{ + &Range{From: 'a', To: 'z'}, + &Char{'-'}, + }, + Replacement: '-', + } + assert.Equal(t, "ab-cd", n.Normalize("ab-cd"), "all valid") + assert.Equal(t, "ab-cd", n.Normalize("ab.cd"), "single mismatch") + assert.Equal(t, "a--cd", n.Normalize("aB-cd"), "range letter mismatch") +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer.go new file mode 100644 index 00000000..eca5ff6f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer.go @@ -0,0 +1,171 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "strconv" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/jaeger-lib/metrics" + + jaeger "github.com/uber/jaeger-client-go" +) + +const defaultMaxNumberOfEndpoints = 200 + +// Observer is an observer that can emit RPC metrics. +type Observer struct { + metricsByEndpoint *MetricsByEndpoint +} + +// NewObserver creates a new observer that can emit RPC metrics. +func NewObserver(metricsFactory metrics.Factory, normalizer NameNormalizer) *Observer { + return &Observer{ + metricsByEndpoint: newMetricsByEndpoint( + metricsFactory, + normalizer, + defaultMaxNumberOfEndpoints, + ), + } +} + +// OnStartSpan creates a new Observer for the span. +func (o *Observer) OnStartSpan( + operationName string, + options opentracing.StartSpanOptions, +) jaeger.SpanObserver { + return NewSpanObserver(o.metricsByEndpoint, operationName, options) +} + +// SpanKind identifies the span as inboud, outbound, or internal +type SpanKind int + +const ( + // Local span kind + Local SpanKind = iota + // Inbound span kind + Inbound + // Outbound span kind + Outbound +) + +// SpanObserver collects RPC metrics +type SpanObserver struct { + metricsByEndpoint *MetricsByEndpoint + operationName string + startTime time.Time + mux sync.Mutex + kind SpanKind + httpStatusCode uint16 + err bool +} + +// NewSpanObserver creates a new SpanObserver that can emit RPC metrics. +func NewSpanObserver( + metricsByEndpoint *MetricsByEndpoint, + operationName string, + options opentracing.StartSpanOptions, +) *SpanObserver { + so := &SpanObserver{ + metricsByEndpoint: metricsByEndpoint, + operationName: operationName, + startTime: options.StartTime, + } + for k, v := range options.Tags { + so.handleTagInLock(k, v) + } + return so +} + +// handleTags watches for special tags +// - SpanKind +// - HttpStatusCode +// - Error +func (so *SpanObserver) handleTagInLock(key string, value interface{}) { + if key == string(ext.SpanKind) { + if v, ok := value.(ext.SpanKindEnum); ok { + value = string(v) + } + if v, ok := value.(string); ok { + if v == string(ext.SpanKindRPCClientEnum) { + so.kind = Outbound + } else if v == string(ext.SpanKindRPCServerEnum) { + so.kind = Inbound + } + } + return + } + if key == string(ext.HTTPStatusCode) { + if v, ok := value.(uint16); ok { + so.httpStatusCode = v + } else if v, ok := value.(int); ok { + so.httpStatusCode = uint16(v) + } else if v, ok := value.(string); ok { + if vv, err := strconv.Atoi(v); err == nil { + so.httpStatusCode = uint16(vv) + } + } + return + } + if key == string(ext.Error) { + if v, ok := value.(bool); ok { + so.err = v + } else if v, ok := value.(string); ok { + if vv, err := strconv.ParseBool(v); err == nil { + so.err = vv + } + } + return + } +} + +// OnFinish emits the RPC metrics. It only has an effect when operation name +// is not blank, and the span kind is an RPC server. +func (so *SpanObserver) OnFinish(options opentracing.FinishOptions) { + so.mux.Lock() + defer so.mux.Unlock() + + if so.operationName == "" || so.kind != Inbound { + return + } + + mets := so.metricsByEndpoint.get(so.operationName) + latency := options.FinishTime.Sub(so.startTime) + if so.err { + mets.RequestCountFailures.Inc(1) + mets.RequestLatencyFailures.Record(latency) + } else { + mets.RequestCountSuccess.Inc(1) + mets.RequestLatencySuccess.Record(latency) + } + mets.recordHTTPStatusCode(so.httpStatusCode) +} + +// OnSetOperationName records new operation name. +func (so *SpanObserver) OnSetOperationName(operationName string) { + so.mux.Lock() + so.operationName = operationName + so.mux.Unlock() +} + +// OnSetTag implements SpanObserver +func (so *SpanObserver) OnSetTag(key string, value interface{}) { + so.mux.Lock() + so.handleTagInLock(key, value) + so.mux.Unlock() +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go new file mode 100644 index 00000000..c2c31ae0 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/rpcmetrics/observer_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package rpcmetrics + +import ( + "fmt" + "testing" + "time" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + u "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/opentracing/opentracing-go/ext" + jaeger "github.com/uber/jaeger-client-go" +) + +func ExampleObserver() { + metricsFactory := metrics.NewLocalFactory(0) + metricsObserver := NewObserver( + metricsFactory, + DefaultNameNormalizer, + ) + tracer, closer := jaeger.NewTracer( + "serviceName", + jaeger.NewConstSampler(true), + jaeger.NewInMemoryReporter(), + jaeger.TracerOptions.Observer(metricsObserver), + ) + defer closer.Close() + + span := tracer.StartSpan("test", ext.SpanKindRPCServer) + span.Finish() + + c, _ := metricsFactory.Snapshot() + fmt.Printf("requests (success): %d\n", c["requests|endpoint=test|error=false"]) + fmt.Printf("requests (failure): %d\n", c["requests|endpoint=test|error=true"]) + // Output: + // requests (success): 1 + // requests (failure): 0 +} + +type testTracer struct { + metrics *metrics.LocalFactory + tracer opentracing.Tracer +} + +func withTestTracer(runTest func(tt *testTracer)) { + sampler := jaeger.NewConstSampler(true) + reporter := jaeger.NewInMemoryReporter() + metrics := metrics.NewLocalFactory(time.Minute) + observer := NewObserver(metrics, DefaultNameNormalizer) + tracer, closer := jaeger.NewTracer( + "test", + sampler, + reporter, + jaeger.TracerOptions.Observer(observer)) + defer closer.Close() + runTest(&testTracer{ + metrics: metrics, + tracer: tracer, + }) +} + +func TestObserver(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + ts := time.Now() + finishOptions := opentracing.FinishOptions{ + FinishTime: ts.Add(50 * time.Millisecond), + } + + testCases := []struct { + name string + tag opentracing.Tag + opNameOverride string + err bool + }{ + {name: "local-span", tag: opentracing.Tag{Key: "x", Value: "y"}}, + {name: "get-user", tag: ext.SpanKindRPCServer}, + {name: "get-user", tag: ext.SpanKindRPCServer, opNameOverride: "get-user-override"}, + {name: "get-user", tag: ext.SpanKindRPCServer, err: true}, + {name: "get-user-client", tag: ext.SpanKindRPCClient}, + } + + for _, testCase := range testCases { + span := testTracer.tracer.StartSpan( + testCase.name, + testCase.tag, + opentracing.StartTime(ts), + ) + if testCase.opNameOverride != "" { + span.SetOperationName(testCase.opNameOverride) + } + if testCase.err { + ext.Error.Set(span, true) + } + span.FinishWithOptions(finishOptions) + } + + u.AssertCounterMetrics(t, + testTracer.metrics, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("local-span", "error", "false"), Value: 0}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user", "error", "true"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-override", "error", "false"), Value: 1}, + u.ExpectedMetric{Name: "requests", Tags: endpointTags("get-user-client", "error", "false"), Value: 0}, + ) + // TODO something wrong with string generation, .P99 should not be appended to the tag + // as a result we cannot use u.AssertGaugeMetrics + _, g := testTracer.metrics.Snapshot() + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=false.P99"]) + assert.EqualValues(t, 51, g["request_latency|endpoint=get-user|error=true.P99"]) + }) +} + +func TestTags(t *testing.T) { + type tagTestCase struct { + key string + value interface{} + metrics []u.ExpectedMetric + } + + testCases := []tagTestCase{ + {key: "something", value: 42, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "false")}, + }}, + {key: "error", value: true, metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + {key: "error", value: "true", metrics: []u.ExpectedMetric{ + {Name: "requests", Value: 1, Tags: tags("error", "true")}, + }}, + } + + for i := 2; i <= 5; i++ { + values := []interface{}{ + i * 100, + uint16(i * 100), + fmt.Sprintf("%d00", i), + } + for _, v := range values { + testCases = append(testCases, tagTestCase{ + key: "http.status_code", value: v, metrics: []u.ExpectedMetric{ + {Name: "http_requests", Value: 1, Tags: tags("status_code", fmt.Sprintf("%dxx", i))}, + }, + }) + } + } + + for _, tc := range testCases { + testCase := tc // capture loop var + for i := range testCase.metrics { + testCase.metrics[i].Tags["endpoint"] = "span" + } + t.Run(fmt.Sprintf("%s-%v", testCase.key, testCase.value), func(t *testing.T) { + withTestTracer(func(testTracer *testTracer) { + span := testTracer.tracer.StartSpan("span", ext.SpanKindRPCServer) + span.SetTag(testCase.key, testCase.value) + span.Finish() + u.AssertCounterMetrics(t, testTracer.metrics, testCase.metrics...) + }) + }) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/sampler.go b/vendor/src/github.com/uber/jaeger-client-go/sampler.go new file mode 100644 index 00000000..f2e8c994 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/sampler.go @@ -0,0 +1,531 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "math" + "net/url" + "sync" + "time" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + defaultSamplingServerURL = "http://localhost:5778/sampling" + defaultSamplingRefreshInterval = time.Minute + defaultMaxOperations = 2000 +) + +// Sampler decides whether a new trace should be sampled or not. +type Sampler interface { + // IsSampled decides whether a trace with given `id` and `operation` + // should be sampled. This function will also return the tags that + // can be used to identify the type of sampling that was applied to + // the root span. Most simple samplers would return two tags, + // sampler.type and sampler.param, similar to those used in the Configuration + IsSampled(id TraceID, operation string) (sampled bool, tags []Tag) + + // Close does a clean shutdown of the sampler, stopping any background + // go-routines it may have started. + Close() + + // Equal checks if the `other` sampler is functionally equivalent + // to this sampler. + // TODO remove this function. This function is used to determine if 2 samplers are equivalent + // which does not bode well with the adaptive sampler which has to create all the composite samplers + // for the comparison to occur. This is expensive to do if only one sampler has changed. + Equal(other Sampler) bool +} + +// ----------------------- + +// ConstSampler is a sampler that always makes the same decision. +type ConstSampler struct { + Decision bool + tags []Tag +} + +// NewConstSampler creates a ConstSampler. +func NewConstSampler(sample bool) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeConst}, + {key: SamplerParamTagKey, value: sample}, + } + return &ConstSampler{Decision: sample, tags: tags} +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ConstSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.Decision, s.tags +} + +// Close implements Close() of Sampler. +func (s *ConstSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ConstSampler) Equal(other Sampler) bool { + if o, ok := other.(*ConstSampler); ok { + return s.Decision == o.Decision + } + return false +} + +// ----------------------- + +// ProbabilisticSampler is a sampler that randomly samples a certain percentage +// of traces. +type ProbabilisticSampler struct { + samplingRate float64 + samplingBoundary uint64 + tags []Tag +} + +const maxRandomNumber = ^(uint64(1) << 63) // i.e. 0x7fffffffffffffff + +// NewProbabilisticSampler creates a sampler that randomly samples a certain percentage of traces specified by the +// samplingRate, in the range between 0.0 and 1.0. +// +// It relies on the fact that new trace IDs are 63bit random numbers themselves, thus making the sampling decision +// without generating a new random number, but simply calculating if traceID < (samplingRate * 2^63). +// TODO remove the error from this function for next major release +func NewProbabilisticSampler(samplingRate float64) (*ProbabilisticSampler, error) { + if samplingRate < 0.0 || samplingRate > 1.0 { + return nil, fmt.Errorf("Sampling Rate must be between 0.0 and 1.0, received %f", samplingRate) + } + return newProbabilisticSampler(samplingRate), nil +} + +func newProbabilisticSampler(samplingRate float64) *ProbabilisticSampler { + samplingRate = math.Max(0.0, math.Min(samplingRate, 1.0)) + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeProbabilistic}, + {key: SamplerParamTagKey, value: samplingRate}, + } + return &ProbabilisticSampler{ + samplingRate: samplingRate, + samplingBoundary: uint64(float64(maxRandomNumber) * samplingRate), + tags: tags, + } +} + +// SamplingRate returns the sampling probability this sampled was constructed with. +func (s *ProbabilisticSampler) SamplingRate() float64 { + return s.samplingRate +} + +// IsSampled implements IsSampled() of Sampler. +func (s *ProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.samplingBoundary >= id.Low, s.tags +} + +// Close implements Close() of Sampler. +func (s *ProbabilisticSampler) Close() { + // nothing to do +} + +// Equal implements Equal() of Sampler. +func (s *ProbabilisticSampler) Equal(other Sampler) bool { + if o, ok := other.(*ProbabilisticSampler); ok { + return s.samplingBoundary == o.samplingBoundary + } + return false +} + +// ----------------------- + +type rateLimitingSampler struct { + maxTracesPerSecond float64 + rateLimiter utils.RateLimiter + tags []Tag +} + +// NewRateLimitingSampler creates a sampler that samples at most maxTracesPerSecond. The distribution of sampled +// traces follows burstiness of the service, i.e. a service with uniformly distributed requests will have those +// requests sampled uniformly as well, but if requests are bursty, especially sub-second, then a number of +// sequential requests can be sampled each second. +func NewRateLimitingSampler(maxTracesPerSecond float64) Sampler { + tags := []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeRateLimiting}, + {key: SamplerParamTagKey, value: maxTracesPerSecond}, + } + return &rateLimitingSampler{ + maxTracesPerSecond: maxTracesPerSecond, + rateLimiter: utils.NewRateLimiter(maxTracesPerSecond, math.Max(maxTracesPerSecond, 1.0)), + tags: tags, + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *rateLimitingSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + return s.rateLimiter.CheckCredit(1.0), s.tags +} + +func (s *rateLimitingSampler) Close() { + // nothing to do +} + +func (s *rateLimitingSampler) Equal(other Sampler) bool { + if o, ok := other.(*rateLimitingSampler); ok { + return s.maxTracesPerSecond == o.maxTracesPerSecond + } + return false +} + +// ----------------------- + +// GuaranteedThroughputProbabilisticSampler is a sampler that leverages both probabilisticSampler and +// rateLimitingSampler. The rateLimitingSampler is used as a guaranteed lower bound sampler such that +// every operation is sampled at least once in a time interval defined by the lowerBound. ie a lowerBound +// of 1.0 / (60 * 10) will sample an operation at least once every 10 minutes. +// +// The probabilisticSampler is given higher priority when tags are emitted, ie. if IsSampled() for both +// samplers return true, the tags for probabilisticSampler will be used. +type GuaranteedThroughputProbabilisticSampler struct { + probabilisticSampler *ProbabilisticSampler + lowerBoundSampler Sampler + tags []Tag + samplingRate float64 + lowerBound float64 +} + +// NewGuaranteedThroughputProbabilisticSampler returns a delegating sampler that applies both +// probabilisticSampler and rateLimitingSampler. +func NewGuaranteedThroughputProbabilisticSampler( + lowerBound, samplingRate float64, +) (*GuaranteedThroughputProbabilisticSampler, error) { + return newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate), nil +} + +func newGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate float64) *GuaranteedThroughputProbabilisticSampler { + s := &GuaranteedThroughputProbabilisticSampler{ + lowerBoundSampler: NewRateLimitingSampler(lowerBound), + lowerBound: lowerBound, + } + s.setProbabilisticSampler(samplingRate) + return s +} + +func (s *GuaranteedThroughputProbabilisticSampler) setProbabilisticSampler(samplingRate float64) { + if s.probabilisticSampler == nil || s.samplingRate != samplingRate { + s.probabilisticSampler = newProbabilisticSampler(samplingRate) + s.samplingRate = s.probabilisticSampler.SamplingRate() + s.tags = []Tag{ + {key: SamplerTypeTagKey, value: SamplerTypeLowerBound}, + {key: SamplerParamTagKey, value: s.samplingRate}, + } + } +} + +// IsSampled implements IsSampled() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + if sampled, tags := s.probabilisticSampler.IsSampled(id, operation); sampled { + s.lowerBoundSampler.IsSampled(id, operation) + return true, tags + } + sampled, _ := s.lowerBoundSampler.IsSampled(id, operation) + return sampled, s.tags +} + +// Close implements Close() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Close() { + s.probabilisticSampler.Close() + s.lowerBoundSampler.Close() +} + +// Equal implements Equal() of Sampler. +func (s *GuaranteedThroughputProbabilisticSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + return false +} + +// this function should only be called while holding a Write lock +func (s *GuaranteedThroughputProbabilisticSampler) update(lowerBound, samplingRate float64) { + s.setProbabilisticSampler(samplingRate) + if s.lowerBound != lowerBound { + s.lowerBoundSampler = NewRateLimitingSampler(lowerBound) + s.lowerBound = lowerBound + } +} + +// ----------------------- + +type adaptiveSampler struct { + sync.RWMutex + + samplers map[string]*GuaranteedThroughputProbabilisticSampler + defaultSampler *ProbabilisticSampler + lowerBound float64 + maxOperations int +} + +// NewAdaptiveSampler returns a delegating sampler that applies both probabilisticSampler and +// rateLimitingSampler via the guaranteedThroughputProbabilisticSampler. This sampler keeps track of all +// operations and delegates calls to the respective guaranteedThroughputProbabilisticSampler. +func NewAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) (Sampler, error) { + return newAdaptiveSampler(strategies, maxOperations), nil +} + +func newAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies, maxOperations int) Sampler { + samplers := make(map[string]*GuaranteedThroughputProbabilisticSampler) + for _, strategy := range strategies.PerOperationStrategies { + sampler := newGuaranteedThroughputProbabilisticSampler( + strategies.DefaultLowerBoundTracesPerSecond, + strategy.ProbabilisticSampling.SamplingRate, + ) + samplers[strategy.Operation] = sampler + } + return &adaptiveSampler{ + samplers: samplers, + defaultSampler: newProbabilisticSampler(strategies.DefaultSamplingProbability), + lowerBound: strategies.DefaultLowerBoundTracesPerSecond, + maxOperations: maxOperations, + } +} + +func (s *adaptiveSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + sampler, ok := s.samplers[operation] + if ok { + defer s.RUnlock() + return sampler.IsSampled(id, operation) + } + s.RUnlock() + s.Lock() + defer s.Unlock() + + // Check if sampler has already been created + sampler, ok = s.samplers[operation] + if ok { + return sampler.IsSampled(id, operation) + } + // Store only up to maxOperations of unique ops. + if len(s.samplers) >= s.maxOperations { + return s.defaultSampler.IsSampled(id, operation) + } + newSampler := newGuaranteedThroughputProbabilisticSampler(s.lowerBound, s.defaultSampler.SamplingRate()) + s.samplers[operation] = newSampler + return newSampler.IsSampled(id, operation) +} + +func (s *adaptiveSampler) Close() { + s.Lock() + defer s.Unlock() + for _, sampler := range s.samplers { + sampler.Close() + } + s.defaultSampler.Close() +} + +func (s *adaptiveSampler) Equal(other Sampler) bool { + // NB The Equal() function is overly expensive for adaptiveSampler since it's composed of multiple + // samplers which all need to be initialized before this function can be called for a comparison. + // Therefore, adaptiveSampler uses the update() function to only alter the samplers that need + // changing. Hence this function always returns false so that the update function can be called. + // Once the Equal() function is removed from the Sampler API, this will no longer be needed. + return false +} + +func (s *adaptiveSampler) update(strategies *sampling.PerOperationSamplingStrategies) { + s.Lock() + defer s.Unlock() + for _, strategy := range strategies.PerOperationStrategies { + operation := strategy.Operation + samplingRate := strategy.ProbabilisticSampling.SamplingRate + lowerBound := strategies.DefaultLowerBoundTracesPerSecond + if sampler, ok := s.samplers[operation]; ok { + sampler.update(lowerBound, samplingRate) + } else { + sampler := newGuaranteedThroughputProbabilisticSampler( + lowerBound, + samplingRate, + ) + s.samplers[operation] = sampler + } + } + s.lowerBound = strategies.DefaultLowerBoundTracesPerSecond + if s.defaultSampler.SamplingRate() != strategies.DefaultSamplingProbability { + s.defaultSampler = newProbabilisticSampler(strategies.DefaultSamplingProbability) + } +} + +// ----------------------- + +// RemotelyControlledSampler is a delegating sampler that polls a remote server +// for the appropriate sampling strategy, constructs a corresponding sampler and +// delegates to it for sampling decisions. +type RemotelyControlledSampler struct { + sync.RWMutex + samplerOptions + + serviceName string + timer *time.Ticker + manager sampling.SamplingManager + pollStopped sync.WaitGroup +} + +type httpSamplingManager struct { + serverURL string +} + +func (s *httpSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + var out sampling.SamplingStrategyResponse + v := url.Values{} + v.Set("service", serviceName) + if err := utils.GetJSON(s.serverURL+"?"+v.Encode(), &out); err != nil { + return nil, err + } + return &out, nil +} + +// NewRemotelyControlledSampler creates a sampler that periodically pulls +// the sampling strategy from an HTTP sampling server (e.g. jaeger-agent). +func NewRemotelyControlledSampler( + serviceName string, + opts ...SamplerOption, +) *RemotelyControlledSampler { + options := applySamplerOptions(opts...) + sampler := &RemotelyControlledSampler{ + samplerOptions: options, + serviceName: serviceName, + timer: time.NewTicker(options.samplingRefreshInterval), + manager: &httpSamplingManager{serverURL: options.samplingServerURL}, + } + + go sampler.pollController() + return sampler +} + +func applySamplerOptions(opts ...SamplerOption) samplerOptions { + options := samplerOptions{} + for _, option := range opts { + option(&options) + } + if options.sampler == nil { + options.sampler = newProbabilisticSampler(0.001) + } + if options.logger == nil { + options.logger = log.NullLogger + } + if options.maxOperations <= 0 { + options.maxOperations = defaultMaxOperations + } + if options.samplingServerURL == "" { + options.samplingServerURL = defaultSamplingServerURL + } + if options.metrics == nil { + options.metrics = NewNullMetrics() + } + if options.samplingRefreshInterval <= 0 { + options.samplingRefreshInterval = defaultSamplingRefreshInterval + } + return options +} + +// IsSampled implements IsSampled() of Sampler. +func (s *RemotelyControlledSampler) IsSampled(id TraceID, operation string) (bool, []Tag) { + s.RLock() + defer s.RUnlock() + return s.sampler.IsSampled(id, operation) +} + +// Close implements Close() of Sampler. +func (s *RemotelyControlledSampler) Close() { + s.RLock() + s.timer.Stop() + s.RUnlock() + + s.pollStopped.Wait() +} + +// Equal implements Equal() of Sampler. +func (s *RemotelyControlledSampler) Equal(other Sampler) bool { + // NB The Equal() function is expensive and will be removed. See adaptiveSampler.Equal() for + // more information. + if o, ok := other.(*RemotelyControlledSampler); ok { + s.RLock() + o.RLock() + defer s.RUnlock() + defer o.RUnlock() + return s.sampler.Equal(o.sampler) + } + return false +} + +func (s *RemotelyControlledSampler) pollController() { + // in unit tests we re-assign the timer Ticker, so need to lock to avoid data races + s.Lock() + timer := s.timer + s.Unlock() + + for range timer.C { + s.updateSampler() + } + s.pollStopped.Add(1) +} + +func (s *RemotelyControlledSampler) updateSampler() { + res, err := s.manager.GetSamplingStrategy(s.serviceName) + if err != nil { + s.metrics.SamplerQueryFailure.Inc(1) + return + } + s.Lock() + defer s.Unlock() + + s.metrics.SamplerRetrieved.Inc(1) + if strategies := res.GetOperationSampling(); strategies != nil { + s.updateAdaptiveSampler(strategies) + } else { + err = s.updateRateLimitingOrProbabilisticSampler(res) + } + if err != nil { + s.metrics.SamplerUpdateFailure.Inc(1) + s.logger.Infof("Unable to handle sampling strategy response %+v. Got error: %v", res, err) + return + } + s.metrics.SamplerUpdated.Inc(1) +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateAdaptiveSampler(strategies *sampling.PerOperationSamplingStrategies) { + if adaptiveSampler, ok := s.sampler.(*adaptiveSampler); ok { + adaptiveSampler.update(strategies) + } else { + s.sampler = newAdaptiveSampler(strategies, s.maxOperations) + } +} + +// NB: this function should only be called while holding a Write lock +func (s *RemotelyControlledSampler) updateRateLimitingOrProbabilisticSampler(res *sampling.SamplingStrategyResponse) error { + var newSampler Sampler + if probabilistic := res.GetProbabilisticSampling(); probabilistic != nil { + newSampler = newProbabilisticSampler(probabilistic.SamplingRate) + } else if rateLimiting := res.GetRateLimitingSampling(); rateLimiting != nil { + newSampler = NewRateLimitingSampler(float64(rateLimiting.MaxTracesPerSecond)) + } else { + return fmt.Errorf("Unsupported sampling strategy type %v", res.GetStrategyType()) + } + if !s.sampler.Equal(newSampler) { + s.sampler = newSampler + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/sampler_options.go b/vendor/src/github.com/uber/jaeger-client-go/sampler_options.go new file mode 100644 index 00000000..75d28a56 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/sampler_options.go @@ -0,0 +1,81 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" +) + +// SamplerOption is a function that sets some option on the sampler +type SamplerOption func(options *samplerOptions) + +// SamplerOptions is a factory for all available SamplerOption's +var SamplerOptions samplerOptions + +type samplerOptions struct { + metrics *Metrics + maxOperations int + sampler Sampler + logger Logger + samplingServerURL string + samplingRefreshInterval time.Duration +} + +// Metrics creates a SamplerOption that initializes Metrics on the sampler, +// which is used to emit statistics. +func (samplerOptions) Metrics(m *Metrics) SamplerOption { + return func(o *samplerOptions) { + o.metrics = m + } +} + +// MaxOperations creates a SamplerOption that sets the maximum number of +// operations the sampler will keep track of. +func (samplerOptions) MaxOperations(maxOperations int) SamplerOption { + return func(o *samplerOptions) { + o.maxOperations = maxOperations + } +} + +// InitialSampler creates a SamplerOption that sets the initial sampler +// to use before a remote sampler is created and used. +func (samplerOptions) InitialSampler(sampler Sampler) SamplerOption { + return func(o *samplerOptions) { + o.sampler = sampler + } +} + +// Logger creates a SamplerOption that sets the logger used by the sampler. +func (samplerOptions) Logger(logger Logger) SamplerOption { + return func(o *samplerOptions) { + o.logger = logger + } +} + +// SamplingServerURL creates a SamplerOption that sets the sampling server url +// of the local agent that contains the sampling strategies. +func (samplerOptions) SamplingServerURL(samplingServerURL string) SamplerOption { + return func(o *samplerOptions) { + o.samplingServerURL = samplingServerURL + } +} + +// SamplingRefreshInterval creates a SamplerOption that sets how often the +// sampler will poll local agent for the appropriate sampling strategy. +func (samplerOptions) SamplingRefreshInterval(samplingRefreshInterval time.Duration) SamplerOption { + return func(o *samplerOptions) { + o.samplingRefreshInterval = samplingRefreshInterval + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/sampler_test.go b/vendor/src/github.com/uber/jaeger-client-go/sampler_test.go new file mode 100644 index 00000000..d1707254 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/sampler_test.go @@ -0,0 +1,703 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "runtime" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" + mTestutils "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + testOperationName = "op" + testFirstTimeOperationName = "firstTimeOp" + + testDefaultSamplingProbability = 0.5 + testMaxID = uint64(1) << 62 + testDefaultMaxOperations = 10 +) + +var ( + testProbabilisticExpectedTags = []Tag{ + {"sampler.type", "probabilistic"}, + {"sampler.param", 0.5}, + } + testLowerBoundExpectedTags = []Tag{ + {"sampler.type", "lowerbound"}, + {"sampler.param", 0.5}, + } +) + +func TestSamplerTags(t *testing.T) { + prob, err := NewProbabilisticSampler(0.1) + require.NoError(t, err) + rate := NewRateLimitingSampler(0.1) + remote := &RemotelyControlledSampler{} + remote.sampler = NewConstSampler(true) + tests := []struct { + sampler Sampler + typeTag string + paramTag interface{} + }{ + {NewConstSampler(true), "const", true}, + {NewConstSampler(false), "const", false}, + {prob, "probabilistic", 0.1}, + {rate, "ratelimiting", 0.1}, + {remote, "const", true}, + } + for _, test := range tests { + _, tags := test.sampler.IsSampled(TraceID{}, testOperationName) + count := 0 + for _, tag := range tags { + if tag.key == SamplerTypeTagKey { + assert.Equal(t, test.typeTag, tag.value) + count++ + } + if tag.key == SamplerParamTagKey { + assert.Equal(t, test.paramTag, tag.value) + count++ + } + } + assert.Equal(t, 2, count) + } +} + +func TestApplySamplerOptions(t *testing.T) { + options := applySamplerOptions() + sampler, ok := options.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.Equal(t, 0.001, sampler.samplingRate) + + assert.NotNil(t, options.logger) + assert.NotZero(t, options.maxOperations) + assert.NotEmpty(t, options.samplingServerURL) + assert.NotNil(t, options.metrics) + assert.NotZero(t, options.samplingRefreshInterval) +} + +func TestProbabilisticSamplerErrors(t *testing.T) { + _, err := NewProbabilisticSampler(-0.1) + assert.Error(t, err) + _, err = NewProbabilisticSampler(1.1) + assert.Error(t, err) +} + +func TestProbabilisticSampler(t *testing.T) { + sampler, _ := NewProbabilisticSampler(0.5) + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampler2, _ := NewProbabilisticSampler(0.5) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(NewConstSampler(true))) +} + +func TestProbabilisticSamplerPerformance(t *testing.T) { + t.Skip("Skipped performance test") + sampler, _ := NewProbabilisticSampler(0.01) + rand := utils.NewRand(8736823764) + var count uint64 + for i := 0; i < 100000000; i++ { + id := TraceID{Low: uint64(rand.Int63())} + if sampled, _ := sampler.IsSampled(id, testOperationName); sampled { + count++ + } + } + println("Sampled:", count, "rate=", float64(count)/float64(100000000)) + // Sampled: 999829 rate= 0.009998290 +} + +func TestRateLimitingSampler(t *testing.T) { + sampler := NewRateLimitingSampler(2) + sampler2 := NewRateLimitingSampler(2) + sampler3 := NewRateLimitingSampler(3) + assert.True(t, sampler.Equal(sampler2)) + assert.False(t, sampler.Equal(sampler3)) + assert.False(t, sampler.Equal(NewConstSampler(false))) + + sampler = NewRateLimitingSampler(2) + sampled, _ := sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) + + sampler = NewRateLimitingSampler(0.1) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.True(t, sampled) + sampled, _ = sampler.IsSampled(TraceID{}, testOperationName) + assert.False(t, sampled) +} + +func TestGuaranteedThroughputProbabilisticSamplerUpdate(t *testing.T) { + samplingRate := 0.5 + lowerBound := 2.0 + sampler, err := NewGuaranteedThroughputProbabilisticSampler(lowerBound, samplingRate) + assert.NoError(t, err) + + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, samplingRate, sampler.samplingRate) + + newSamplingRate := 0.6 + newLowerBound := 1.0 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newSamplingRate, sampler.samplingRate) + + newSamplingRate = 1.1 + sampler.update(newLowerBound, newSamplingRate) + assert.Equal(t, 1.0, sampler.samplingRate) +} + +func TestAdaptiveSampler(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: testDefaultSamplingProbability}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + defer sampler.Close() + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testLowerBoundExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 20}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + + // This operation is seen for the first time by the sampler + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestAdaptiveSamplerErrors(t *testing.T) { + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: -0.1}, + }, + }, + } + + sampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 0.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) + + strategies.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate = 1.1 + sampler, err = NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + assert.Equal(t, 1.0, sampler.(*adaptiveSampler).samplers[testOperationName].samplingRate) +} + +func TestAdaptiveSamplerUpdate(t *testing.T) { + samplingRate := 0.1 + lowerBound := 2.0 + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: samplingRate}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: lowerBound, + PerOperationStrategies: samplingRates, + } + + s, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + assert.NoError(t, err) + + sampler, ok := s.(*adaptiveSampler) + assert.True(t, ok) + assert.Equal(t, lowerBound, sampler.lowerBound) + assert.Equal(t, testDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 1) + + // Update the sampler with new sampling rates + newSamplingRate := 0.2 + newLowerBound := 3.0 + newDefaultSamplingProbability := 0.1 + newSamplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + { + Operation: testFirstTimeOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: newSamplingRate}, + }, + } + strategies = &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: newDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: newLowerBound, + PerOperationStrategies: newSamplingRates, + } + + sampler.update(strategies) + assert.Equal(t, newLowerBound, sampler.lowerBound) + assert.Equal(t, newDefaultSamplingProbability, sampler.defaultSampler.SamplingRate()) + assert.Len(t, sampler.samplers, 2) +} + +func initAgent(t *testing.T) (*testutils.MockAgent, *RemotelyControlledSampler, *metrics.LocalFactory) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + + metricsFactory := metrics.NewLocalFactory(0) + metrics := NewMetrics(metricsFactory, nil) + + initialSampler, _ := NewProbabilisticSampler(0.001) + sampler := NewRemotelyControlledSampler( + "client app", + SamplerOptions.Metrics(metrics), + SamplerOptions.SamplingServerURL("http://"+agent.SamplingServerAddr()), + SamplerOptions.MaxOperations(testDefaultMaxOperations), + SamplerOptions.InitialSampler(initialSampler), + SamplerOptions.Logger(log.NullLogger), + SamplerOptions.SamplingRefreshInterval(time.Minute), + ) + sampler.Close() // stop timer-based updates, we want to call them manually + + return agent, sampler, metricsFactory +} + +func TestRemotelyControlledSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, testDefaultSamplingProbability)) + remoteSampler.updateSampler() + mTestutils.AssertCounterMetrics(t, metricsFactory, []mTestutils.ExpectedMetric{ + {Name: "jaeger.sampler", Tags: map[string]string{"state": "retrieved"}, Value: 1}, + {Name: "jaeger.sampler", Tags: map[string]string{"state": "updated"}, Value: 1}, + }...) + _, ok = remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, remoteSampler.sampler, "Sampler should have been updated") + + sampled, tags := remoteSampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.False(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + sampled, tags = remoteSampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) + + remoteSampler.sampler = initSampler + c := make(chan time.Time) + remoteSampler.Lock() + remoteSampler.timer = &time.Ticker{C: c} + remoteSampler.Unlock() + go remoteSampler.pollController() + + c <- time.Now() // force update based on timer + time.Sleep(10 * time.Millisecond) + remoteSampler.Close() + + _, ok = remoteSampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, remoteSampler.sampler, "Sampler should have been updated from timer") + + assert.True(t, remoteSampler.Equal(remoteSampler)) +} + +func generateTags(key string, value float64) []Tag { + return []Tag{ + {"sampler.type", key}, + {"sampler.param", value}, + } +} + +func TestRemotelyControlledSampler_updateSampler(t *testing.T) { + tests := []struct { + probabilities map[string]float64 + defaultProbability float64 + expectedDefaultProbability float64 + expectedTags []Tag + }{ + { + probabilities: map[string]float64{testOperationName: 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: generateTags("probabilistic", 1.0), + }, + { + probabilities: map[string]float64{testOperationName: testDefaultSamplingProbability}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{ + testOperationName: testDefaultSamplingProbability, + testFirstTimeOperationName: testDefaultSamplingProbability, + }, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: testDefaultSamplingProbability, + expectedDefaultProbability: testDefaultSamplingProbability, + expectedTags: testProbabilisticExpectedTags, + }, + { + probabilities: map[string]float64{"new op": 1.1}, + defaultProbability: 1.1, + expectedDefaultProbability: 1.0, + expectedTags: generateTags("probabilistic", 1.0), + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + res := &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + OperationSampling: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: test.defaultProbability, + DefaultLowerBoundTracesPerSecond: 0.001, + }, + } + for opName, prob := range test.probabilities { + res.OperationSampling.PerOperationStrategies = append(res.OperationSampling.PerOperationStrategies, + &sampling.OperationSamplingStrategy{ + Operation: opName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: prob, + }, + }, + ) + } + + agent.AddSamplingStrategy("client app", res) + sampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler" + "|state=updated", Value: 1, + }, + ) + + s, ok := sampler.sampler.(*adaptiveSampler) + assert.True(t, ok) + assert.NotEqual(t, initSampler, sampler.sampler, "Sampler should have been updated") + assert.Equal(t, test.expectedDefaultProbability, s.defaultSampler.SamplingRate()) + + // First call is always sampled + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID + 10}, testOperationName) + assert.True(t, sampled) + + sampled, tags = sampler.IsSampled(TraceID{Low: testMaxID - 10}, testOperationName) + assert.True(t, sampled) + assert.Equal(t, test.expectedTags, tags) + }) + } +} + +func TestMaxOperations(t *testing.T) { + samplingRates := []*sampling.OperationSamplingStrategy{ + { + Operation: testOperationName, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 0.1}, + }, + } + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 2.0, + PerOperationStrategies: samplingRates, + } + + sampler, err := NewAdaptiveSampler(strategies, 1) + assert.NoError(t, err) + + sampled, tags := sampler.IsSampled(TraceID{Low: testMaxID - 10}, testFirstTimeOperationName) + assert.True(t, sampled) + assert.Equal(t, testProbabilisticExpectedTags, tags) +} + +func TestSamplerQueryError(t *testing.T) { + agent, sampler, metricsFactory := initAgent(t) + defer agent.Close() + + // override the actual handler + sampler.manager = &fakeSamplingManager{} + + initSampler, ok := sampler.sampler.(*ProbabilisticSampler) + assert.True(t, ok) + + sampler.Close() // stop timer-based updates, we want to call them manually + + sampler.updateSampler() + assert.Equal(t, initSampler, sampler.sampler, "Sampler should not have been updated due to query error") + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"phase": "query", "state": "failure"}, + Value: 1, + }, + ) +} + +type fakeSamplingManager struct{} + +func (c *fakeSamplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + return nil, errors.New("query error") +} + +func TestRemotelyControlledSampler_updateSamplerFromAdaptiveSampler(t *testing.T) { + agent, remoteSampler, metricsFactory := initAgent(t) + defer agent.Close() + remoteSampler.Close() // stop timer-based updates, we want to call them manually + + strategies := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: testDefaultSamplingProbability, + DefaultLowerBoundTracesPerSecond: 1.0, + } + + adaptiveSampler, err := NewAdaptiveSampler(strategies, testDefaultMaxOperations) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.5)) + remoteSampler.updateSampler() + + // Sampler should have been updated to probabilistic + _, ok := remoteSampler.sampler.(*ProbabilisticSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + agent.AddSamplingStrategy("client app", + getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 1)) + remoteSampler.updateSampler() + + // Sampler should have been updated to ratelimiting + _, ok = remoteSampler.sampler.(*rateLimitingSampler) + require.True(t, ok) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + // Update existing adaptive sampler + agent.AddSamplingStrategy("client app", &sampling.SamplingStrategyResponse{OperationSampling: strategies}) + remoteSampler.updateSampler() + + mTestutils.AssertCounterMetrics(t, metricsFactory, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"state": "retrieved"}, + Value: 3, + }, + mTestutils.ExpectedMetric{ + Name: "jaeger.sampler", + Tags: map[string]string{"state": "updated"}, + Value: 3, + }, + ) +} + +func TestRemotelyControlledSampler_updateRateLimitingOrProbabilisticSampler(t *testing.T) { + probabilisticSampler, err := NewProbabilisticSampler(0.002) + require.NoError(t, err) + otherProbabilisticSampler, err := NewProbabilisticSampler(0.003) + require.NoError(t, err) + maxProbabilisticSampler, err := NewProbabilisticSampler(1.0) + require.NoError(t, err) + + rateLimitingSampler := NewRateLimitingSampler(2) + otherRateLimitingSampler := NewRateLimitingSampler(3) + + testCases := []struct { + res *sampling.SamplingStrategyResponse + initSampler Sampler + expectedSampler Sampler + shouldErr bool + referenceEquivalence bool + caption string + }{ + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 1.5), + initSampler: probabilisticSampler, + expectedSampler: maxProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "invalid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.002), + initSampler: probabilisticSampler, + expectedSampler: probabilisticSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_PROBABILISTIC, 0.003), + initSampler: probabilisticSampler, + expectedSampler: otherProbabilisticSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid probabilistic strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 2), + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: false, + referenceEquivalence: true, + caption: "unchanged rate limiting strategy", + }, + { + res: getSamplingStrategyResponse(sampling.SamplingStrategyType_RATE_LIMITING, 3), + initSampler: rateLimitingSampler, + expectedSampler: otherRateLimitingSampler, + shouldErr: false, + referenceEquivalence: false, + caption: "valid rate limiting strategy", + }, + { + res: &sampling.SamplingStrategyResponse{}, + initSampler: rateLimitingSampler, + expectedSampler: rateLimitingSampler, + shouldErr: true, + referenceEquivalence: true, + caption: "invalid strategy", + }, + } + + for _, tc := range testCases { + testCase := tc // capture loop var + t.Run(testCase.caption, func(t *testing.T) { + remoteSampler := &RemotelyControlledSampler{samplerOptions: samplerOptions{sampler: testCase.initSampler}} + err := remoteSampler.updateRateLimitingOrProbabilisticSampler(testCase.res) + if testCase.shouldErr { + require.Error(t, err) + } + if testCase.referenceEquivalence { + assert.Equal(t, testCase.expectedSampler, remoteSampler.sampler) + } else { + assert.True(t, testCase.expectedSampler.Equal(remoteSampler.sampler)) + } + }) + } +} + +func getSamplingStrategyResponse(strategyType sampling.SamplingStrategyType, value float64) *sampling.SamplingStrategyResponse { + if strategyType == sampling.SamplingStrategyType_PROBABILISTIC { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: value, + }, + } + } + if strategyType == sampling.SamplingStrategyType_RATE_LIMITING { + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int16(value), + }, + } + } + return nil +} + +func TestAdaptiveSampler_lockRaceCondition(t *testing.T) { + agent, remoteSampler, _ := initAgent(t) + defer agent.Close() + remoteSampler.Close() // stop timer-based updates, we want to call them manually + + numOperations := 1000 + adaptiveSampler, err := NewAdaptiveSampler( + &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 1, + }, + 2000, + ) + require.NoError(t, err) + + // Overwrite the sampler with an adaptive sampler + remoteSampler.sampler = adaptiveSampler + + var wg sync.WaitGroup + defer wg.Wait() + wg.Add(2) + + // Start 2 go routines that will simulate simultaneous calls to IsSampled + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "a") + }() + go func() { + defer wg.Done() + isSampled(t, remoteSampler, numOperations, "b") + }() +} + +func isSampled(t *testing.T, remoteSampler *RemotelyControlledSampler, numOperations int, operationNamePrefix string) { + for i := 0; i < numOperations; i++ { + runtime.Gosched() + sampled, _ := remoteSampler.IsSampled(TraceID{}, fmt.Sprintf("%s%d", operationNamePrefix, i)) + assert.True(t, sampled) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/scripts/cover.sh b/vendor/src/github.com/uber/jaeger-client-go/scripts/cover.sh new file mode 100644 index 00000000..d0fafbdb --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/scripts/cover.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -e + +COVER=.cover +ROOT_PKG=github.com/uber/jaeger-client-go/ + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +# If a package directory has a .nocover file, don't count it when calculating +# coverage. +filter="" +for pkg in "$@"; do + if [[ -f "$GOPATH/src/$pkg/.nocover" ]]; then + if [[ -n "$filter" ]]; then + filter="$filter, " + fi + filter="\"$pkg\": true" + fi +done + + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | . + ["'"$pkg"'"] + | map + ( select(startswith("'"$ROOT_PKG"'")) + | select(contains("/vendor/") | not) + | select(in({'"$filter"'}) | not) + ) + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + args="" + if [[ -n "$coverpkg" ]]; then + args="-coverprofile $COVER/cover.${i}.out" # -coverpkg $coverpkg" + fi + + echo go test -v -race "$pkg" + go test $args -v -race "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicense.py b/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicense.py new file mode 100644 index 00000000..5374d36f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicense.py @@ -0,0 +1,120 @@ +from __future__ import ( + absolute_import, print_function, division, unicode_literals +) + +import logging +import re +import sys +from datetime import datetime + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +CURRENT_YEAR = datetime.today().year + +MIT_LICENSE_BLOB = """Copyright (c) %d Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.""" % CURRENT_YEAR + +LICENSE_BLOB = """Copyright (c) %d Uber Technologies, Inc. + +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.""" % CURRENT_YEAR + +MIT_LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in MIT_LICENSE_BLOB.split('\n') +] + +LICENSE_BLOB_LINES_GO = [ + ('// ' + l).strip() + '\n' for l in LICENSE_BLOB.split('\n') +] + +COPYRIGHT_RE = re.compile(r'Copyright \(c\) (\d+)', re.I) + + +def update_go_license(name, force=False): + with open(name) as f: + orig_lines = list(f) + lines = list(orig_lines) + + current_header = ''.join(lines[0:len(MIT_LICENSE_BLOB_LINES_GO)]) + mit_header = ''.join(MIT_LICENSE_BLOB_LINES_GO) + if current_header == mit_header: + lines = lines[len(MIT_LICENSE_BLOB_LINES_GO)+1:] + + found = False + changed = False + for i, line in enumerate(lines[:5]): + m = COPYRIGHT_RE.search(line) + if not m: + continue + + found = True + year = int(m.group(1)) + if year == CURRENT_YEAR: + break + + new_line = COPYRIGHT_RE.sub('Copyright (c) %d' % CURRENT_YEAR, line) + assert line != new_line, ('Could not change year in: %s' % line) + lines[i] = new_line + changed = True + break + + if not found: + if 'Code generated by' in lines[0]: + lines[1:1] = ['\n'] + LICENSE_BLOB_LINES_GO + else: + lines[0:0] = LICENSE_BLOB_LINES_GO + ['\n'] + changed = True + + if changed: + with open(name, 'w') as f: + for line in lines: + f.write(line) + print(name) + + +def main(): + if len(sys.argv) == 1: + print('USAGE: %s FILE ...' % sys.argv[0]) + sys.exit(1) + + for name in sys.argv[1:]: + if name.endswith('.go'): + try: + update_go_license(name) + except Exception as error: + logger.error('Failed to process file %s', name) + logger.exception(error) + raise error + else: + raise NotImplementedError('Unsupported file type: %s' % name) + + +if __name__ == "__main__": + main() diff --git a/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh b/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh new file mode 100644 index 00000000..fa97364e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/scripts/updateLicenses.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +python scripts/updateLicense.py $(git ls-files "*\.go" | grep -v thrift-gen | grep -v tracetest) diff --git a/vendor/src/github.com/uber/jaeger-client-go/span.go b/vendor/src/github.com/uber/jaeger-client-go/span.go new file mode 100644 index 00000000..132fb721 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/span.go @@ -0,0 +1,242 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" +) + +// Span implements opentracing.Span +type Span struct { + sync.RWMutex + + tracer *Tracer + + context SpanContext + + // The name of the "operation" this span is an instance of. + // Known as a "span name" in some implementations. + operationName string + + // firstInProcess, if true, indicates that this span is the root of the (sub)tree + // of spans in the current process. In other words it's true for the root spans, + // and the ingress spans when the process joins another trace. + firstInProcess bool + + // startTime is the timestamp indicating when the span began, with microseconds precision. + startTime time.Time + + // duration returns duration of the span with microseconds precision. + // Zero value means duration is unknown. + duration time.Duration + + // tags attached to this span + tags []Tag + + // The span's "micro-log" + logs []opentracing.LogRecord + + // references for this span + references []Reference + + observer ContribSpanObserver +} + +// Tag is a simple key value wrapper. +// TODO deprecate in the next major release, use opentracing.Tag instead. +type Tag struct { + key string + value interface{} +} + +// SetOperationName sets or changes the operation name. +func (s *Span) SetOperationName(operationName string) opentracing.Span { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.operationName = operationName + } + s.observer.OnSetOperationName(operationName) + return s +} + +// SetTag implements SetTag() of opentracing.Span +func (s *Span) SetTag(key string, value interface{}) opentracing.Span { + s.observer.OnSetTag(key, value) + if key == string(ext.SamplingPriority) && setSamplingPriority(s, value) { + return s + } + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + s.setTagNoLocking(key, value) + } + return s +} + +func (s *Span) setTagNoLocking(key string, value interface{}) { + s.tags = append(s.tags, Tag{key: key, value: value}) +} + +// LogFields implements opentracing.Span API +func (s *Span) LogFields(fields ...log.Field) { + s.Lock() + defer s.Unlock() + if !s.context.IsSampled() { + return + } + s.logFieldsNoLocking(fields...) +} + +// this function should only be called while holding a Write lock +func (s *Span) logFieldsNoLocking(fields ...log.Field) { + lr := opentracing.LogRecord{ + Fields: fields, + Timestamp: time.Now(), + } + s.appendLog(lr) +} + +// LogKV implements opentracing.Span API +func (s *Span) LogKV(alternatingKeyValues ...interface{}) { + s.RLock() + sampled := s.context.IsSampled() + s.RUnlock() + if !sampled { + return + } + fields, err := log.InterleavedKVToFields(alternatingKeyValues...) + if err != nil { + s.LogFields(log.Error(err), log.String("function", "LogKV")) + return + } + s.LogFields(fields...) +} + +// LogEvent implements opentracing.Span API +func (s *Span) LogEvent(event string) { + s.Log(opentracing.LogData{Event: event}) +} + +// LogEventWithPayload implements opentracing.Span API +func (s *Span) LogEventWithPayload(event string, payload interface{}) { + s.Log(opentracing.LogData{Event: event, Payload: payload}) +} + +// Log implements opentracing.Span API +func (s *Span) Log(ld opentracing.LogData) { + s.Lock() + defer s.Unlock() + if s.context.IsSampled() { + if ld.Timestamp.IsZero() { + ld.Timestamp = s.tracer.timeNow() + } + s.appendLog(ld.ToLogRecord()) + } +} + +// this function should only be called while holding a Write lock +func (s *Span) appendLog(lr opentracing.LogRecord) { + // TODO add logic to limit number of logs per span (issue #46) + s.logs = append(s.logs, lr) +} + +// SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext +func (s *Span) SetBaggageItem(key, value string) opentracing.Span { + s.Lock() + defer s.Unlock() + s.tracer.setBaggage(s, key, value) + return s +} + +// BaggageItem implements BaggageItem() of opentracing.SpanContext +func (s *Span) BaggageItem(key string) string { + s.RLock() + defer s.RUnlock() + return s.context.baggage[key] +} + +// Finish implements opentracing.Span API +func (s *Span) Finish() { + s.FinishWithOptions(opentracing.FinishOptions{}) +} + +// FinishWithOptions implements opentracing.Span API +func (s *Span) FinishWithOptions(options opentracing.FinishOptions) { + if options.FinishTime.IsZero() { + options.FinishTime = s.tracer.timeNow() + } + s.observer.OnFinish(options) + s.Lock() + if s.context.IsSampled() { + s.duration = options.FinishTime.Sub(s.startTime) + // Note: bulk logs are not subject to maxLogsPerSpan limit + if options.LogRecords != nil { + s.logs = append(s.logs, options.LogRecords...) + } + for _, ld := range options.BulkLogData { + s.logs = append(s.logs, ld.ToLogRecord()) + } + } + s.Unlock() + // call reportSpan even for non-sampled traces, to return span to the pool + s.tracer.reportSpan(s) +} + +// Context implements opentracing.Span API +func (s *Span) Context() opentracing.SpanContext { + return s.context +} + +// Tracer implements opentracing.Span API +func (s *Span) Tracer() opentracing.Tracer { + return s.tracer +} + +func (s *Span) String() string { + s.RLock() + defer s.RUnlock() + return s.context.String() +} + +// OperationName allows retrieving current operation name. +func (s *Span) OperationName() string { + s.RLock() + defer s.RUnlock() + return s.operationName +} + +func (s *Span) serviceName() string { + return s.tracer.serviceName +} + +func setSamplingPriority(s *Span, value interface{}) bool { + s.Lock() + defer s.Unlock() + if val, ok := value.(uint16); ok { + if val > 0 { + s.context.flags = s.context.flags | flagDebug | flagSampled + } else { + s.context.flags = s.context.flags & (^flagSampled) + } + return true + } + return false +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/span_test.go b/vendor/src/github.com/uber/jaeger-client-go/span_test.go new file mode 100644 index 00000000..471ffac0 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/span_test.go @@ -0,0 +1,90 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBaggageIterator(t *testing.T) { + service := "DOOP" + tracer, closer := NewTracer(service, NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetBaggageItem("Some_Key", "12345") + sp1.SetBaggageItem("Some-other-key", "42") + expectedBaggage := map[string]string{"Some_Key": "12345", "Some-other-key": "42"} + assertBaggage(t, sp1, expectedBaggage) + assertBaggageRecords(t, sp1, expectedBaggage) + + b := extractBaggage(sp1, false) // break out early + assert.Equal(t, 1, len(b), "only one baggage item should be extracted") + + sp2 := tracer.StartSpan("s2", opentracing.ChildOf(sp1.Context())).(*Span) + assertBaggage(t, sp2, expectedBaggage) // child inherits the same baggage + require.Len(t, sp2.logs, 0) // child doesn't inherit the baggage logs +} + +func assertBaggageRecords(t *testing.T, sp *Span, expected map[string]string) { + require.Len(t, sp.logs, len(expected)) + for _, logRecord := range sp.logs { + require.Len(t, logRecord.Fields, 3) + require.Equal(t, "event:baggage", logRecord.Fields[0].String()) + key := logRecord.Fields[1].Value().(string) + value := logRecord.Fields[2].Value().(string) + + require.Contains(t, expected, key) + assert.Equal(t, expected[key], value) + } +} + +func assertBaggage(t *testing.T, sp opentracing.Span, expected map[string]string) { + b := extractBaggage(sp, true) + assert.Equal(t, expected, b) +} + +func extractBaggage(sp opentracing.Span, allItems bool) map[string]string { + b := make(map[string]string) + sp.Context().ForeachBaggageItem(func(k, v string) bool { + b[k] = v + return allItems + }) + return b +} + +func TestSpanProperties(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + assert.Equal(t, tracer, sp1.Tracer()) + assert.NotNil(t, sp1.Context()) +} + +func TestSpanOperationName(t *testing.T) { + tracer, closer := NewTracer("DOOP", NewConstSampler(true), NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp1.SetOperationName("s2") + sp1.Finish() + + assert.Equal(t, "s2", sp1.OperationName()) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent.go b/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent.go new file mode 100644 index 00000000..6fa54380 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent.go @@ -0,0 +1,189 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "sync/atomic" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +// StartMockAgent runs a mock representation of jaeger-agent. +// This function returns a started server. +func StartMockAgent() (*MockAgent, error) { + transport, err := NewTUDPServerTransport("127.0.0.1:0") + if err != nil { + return nil, err + } + + samplingManager := newSamplingManager() + samplingHandler := &samplingHandler{manager: samplingManager} + samplingServer := httptest.NewServer(samplingHandler) + + agent := &MockAgent{ + transport: transport, + samplingMgr: samplingManager, + samplingSrv: samplingServer, + } + + var started sync.WaitGroup + started.Add(1) + go agent.serve(&started) + started.Wait() + + return agent, nil +} + +// Close stops the serving of traffic +func (s *MockAgent) Close() { + atomic.StoreUint32(&s.serving, 0) + s.transport.Close() + s.samplingSrv.Close() +} + +// MockAgent is a mock representation of Jaeger Agent. +// It receives spans over UDP, and has an HTTP endpoint for sampling strategies. +type MockAgent struct { + transport *TUDPTransport + jaegerBatches []*jaeger.Batch + mutex sync.Mutex + serving uint32 + samplingMgr *samplingManager + samplingSrv *httptest.Server +} + +// SpanServerAddr returns the UDP host:port where MockAgent listens for spans +func (s *MockAgent) SpanServerAddr() string { + return s.transport.Addr().String() +} + +// SpanServerClient returns a UDP client that can be used to send spans to the MockAgent +func (s *MockAgent) SpanServerClient() (agent.Agent, error) { + return utils.NewAgentClientUDP(s.SpanServerAddr(), 0) +} + +// SamplingServerAddr returns the host:port of HTTP server exposing sampling strategy endpoint +func (s *MockAgent) SamplingServerAddr() string { + return s.samplingSrv.Listener.Addr().String() +} + +func (s *MockAgent) serve(started *sync.WaitGroup) { + handler := agent.NewAgentProcessor(s) + protocolFact := thrift.NewTCompactProtocolFactory() + buf := make([]byte, utils.UDPPacketMaxLength, utils.UDPPacketMaxLength) + trans := thrift.NewTMemoryBufferLen(utils.UDPPacketMaxLength) + + atomic.StoreUint32(&s.serving, 1) + started.Done() + for s.IsServing() { + n, err := s.transport.Read(buf) + if err == nil { + trans.Write(buf[:n]) + protocol := protocolFact.GetProtocol(trans) + handler.Process(protocol, protocol) + } + } +} + +// EmitZipkinBatch is deprecated, use EmitBatch +func (s *MockAgent) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + // TODO remove this for 3.0.0 + return errors.New("Not implemented") +} + +// GetZipkinSpans is deprecated use GetJaegerBatches +func (s *MockAgent) GetZipkinSpans() []*zipkincore.Span { + return nil +} + +// ResetZipkinSpans is deprecated use ResetJaegerBatches +func (s *MockAgent) ResetZipkinSpans() {} + +// EmitBatch implements EmitBatch() of TChanSamplingManagerServer +func (s *MockAgent) EmitBatch(batch *jaeger.Batch) (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = append(s.jaegerBatches, batch) + return err +} + +// IsServing indicates whether the server is currently serving traffic +func (s *MockAgent) IsServing() bool { + return atomic.LoadUint32(&s.serving) == 1 +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *MockAgent) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.samplingMgr.AddSamplingStrategy(service, strategy) +} + +// GetJaegerBatches returns accumulated Jaeger batches +func (s *MockAgent) GetJaegerBatches() []*jaeger.Batch { + s.mutex.Lock() + defer s.mutex.Unlock() + n := len(s.jaegerBatches) + batches := make([]*jaeger.Batch, n, n) + copy(batches, s.jaegerBatches) + return batches +} + +// ResetJaegerBatches discards accumulated Jaeger batches +func (s *MockAgent) ResetJaegerBatches() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.jaegerBatches = nil +} + +type samplingHandler struct { + manager *samplingManager +} + +func (h *samplingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + services := r.URL.Query()["service"] + if len(services) == 0 { + http.Error(w, "'service' parameter is empty", http.StatusBadRequest) + return + } + if len(services) > 1 { + http.Error(w, "'service' parameter must occur only once", http.StatusBadRequest) + return + } + resp, err := h.manager.GetSamplingStrategy(services[0]) + if err != nil { + http.Error(w, fmt.Sprintf("Error retrieving strategy: %+v", err), http.StatusInternalServerError) + return + } + json, err := json.Marshal(resp) + if err != nil { + http.Error(w, "Cannot marshall Thrift to JSON", http.StatusInternalServerError) + return + } + w.Header().Add("Content-Type", "application/json") + if _, err := w.Write(json); err != nil { + return + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go b/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go new file mode 100644 index 00000000..4afa8387 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/testutils/mock_agent_test.go @@ -0,0 +1,93 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/sampling" + "github.com/uber/jaeger-client-go/utils" +) + +func TestMockAgentSpanServer(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + client, err := mockAgent.SpanServerClient() + require.NoError(t, err) + + for i := 1; i < 5; i++ { + batch := &jaeger.Batch{Process: &jaeger.Process{ServiceName: "svc"}} + spans := make([]*jaeger.Span, i, i) + for j := 0; j < i; j++ { + spans[j] = jaeger.NewSpan() + spans[j].OperationName = fmt.Sprintf("span-%d", j) + } + batch.Spans = spans + + err = client.EmitBatch(batch) + assert.NoError(t, err) + + for k := 0; k < 100; k++ { + time.Sleep(time.Millisecond) + batches := mockAgent.GetJaegerBatches() + if len(batches) > 0 && len(batches[0].Spans) == i { + break + } + } + batches := mockAgent.GetJaegerBatches() + require.NotEmpty(t, len(batches)) + require.Equal(t, i, len(batches[0].Spans)) + for j := 0; j < i; j++ { + assert.Equal(t, fmt.Sprintf("span-%d", j), batches[0].Spans[j].OperationName) + } + mockAgent.ResetJaegerBatches() + } +} + +func TestMockAgentSamplingManager(t *testing.T) { + mockAgent, err := StartMockAgent() + require.NoError(t, err) + defer mockAgent.Close() + + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/", nil) + require.Error(t, err, "no 'service' parameter") + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=a&service=b", nil) + require.Error(t, err, "Too many 'service' parameters") + + var resp sampling.SamplingStrategyResponse + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=something", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_PROBABILISTIC, resp.StrategyType) + + mockAgent.AddSamplingStrategy("service123", &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: 123, + }, + }) + err = utils.GetJSON("http://"+mockAgent.SamplingServerAddr()+"/?service=service123", &resp) + require.NoError(t, err) + assert.Equal(t, sampling.SamplingStrategyType_RATE_LIMITING, resp.StrategyType) + require.NotNil(t, resp.RateLimitingSampling) + assert.EqualValues(t, 123, resp.RateLimitingSampling.MaxTracesPerSecond) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/testutils/sampling_manager.go b/vendor/src/github.com/uber/jaeger-client-go/testutils/sampling_manager.go new file mode 100644 index 00000000..6352c510 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/testutils/sampling_manager.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "sync" + + "github.com/uber/jaeger-client-go/thrift-gen/sampling" +) + +func newSamplingManager() *samplingManager { + return &samplingManager{ + sampling: make(map[string]*sampling.SamplingStrategyResponse), + } +} + +type samplingManager struct { + sampling map[string]*sampling.SamplingStrategyResponse + mutex sync.Mutex +} + +// GetSamplingStrategy implements handler method of sampling.SamplingManager +func (s *samplingManager) GetSamplingStrategy(serviceName string) (*sampling.SamplingStrategyResponse, error) { + s.mutex.Lock() + defer s.mutex.Unlock() + if strategy, ok := s.sampling[serviceName]; ok { + return strategy, nil + } + return &sampling.SamplingStrategyResponse{ + StrategyType: sampling.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{ + SamplingRate: 0.01, + }}, nil +} + +// AddSamplingStrategy registers a sampling strategy for a service +func (s *samplingManager) AddSamplingStrategy(service string, strategy *sampling.SamplingStrategyResponse) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.sampling[service] = strategy +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport.go b/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport.go new file mode 100644 index 00000000..84daa134 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport.go @@ -0,0 +1,106 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "bytes" + "net" + "sync/atomic" + + "github.com/apache/thrift/lib/go/thrift" +) + +const ( + // used in RemainingBytes() + maxRemainingBytes = ^uint64(0) +) + +// TUDPTransport does UDP as a thrift.TTransport (read-only, write functions not implemented). +type TUDPTransport struct { + conn *net.UDPConn + addr net.Addr + writeBuf bytes.Buffer + closed uint32 +} + +// NewTUDPServerTransport creates a net.UDPConn-backed TTransport for Thrift servers +// It will listen for incoming udp packets on the specified host/port +// Example: +// trans, err := utils.NewTUDPClientTransport("localhost:9001") +func NewTUDPServerTransport(hostPort string) (*TUDPTransport, error) { + addr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + conn, err := net.ListenUDP(addr.Network(), addr) + if err != nil { + return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) + } + return &TUDPTransport{addr: conn.LocalAddr(), conn: conn}, nil +} + +// Open does nothing as connection is opened on creation +// Required to maintain thrift.TTransport interface +func (p *TUDPTransport) Open() error { + return nil +} + +// Conn retrieves the underlying net.UDPConn +func (p *TUDPTransport) Conn() *net.UDPConn { + return p.conn +} + +// IsOpen returns true if the connection is open +func (p *TUDPTransport) IsOpen() bool { + return p.conn != nil && atomic.LoadUint32(&p.closed) == 0 +} + +// Close closes the connection +func (p *TUDPTransport) Close() error { + if p.conn != nil && atomic.CompareAndSwapUint32(&p.closed, 0, 1) { + return p.conn.Close() + } + return nil +} + +// Addr returns the address that the transport is listening on or writing to +func (p *TUDPTransport) Addr() net.Addr { + return p.addr +} + +// Read reads one UDP packet and puts it in the specified buf +func (p *TUDPTransport) Read(buf []byte) (int, error) { + if !p.IsOpen() { + return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") + } + n, err := p.conn.Read(buf) + return n, thrift.NewTTransportExceptionFromError(err) +} + +// RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we +// do not know how many bytes we have left. +func (p *TUDPTransport) RemainingBytes() uint64 { + return maxRemainingBytes +} + +// Write writes specified buf to the write buffer +func (p *TUDPTransport) Write(buf []byte) (int, error) { + return 0, thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Write not implemented") +} + +// Flush flushes the write buffer as one udp packet +func (p *TUDPTransport) Flush() error { + return thrift.NewTTransportException(thrift.UNKNOWN_TRANSPORT_EXCEPTION, "Flush not implemented") +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go b/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go new file mode 100644 index 00000000..14708b54 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/testutils/udp_transport_test.go @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUDPTransport(t *testing.T) { + server, err := NewTUDPServerTransport("127.0.0.1:0") + require.NoError(t, err) + defer server.Close() + + assert.NoError(t, server.Open()) + assert.True(t, server.IsOpen()) + assert.NotNil(t, server.Conn()) + + c := make(chan []byte) + defer close(c) + + go serveOnce(t, server, c) + + destAddr, err := net.ResolveUDPAddr("udp", server.Addr().String()) + require.NoError(t, err) + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + require.NoError(t, err) + defer connUDP.Close() + + n, err := connUDP.Write([]byte("test")) + assert.NoError(t, err) + assert.Equal(t, 4, n) + + select { + case data := <-c: + assert.Equal(t, "test", string(data)) + case <-time.After(time.Second * 1): + t.Error("Server did not respond in time") + } +} + +func serveOnce(t *testing.T, transport *TUDPTransport, c chan []byte) { + b := make([]byte, 65000, 65000) + n, err := transport.Read(b) + if err == nil { + c <- b[:n] + } else { + panic("Server failed to read: " + err.Error()) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go new file mode 100644 index 00000000..a192cb6f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go @@ -0,0 +1,410 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ + +type Agent interface { + // Parameters: + // - Spans + EmitZipkinBatch(spans []*zipkincore.Span) (err error) + // Parameters: + // - Batch + EmitBatch(batch *jaeger.Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *AgentClient) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { + if err = p.sendEmitZipkinBatch(spans); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitZipkinBatch(spans []*zipkincore.Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitZipkinBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *jaeger.Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *jaeger.Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self0 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self0.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler} + self0.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self0 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x1 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x1.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x1 + +} + +type agentProcessorEmitZipkinBatch struct { + handler Agent +} + +func (p *agentProcessorEmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitZipkinBatch(args.Spans); err2 != nil { + return true, err2 + } + return true, nil +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type AgentEmitZipkinBatchArgs struct { + Spans []*zipkincore.Span `thrift:"spans,1" json:"spans"` +} + +func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs { + return &AgentEmitZipkinBatchArgs{} +} + +func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span { + return p.Spans +} +func (p *AgentEmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*zipkincore.Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem2 := &zipkincore.Span{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Spans = append(p.Spans, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *AgentEmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *jaeger.Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch + +func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &jaeger.Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go new file mode 100644 index 00000000..369d016e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go @@ -0,0 +1,21 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go new file mode 100644 index 00000000..f377c227 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go @@ -0,0 +1,19 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package agent + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var _ = zipkincore.GoUnusedProtection__ +var GoUnusedProtection__ int diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go new file mode 100644 index 00000000..1931cd72 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/baggagerestrictionmanager.go @@ -0,0 +1,435 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type BaggageRestrictionManager interface { + // getBaggageRestrictions retrieves the baggage restrictions for a specific service. + // Usually, baggageRestrictions apply to all services however there may be situations + // where a baggageKey might only be allowed to be set by a specific service. + // + // Parameters: + // - ServiceName + GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) +} + +type BaggageRestrictionManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewBaggageRestrictionManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewBaggageRestrictionManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaggageRestrictionManagerClient { + return &BaggageRestrictionManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// getBaggageRestrictions retrieves the baggage restrictions for a specific service. +// Usually, baggageRestrictions apply to all services however there may be situations +// where a baggageKey might only be allowed to be set by a specific service. +// +// Parameters: +// - ServiceName +func (p *BaggageRestrictionManagerClient) GetBaggageRestrictions(serviceName string) (r []*BaggageRestriction, err error) { + if err = p.sendGetBaggageRestrictions(serviceName); err != nil { + return + } + return p.recvGetBaggageRestrictions() +} + +func (p *BaggageRestrictionManagerClient) sendGetBaggageRestrictions(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.CALL, p.SeqId); err != nil { + return + } + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *BaggageRestrictionManagerClient) recvGetBaggageRestrictions() (value []*BaggageRestriction, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getBaggageRestrictions" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getBaggageRestrictions failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getBaggageRestrictions failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getBaggageRestrictions failed: invalid message type") + return + } + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type BaggageRestrictionManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler BaggageRestrictionManager +} + +func (p *BaggageRestrictionManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *BaggageRestrictionManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *BaggageRestrictionManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewBaggageRestrictionManagerProcessor(handler BaggageRestrictionManager) *BaggageRestrictionManagerProcessor { + + self2 := &BaggageRestrictionManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self2.processorMap["getBaggageRestrictions"] = &baggageRestrictionManagerProcessorGetBaggageRestrictions{handler: handler} + return self2 +} + +func (p *BaggageRestrictionManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x3.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x3 + +} + +type baggageRestrictionManagerProcessorGetBaggageRestrictions struct { + handler BaggageRestrictionManager +} + +func (p *baggageRestrictionManagerProcessorGetBaggageRestrictions) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := BaggageRestrictionManagerGetBaggageRestrictionsArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := BaggageRestrictionManagerGetBaggageRestrictionsResult{} + var retval []*BaggageRestriction + var err2 error + if retval, err2 = p.handler.GetBaggageRestrictions(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getBaggageRestrictions: "+err2.Error()) + oprot.WriteMessageBegin("getBaggageRestrictions", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getBaggageRestrictions", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type BaggageRestrictionManagerGetBaggageRestrictionsArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsArgs() *BaggageRestrictionManagerGetBaggageRestrictionsArgs { + return &BaggageRestrictionManagerGetBaggageRestrictionsArgs{} +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) GetServiceName() string { + return p.ServiceName +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsArgs(%+v)", *p) +} + +// Attributes: +// - Success +type BaggageRestrictionManagerGetBaggageRestrictionsResult struct { + Success []*BaggageRestriction `thrift:"success,0" json:"success,omitempty"` +} + +func NewBaggageRestrictionManagerGetBaggageRestrictionsResult() *BaggageRestrictionManagerGetBaggageRestrictionsResult { + return &BaggageRestrictionManagerGetBaggageRestrictionsResult{} +} + +var BaggageRestrictionManagerGetBaggageRestrictionsResult_Success_DEFAULT []*BaggageRestriction + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) GetSuccess() []*BaggageRestriction { + return p.Success +} +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BaggageRestriction, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem4 := &BaggageRestriction{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Success = append(p.Success, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getBaggageRestrictions_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *BaggageRestrictionManagerGetBaggageRestrictionsResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestrictionManagerGetBaggageRestrictionsResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go new file mode 100644 index 00000000..6668424a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go new file mode 100644 index 00000000..be442fbd --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/baggage/ttypes.go @@ -0,0 +1,154 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package baggage + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - BaggageKey +// - MaxValueLength +type BaggageRestriction struct { + BaggageKey string `thrift:"baggageKey,1,required" json:"baggageKey"` + MaxValueLength int32 `thrift:"maxValueLength,2,required" json:"maxValueLength"` +} + +func NewBaggageRestriction() *BaggageRestriction { + return &BaggageRestriction{} +} + +func (p *BaggageRestriction) GetBaggageKey() string { + return p.BaggageKey +} + +func (p *BaggageRestriction) GetMaxValueLength() int32 { + return p.MaxValueLength +} +func (p *BaggageRestriction) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetBaggageKey bool = false + var issetMaxValueLength bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetBaggageKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetMaxValueLength = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetBaggageKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field BaggageKey is not set")) + } + if !issetMaxValueLength { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxValueLength is not set")) + } + return nil +} + +func (p *BaggageRestriction) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.BaggageKey = v + } + return nil +} + +func (p *BaggageRestriction) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.MaxValueLength = v + } + return nil +} + +func (p *BaggageRestriction) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BaggageRestriction"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaggageRestriction) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("baggageKey", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:baggageKey: ", p), err) + } + if err := oprot.WriteString(string(p.BaggageKey)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.baggageKey (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:baggageKey: ", p), err) + } + return err +} + +func (p *BaggageRestriction) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxValueLength", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:maxValueLength: ", p), err) + } + if err := oprot.WriteI32(int32(p.MaxValueLength)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxValueLength (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:maxValueLength: ", p), err) + } + return err +} + +func (p *BaggageRestriction) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaggageRestriction(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go new file mode 100644 index 00000000..db6cac9f --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go @@ -0,0 +1,242 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Agent interface { + // Parameters: + // - Batch + EmitBatch(batch *Batch) (err error) +} + +type AgentClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { + return &AgentClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Batch +func (p *AgentClient) EmitBatch(batch *Batch) (err error) { + if err = p.sendEmitBatch(batch); err != nil { + return + } + return +} + +func (p *AgentClient) sendEmitBatch(batch *Batch) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { + return + } + args := AgentEmitBatchArgs{ + Batch: batch, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +type AgentProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Agent +} + +func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewAgentProcessor(handler Agent) *AgentProcessor { + + self6 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self6.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} + return self6 +} + +func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x7.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x7 + +} + +type agentProcessorEmitBatch struct { + handler Agent +} + +func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + return false, err + } + + iprot.ReadMessageEnd() + var err2 error + if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { + return true, err2 + } + return true, nil +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Batch +type AgentEmitBatchArgs struct { + Batch *Batch `thrift:"batch,1" json:"batch"` +} + +func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { + return &AgentEmitBatchArgs{} +} + +var AgentEmitBatchArgs_Batch_DEFAULT *Batch + +func (p *AgentEmitBatchArgs) GetBatch() *Batch { + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } + return p.Batch +} +func (p *AgentEmitBatchArgs) IsSetBatch() bool { + return p.Batch != nil +} + +func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { + p.Batch = &Batch{} + if err := p.Batch.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) + } + if err := p.Batch.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) + } + return err +} + +func (p *AgentEmitBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go new file mode 100644 index 00000000..25047422 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go new file mode 100644 index 00000000..b5ddfa64 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go @@ -0,0 +1,1838 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package jaeger + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type TagType int64 + +const ( + TagType_STRING TagType = 0 + TagType_DOUBLE TagType = 1 + TagType_BOOL TagType = 2 + TagType_LONG TagType = 3 + TagType_BINARY TagType = 4 +) + +func (p TagType) String() string { + switch p { + case TagType_STRING: + return "STRING" + case TagType_DOUBLE: + return "DOUBLE" + case TagType_BOOL: + return "BOOL" + case TagType_LONG: + return "LONG" + case TagType_BINARY: + return "BINARY" + } + return "" +} + +func TagTypeFromString(s string) (TagType, error) { + switch s { + case "STRING": + return TagType_STRING, nil + case "DOUBLE": + return TagType_DOUBLE, nil + case "BOOL": + return TagType_BOOL, nil + case "LONG": + return TagType_LONG, nil + case "BINARY": + return TagType_BINARY, nil + } + return TagType(0), fmt.Errorf("not a valid TagType string") +} + +func TagTypePtr(v TagType) *TagType { return &v } + +func (p TagType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *TagType) UnmarshalText(text []byte) error { + q, err := TagTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +type SpanRefType int64 + +const ( + SpanRefType_CHILD_OF SpanRefType = 0 + SpanRefType_FOLLOWS_FROM SpanRefType = 1 +) + +func (p SpanRefType) String() string { + switch p { + case SpanRefType_CHILD_OF: + return "CHILD_OF" + case SpanRefType_FOLLOWS_FROM: + return "FOLLOWS_FROM" + } + return "" +} + +func SpanRefTypeFromString(s string) (SpanRefType, error) { + switch s { + case "CHILD_OF": + return SpanRefType_CHILD_OF, nil + case "FOLLOWS_FROM": + return SpanRefType_FOLLOWS_FROM, nil + } + return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") +} + +func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } + +func (p SpanRefType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SpanRefType) UnmarshalText(text []byte) error { + q, err := SpanRefTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - Key +// - VType +// - VStr +// - VDouble +// - VBool +// - VLong +// - VBinary +type Tag struct { + Key string `thrift:"key,1,required" json:"key"` + VType TagType `thrift:"vType,2,required" json:"vType"` + VStr *string `thrift:"vStr,3" json:"vStr,omitempty"` + VDouble *float64 `thrift:"vDouble,4" json:"vDouble,omitempty"` + VBool *bool `thrift:"vBool,5" json:"vBool,omitempty"` + VLong *int64 `thrift:"vLong,6" json:"vLong,omitempty"` + VBinary []byte `thrift:"vBinary,7" json:"vBinary,omitempty"` +} + +func NewTag() *Tag { + return &Tag{} +} + +func (p *Tag) GetKey() string { + return p.Key +} + +func (p *Tag) GetVType() TagType { + return p.VType +} + +var Tag_VStr_DEFAULT string + +func (p *Tag) GetVStr() string { + if !p.IsSetVStr() { + return Tag_VStr_DEFAULT + } + return *p.VStr +} + +var Tag_VDouble_DEFAULT float64 + +func (p *Tag) GetVDouble() float64 { + if !p.IsSetVDouble() { + return Tag_VDouble_DEFAULT + } + return *p.VDouble +} + +var Tag_VBool_DEFAULT bool + +func (p *Tag) GetVBool() bool { + if !p.IsSetVBool() { + return Tag_VBool_DEFAULT + } + return *p.VBool +} + +var Tag_VLong_DEFAULT int64 + +func (p *Tag) GetVLong() int64 { + if !p.IsSetVLong() { + return Tag_VLong_DEFAULT + } + return *p.VLong +} + +var Tag_VBinary_DEFAULT []byte + +func (p *Tag) GetVBinary() []byte { + return p.VBinary +} +func (p *Tag) IsSetVStr() bool { + return p.VStr != nil +} + +func (p *Tag) IsSetVDouble() bool { + return p.VDouble != nil +} + +func (p *Tag) IsSetVBool() bool { + return p.VBool != nil +} + +func (p *Tag) IsSetVLong() bool { + return p.VLong != nil +} + +func (p *Tag) IsSetVBinary() bool { + return p.VBinary != nil +} + +func (p *Tag) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetKey bool = false + var issetVType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetKey = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetVType = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetKey { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) + } + if !issetVType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")) + } + return nil +} + +func (p *Tag) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *Tag) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + temp := TagType(v) + p.VType = temp + } + return nil +} + +func (p *Tag) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.VStr = &v + } + return nil +} + +func (p *Tag) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.VDouble = &v + } + return nil +} + +func (p *Tag) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.VBool = &v + } + return nil +} + +func (p *Tag) readField6(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 6: ", err) + } else { + p.VLong = &v + } + return nil +} + +func (p *Tag) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.VBinary = v + } + return nil +} + +func (p *Tag) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Tag"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Tag) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *Tag) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("vType", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) + } + if err := oprot.WriteI32(int32(p.VType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) + } + return err +} + +func (p *Tag) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetVStr() { + if err := oprot.WriteFieldBegin("vStr", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) + } + if err := oprot.WriteString(string(*p.VStr)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) + } + } + return err +} + +func (p *Tag) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetVDouble() { + if err := oprot.WriteFieldBegin("vDouble", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.VDouble)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) + } + } + return err +} + +func (p *Tag) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetVBool() { + if err := oprot.WriteFieldBegin("vBool", thrift.BOOL, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) + } + if err := oprot.WriteBool(bool(*p.VBool)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) + } + } + return err +} + +func (p *Tag) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetVLong() { + if err := oprot.WriteFieldBegin("vLong", thrift.I64, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) + } + if err := oprot.WriteI64(int64(*p.VLong)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) + } + } + return err +} + +func (p *Tag) writeField7(oprot thrift.TProtocol) (err error) { + if p.IsSetVBinary() { + if err := oprot.WriteFieldBegin("vBinary", thrift.STRING, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) + } + if err := oprot.WriteBinary(p.VBinary); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) + } + } + return err +} + +func (p *Tag) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Tag(%+v)", *p) +} + +// Attributes: +// - Timestamp +// - Fields +type Log struct { + Timestamp int64 `thrift:"timestamp,1,required" json:"timestamp"` + Fields []*Tag `thrift:"fields,2,required" json:"fields"` +} + +func NewLog() *Log { + return &Log{} +} + +func (p *Log) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Log) GetFields() []*Tag { + return p.Fields +} +func (p *Log) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTimestamp bool = false + var issetFields bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTimestamp = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetFields = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTimestamp { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) + } + if !issetFields { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")) + } + return nil +} + +func (p *Log) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Log) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Fields = tSlice + for i := 0; i < size; i++ { + _elem0 := &Tag{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Fields = append(p.Fields, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Log) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Log"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Log) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Log) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("fields", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Fields)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Fields { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) + } + return err +} + +func (p *Log) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Log(%+v)", *p) +} + +// Attributes: +// - RefType +// - TraceIdLow +// - TraceIdHigh +// - SpanId +type SpanRef struct { + RefType SpanRefType `thrift:"refType,1,required" json:"refType"` + TraceIdLow int64 `thrift:"traceIdLow,2,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,3,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,4,required" json:"spanId"` +} + +func NewSpanRef() *SpanRef { + return &SpanRef{} +} + +func (p *SpanRef) GetRefType() SpanRefType { + return p.RefType +} + +func (p *SpanRef) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *SpanRef) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *SpanRef) GetSpanId() int64 { + return p.SpanId +} +func (p *SpanRef) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetRefType bool = false + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetRefType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetSpanId = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetRefType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + return nil +} + +func (p *SpanRef) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SpanRefType(v) + p.RefType = temp + } + return nil +} + +func (p *SpanRef) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *SpanRef) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *SpanRef) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *SpanRef) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SpanRef"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SpanRef) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("refType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) + } + if err := oprot.WriteI32(int32(p.RefType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) + } + return err +} + +func (p *SpanRef) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) + } + return err +} + +func (p *SpanRef) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) + } + return err +} + +func (p *SpanRef) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) + } + return err +} + +func (p *SpanRef) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SpanRef(%+v)", *p) +} + +// Attributes: +// - TraceIdLow +// - TraceIdHigh +// - SpanId +// - ParentSpanId +// - OperationName +// - References +// - Flags +// - StartTime +// - Duration +// - Tags +// - Logs +type Span struct { + TraceIdLow int64 `thrift:"traceIdLow,1,required" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,2,required" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,3,required" json:"spanId"` + ParentSpanId int64 `thrift:"parentSpanId,4,required" json:"parentSpanId"` + OperationName string `thrift:"operationName,5,required" json:"operationName"` + References []*SpanRef `thrift:"references,6" json:"references,omitempty"` + Flags int32 `thrift:"flags,7,required" json:"flags"` + StartTime int64 `thrift:"startTime,8,required" json:"startTime"` + Duration int64 `thrift:"duration,9,required" json:"duration"` + Tags []*Tag `thrift:"tags,10" json:"tags,omitempty"` + Logs []*Log `thrift:"logs,11" json:"logs,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *Span) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *Span) GetSpanId() int64 { + return p.SpanId +} + +func (p *Span) GetParentSpanId() int64 { + return p.ParentSpanId +} + +func (p *Span) GetOperationName() string { + return p.OperationName +} + +var Span_References_DEFAULT []*SpanRef + +func (p *Span) GetReferences() []*SpanRef { + return p.References +} + +func (p *Span) GetFlags() int32 { + return p.Flags +} + +func (p *Span) GetStartTime() int64 { + return p.StartTime +} + +func (p *Span) GetDuration() int64 { + return p.Duration +} + +var Span_Tags_DEFAULT []*Tag + +func (p *Span) GetTags() []*Tag { + return p.Tags +} + +var Span_Logs_DEFAULT []*Log + +func (p *Span) GetLogs() []*Log { + return p.Logs +} +func (p *Span) IsSetReferences() bool { + return p.References != nil +} + +func (p *Span) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Span) IsSetLogs() bool { + return p.Logs != nil +} + +func (p *Span) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceIdLow bool = false + var issetTraceIdHigh bool = false + var issetSpanId bool = false + var issetParentSpanId bool = false + var issetOperationName bool = false + var issetFlags bool = false + var issetStartTime bool = false + var issetDuration bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetTraceIdLow = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetTraceIdHigh = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetSpanId = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + issetParentSpanId = true + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + issetOperationName = true + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 7: + if err := p.readField7(iprot); err != nil { + return err + } + issetFlags = true + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + issetStartTime = true + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + issetDuration = true + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceIdLow { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) + } + if !issetTraceIdHigh { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) + } + if !issetSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) + } + if !issetParentSpanId { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")) + } + if !issetOperationName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")) + } + if !issetFlags { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")) + } + if !issetStartTime { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")) + } + if !issetDuration { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceIdLow = v + } + return nil +} + +func (p *Span) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.TraceIdHigh = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.SpanId = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ParentSpanId = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.OperationName = v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*SpanRef, 0, size) + p.References = tSlice + for i := 0; i < size; i++ { + _elem1 := &SpanRef{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.References = append(p.References, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField7(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 7: ", err) + } else { + p.Flags = v + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 8: ", err) + } else { + p.StartTime = v + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Duration = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem2 := &Tag{} + if err := _elem2.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.Tags = append(p.Tags, _elem2) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Log, 0, size) + p.Logs = tSlice + for i := 0; i < size; i++ { + _elem3 := &Log{} + if err := _elem3.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) + } + p.Logs = append(p.Logs, _elem3) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField7(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) + } + return err +} + +func (p *Span) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spanId", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("parentSpanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) + } + if err := oprot.WriteI64(int64(p.ParentSpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operationName", thrift.STRING, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) + } + if err := oprot.WriteString(string(p.OperationName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if p.IsSetReferences() { + if err := oprot.WriteFieldBegin("references", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.References)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.References { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) + } + } + return err +} + +func (p *Span) writeField7(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("flags", thrift.I32, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) + } + if err := oprot.WriteI32(int32(p.Flags)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("startTime", thrift.I64, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) + } + if err := oprot.WriteI64(int64(p.StartTime)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) + } + if err := oprot.WriteI64(int64(p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetLogs() { + if err := oprot.WriteFieldBegin("logs", thrift.LIST, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Logs)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Logs { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - ServiceName +// - Tags +type Process struct { + ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` + Tags []*Tag `thrift:"tags,2" json:"tags,omitempty"` +} + +func NewProcess() *Process { + return &Process{} +} + +func (p *Process) GetServiceName() string { + return p.ServiceName +} + +var Process_Tags_DEFAULT []*Tag + +func (p *Process) GetTags() []*Tag { + return p.Tags +} +func (p *Process) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Process) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetServiceName = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *Process) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Process) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i++ { + _elem4 := &Tag{} + if err := _elem4.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Tags = append(p.Tags, _elem4) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Process) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Process"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Process) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *Process) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin("tags", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) + } + } + return err +} + +func (p *Process) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Process(%+v)", *p) +} + +// Attributes: +// - Process +// - Spans +type Batch struct { + Process *Process `thrift:"process,1,required" json:"process"` + Spans []*Span `thrift:"spans,2,required" json:"spans"` +} + +func NewBatch() *Batch { + return &Batch{} +} + +var Batch_Process_DEFAULT *Process + +func (p *Batch) GetProcess() *Process { + if !p.IsSetProcess() { + return Batch_Process_DEFAULT + } + return p.Process +} + +func (p *Batch) GetSpans() []*Span { + return p.Spans +} +func (p *Batch) IsSetProcess() bool { + return p.Process != nil +} + +func (p *Batch) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetProcess bool = false + var issetSpans bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetProcess = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetSpans = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetProcess { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")) + } + if !issetSpans { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")) + } + return nil +} + +func (p *Batch) readField1(iprot thrift.TProtocol) error { + p.Process = &Process{} + if err := p.Process.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) + } + return nil +} + +func (p *Batch) readField2(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem5 := &Span{} + if err := _elem5.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem5), err) + } + p.Spans = append(p.Spans, _elem5) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Batch) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Batch"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Batch) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("process", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) + } + if err := p.Process.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) + } + return err +} + +func (p *Batch) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) + } + return err +} + +func (p *Batch) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Batch(%+v)", *p) +} + +// Attributes: +// - Ok +type BatchSubmitResponse struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewBatchSubmitResponse() *BatchSubmitResponse { + return &BatchSubmitResponse{} +} + +func (p *BatchSubmitResponse) GetOk() bool { + return p.Ok +} +func (p *BatchSubmitResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *BatchSubmitResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *BatchSubmitResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BatchSubmitResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BatchSubmitResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *BatchSubmitResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go new file mode 100644 index 00000000..728988b8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go new file mode 100644 index 00000000..563e2b4c --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/samplingmanager.go @@ -0,0 +1,410 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type SamplingManager interface { + // Parameters: + // - ServiceName + GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) +} + +type SamplingManagerClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSamplingManagerClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSamplingManagerClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SamplingManagerClient { + return &SamplingManagerClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - ServiceName +func (p *SamplingManagerClient) GetSamplingStrategy(serviceName string) (r *SamplingStrategyResponse, err error) { + if err = p.sendGetSamplingStrategy(serviceName); err != nil { + return + } + return p.recvGetSamplingStrategy() +} + +func (p *SamplingManagerClient) sendGetSamplingStrategy(serviceName string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("getSamplingStrategy", thrift.CALL, p.SeqId); err != nil { + return + } + args := SamplingManagerGetSamplingStrategyArgs{ + ServiceName: serviceName, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SamplingManagerClient) recvGetSamplingStrategy() (value *SamplingStrategyResponse, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "getSamplingStrategy" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "getSamplingStrategy failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "getSamplingStrategy failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error1 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error2 error + error2, err = error1.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error2 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "getSamplingStrategy failed: invalid message type") + return + } + result := SamplingManagerGetSamplingStrategyResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type SamplingManagerProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler SamplingManager +} + +func (p *SamplingManagerProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SamplingManagerProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SamplingManagerProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSamplingManagerProcessor(handler SamplingManager) *SamplingManagerProcessor { + + self3 := &SamplingManagerProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self3.processorMap["getSamplingStrategy"] = &samplingManagerProcessorGetSamplingStrategy{handler: handler} + return self3 +} + +func (p *SamplingManagerProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x4.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x4 + +} + +type samplingManagerProcessorGetSamplingStrategy struct { + handler SamplingManager +} + +func (p *samplingManagerProcessorGetSamplingStrategy) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SamplingManagerGetSamplingStrategyArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SamplingManagerGetSamplingStrategyResult{} + var retval *SamplingStrategyResponse + var err2 error + if retval, err2 = p.handler.GetSamplingStrategy(args.ServiceName); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing getSamplingStrategy: "+err2.Error()) + oprot.WriteMessageBegin("getSamplingStrategy", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("getSamplingStrategy", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - ServiceName +type SamplingManagerGetSamplingStrategyArgs struct { + ServiceName string `thrift:"serviceName,1" json:"serviceName"` +} + +func NewSamplingManagerGetSamplingStrategyArgs() *SamplingManagerGetSamplingStrategyArgs { + return &SamplingManagerGetSamplingStrategyArgs{} +} + +func (p *SamplingManagerGetSamplingStrategyArgs) GetServiceName() string { + return p.ServiceName +} +func (p *SamplingManagerGetSamplingStrategyArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyArgs(%+v)", *p) +} + +// Attributes: +// - Success +type SamplingManagerGetSamplingStrategyResult struct { + Success *SamplingStrategyResponse `thrift:"success,0" json:"success,omitempty"` +} + +func NewSamplingManagerGetSamplingStrategyResult() *SamplingManagerGetSamplingStrategyResult { + return &SamplingManagerGetSamplingStrategyResult{} +} + +var SamplingManagerGetSamplingStrategyResult_Success_DEFAULT *SamplingStrategyResponse + +func (p *SamplingManagerGetSamplingStrategyResult) GetSuccess() *SamplingStrategyResponse { + if !p.IsSetSuccess() { + return SamplingManagerGetSamplingStrategyResult_Success_DEFAULT + } + return p.Success +} +func (p *SamplingManagerGetSamplingStrategyResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) readField0(iprot thrift.TProtocol) error { + p.Success = &SamplingStrategyResponse{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("getSamplingStrategy_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingManagerGetSamplingStrategyResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *SamplingManagerGetSamplingStrategyResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingManagerGetSamplingStrategyResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go new file mode 100644 index 00000000..3e831af4 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/sampling/ttypes.go @@ -0,0 +1,873 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package sampling + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type SamplingStrategyType int64 + +const ( + SamplingStrategyType_PROBABILISTIC SamplingStrategyType = 0 + SamplingStrategyType_RATE_LIMITING SamplingStrategyType = 1 +) + +func (p SamplingStrategyType) String() string { + switch p { + case SamplingStrategyType_PROBABILISTIC: + return "PROBABILISTIC" + case SamplingStrategyType_RATE_LIMITING: + return "RATE_LIMITING" + } + return "" +} + +func SamplingStrategyTypeFromString(s string) (SamplingStrategyType, error) { + switch s { + case "PROBABILISTIC": + return SamplingStrategyType_PROBABILISTIC, nil + case "RATE_LIMITING": + return SamplingStrategyType_RATE_LIMITING, nil + } + return SamplingStrategyType(0), fmt.Errorf("not a valid SamplingStrategyType string") +} + +func SamplingStrategyTypePtr(v SamplingStrategyType) *SamplingStrategyType { return &v } + +func (p SamplingStrategyType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SamplingStrategyType) UnmarshalText(text []byte) error { + q, err := SamplingStrategyTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Attributes: +// - SamplingRate +type ProbabilisticSamplingStrategy struct { + SamplingRate float64 `thrift:"samplingRate,1,required" json:"samplingRate"` +} + +func NewProbabilisticSamplingStrategy() *ProbabilisticSamplingStrategy { + return &ProbabilisticSamplingStrategy{} +} + +func (p *ProbabilisticSamplingStrategy) GetSamplingRate() float64 { + return p.SamplingRate +} +func (p *ProbabilisticSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetSamplingRate bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetSamplingRate = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetSamplingRate { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SamplingRate is not set")) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.SamplingRate = v + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ProbabilisticSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ProbabilisticSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("samplingRate", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:samplingRate: ", p), err) + } + if err := oprot.WriteDouble(float64(p.SamplingRate)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.samplingRate (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:samplingRate: ", p), err) + } + return err +} + +func (p *ProbabilisticSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ProbabilisticSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - MaxTracesPerSecond +type RateLimitingSamplingStrategy struct { + MaxTracesPerSecond int16 `thrift:"maxTracesPerSecond,1,required" json:"maxTracesPerSecond"` +} + +func NewRateLimitingSamplingStrategy() *RateLimitingSamplingStrategy { + return &RateLimitingSamplingStrategy{} +} + +func (p *RateLimitingSamplingStrategy) GetMaxTracesPerSecond() int16 { + return p.MaxTracesPerSecond +} +func (p *RateLimitingSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetMaxTracesPerSecond bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetMaxTracesPerSecond = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetMaxTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MaxTracesPerSecond is not set")) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.MaxTracesPerSecond = v + } + return nil +} + +func (p *RateLimitingSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("RateLimitingSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *RateLimitingSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("maxTracesPerSecond", thrift.I16, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:maxTracesPerSecond: ", p), err) + } + if err := oprot.WriteI16(int16(p.MaxTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.maxTracesPerSecond (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:maxTracesPerSecond: ", p), err) + } + return err +} + +func (p *RateLimitingSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("RateLimitingSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - Operation +// - ProbabilisticSampling +type OperationSamplingStrategy struct { + Operation string `thrift:"operation,1,required" json:"operation"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2,required" json:"probabilisticSampling"` +} + +func NewOperationSamplingStrategy() *OperationSamplingStrategy { + return &OperationSamplingStrategy{} +} + +func (p *OperationSamplingStrategy) GetOperation() string { + return p.Operation +} + +var OperationSamplingStrategy_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *OperationSamplingStrategy) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return OperationSamplingStrategy_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} +func (p *OperationSamplingStrategy) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *OperationSamplingStrategy) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOperation bool = false + var issetProbabilisticSampling bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOperation = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetProbabilisticSampling = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOperation { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Operation is not set")) + } + if !issetProbabilisticSampling { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ProbabilisticSampling is not set")) + } + return nil +} + +func (p *OperationSamplingStrategy) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Operation = v + } + return nil +} + +func (p *OperationSamplingStrategy) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *OperationSamplingStrategy) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("OperationSamplingStrategy"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *OperationSamplingStrategy) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("operation", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:operation: ", p), err) + } + if err := oprot.WriteString(string(p.Operation)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operation (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:operation: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + return err +} + +func (p *OperationSamplingStrategy) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("OperationSamplingStrategy(%+v)", *p) +} + +// Attributes: +// - DefaultSamplingProbability +// - DefaultLowerBoundTracesPerSecond +// - PerOperationStrategies +// - DefaultUpperBoundTracesPerSecond +type PerOperationSamplingStrategies struct { + DefaultSamplingProbability float64 `thrift:"defaultSamplingProbability,1,required" json:"defaultSamplingProbability"` + DefaultLowerBoundTracesPerSecond float64 `thrift:"defaultLowerBoundTracesPerSecond,2,required" json:"defaultLowerBoundTracesPerSecond"` + PerOperationStrategies []*OperationSamplingStrategy `thrift:"perOperationStrategies,3,required" json:"perOperationStrategies"` + DefaultUpperBoundTracesPerSecond *float64 `thrift:"defaultUpperBoundTracesPerSecond,4" json:"defaultUpperBoundTracesPerSecond,omitempty"` +} + +func NewPerOperationSamplingStrategies() *PerOperationSamplingStrategies { + return &PerOperationSamplingStrategies{} +} + +func (p *PerOperationSamplingStrategies) GetDefaultSamplingProbability() float64 { + return p.DefaultSamplingProbability +} + +func (p *PerOperationSamplingStrategies) GetDefaultLowerBoundTracesPerSecond() float64 { + return p.DefaultLowerBoundTracesPerSecond +} + +func (p *PerOperationSamplingStrategies) GetPerOperationStrategies() []*OperationSamplingStrategy { + return p.PerOperationStrategies +} + +var PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT float64 + +func (p *PerOperationSamplingStrategies) GetDefaultUpperBoundTracesPerSecond() float64 { + if !p.IsSetDefaultUpperBoundTracesPerSecond() { + return PerOperationSamplingStrategies_DefaultUpperBoundTracesPerSecond_DEFAULT + } + return *p.DefaultUpperBoundTracesPerSecond +} +func (p *PerOperationSamplingStrategies) IsSetDefaultUpperBoundTracesPerSecond() bool { + return p.DefaultUpperBoundTracesPerSecond != nil +} + +func (p *PerOperationSamplingStrategies) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetDefaultSamplingProbability bool = false + var issetDefaultLowerBoundTracesPerSecond bool = false + var issetPerOperationStrategies bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetDefaultSamplingProbability = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetDefaultLowerBoundTracesPerSecond = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetPerOperationStrategies = true + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetDefaultSamplingProbability { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultSamplingProbability is not set")) + } + if !issetDefaultLowerBoundTracesPerSecond { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field DefaultLowerBoundTracesPerSecond is not set")) + } + if !issetPerOperationStrategies { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field PerOperationStrategies is not set")) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.DefaultSamplingProbability = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.DefaultLowerBoundTracesPerSecond = v + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField3(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*OperationSamplingStrategy, 0, size) + p.PerOperationStrategies = tSlice + for i := 0; i < size; i++ { + _elem0 := &OperationSamplingStrategy{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.PerOperationStrategies = append(p.PerOperationStrategies, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.DefaultUpperBoundTracesPerSecond = &v + } + return nil +} + +func (p *PerOperationSamplingStrategies) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("PerOperationSamplingStrategies"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *PerOperationSamplingStrategies) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultSamplingProbability", thrift.DOUBLE, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:defaultSamplingProbability: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultSamplingProbability)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultSamplingProbability (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:defaultSamplingProbability: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("defaultLowerBoundTracesPerSecond", thrift.DOUBLE, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(p.DefaultLowerBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultLowerBoundTracesPerSecond (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:defaultLowerBoundTracesPerSecond: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("perOperationStrategies", thrift.LIST, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:perOperationStrategies: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.PerOperationStrategies)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.PerOperationStrategies { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:perOperationStrategies: ", p), err) + } + return err +} + +func (p *PerOperationSamplingStrategies) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetDefaultUpperBoundTracesPerSecond() { + if err := oprot.WriteFieldBegin("defaultUpperBoundTracesPerSecond", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + if err := oprot.WriteDouble(float64(*p.DefaultUpperBoundTracesPerSecond)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.defaultUpperBoundTracesPerSecond (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:defaultUpperBoundTracesPerSecond: ", p), err) + } + } + return err +} + +func (p *PerOperationSamplingStrategies) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("PerOperationSamplingStrategies(%+v)", *p) +} + +// Attributes: +// - StrategyType +// - ProbabilisticSampling +// - RateLimitingSampling +// - OperationSampling +type SamplingStrategyResponse struct { + StrategyType SamplingStrategyType `thrift:"strategyType,1,required" json:"strategyType"` + ProbabilisticSampling *ProbabilisticSamplingStrategy `thrift:"probabilisticSampling,2" json:"probabilisticSampling,omitempty"` + RateLimitingSampling *RateLimitingSamplingStrategy `thrift:"rateLimitingSampling,3" json:"rateLimitingSampling,omitempty"` + OperationSampling *PerOperationSamplingStrategies `thrift:"operationSampling,4" json:"operationSampling,omitempty"` +} + +func NewSamplingStrategyResponse() *SamplingStrategyResponse { + return &SamplingStrategyResponse{} +} + +func (p *SamplingStrategyResponse) GetStrategyType() SamplingStrategyType { + return p.StrategyType +} + +var SamplingStrategyResponse_ProbabilisticSampling_DEFAULT *ProbabilisticSamplingStrategy + +func (p *SamplingStrategyResponse) GetProbabilisticSampling() *ProbabilisticSamplingStrategy { + if !p.IsSetProbabilisticSampling() { + return SamplingStrategyResponse_ProbabilisticSampling_DEFAULT + } + return p.ProbabilisticSampling +} + +var SamplingStrategyResponse_RateLimitingSampling_DEFAULT *RateLimitingSamplingStrategy + +func (p *SamplingStrategyResponse) GetRateLimitingSampling() *RateLimitingSamplingStrategy { + if !p.IsSetRateLimitingSampling() { + return SamplingStrategyResponse_RateLimitingSampling_DEFAULT + } + return p.RateLimitingSampling +} + +var SamplingStrategyResponse_OperationSampling_DEFAULT *PerOperationSamplingStrategies + +func (p *SamplingStrategyResponse) GetOperationSampling() *PerOperationSamplingStrategies { + if !p.IsSetOperationSampling() { + return SamplingStrategyResponse_OperationSampling_DEFAULT + } + return p.OperationSampling +} +func (p *SamplingStrategyResponse) IsSetProbabilisticSampling() bool { + return p.ProbabilisticSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetRateLimitingSampling() bool { + return p.RateLimitingSampling != nil +} + +func (p *SamplingStrategyResponse) IsSetOperationSampling() bool { + return p.OperationSampling != nil +} + +func (p *SamplingStrategyResponse) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetStrategyType bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetStrategyType = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetStrategyType { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StrategyType is not set")) + } + return nil +} + +func (p *SamplingStrategyResponse) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + temp := SamplingStrategyType(v) + p.StrategyType = temp + } + return nil +} + +func (p *SamplingStrategyResponse) readField2(iprot thrift.TProtocol) error { + p.ProbabilisticSampling = &ProbabilisticSamplingStrategy{} + if err := p.ProbabilisticSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.ProbabilisticSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField3(iprot thrift.TProtocol) error { + p.RateLimitingSampling = &RateLimitingSamplingStrategy{} + if err := p.RateLimitingSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.RateLimitingSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) readField4(iprot thrift.TProtocol) error { + p.OperationSampling = &PerOperationSamplingStrategies{} + if err := p.OperationSampling.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.OperationSampling), err) + } + return nil +} + +func (p *SamplingStrategyResponse) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SamplingStrategyResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SamplingStrategyResponse) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("strategyType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:strategyType: ", p), err) + } + if err := oprot.WriteI32(int32(p.StrategyType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.strategyType (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:strategyType: ", p), err) + } + return err +} + +func (p *SamplingStrategyResponse) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetProbabilisticSampling() { + if err := oprot.WriteFieldBegin("probabilisticSampling", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:probabilisticSampling: ", p), err) + } + if err := p.ProbabilisticSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.ProbabilisticSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:probabilisticSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetRateLimitingSampling() { + if err := oprot.WriteFieldBegin("rateLimitingSampling", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:rateLimitingSampling: ", p), err) + } + if err := p.RateLimitingSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.RateLimitingSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:rateLimitingSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetOperationSampling() { + if err := oprot.WriteFieldBegin("operationSampling", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:operationSampling: ", p), err) + } + if err := p.OperationSampling.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.OperationSampling), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:operationSampling: ", p), err) + } + } + return err +} + +func (p *SamplingStrategyResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SamplingStrategyResponse(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go new file mode 100644 index 00000000..dafd2b8e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go @@ -0,0 +1,32 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +const CLIENT_SEND = "cs" +const CLIENT_RECV = "cr" +const SERVER_SEND = "ss" +const SERVER_RECV = "sr" +const WIRE_SEND = "ws" +const WIRE_RECV = "wr" +const CLIENT_SEND_FRAGMENT = "csf" +const CLIENT_RECV_FRAGMENT = "crf" +const SERVER_SEND_FRAGMENT = "ssf" +const SERVER_RECV_FRAGMENT = "srf" +const LOCAL_COMPONENT = "lc" +const CLIENT_ADDR = "ca" +const SERVER_ADDR = "sa" + +func init() { +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go new file mode 100644 index 00000000..50c7a994 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go @@ -0,0 +1,1247 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type AnnotationType int64 + +const ( + AnnotationType_BOOL AnnotationType = 0 + AnnotationType_BYTES AnnotationType = 1 + AnnotationType_I16 AnnotationType = 2 + AnnotationType_I32 AnnotationType = 3 + AnnotationType_I64 AnnotationType = 4 + AnnotationType_DOUBLE AnnotationType = 5 + AnnotationType_STRING AnnotationType = 6 +) + +func (p AnnotationType) String() string { + switch p { + case AnnotationType_BOOL: + return "BOOL" + case AnnotationType_BYTES: + return "BYTES" + case AnnotationType_I16: + return "I16" + case AnnotationType_I32: + return "I32" + case AnnotationType_I64: + return "I64" + case AnnotationType_DOUBLE: + return "DOUBLE" + case AnnotationType_STRING: + return "STRING" + } + return "" +} + +func AnnotationTypeFromString(s string) (AnnotationType, error) { + switch s { + case "BOOL": + return AnnotationType_BOOL, nil + case "BYTES": + return AnnotationType_BYTES, nil + case "I16": + return AnnotationType_I16, nil + case "I32": + return AnnotationType_I32, nil + case "I64": + return AnnotationType_I64, nil + case "DOUBLE": + return AnnotationType_DOUBLE, nil + case "STRING": + return AnnotationType_STRING, nil + } + return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") +} + +func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } + +func (p AnnotationType) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *AnnotationType) UnmarshalText(text []byte) error { + q, err := AnnotationTypeFromString(string(text)) + if err != nil { + return err + } + *p = q + return nil +} + +// Indicates the network context of a service recording an annotation with two +// exceptions. +// +// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, +// the endpoint indicates the source or destination of an RPC. This exception +// allows zipkin to display network context of uninstrumented services, or +// clients such as web browsers. +// +// Attributes: +// - Ipv4: IPv4 host address packed into 4 bytes. +// +// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 +// - Port: IPv4 port +// +// Note: this is to be treated as an unsigned integer, so watch for negatives. +// +// Conventionally, when the port isn't known, port = 0. +// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" +// +// Conventionally, when the service name isn't known, service_name = "unknown". +type Endpoint struct { + Ipv4 int32 `thrift:"ipv4,1" json:"ipv4"` + Port int16 `thrift:"port,2" json:"port"` + ServiceName string `thrift:"service_name,3" json:"service_name"` +} + +func NewEndpoint() *Endpoint { + return &Endpoint{} +} + +func (p *Endpoint) GetIpv4() int32 { + return p.Ipv4 +} + +func (p *Endpoint) GetPort() int16 { + return p.Port +} + +func (p *Endpoint) GetServiceName() string { + return p.ServiceName +} +func (p *Endpoint) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Endpoint) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ipv4 = v + } + return nil +} + +func (p *Endpoint) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *Endpoint) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *Endpoint) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Endpoint"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Endpoint) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) + } + if err := oprot.WriteI32(int32(p.Ipv4)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) + } + return err +} + +func (p *Endpoint) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.I16, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) + } + if err := oprot.WriteI16(int16(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) + } + return err +} + +func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("service_name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) + } + return err +} + +func (p *Endpoint) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Endpoint(%+v)", *p) +} + +// An annotation is similar to a log statement. It includes a host field which +// allows these events to be attributed properly, and also aggregatable. +// +// Attributes: +// - Timestamp: Microseconds from epoch. +// +// This value should use the most precise value possible. For example, +// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. +// - Value +// - Host: Always the host that recorded the event. By specifying the host you allow +// rollup of all events (such as client requests to a service) by IP address. +type Annotation struct { + Timestamp int64 `thrift:"timestamp,1" json:"timestamp"` + Value string `thrift:"value,2" json:"value"` + Host *Endpoint `thrift:"host,3" json:"host,omitempty"` +} + +func NewAnnotation() *Annotation { + return &Annotation{} +} + +func (p *Annotation) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Annotation) GetValue() string { + return p.Value +} + +var Annotation_Host_DEFAULT *Endpoint + +func (p *Annotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return Annotation_Host_DEFAULT + } + return p.Host +} +func (p *Annotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *Annotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Annotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Timestamp = v + } + return nil +} + +func (p *Annotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *Annotation) readField3(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *Annotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Annotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Annotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) + } + return err +} + +func (p *Annotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteString(string(p.Value)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *Annotation) writeField3(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) + } + } + return err +} + +func (p *Annotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Annotation(%+v)", *p) +} + +// Binary annotations are tags applied to a Span to give it context. For +// example, a binary annotation of "http.uri" could the path to a resource in a +// RPC call. +// +// Binary annotations of type STRING are always queryable, though more a +// historical implementation detail than a structural concern. +// +// Binary annotations can repeat, and vary on the host. Similar to Annotation, +// the host indicates who logged the event. This allows you to tell the +// difference between the client and server side of the same key. For example, +// the key "http.uri" might be different on the client and server side due to +// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, +// you can see the different points of view, which often help in debugging. +// +// Attributes: +// - Key +// - Value +// - AnnotationType +// - Host: The host that recorded tag, which allows you to differentiate between +// multiple tags with the same key. There are two exceptions to this. +// +// When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or +// destination of an RPC. This exception allows zipkin to display network +// context of uninstrumented services, or clients such as web browsers. +type BinaryAnnotation struct { + Key string `thrift:"key,1" json:"key"` + Value []byte `thrift:"value,2" json:"value"` + AnnotationType AnnotationType `thrift:"annotation_type,3" json:"annotation_type"` + Host *Endpoint `thrift:"host,4" json:"host,omitempty"` +} + +func NewBinaryAnnotation() *BinaryAnnotation { + return &BinaryAnnotation{} +} + +func (p *BinaryAnnotation) GetKey() string { + return p.Key +} + +func (p *BinaryAnnotation) GetValue() []byte { + return p.Value +} + +func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { + return p.AnnotationType +} + +var BinaryAnnotation_Host_DEFAULT *Endpoint + +func (p *BinaryAnnotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return BinaryAnnotation_Host_DEFAULT + } + return p.Host +} +func (p *BinaryAnnotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *BinaryAnnotation) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BinaryAnnotation) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *BinaryAnnotation) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *BinaryAnnotation) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + temp := AnnotationType(v) + p.AnnotationType = temp + } + return nil +} + +func (p *BinaryAnnotation) readField4(iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *BinaryAnnotation) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BinaryAnnotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BinaryAnnotation) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteBinary(p.Value); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotation_type", thrift.I32, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) + } + if err := oprot.WriteI32(int32(p.AnnotationType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) + } + return err +} + +func (p *BinaryAnnotation) writeField4(oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) + } + if err := p.Host.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) + } + } + return err +} + +func (p *BinaryAnnotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BinaryAnnotation(%+v)", *p) +} + +// A trace is a series of spans (often RPC calls) which form a latency tree. +// +// The root span is where trace_id = id and parent_id = Nil. The root span is +// usually the longest interval in the trace, starting with a SERVER_RECV +// annotation and ending with a SERVER_SEND. +// +// Attributes: +// - TraceID +// - Name: Span name in lowercase, rpc method for example +// +// Conventionally, when the span name isn't known, name = "unknown". +// - ID +// - ParentID +// - Annotations +// - BinaryAnnotations +// - Debug +// - Timestamp: Microseconds from epoch of the creation of this span. +// +// This value should be set directly by instrumentation, using the most +// precise value possible. For example, gettimeofday or syncing nanoTime +// against a tick of currentTimeMillis. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this via Annotation.timestamp. +// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. +// +// This field is optional for compatibility with old data: first-party span +// stores are expected to support this at time of introduction. +// - Duration: Measurement of duration in microseconds, used to support queries. +// +// This value should be set directly, where possible. Doing so encourages +// precise measurement decoupled from problems of clocks, such as skew or NTP +// updates causing time to move backwards. +// +// For compatibilty with instrumentation that precede this field, collectors +// or span stores can derive this by subtracting Annotation.timestamp. +// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. +// +// If this field is persisted as unset, zipkin will continue to work, except +// duration query support will be implementation-specific. Similarly, setting +// this field non-atomically is implementation-specific. +// +// This field is i64 vs i32 to support spans longer than 35 minutes. +type Span struct { + TraceID int64 `thrift:"trace_id,1" json:"trace_id"` + // unused field # 2 + Name string `thrift:"name,3" json:"name"` + ID int64 `thrift:"id,4" json:"id"` + ParentID *int64 `thrift:"parent_id,5" json:"parent_id,omitempty"` + Annotations []*Annotation `thrift:"annotations,6" json:"annotations"` + // unused field # 7 + BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" json:"binary_annotations"` + Debug bool `thrift:"debug,9" json:"debug,omitempty"` + Timestamp *int64 `thrift:"timestamp,10" json:"timestamp,omitempty"` + Duration *int64 `thrift:"duration,11" json:"duration,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + +func (p *Span) GetTraceID() int64 { + return p.TraceID +} + +func (p *Span) GetName() string { + return p.Name +} + +func (p *Span) GetID() int64 { + return p.ID +} + +var Span_ParentID_DEFAULT int64 + +func (p *Span) GetParentID() int64 { + if !p.IsSetParentID() { + return Span_ParentID_DEFAULT + } + return *p.ParentID +} + +func (p *Span) GetAnnotations() []*Annotation { + return p.Annotations +} + +func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { + return p.BinaryAnnotations +} + +var Span_Debug_DEFAULT bool = false + +func (p *Span) GetDebug() bool { + return p.Debug +} + +var Span_Timestamp_DEFAULT int64 + +func (p *Span) GetTimestamp() int64 { + if !p.IsSetTimestamp() { + return Span_Timestamp_DEFAULT + } + return *p.Timestamp +} + +var Span_Duration_DEFAULT int64 + +func (p *Span) GetDuration() int64 { + if !p.IsSetDuration() { + return Span_Duration_DEFAULT + } + return *p.Duration +} +func (p *Span) IsSetParentID() bool { + return p.ParentID != nil +} + +func (p *Span) IsSetDebug() bool { + return p.Debug != Span_Debug_DEFAULT +} + +func (p *Span) IsSetTimestamp() bool { + return p.Timestamp != nil +} + +func (p *Span) IsSetDuration() bool { + return p.Duration != nil +} + +func (p *Span) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + case 4: + if err := p.readField4(iprot); err != nil { + return err + } + case 5: + if err := p.readField5(iprot); err != nil { + return err + } + case 6: + if err := p.readField6(iprot); err != nil { + return err + } + case 8: + if err := p.readField8(iprot); err != nil { + return err + } + case 9: + if err := p.readField9(iprot); err != nil { + return err + } + case 10: + if err := p.readField10(iprot); err != nil { + return err + } + case 11: + if err := p.readField11(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Span) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.TraceID = v + } + return nil +} + +func (p *Span) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Name = v + } + return nil +} + +func (p *Span) readField4(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 4: ", err) + } else { + p.ID = v + } + return nil +} + +func (p *Span) readField5(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 5: ", err) + } else { + p.ParentID = &v + } + return nil +} + +func (p *Span) readField6(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Annotation, 0, size) + p.Annotations = tSlice + for i := 0; i < size; i++ { + _elem0 := &Annotation{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Annotations = append(p.Annotations, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField8(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BinaryAnnotation, 0, size) + p.BinaryAnnotations = tSlice + for i := 0; i < size; i++ { + _elem1 := &BinaryAnnotation{} + if err := _elem1.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) readField9(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 9: ", err) + } else { + p.Debug = v + } + return nil +} + +func (p *Span) readField10(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 10: ", err) + } else { + p.Timestamp = &v + } + return nil +} + +func (p *Span) readField11(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(); err != nil { + return thrift.PrependError("error reading field 11: ", err) + } else { + p.Duration = &v + } + return nil +} + +func (p *Span) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := p.writeField4(oprot); err != nil { + return err + } + if err := p.writeField5(oprot); err != nil { + return err + } + if err := p.writeField6(oprot); err != nil { + return err + } + if err := p.writeField8(oprot); err != nil { + return err + } + if err := p.writeField9(oprot); err != nil { + return err + } + if err := p.writeField10(oprot); err != nil { + return err + } + if err := p.writeField11(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("trace_id", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) + } + if err := oprot.WriteI64(int64(p.TraceID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) + } + return err +} + +func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) + } + if err := oprot.WriteString(string(p.Name)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) + } + return err +} + +func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("id", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) + } + if err := oprot.WriteI64(int64(p.ID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) + } + return err +} + +func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { + if p.IsSetParentID() { + if err := oprot.WriteFieldBegin("parent_id", thrift.I64, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) + } + if err := oprot.WriteI64(int64(*p.ParentID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) + } + } + return err +} + +func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("annotations", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Annotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Annotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) + } + return err +} + +func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("binary_annotations", thrift.LIST, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.BinaryAnnotations { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) + } + return err +} + +func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { + if p.IsSetDebug() { + if err := oprot.WriteFieldBegin("debug", thrift.BOOL, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) + } + if err := oprot.WriteBool(bool(p.Debug)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) + } + } + return err +} + +func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { + if p.IsSetTimestamp() { + if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) + } + } + return err +} + +func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { + if p.IsSetDuration() { + if err := oprot.WriteFieldBegin("duration", thrift.I64, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) + } + if err := oprot.WriteI64(int64(*p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) + } + } + return err +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - Ok +type Response struct { + Ok bool `thrift:"ok,1,required" json:"ok"` +} + +func NewResponse() *Response { + return &Response{} +} + +func (p *Response) GetOk() bool { + return p.Ok +} +func (p *Response) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *Response) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *Response) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Response"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Response) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *Response) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Response(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go new file mode 100644 index 00000000..bcc54f36 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go @@ -0,0 +1,446 @@ +// Autogenerated by Thrift Compiler (0.9.3) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package zipkincore + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type ZipkinCollector interface { + // Parameters: + // - Spans + SubmitZipkinBatch(spans []*Span) (r []*Response, err error) +} + +type ZipkinCollectorClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { + return &ZipkinCollectorClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Spans +func (p *ZipkinCollectorClient) SubmitZipkinBatch(spans []*Span) (r []*Response, err error) { + if err = p.sendSubmitZipkinBatch(spans); err != nil { + return + } + return p.recvSubmitZipkinBatch() +} + +func (p *ZipkinCollectorClient) sendSubmitZipkinBatch(spans []*Span) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("submitZipkinBatch", thrift.CALL, p.SeqId); err != nil { + return + } + args := ZipkinCollectorSubmitZipkinBatchArgs{ + Spans: spans, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *ZipkinCollectorClient) recvSubmitZipkinBatch() (value []*Response, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "submitZipkinBatch" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submitZipkinBatch failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submitZipkinBatch failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submitZipkinBatch failed: invalid message type") + return + } + result := ZipkinCollectorSubmitZipkinBatchResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type ZipkinCollectorProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler ZipkinCollector +} + +func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { + + self4 := &ZipkinCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self4.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler: handler} + return self4 +} + +func (p *ZipkinCollectorProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x5.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x5 + +} + +type zipkinCollectorProcessorSubmitZipkinBatch struct { + handler ZipkinCollector +} + +func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := ZipkinCollectorSubmitZipkinBatchArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := ZipkinCollectorSubmitZipkinBatchResult{} + var retval []*Response + var err2 error + if retval, err2 = p.handler.SubmitZipkinBatch(args.Spans); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: "+err2.Error()) + oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type ZipkinCollectorSubmitZipkinBatchArgs struct { + Spans []*Span `thrift:"spans,1" json:"spans"` +} + +func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { + return &ZipkinCollectorSubmitZipkinBatchArgs{} +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { + return p.Spans +} +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i++ { + _elem6 := &Span{} + if err := _elem6.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem6), err) + } + p.Spans = append(p.Spans, _elem6) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Success +type ZipkinCollectorSubmitZipkinBatchResult struct { + Success []*Response `thrift:"success,0" json:"success,omitempty"` +} + +func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { + return &ZipkinCollectorSubmitZipkinBatchResult{} +} + +var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response + +func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { + return p.Success +} +func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) readField0(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Response, 0, size) + p.Success = tSlice + for i := 0; i < size; i++ { + _elem7 := &Response{} + if err := _elem7.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem7), err) + } + p.Success = append(p.Success, _elem7) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("submitZipkinBatch_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/tracer.go b/vendor/src/github.com/uber/jaeger-client-go/tracer.go new file mode 100644 index 00000000..1a71364a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/tracer.go @@ -0,0 +1,387 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "fmt" + "io" + "os" + "reflect" + "sync" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +// Tracer implements opentracing.Tracer. +type Tracer struct { + serviceName string + hostIPv4 uint32 // this is for zipkin endpoint conversion + + sampler Sampler + reporter Reporter + metrics Metrics + logger log.Logger + + timeNow func() time.Time + randomNumber func() uint64 + + options struct { + poolSpans bool + gen128Bit bool // whether to generate 128bit trace IDs + zipkinSharedRPCSpan bool + // more options to come + } + // pool for Span objects + spanPool sync.Pool + + injectors map[interface{}]Injector + extractors map[interface{}]Extractor + + observer compositeObserver + + tags []Tag + + baggageRestrictionManager baggage.RestrictionManager + baggageSetter *baggageSetter +} + +// NewTracer creates Tracer implementation that reports tracing to Jaeger. +// The returned io.Closer can be used in shutdown hooks to ensure that the internal +// queue of the Reporter is drained and all buffered spans are submitted to collectors. +func NewTracer( + serviceName string, + sampler Sampler, + reporter Reporter, + options ...TracerOption, +) (opentracing.Tracer, io.Closer) { + t := &Tracer{ + serviceName: serviceName, + sampler: sampler, + reporter: reporter, + injectors: make(map[interface{}]Injector), + extractors: make(map[interface{}]Extractor), + metrics: *NewNullMetrics(), + spanPool: sync.Pool{New: func() interface{} { + return &Span{} + }}, + } + + for _, option := range options { + option(t) + } + + // register default injectors/extractors unless they are already provided via options + textPropagator := newTextMapPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(getDefaultHeadersConfig(), t.metrics) + t.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + + binaryPropagator := newBinaryPropagator(t) + t.addCodec(opentracing.Binary, binaryPropagator, binaryPropagator) + + // TODO remove after TChannel supports OpenTracing + interopPropagator := &jaegerTraceContextPropagator{tracer: t} + t.addCodec(SpanContextFormat, interopPropagator, interopPropagator) + + zipkinPropagator := &zipkinPropagator{tracer: t} + t.addCodec(ZipkinSpanFormat, zipkinPropagator, zipkinPropagator) + + if t.baggageRestrictionManager != nil { + t.baggageSetter = newBaggageSetter(t.baggageRestrictionManager, &t.metrics) + } else { + t.baggageSetter = newBaggageSetter(baggage.NewDefaultRestrictionManager(0), &t.metrics) + } + if t.randomNumber == nil { + rng := utils.NewRand(time.Now().UnixNano()) + t.randomNumber = func() uint64 { + return uint64(rng.Int63()) + } + } + if t.timeNow == nil { + t.timeNow = time.Now + } + if t.logger == nil { + t.logger = log.NullLogger + } + // Set tracer-level tags + t.tags = append(t.tags, Tag{key: JaegerClientVersionTagKey, value: JaegerClientVersion}) + if hostname, err := os.Hostname(); err == nil { + t.tags = append(t.tags, Tag{key: TracerHostnameTagKey, value: hostname}) + } + if ip, err := utils.HostIP(); err == nil { + t.tags = append(t.tags, Tag{key: TracerIPTagKey, value: ip.String()}) + t.hostIPv4 = utils.PackIPAsUint32(ip) + } else { + t.logger.Error("Unable to determine this host's IP address: " + err.Error()) + } + + return t, t +} + +// addCodec adds registers injector and extractor for given propagation format if not already defined. +func (t *Tracer) addCodec(format interface{}, injector Injector, extractor Extractor) { + if _, ok := t.injectors[format]; !ok { + t.injectors[format] = injector + } + if _, ok := t.extractors[format]; !ok { + t.extractors[format] = extractor + } +} + +// StartSpan implements StartSpan() method of opentracing.Tracer. +func (t *Tracer) StartSpan( + operationName string, + options ...opentracing.StartSpanOption, +) opentracing.Span { + sso := opentracing.StartSpanOptions{} + for _, o := range options { + o.Apply(&sso) + } + return t.startSpanWithOptions(operationName, sso) +} + +func (t *Tracer) startSpanWithOptions( + operationName string, + options opentracing.StartSpanOptions, +) opentracing.Span { + if options.StartTime.IsZero() { + options.StartTime = t.timeNow() + } + + var references []Reference + var parent SpanContext + var hasParent bool // need this because `parent` is a value, not reference + for _, ref := range options.References { + ctx, ok := ref.ReferencedContext.(SpanContext) + if !ok { + t.logger.Error(fmt.Sprintf( + "Reference contains invalid type of SpanReference: %s", + reflect.ValueOf(ref.ReferencedContext))) + continue + } + if !(ctx.IsValid() || ctx.isDebugIDContainerOnly() || len(ctx.baggage) != 0) { + continue + } + references = append(references, Reference{Type: ref.Type, Context: ctx}) + if !hasParent { + parent = ctx + hasParent = ref.Type == opentracing.ChildOfRef + } + } + if !hasParent && parent.IsValid() { + // If ChildOfRef wasn't found but a FollowFromRef exists, use the context from + // the FollowFromRef as the parent + hasParent = true + } + + rpcServer := false + if v, ok := options.Tags[ext.SpanKindRPCServer.Key]; ok { + rpcServer = (v == ext.SpanKindRPCServerEnum || v == string(ext.SpanKindRPCServerEnum)) + } + + var samplerTags []Tag + var ctx SpanContext + newTrace := false + if !hasParent || !parent.IsValid() { + newTrace = true + ctx.traceID.Low = t.randomID() + if t.options.gen128Bit { + ctx.traceID.High = t.randomID() + } + ctx.spanID = SpanID(ctx.traceID.Low) + ctx.parentID = 0 + ctx.flags = byte(0) + if hasParent && parent.isDebugIDContainerOnly() { + ctx.flags |= (flagSampled | flagDebug) + samplerTags = []Tag{{key: JaegerDebugHeader, value: parent.debugID}} + } else if sampled, tags := t.sampler.IsSampled(ctx.traceID, operationName); sampled { + ctx.flags |= flagSampled + samplerTags = tags + } + } else { + ctx.traceID = parent.traceID + if rpcServer && t.options.zipkinSharedRPCSpan { + // Support Zipkin's one-span-per-RPC model + ctx.spanID = parent.spanID + ctx.parentID = parent.parentID + } else { + ctx.spanID = SpanID(t.randomID()) + ctx.parentID = parent.spanID + } + ctx.flags = parent.flags + } + if hasParent { + // copy baggage items + if l := len(parent.baggage); l > 0 { + ctx.baggage = make(map[string]string, len(parent.baggage)) + for k, v := range parent.baggage { + ctx.baggage[k] = v + } + } + } + + sp := t.newSpan() + sp.context = ctx + sp.observer = t.observer.OnStartSpan(sp, operationName, options) + return t.startSpanInternal( + sp, + operationName, + options.StartTime, + samplerTags, + options.Tags, + newTrace, + rpcServer, + references, + ) +} + +// Inject implements Inject() method of opentracing.Tracer +func (t *Tracer) Inject(ctx opentracing.SpanContext, format interface{}, carrier interface{}) error { + c, ok := ctx.(SpanContext) + if !ok { + return opentracing.ErrInvalidSpanContext + } + if injector, ok := t.injectors[format]; ok { + return injector.Inject(c, carrier) + } + return opentracing.ErrUnsupportedFormat +} + +// Extract implements Extract() method of opentracing.Tracer +func (t *Tracer) Extract( + format interface{}, + carrier interface{}, +) (opentracing.SpanContext, error) { + if extractor, ok := t.extractors[format]; ok { + return extractor.Extract(carrier) + } + return nil, opentracing.ErrUnsupportedFormat +} + +// Close releases all resources used by the Tracer and flushes any remaining buffered spans. +func (t *Tracer) Close() error { + t.reporter.Close() + t.sampler.Close() + if mgr, ok := t.baggageRestrictionManager.(io.Closer); ok { + mgr.Close() + } + return nil +} + +// Tags returns a slice of tracer-level tags. +func (t *Tracer) Tags() []opentracing.Tag { + tags := make([]opentracing.Tag, len(t.tags)) + for i, tag := range t.tags { + tags[i] = opentracing.Tag{Key: tag.key, Value: tag.value} + } + return tags +} + +// newSpan returns an instance of a clean Span object. +// If options.PoolSpans is true, the spans are retrieved from an object pool. +func (t *Tracer) newSpan() *Span { + if !t.options.poolSpans { + return &Span{} + } + sp := t.spanPool.Get().(*Span) + sp.context = emptyContext + sp.tracer = nil + sp.tags = nil + sp.logs = nil + return sp +} + +func (t *Tracer) startSpanInternal( + sp *Span, + operationName string, + startTime time.Time, + internalTags []Tag, + tags opentracing.Tags, + newTrace bool, + rpcServer bool, + references []Reference, +) *Span { + sp.tracer = t + sp.operationName = operationName + sp.startTime = startTime + sp.duration = 0 + sp.references = references + sp.firstInProcess = rpcServer || sp.context.parentID == 0 + if len(tags) > 0 || len(internalTags) > 0 { + sp.tags = make([]Tag, len(internalTags), len(tags)+len(internalTags)) + copy(sp.tags, internalTags) + for k, v := range tags { + sp.observer.OnSetTag(k, v) + if k == string(ext.SamplingPriority) && setSamplingPriority(sp, v) { + continue + } + sp.setTagNoLocking(k, v) + } + } + // emit metrics + t.metrics.SpansStarted.Inc(1) + if sp.context.IsSampled() { + t.metrics.SpansSampled.Inc(1) + if newTrace { + // We cannot simply check for parentID==0 because in Zipkin model the + // server-side RPC span has the exact same trace/span/parent IDs as the + // calling client-side span, but obviously the server side span is + // no longer a root span of the trace. + t.metrics.TracesStartedSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedSampled.Inc(1) + } + } else { + t.metrics.SpansNotSampled.Inc(1) + if newTrace { + t.metrics.TracesStartedNotSampled.Inc(1) + } else if sp.firstInProcess { + t.metrics.TracesJoinedNotSampled.Inc(1) + } + } + return sp +} + +func (t *Tracer) reportSpan(sp *Span) { + t.metrics.SpansFinished.Inc(1) + if sp.context.IsSampled() { + t.reporter.Report(sp) + } + if t.options.poolSpans { + t.spanPool.Put(sp) + } +} + +// randomID generates a random trace/span ID, using tracer.random() generator. +// It never returns 0. +func (t *Tracer) randomID() uint64 { + val := t.randomNumber() + for val == 0 { + val = t.randomNumber() + } + return val +} + +// (NB) span should hold the lock before making this call +func (t *Tracer) setBaggage(sp *Span, key, value string) { + t.baggageSetter.setBaggage(sp, key, value) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/tracer_options.go b/vendor/src/github.com/uber/jaeger-client-go/tracer_options.go new file mode 100644 index 00000000..46f05282 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/tracer_options.go @@ -0,0 +1,140 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "time" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go/internal/baggage" +) + +// TracerOption is a function that sets some option on the tracer +type TracerOption func(tracer *Tracer) + +// TracerOptions is a factory for all available TracerOption's +var TracerOptions tracerOptions + +type tracerOptions struct{} + +// Metrics creates a TracerOption that initializes Metrics on the tracer, +// which is used to emit statistics. +func (tracerOptions) Metrics(m *Metrics) TracerOption { + return func(tracer *Tracer) { + tracer.metrics = *m + } +} + +// Logger creates a TracerOption that gives the tracer a Logger. +func (tracerOptions) Logger(logger Logger) TracerOption { + return func(tracer *Tracer) { + tracer.logger = logger + } +} + +func (tracerOptions) CustomHeaderKeys(headerKeys *HeadersConfig) TracerOption { + return func(tracer *Tracer) { + if headerKeys == nil { + return + } + textPropagator := newTextMapPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.TextMap, textPropagator, textPropagator) + + httpHeaderPropagator := newHTTPHeaderPropagator(headerKeys.applyDefaults(), tracer.metrics) + tracer.addCodec(opentracing.HTTPHeaders, httpHeaderPropagator, httpHeaderPropagator) + } +} + +// TimeNow creates a TracerOption that gives the tracer a function +// used to generate timestamps for spans. +func (tracerOptions) TimeNow(timeNow func() time.Time) TracerOption { + return func(tracer *Tracer) { + tracer.timeNow = timeNow + } +} + +// RandomNumber creates a TracerOption that gives the tracer +// a thread-safe random number generator function for generating trace IDs. +func (tracerOptions) RandomNumber(randomNumber func() uint64) TracerOption { + return func(tracer *Tracer) { + tracer.randomNumber = randomNumber + } +} + +// PoolSpans creates a TracerOption that tells the tracer whether it should use +// an object pool to minimize span allocations. +// This should be used with care, only if the service is not running any async tasks +// that can access parent spans after those spans have been finished. +func (tracerOptions) PoolSpans(poolSpans bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.poolSpans = poolSpans + } +} + +// Deprecated: HostIPv4 creates a TracerOption that identifies the current service/process. +// If not set, the factory method will obtain the current IP address. +// The TracerOption is deprecated; the tracer will attempt to automatically detect the IP. +func (tracerOptions) HostIPv4(hostIPv4 uint32) TracerOption { + return func(tracer *Tracer) { + tracer.hostIPv4 = hostIPv4 + } +} + +func (tracerOptions) Injector(format interface{}, injector Injector) TracerOption { + return func(tracer *Tracer) { + tracer.injectors[format] = injector + } +} + +func (tracerOptions) Extractor(format interface{}, extractor Extractor) TracerOption { + return func(tracer *Tracer) { + tracer.extractors[format] = extractor + } +} + +func (t tracerOptions) Observer(observer Observer) TracerOption { + return t.ContribObserver(&oldObserver{obs: observer}) +} + +func (tracerOptions) ContribObserver(observer ContribObserver) TracerOption { + return func(tracer *Tracer) { + tracer.observer.append(observer) + } +} + +func (tracerOptions) Gen128Bit(gen128Bit bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.gen128Bit = gen128Bit + } +} + +func (tracerOptions) ZipkinSharedRPCSpan(zipkinSharedRPCSpan bool) TracerOption { + return func(tracer *Tracer) { + tracer.options.zipkinSharedRPCSpan = zipkinSharedRPCSpan + } +} + +func (tracerOptions) Tag(key string, value interface{}) TracerOption { + return func(tracer *Tracer) { + tracer.tags = append(tracer.tags, Tag{key: key, value: value}) + } +} + +func (tracerOptions) BaggageRestrictionManager(mgr baggage.RestrictionManager) TracerOption { + return func(tracer *Tracer) { + tracer.baggageRestrictionManager = mgr + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/tracer_test.go b/vendor/src/github.com/uber/jaeger-client-go/tracer_test.go new file mode 100644 index 00000000..d38d671e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/tracer_test.go @@ -0,0 +1,365 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "io" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" + + "github.com/uber/jaeger-client-go/internal/baggage" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/utils" +) + +type tracerSuite struct { + suite.Suite + tracer opentracing.Tracer + closer io.Closer + metricsFactory *metrics.LocalFactory +} + +func (s *tracerSuite) SetupTest() { + s.metricsFactory = metrics.NewLocalFactory(0) + metrics := NewMetrics(s.metricsFactory, nil) + + s.tracer, s.closer = NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Metrics(metrics), + TracerOptions.ZipkinSharedRPCSpan(true), + TracerOptions.BaggageRestrictionManager(baggage.NewDefaultRestrictionManager(0)), + ) + s.NotNil(s.tracer) +} + +func (s *tracerSuite) TearDownTest() { + if s.tracer != nil { + s.closer.Close() + s.tracer = nil + } +} + +func TestTracerSuite(t *testing.T) { + suite.Run(t, new(tracerSuite)) +} + +func (s *tracerSuite) TestBeginRootSpan() { + s.metricsFactory.Clear() + startTime := time.Now() + s.tracer.(*Tracer).timeNow = func() time.Time { return startTime } + someID := uint64(12345) + s.tracer.(*Tracer).randomNumber = func() uint64 { return someID } + + sp := s.tracer.StartSpan("get_name") + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer-service") + s.NotNil(sp) + ss := sp.(*Span) + s.NotNil(ss.tracer, "Tracer must be referenced from span") + s.Equal("get_name", ss.operationName) + s.Len(ss.tags, 4, "Span should have 2 sampler tags, span.kind tag and peer.service tag") + s.EqualValues(Tag{key: "span.kind", value: ext.SpanKindRPCServerEnum}, ss.tags[2], "Span must be server-side") + s.EqualValues(Tag{key: "peer.service", value: "peer-service"}, ss.tags[3], "Client is 'peer-service'") + + s.EqualValues(someID, ss.context.traceID.Low) + s.EqualValues(0, ss.context.parentID) + + s.Equal(startTime, ss.startTime) + + sp.Finish() + s.NotNil(ss.duration) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + }...) +} + +func (s *tracerSuite) TestStartRootSpanWithOptions() { + ts := time.Now() + sp := s.tracer.StartSpan("get_address", opentracing.StartTime(ts)) + ss := sp.(*Span) + s.Equal("get_address", ss.operationName) + s.Equal(ts, ss.startTime) +} + +func (s *tracerSuite) TestStartChildSpan() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("get_address") + sp2 := s.tracer.StartSpan("get_street", opentracing.ChildOf(sp1.Context())) + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 2}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 2}, + }...) +} + +type nonJaegerSpanContext struct{} + +func (c nonJaegerSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +func (s *tracerSuite) TestStartSpanWithMultipleReferences() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan("B") + sp3 := s.tracer.StartSpan("C") + sp4 := s.tracer.StartSpan( + "D", + opentracing.ChildOf(sp1.Context()), + opentracing.ChildOf(sp2.Context()), + opentracing.FollowsFrom(sp3.Context()), + opentracing.FollowsFrom(nonJaegerSpanContext{}), + opentracing.FollowsFrom(SpanContext{}), // Empty span context should be excluded + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp4.(*Span).context.parentID) + sp4.Finish() + s.NotNil(sp4.(*Span).duration) + sp3.Finish() + sp2.Finish() + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 4}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 4}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 4}, + }...) + assert.Len(s.T(), sp4.(*Span).references, 3) +} + +func (s *tracerSuite) TestStartSpanWithOnlyFollowFromReference() { + s.metricsFactory.Clear() + sp1 := s.tracer.StartSpan("A") + sp2 := s.tracer.StartSpan( + "B", + opentracing.FollowsFrom(sp1.Context()), + ) + // Should use the first ChildOf ref span as the parent + s.Equal(sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + sp2.Finish() + s.NotNil(sp2.(*Span).duration) + sp1.Finish() + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": "y"}, Value: 2}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": "y", "state": "started"}, Value: 1}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 2}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 2}, + }...) + assert.Len(s.T(), sp2.(*Span).references, 1) +} + +func (s *tracerSuite) TestTraceStartedOrJoinedMetrics() { + tests := []struct { + sampled bool + label string + }{ + {true, "y"}, + {false, "n"}, + } + for _, test := range tests { + s.metricsFactory.Clear() + s.tracer.(*Tracer).sampler = NewConstSampler(test.sampled) + sp1 := s.tracer.StartSpan("parent", ext.RPCServerOption(nil)) + sp2 := s.tracer.StartSpan("child1", opentracing.ChildOf(sp1.Context())) + sp3 := s.tracer.StartSpan("child2", ext.RPCServerOption(sp2.Context())) + s.Equal(sp2.(*Span).context.spanID, sp3.(*Span).context.spanID) + s.Equal(sp2.(*Span).context.parentID, sp3.(*Span).context.parentID) + sp3.Finish() + sp2.Finish() + sp1.Finish() + s.Equal(test.sampled, sp1.Context().(SpanContext).IsSampled()) + s.Equal(test.sampled, sp2.Context().(SpanContext).IsSampled()) + + testutils.AssertCounterMetrics(s.T(), s.metricsFactory, []testutils.ExpectedMetric{ + {Name: "jaeger.spans", Tags: map[string]string{"group": "sampling", "sampled": test.label}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "started"}, Value: 3}, + {Name: "jaeger.spans", Tags: map[string]string{"group": "lifecycle", "state": "finished"}, Value: 3}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "started"}, Value: 1}, + {Name: "jaeger.traces", Tags: map[string]string{"sampled": test.label, "state": "joined"}, Value: 1}, + }...) + } +} + +func (s *tracerSuite) TestSetOperationName() { + sp1 := s.tracer.StartSpan("get_address") + sp1.SetOperationName("get_street") + s.Equal("get_street", sp1.(*Span).operationName) +} + +func (s *tracerSuite) TestSamplerEffects() { + s.tracer.(*Tracer).sampler = NewConstSampler(true) + sp := s.tracer.StartSpan("test") + flags := sp.(*Span).context.flags + s.EqualValues(flagSampled, flags&flagSampled) + + s.tracer.(*Tracer).sampler = NewConstSampler(false) + sp = s.tracer.StartSpan("test") + flags = sp.(*Span).context.flags + s.EqualValues(0, flags&flagSampled) +} + +func (s *tracerSuite) TestRandomIDNotZero() { + val := uint64(0) + s.tracer.(*Tracer).randomNumber = func() (r uint64) { + r = val + val++ + return + } + sp := s.tracer.StartSpan("get_name").(*Span) + s.EqualValues(TraceID{Low: 1}, sp.context.traceID) + + rng := utils.NewRand(0) + rng.Seed(1) // for test coverage +} + +func TestTracerOptions(t *testing.T) { + t1, e := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00") + assert.NoError(t, e) + + timeNow := func() time.Time { + return t1 + } + rnd := func() uint64 { + return 1 + } + + openTracer, closer := NewTracer("DOOP", // respect the classics, man! + NewConstSampler(true), + NewNullReporter(), + TracerOptions.Logger(log.StdLogger), + TracerOptions.TimeNow(timeNow), + TracerOptions.RandomNumber(rnd), + TracerOptions.PoolSpans(true), + TracerOptions.Tag("tag_key", "tag_value"), + ) + defer closer.Close() + + tracer := openTracer.(*Tracer) + assert.Equal(t, log.StdLogger, tracer.logger) + assert.Equal(t, t1, tracer.timeNow()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) + assert.Equal(t, uint64(1), tracer.randomNumber()) // always 1 + assert.Equal(t, true, tracer.options.poolSpans) + assert.Equal(t, opentracing.Tag{Key: "tag_key", Value: "tag_value"}, tracer.Tags()[0]) +} + +func TestInjectorExtractorOptions(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), + TracerOptions.Injector("dummy", &dummyPropagator{}), + TracerOptions.Extractor("dummy", &dummyPropagator{}), + ) + defer tc.Close() + + sp := tracer.StartSpan("x") + c := &dummyCarrier{} + err := tracer.Inject(sp.Context(), "dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + err = tracer.Inject(sp.Context(), "dummy", c) + assert.NoError(t, err) + assert.True(t, c.ok) + + c.ok = false + _, err = tracer.Extract("dummy", []int{}) + assert.Equal(t, opentracing.ErrInvalidCarrier, err) + _, err = tracer.Extract("dummy", c) + assert.Equal(t, opentracing.ErrSpanContextNotFound, err) + c.ok = true + _, err = tracer.Extract("dummy", c) + assert.NoError(t, err) +} + +func TestEmptySpanContextAsParent(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter()) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + ctx := span.Context().(SpanContext) + assert.True(t, ctx.traceID.IsValid()) + assert.True(t, ctx.IsValid()) +} + +func TestGen128Bit(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.Gen128Bit(true)) + defer tc.Close() + + span := tracer.StartSpan("test", opentracing.ChildOf(emptyContext)) + defer span.Finish() + traceID := span.Context().(SpanContext).TraceID() + assert.True(t, traceID.High != 0) + assert.True(t, traceID.Low != 0) +} + +func TestZipkinSharedRPCSpan(t *testing.T) { + tracer, tc := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(false)) + + sp1 := tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 := tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.parentID) + assert.NotEqual(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + sp2.Finish() + sp1.Finish() + tc.Close() + + tracer, tc = NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + + sp1 = tracer.StartSpan("client", ext.SpanKindRPCClient) + sp2 = tracer.StartSpan("server", opentracing.ChildOf(sp1.Context()), ext.SpanKindRPCServer) + assert.Equal(t, sp1.(*Span).context.spanID, sp2.(*Span).context.spanID) + assert.Equal(t, sp1.(*Span).context.parentID, sp2.(*Span).context.parentID) + sp2.Finish() + sp1.Finish() + tc.Close() +} + +type dummyPropagator struct{} +type dummyCarrier struct { + ok bool +} + +func (p *dummyPropagator) Inject(ctx SpanContext, carrier interface{}) error { + c, ok := carrier.(*dummyCarrier) + if !ok { + return opentracing.ErrInvalidCarrier + } + c.ok = true + return nil +} + +func (p *dummyPropagator) Extract(carrier interface{}) (SpanContext, error) { + c, ok := carrier.(*dummyCarrier) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if c.ok { + return emptyContext, nil + } + return emptyContext, opentracing.ErrSpanContextNotFound +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport.go b/vendor/src/github.com/uber/jaeger-client-go/transport.go new file mode 100644 index 00000000..c5f5b195 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport.go @@ -0,0 +1,38 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "io" +) + +// Transport abstracts the method of sending spans out of process. +// Implementations are NOT required to be thread-safe; the RemoteReporter +// is expected to only call methods on the Transport from the same go-routine. +type Transport interface { + // Append converts the span to the wire representation and adds it + // to sender's internal buffer. If the buffer exceeds its designated + // size, the transport should call Flush() and return the number of spans + // flushed, otherwise return 0. If error is returned, the returned number + // of spans is treated as failed span, and reported to metrics accordingly. + Append(span *Span) (int, error) + + // Flush submits the internal buffer to the remote server. It returns the + // number of spans flushed. If error is returned, the returned number of + // spans is treated as failed span, and reported to metrics accordingly. + Flush() (int, error) + + io.Closer +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/doc.go b/vendor/src/github.com/uber/jaeger-client-go/transport/doc.go new file mode 100644 index 00000000..6b961fb6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/doc.go @@ -0,0 +1,23 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package transport defines various transports that can be used with +// RemoteReporter to send spans out of process. Transport is responsible +// for serializing the spans into a specific format suitable for sending +// to the tracing backend. Examples may include Thrift over UDP, Thrift +// or JSON over HTTP, Thrift over Kafka, etc. +// +// Implementations are NOT required to be thread-safe; the RemoteReporter +// is expected to only call methods on the Transport from the same go-routine. +package transport diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/http.go b/vendor/src/github.com/uber/jaeger-client-go/transport/http.go new file mode 100644 index 00000000..7714ccca --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/http.go @@ -0,0 +1,155 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package transport + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "time" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +// Default timeout for http request in seconds +const defaultHTTPTimeout = time.Second * 5 + +// HTTPTransport implements Transport by forwarding spans to a http server. +type HTTPTransport struct { + url string + client *http.Client + batchSize int + spans []*j.Span + process *j.Process + httpCredentials *HTTPBasicAuthCredentials +} + +// HTTPBasicAuthCredentials stores credentials for HTTP basic auth. +type HTTPBasicAuthCredentials struct { + username string + password string +} + +// HTTPOption sets a parameter for the HttpCollector +type HTTPOption func(c *HTTPTransport) + +// HTTPTimeout sets maximum timeout for http request. +func HTTPTimeout(duration time.Duration) HTTPOption { + return func(c *HTTPTransport) { c.client.Timeout = duration } +} + +// HTTPBatchSize sets the maximum batch size, after which a collect will be +// triggered. The default batch size is 100 spans. +func HTTPBatchSize(n int) HTTPOption { + return func(c *HTTPTransport) { c.batchSize = n } +} + +// HTTPBasicAuth sets the credentials required to perform HTTP basic auth +func HTTPBasicAuth(username string, password string) HTTPOption { + return func(c *HTTPTransport) { + c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password} + } +} + +// NewHTTPTransport returns a new HTTP-backend transport. url should be an http +// url of the collector to handle POST request, typically something like: +// http://hostname:14268/api/traces?format=jaeger.thrift +func NewHTTPTransport(url string, options ...HTTPOption) *HTTPTransport { + c := &HTTPTransport{ + url: url, + client: &http.Client{Timeout: defaultHTTPTimeout}, + batchSize: 100, + spans: []*j.Span{}, + } + + for _, option := range options { + option(c) + } + return c +} + +// Append implements Transport. +func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) { + if c.process == nil { + c.process = jaeger.BuildJaegerProcessThrift(span) + } + jSpan := jaeger.BuildJaegerThrift(span) + c.spans = append(c.spans, jSpan) + if len(c.spans) >= c.batchSize { + return c.Flush() + } + return 0, nil +} + +// Flush implements Transport. +func (c *HTTPTransport) Flush() (int, error) { + count := len(c.spans) + if count == 0 { + return 0, nil + } + err := c.send(c.spans) + c.spans = c.spans[:0] + return count, err +} + +// Close implements Transport. +func (c *HTTPTransport) Close() error { + return nil +} + +func (c *HTTPTransport) send(spans []*j.Span) error { + batch := &j.Batch{ + Spans: spans, + Process: c.process, + } + body, err := serializeThrift(batch) + if err != nil { + return err + } + req, err := http.NewRequest("POST", c.url, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-thrift") + + if c.httpCredentials != nil { + req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password) + } + + resp, err := c.client.Do(req) + if err != nil { + return err + } + io.Copy(ioutil.Discard, resp.Body) + resp.Body.Close() + if resp.StatusCode >= http.StatusBadRequest { + return fmt.Errorf("error from collector: %d", resp.StatusCode) + } + return nil +} + +func serializeThrift(obj thrift.TStruct) (*bytes.Buffer, error) { + t := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(t) + if err := obj.Write(p); err != nil { + return nil, err + } + return t.Buffer, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/http_test.go b/vendor/src/github.com/uber/jaeger-client-go/transport/http_test.go new file mode 100644 index 00000000..cf15c181 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/http_test.go @@ -0,0 +1,156 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package transport + +import ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-client-go" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +func TestHTTPTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Bender" + httpPassword := "Rodriguez" + sender := NewHTTPTransport( + "http://localhost:10001/api/v1/spans", + HTTPBatchSize(1), + HTTPBasicAuth(httpUsername, httpPassword), + ) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + defer closer.Close() + + span := tracer.StartSpan("root") + span.Finish() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.getBatches()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotBatch := server.getBatches()[0] + assert.Len(t, gotBatch.Spans, 1) + assert.Equal(t, "test", gotBatch.Process.ServiceName) + gotSpan := gotBatch.Spans[0] + assert.Equal(t, "root", gotSpan.OperationName) + assert.EqualValues(t, srcSpanCtx.TraceID().High, gotSpan.TraceIdHigh) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceIdLow) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.SpanId) + assert.Equal(t, + &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, + server.authCredentials[0], + ) +} + +func TestHTTPOptions(t *testing.T) { + sender := NewHTTPTransport( + "some url", + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + ) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) +} + +type httpServer struct { + t *testing.T + batches []*j.Batch + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) getBatches() []*j.Batch { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.batches +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +// TODO this function and zipkin/http_test.go#newHTTPServer look like twins lost at birth +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + batches: make([]*j.Batch, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + batch := &j.Batch{} + if err = batch.Read(transport); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.batches = append(server.batches, batch) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10001", nil) + }() + + return server +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/doc.go b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/doc.go new file mode 100644 index 00000000..64abb539 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package zipkin provides various Transports that can be used +// with RemoteReporter for submitting traces to Zipkin backend. +package zipkin diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go new file mode 100644 index 00000000..3c928d31 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/example_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin_test + +import ( + "log" + + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + jlog "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/transport/zipkin" +) + +func ExampleNewHTTPTransport() { + // assume this is your main() + + transport, err := zipkin.NewHTTPTransport( + "http://localhost:9411/api/v1/spans", + zipkin.HTTPBatchSize(10), + zipkin.HTTPLogger(jlog.StdLogger), + ) + if err != nil { + log.Fatalf("Cannot initialize Zipkin HTTP transport: %v", err) + } + tracer, closer := jaeger.NewTracer( + "my-service-name", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(transport, nil), + ) + defer closer.Close() + opentracing.InitGlobalTracer(tracer) + + // initialize servers +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http.go b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http.go new file mode 100644 index 00000000..ba583dd8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http.go @@ -0,0 +1,166 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "bytes" + "net/http" + "time" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// Default timeout for http request in seconds +const defaultHTTPTimeout = time.Second * 5 + +// HTTPTransport implements Transport by forwarding spans to a http server. +type HTTPTransport struct { + logger jaeger.Logger + url string + client *http.Client + batchSize int + batch []*zipkincore.Span + httpCredentials *HTTPBasicAuthCredentials +} + +// HTTPBasicAuthCredentials stores credentials for HTTP basic auth. +type HTTPBasicAuthCredentials struct { + username string + password string +} + +// HTTPOption sets a parameter for the HttpCollector +type HTTPOption func(c *HTTPTransport) + +// HTTPLogger sets the logger used to report errors in the collection +// process. By default, a no-op logger is used, i.e. no errors are logged +// anywhere. It's important to set this option in a production service. +func HTTPLogger(logger jaeger.Logger) HTTPOption { + return func(c *HTTPTransport) { c.logger = logger } +} + +// HTTPTimeout sets maximum timeout for http request. +func HTTPTimeout(duration time.Duration) HTTPOption { + return func(c *HTTPTransport) { c.client.Timeout = duration } +} + +// HTTPBatchSize sets the maximum batch size, after which a collect will be +// triggered. The default batch size is 100 spans. +func HTTPBatchSize(n int) HTTPOption { + return func(c *HTTPTransport) { c.batchSize = n } +} + +// HTTPBasicAuth sets the credentials required to perform HTTP basic auth +func HTTPBasicAuth(username string, password string) HTTPOption { + return func(c *HTTPTransport) { + c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password} + } +} + +// NewHTTPTransport returns a new HTTP-backend transport. url should be an http +// url to handle post request, typically something like: +// http://hostname:9411/api/v1/spans +func NewHTTPTransport(url string, options ...HTTPOption) (*HTTPTransport, error) { + c := &HTTPTransport{ + logger: log.NullLogger, + url: url, + client: &http.Client{Timeout: defaultHTTPTimeout}, + batchSize: 100, + batch: []*zipkincore.Span{}, + } + + for _, option := range options { + option(c) + } + return c, nil +} + +// Append implements Transport. +func (c *HTTPTransport) Append(span *jaeger.Span) (int, error) { + zSpan := jaeger.BuildZipkinThrift(span) + c.batch = append(c.batch, zSpan) + if len(c.batch) >= c.batchSize { + return c.Flush() + } + return 0, nil +} + +// Flush implements Transport. +func (c *HTTPTransport) Flush() (int, error) { + count := len(c.batch) + if count == 0 { + return 0, nil + } + err := c.send(c.batch) + c.batch = c.batch[:0] + return count, err +} + +// Close implements Transport. +func (c *HTTPTransport) Close() error { + return nil +} + +func httpSerialize(spans []*zipkincore.Span) (*bytes.Buffer, error) { + t := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(t) + if err := p.WriteListBegin(thrift.STRUCT, len(spans)); err != nil { + return nil, err + } + for _, s := range spans { + if err := s.Write(p); err != nil { + return nil, err + } + } + if err := p.WriteListEnd(); err != nil { + return nil, err + } + return t.Buffer, nil +} + +func (c *HTTPTransport) send(spans []*zipkincore.Span) error { + body, err := httpSerialize(spans) + if err != nil { + return err + } + req, err := http.NewRequest("POST", c.url, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-thrift") + + if c.httpCredentials != nil { + req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password) + } + + _, err = c.client.Do(req) + + return err +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go new file mode 100644 index 00000000..425fbc23 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport/zipkin/http_test.go @@ -0,0 +1,178 @@ +// Copyright (c) 2017 The OpenTracing Authors +// Copyright (c) 2016 Bas van Beek +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zipkin + +// This code is adapted from 'collector-http_test.go' from +// https://github.com/openzipkin/zipkin-go-opentracing/ + +import ( + "io/ioutil" + "net/http" + "sync" + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/log" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +func TestHttpTransport(t *testing.T) { + server := newHTTPServer(t) + httpUsername := "Aphex" + httpPassword := "Twin" + sender, err := NewHTTPTransport("http://localhost:10000/api/v1/spans", HTTPBasicAuth(httpUsername, httpPassword)) + require.NoError(t, err) + + tracer, closer := jaeger.NewTracer( + "test", + jaeger.NewConstSampler(true), + jaeger.NewRemoteReporter(sender), + ) + + span := tracer.StartSpan("root") + span.Finish() + + closer.Close() + + // Need to yield to the select loop to accept the send request, and then + // yield again to the send operation to write to the socket. I think the + // best way to do that is just give it some time. + + deadline := time.Now().Add(2 * time.Second) + for { + if time.Now().After(deadline) { + t.Fatal("never received a span") + } + if want, have := 1, len(server.spans()); want != have { + time.Sleep(time.Millisecond) + continue + } + break + } + + srcSpanCtx := span.Context().(jaeger.SpanContext) + gotSpan := server.spans()[0] + assert.Equal(t, "root", gotSpan.Name) + assert.EqualValues(t, srcSpanCtx.TraceID().Low, gotSpan.TraceID) + assert.EqualValues(t, srcSpanCtx.SpanID(), gotSpan.ID) + assert.Equal(t, &HTTPBasicAuthCredentials{username: httpUsername, password: httpPassword}, server.authCredentials[0]) +} + +func TestHTTPOptions(t *testing.T) { + sender, err := NewHTTPTransport( + "some url", + HTTPLogger(log.StdLogger), + HTTPBatchSize(123), + HTTPTimeout(123*time.Millisecond), + HTTPBasicAuth("urundai", "kuzhambu"), + ) + require.NoError(t, err) + assert.Equal(t, log.StdLogger, sender.logger) + assert.Equal(t, 123, sender.batchSize) + assert.Equal(t, 123*time.Millisecond, sender.client.Timeout) + assert.Equal(t, "urundai", sender.httpCredentials.username) + assert.Equal(t, "kuzhambu", sender.httpCredentials.password) +} + +type httpServer struct { + t *testing.T + zipkinSpans []*zipkincore.Span + authCredentials []*HTTPBasicAuthCredentials + mutex sync.RWMutex +} + +func (s *httpServer) spans() []*zipkincore.Span { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.zipkinSpans +} + +func (s *httpServer) credentials() []*HTTPBasicAuthCredentials { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.authCredentials +} + +func newHTTPServer(t *testing.T) *httpServer { + server := &httpServer{ + t: t, + zipkinSpans: make([]*zipkincore.Span, 0), + authCredentials: make([]*HTTPBasicAuthCredentials, 0), + mutex: sync.RWMutex{}, + } + http.HandleFunc("/api/v1/spans", func(w http.ResponseWriter, r *http.Request) { + contextType := r.Header.Get("Content-Type") + if contextType != "application/x-thrift" { + t.Errorf( + "except Content-Type should be application/x-thrift, but is %s", + contextType) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Error(err) + return + } + buffer := thrift.NewTMemoryBuffer() + if _, err = buffer.Write(body); err != nil { + t.Error(err) + return + } + transport := thrift.NewTBinaryProtocolTransport(buffer) + _, size, err := transport.ReadListBegin() + if err != nil { + t.Error(err) + return + } + var spans []*zipkincore.Span + for i := 0; i < size; i++ { + zs := &zipkincore.Span{} + if err = zs.Read(transport); err != nil { + t.Error(err) + return + } + spans = append(spans, zs) + } + if err := transport.ReadListEnd(); err != nil { + t.Error(err) + return + } + server.mutex.Lock() + defer server.mutex.Unlock() + server.zipkinSpans = append(server.zipkinSpans, spans...) + u, p, _ := r.BasicAuth() + server.authCredentials = append(server.authCredentials, &HTTPBasicAuthCredentials{username: u, password: p}) + }) + + go func() { + http.ListenAndServe(":10000", nil) + }() + + return server +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport_udp.go b/vendor/src/github.com/uber/jaeger-client-go/transport_udp.go new file mode 100644 index 00000000..cb83cdf9 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport_udp.go @@ -0,0 +1,132 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + + "github.com/apache/thrift/lib/go/thrift" + + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/utils" +) + +// Empirically obtained constant for how many bytes in the message are used for envelope. +// The total datagram size is: +// sizeof(Span) * numSpans + processByteSize + emitBatchOverhead <= maxPacketSize +// There is a unit test `TestEmitBatchOverhead` that validates this number. +// Note that due to the use of Compact Thrift protocol, overhead grows with the number of spans +// in the batch, because the length of the list is encoded as varint32, as well as SeqId. +const emitBatchOverhead = 30 + +const defaultUDPSpanServerHostPort = "localhost:6831" + +var errSpanTooLarge = errors.New("Span is too large") + +type udpSender struct { + client *utils.AgentClientUDP + maxPacketSize int // max size of datagram in bytes + maxSpanBytes int // max number of bytes to record spans (excluding envelope) in the datagram + byteBufferSize int // current number of span bytes accumulated in the buffer + spanBuffer []*j.Span // spans buffered before a flush + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span + thriftProtocol thrift.TProtocol + process *j.Process + processByteSize int +} + +// NewUDPTransport creates a reporter that submits spans to jaeger-agent +func NewUDPTransport(hostPort string, maxPacketSize int) (Transport, error) { + if len(hostPort) == 0 { + hostPort = defaultUDPSpanServerHostPort + } + if maxPacketSize == 0 { + maxPacketSize = utils.UDPPacketMaxLength + } + + protocolFactory := thrift.NewTCompactProtocolFactory() + + // Each span is first written to thriftBuffer to determine its size in bytes. + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + thriftProtocol := protocolFactory.GetProtocol(thriftBuffer) + + client, err := utils.NewAgentClientUDP(hostPort, maxPacketSize) + if err != nil { + return nil, err + } + + sender := &udpSender{ + client: client, + maxSpanBytes: maxPacketSize - emitBatchOverhead, + thriftBuffer: thriftBuffer, + thriftProtocol: thriftProtocol} + return sender, nil +} + +func (s *udpSender) calcSizeOfSerializedThrift(thriftStruct thrift.TStruct) int { + s.thriftBuffer.Reset() + thriftStruct.Write(s.thriftProtocol) + return s.thriftBuffer.Len() +} + +func (s *udpSender) Append(span *Span) (int, error) { + if s.process == nil { + s.process = BuildJaegerProcessThrift(span) + s.processByteSize = s.calcSizeOfSerializedThrift(s.process) + s.byteBufferSize += s.processByteSize + } + jSpan := BuildJaegerThrift(span) + spanSize := s.calcSizeOfSerializedThrift(jSpan) + if spanSize > s.maxSpanBytes { + return 1, errSpanTooLarge + } + + s.byteBufferSize += spanSize + if s.byteBufferSize <= s.maxSpanBytes { + s.spanBuffer = append(s.spanBuffer, jSpan) + if s.byteBufferSize < s.maxSpanBytes { + return 0, nil + } + return s.Flush() + } + // the latest span did not fit in the buffer + n, err := s.Flush() + s.spanBuffer = append(s.spanBuffer, jSpan) + s.byteBufferSize = spanSize + s.processByteSize + return n, err +} + +func (s *udpSender) Flush() (int, error) { + n := len(s.spanBuffer) + if n == 0 { + return 0, nil + } + err := s.client.EmitBatch(&j.Batch{Process: s.process, Spans: s.spanBuffer}) + s.resetBuffers() + + return n, err +} + +func (s *udpSender) Close() error { + return s.client.Close() +} + +func (s *udpSender) resetBuffers() { + for i := range s.spanBuffer { + s.spanBuffer[i] = nil + } + s.spanBuffer = s.spanBuffer[:0] + s.byteBufferSize = s.processByteSize +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/transport_udp_test.go b/vendor/src/github.com/uber/jaeger-client-go/transport_udp_test.go new file mode 100644 index 00000000..99ef1c22 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/transport_udp_test.go @@ -0,0 +1,221 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + "time" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/testutils" + "github.com/uber/jaeger-client-go/thrift-gen/agent" + j "github.com/uber/jaeger-client-go/thrift-gen/jaeger" +) + +var ( + testTracer, _ = NewTracer("svcName", NewConstSampler(false), NewNullReporter()) + jaegerTracer = testTracer.(*Tracer) +) + +func getThriftSpanByteLength(t *testing.T, span *Span) int { + jSpan := BuildJaegerThrift(span) + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := jSpan.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func getThriftProcessByteLengthFromTracer(t *testing.T, tracer *Tracer) int { + process := buildJaegerProcessThrift(tracer) + return getThriftProcessByteLength(t, process) +} + +func getThriftProcessByteLength(t *testing.T, process *j.Process) int { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + err := process.Write(protocolFactory.GetProtocol(transport)) + require.NoError(t, err) + return transport.Len() +} + +func TestEmitBatchOverhead(t *testing.T) { + transport := thrift.NewTMemoryBufferLen(1000) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := agent.NewAgentClientFactory(transport, protocolFactory) + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + tests := []int{1, 2, 14, 15, 377, 500, 65000, 0xFFFF} + for i, n := range tests { + transport.Reset() + batch := make([]*j.Span, n) + processTags := make([]*j.Tag, n) + for x := 0; x < n; x++ { + batch[x] = BuildJaegerThrift(span) + processTags[x] = &j.Tag{} + } + process := &j.Process{ServiceName: "svcName", Tags: processTags} + client.SeqId = -2 // this causes the longest encoding of varint32 as 5 bytes + err := client.EmitBatch(&j.Batch{Process: process, Spans: batch}) + processSize := getThriftProcessByteLength(t, process) + require.NoError(t, err) + overhead := transport.Len() - n*spanSize - processSize + assert.True(t, overhead <= emitBatchOverhead, + "test %d, n=%d, expected overhead %d <= %d", i, n, overhead, emitBatchOverhead) + t.Logf("span count: %d, overhead: %d", n, overhead) + } +} + +func TestUDPSenderFlush(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), 5*spanSize+processSize+emitBatchOverhead) + require.NoError(t, err) + udpSender := sender.(*udpSender) + + // test empty flush + n, err := sender.Flush() + require.NoError(t, err) + assert.Equal(t, 0, n) + + // test early flush + n, err = sender.Append(span) + require.NoError(t, err) + assert.Equal(t, 0, n, "span should be in buffer, not flushed") + buffer := udpSender.spanBuffer + require.Equal(t, 1, len(buffer), "span should be in buffer, not flushed") + assert.Equal(t, BuildJaegerThrift(span), buffer[0], "span should be in buffer, not flushed") + + n, err = sender.Flush() + require.NoError(t, err) + assert.Equal(t, 1, n) + assert.Equal(t, 0, len(udpSender.spanBuffer), "buffer should become empty") + assert.Equal(t, processSize, udpSender.byteBufferSize, "buffer size counter should be equal to the processSize") + assert.Nil(t, buffer[0], "buffer should not keep reference to the span") + + for i := 0; i < 10000; i++ { + batches := agent.GetJaegerBatches() + if len(batches) > 0 { + break + } + time.Sleep(1 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, 1, len(batches), "agent should have received the batch") + require.Equal(t, 1, len(batches[0].Spans)) + assert.Equal(t, span.operationName, batches[0].Spans[0].OperationName) +} + +func TestUDPSenderAppend(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + processSize := getThriftProcessByteLengthFromTracer(t, jaegerTracer) + + tests := []struct { + bufferSizeOffset int + expectFlush bool + expectSpansFlushed int + expectBatchesFlushed int + manualFlush bool + expectSpansFlushed2 int + expectBatchesFlushed2 int + description string + }{ + {1, false, 0, 0, true, 5, 1, "in test: buffer bigger than 5 spans"}, + {0, true, 5, 1, false, 0, 0, "in test: buffer fits exactly 5 spans"}, + {-1, true, 4, 1, true, 1, 1, "in test: buffer smaller than 5 spans"}, + } + + for _, test := range tests { + bufferSize := 5*spanSize + test.bufferSizeOffset + processSize + emitBatchOverhead + sender, err := NewUDPTransport(agent.SpanServerAddr(), bufferSize) + require.NoError(t, err, test.description) + + agent.ResetJaegerBatches() + for i := 0; i < 5; i++ { + n, err := sender.Append(span) + require.NoError(t, err, test.description) + if i < 4 { + assert.Equal(t, 0, n, test.description) + } else { + assert.Equal(t, test.expectSpansFlushed, n, test.description) + } + } + if test.expectFlush { + time.Sleep(5 * time.Millisecond) + } + batches := agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed, len(batches), test.description) + var spans []*j.Span + if test.expectBatchesFlushed > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + + if test.manualFlush { + agent.ResetJaegerBatches() + n, err := sender.Flush() + require.NoError(t, err, test.description) + assert.Equal(t, test.expectSpansFlushed2, n, test.description) + + time.Sleep(5 * time.Millisecond) + batches = agent.GetJaegerBatches() + require.Equal(t, test.expectBatchesFlushed2, len(batches), test.description) + spans = []*j.Span{} + if test.expectBatchesFlushed2 > 0 { + spans = batches[0].Spans + } + require.Equal(t, test.expectSpansFlushed2, len(spans), test.description) + for i := 0; i < test.expectSpansFlushed2; i++ { + assert.Equal(t, span.operationName, spans[i].OperationName, test.description) + } + } + + } +} + +func TestUDPSenderHugeSpan(t *testing.T) { + agent, err := testutils.StartMockAgent() + require.NoError(t, err) + defer agent.Close() + + span := &Span{operationName: "test-span", tracer: jaegerTracer} + spanSize := getThriftSpanByteLength(t, span) + + sender, err := NewUDPTransport(agent.SpanServerAddr(), spanSize/2+emitBatchOverhead) + require.NoError(t, err) + + n, err := sender.Append(span) + assert.Equal(t, errSpanTooLarge, err) + assert.Equal(t, 1, n) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/travis/build-crossdock.sh b/vendor/src/github.com/uber/jaeger-client-go/travis/build-crossdock.sh new file mode 100644 index 00000000..8e98d081 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/travis/build-crossdock.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +make crossdock + +export REPO=jaegertracing/xdock-go +export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi) +export TAG=`if [ "$BRANCH" == "master" ]; then echo "latest"; else echo "${BRANCH///}"; fi` +echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, REPO=$REPO, PR=$PR, BRANCH=$BRANCH, TAG=$TAG" + +# Only push the docker container to Docker Hub for master branch +if [[ "$BRANCH" == "master" && "$TRAVIS_SECURE_ENV_VARS" == "true" ]]; then echo 'upload to Docker Hub'; else echo 'skip docker upload for PR'; exit 0; fi + +docker login -u $DOCKER_USER -p $DOCKER_PASS + +set -x + +docker build -f crossdock/Dockerfile -t $REPO:$COMMIT . + +docker tag $REPO:$COMMIT $REPO:$TAG +docker tag $REPO:$COMMIT $REPO:travis-$TRAVIS_BUILD_NUMBER +docker push $REPO diff --git a/vendor/src/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh b/vendor/src/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh new file mode 100644 index 00000000..f2e15fe7 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/travis/install-crossdock-deps.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +docker version + +# Install docker-compose +sudo rm -f /usr/local/bin/docker-compose +curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose +chmod +x docker-compose +sudo mv docker-compose /usr/local/bin +docker-compose version diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/http_json.go b/vendor/src/github.com/uber/jaeger-client-go/utils/http_json.go new file mode 100644 index 00000000..237211f8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/http_json.go @@ -0,0 +1,54 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// GetJSON makes an HTTP call to the specified URL and parses the returned JSON into `out`. +func GetJSON(url string, out interface{}) error { + resp, err := http.Get(url) + if err != nil { + return err + } + return ReadJSON(resp, out) +} + +// ReadJSON reads JSON from http.Response and parses it into `out` +func ReadJSON(resp *http.Response, out interface{}) error { + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + return fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body) + } + + if out == nil { + io.Copy(ioutil.Discard, resp.Body) + return nil + } + + decoder := json.NewDecoder(resp.Body) + return decoder.Decode(out) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/http_json_test.go b/vendor/src/github.com/uber/jaeger-client-go/utils/http_json_test.go new file mode 100644 index 00000000..6ee984a9 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/http_json_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type testJSONStruct struct { + Name string + Age int +} + +func TestGetJSON(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.Write([]byte("{\"name\": \"Bender\", \"age\": 3}")) + })) + defer server.Close() + + var s testJSONStruct + err := GetJSON(server.URL, &s) + require.NoError(t, err) + + assert.Equal(t, "Bender", s.Name) + assert.Equal(t, 3, s.Age) +} + +func TestGetJSONErrors(t *testing.T) { + var s testJSONStruct + err := GetJSON("localhost:0", &s) + assert.Error(t, err) + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "some error", http.StatusInternalServerError) + })) + defer server.Close() + + err = GetJSON(server.URL, &s) + assert.Error(t, err) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/localip.go b/vendor/src/github.com/uber/jaeger-client-go/utils/localip.go new file mode 100644 index 00000000..b51af771 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/localip.go @@ -0,0 +1,84 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "errors" + "net" +) + +// This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go + +// scoreAddr scores how likely the given addr is to be a remote address and returns the +// IP to use when listening. Any address which receives a negative score should not be used. +// Scores are calculated as: +// -1 for any unknown IP addresses. +// +300 for IPv4 addresses +// +100 for non-local addresses, extra +100 for "up" interaces. +func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) { + var ip net.IP + if netAddr, ok := addr.(*net.IPNet); ok { + ip = netAddr.IP + } else if netIP, ok := addr.(*net.IPAddr); ok { + ip = netIP.IP + } else { + return -1, nil + } + + var score int + if ip.To4() != nil { + score += 300 + } + if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() { + score += 100 + if iface.Flags&net.FlagUp != 0 { + score += 100 + } + } + return score, ip +} + +// HostIP tries to find an IP that can be used by other machines to reach this machine. +func HostIP() (net.IP, error) { + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + bestScore := -1 + var bestIP net.IP + // Select the highest scoring IP as the best IP. + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + // Skip this interface if there is an error. + continue + } + + for _, addr := range addrs { + score, ip := scoreAddr(iface, addr) + if score > bestScore { + bestScore = score + bestIP = ip + } + } + } + + if bestScore == -1 { + return nil, errors.New("no addresses to listen on") + } + + return bestIP, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/rand.go b/vendor/src/github.com/uber/jaeger-client-go/utils/rand.go new file mode 100644 index 00000000..9875f7f5 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/rand.go @@ -0,0 +1,46 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "math/rand" + "sync" +) + +// lockedSource allows a random number generator to be used by multiple goroutines concurrently. +// The code is very similar to math/rand.lockedSource, which is unfortunately not exposed. +type lockedSource struct { + mut sync.Mutex + src rand.Source +} + +// NewRand returns a rand.Rand that is threadsafe. +func NewRand(seed int64) *rand.Rand { + return rand.New(&lockedSource{src: rand.NewSource(seed)}) +} + +func (r *lockedSource) Int63() (n int64) { + r.mut.Lock() + n = r.src.Int63() + r.mut.Unlock() + return +} + +// Seed implements Seed() of Source +func (r *lockedSource) Seed(seed int64) { + r.mut.Lock() + r.src.Seed(seed) + r.mut.Unlock() +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter.go b/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter.go new file mode 100644 index 00000000..1b8db975 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "sync" + "time" +) + +// RateLimiter is a filter used to check if a message that is worth itemCost units is within the rate limits. +type RateLimiter interface { + CheckCredit(itemCost float64) bool +} + +type rateLimiter struct { + sync.Mutex + + creditsPerSecond float64 + balance float64 + maxBalance float64 + lastTick time.Time + + timeNow func() time.Time +} + +// NewRateLimiter creates a new rate limiter based on leaky bucket algorithm, formulated in terms of a +// credits balance that is replenished every time CheckCredit() method is called (tick) by the amount proportional +// to the time elapsed since the last tick, up to max of creditsPerSecond. A call to CheckCredit() takes a cost +// of an item we want to pay with the balance. If the balance exceeds the cost of the item, the item is "purchased" +// and the balance reduced, indicated by returned value of true. Otherwise the balance is unchanged and return false. +// +// This can be used to limit a rate of messages emitted by a service by instantiating the Rate Limiter with the +// max number of messages a service is allowed to emit per second, and calling CheckCredit(1.0) for each message +// to determine if the message is within the rate limit. +// +// It can also be used to limit the rate of traffic in bytes, by setting creditsPerSecond to desired throughput +// as bytes/second, and calling CheckCredit() with the actual message size. +func NewRateLimiter(creditsPerSecond, maxBalance float64) RateLimiter { + return &rateLimiter{ + creditsPerSecond: creditsPerSecond, + balance: maxBalance, + maxBalance: maxBalance, + lastTick: time.Now(), + timeNow: time.Now} +} + +func (b *rateLimiter) CheckCredit(itemCost float64) bool { + b.Lock() + defer b.Unlock() + // calculate how much time passed since the last tick, and update current tick + currentTime := b.timeNow() + elapsedTime := currentTime.Sub(b.lastTick) + b.lastTick = currentTime + // calculate how much credit have we accumulated since the last tick + b.balance += elapsedTime.Seconds() * b.creditsPerSecond + if b.balance > b.maxBalance { + b.balance = b.maxBalance + } + // if we have enough credits to pay for current item, then reduce balance and allow + if b.balance >= itemCost { + b.balance -= itemCost + return true + } + return false +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go b/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go new file mode 100644 index 00000000..a075afb4 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/rate_limiter_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRateLimiter(t *testing.T) { + limiter := NewRateLimiter(2.0, 2.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 250ms forward, not enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second / 4) + } + assert.False(t, limiter.CheckCredit(1.0)) + // move time 500ms forward, now enough credits to pay for 1.0 item + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second/4 + time.Second/2) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + // move time 5s forward, enough to accumulate credits for 10 messages, but it should still be capped at 2 + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(5 * time.Second) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} + +func TestMaxBalance(t *testing.T) { + limiter := NewRateLimiter(0.1, 1.0) + // stop time + ts := time.Now() + limiter.(*rateLimiter).lastTick = ts + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts + } + // on initialization, should have enough credits for 1 message + assert.True(t, limiter.CheckCredit(1.0)) + + // move time 20s forward, enough to accumulate credits for 2 messages, but it should still be capped at 1 + limiter.(*rateLimiter).timeNow = func() time.Time { + return ts.Add(time.Second * 20) + } + assert.True(t, limiter.CheckCredit(1.0)) + assert.False(t, limiter.CheckCredit(1.0)) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/udp_client.go b/vendor/src/github.com/uber/jaeger-client-go/utils/udp_client.go new file mode 100644 index 00000000..2374686e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/udp_client.go @@ -0,0 +1,98 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "errors" + "fmt" + "io" + "net" + + "github.com/apache/thrift/lib/go/thrift" + + "github.com/uber/jaeger-client-go/thrift-gen/agent" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" +) + +// UDPPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent +const UDPPacketMaxLength = 65000 + +// AgentClientUDP is a UDP client to Jaeger agent that implements agent.Agent interface. +type AgentClientUDP struct { + agent.Agent + io.Closer + + connUDP *net.UDPConn + client *agent.AgentClient + maxPacketSize int // max size of datagram in bytes + thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span +} + +// NewAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP. +func NewAgentClientUDP(hostPort string, maxPacketSize int) (*AgentClientUDP, error) { + if maxPacketSize == 0 { + maxPacketSize = UDPPacketMaxLength + } + + thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) + protocolFactory := thrift.NewTCompactProtocolFactory() + client := agent.NewAgentClientFactory(thriftBuffer, protocolFactory) + + destAddr, err := net.ResolveUDPAddr("udp", hostPort) + if err != nil { + return nil, err + } + + connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) + if err != nil { + return nil, err + } + if err := connUDP.SetWriteBuffer(maxPacketSize); err != nil { + return nil, err + } + + clientUDP := &AgentClientUDP{ + connUDP: connUDP, + client: client, + maxPacketSize: maxPacketSize, + thriftBuffer: thriftBuffer} + return clientUDP, nil +} + +// EmitZipkinBatch implements EmitZipkinBatch() of Agent interface +func (a *AgentClientUDP) EmitZipkinBatch(spans []*zipkincore.Span) error { + return errors.New("Not implemented") +} + +// EmitBatch implements EmitBatch() of Agent interface +func (a *AgentClientUDP) EmitBatch(batch *jaeger.Batch) error { + a.thriftBuffer.Reset() + a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages + if err := a.client.EmitBatch(batch); err != nil { + return err + } + if a.thriftBuffer.Len() > a.maxPacketSize { + return fmt.Errorf("Data does not fit within one UDP packet; size %d, max %d, spans %d", + a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans)) + } + _, err := a.connUDP.Write(a.thriftBuffer.Bytes()) + return err +} + +// Close implements Close() of io.Closer and closes the underlying UDP connection. +func (a *AgentClientUDP) Close() error { + return a.connUDP.Close() +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/utils.go b/vendor/src/github.com/uber/jaeger-client-go/utils/utils.go new file mode 100644 index 00000000..ac3c325d --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/utils.go @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "encoding/binary" + "errors" + "net" + "strconv" + "strings" + "time" +) + +var ( + // ErrEmptyIP an error for empty ip strings + ErrEmptyIP = errors.New("empty string given for ip") + + // ErrNotHostColonPort an error for invalid host port string + ErrNotHostColonPort = errors.New("expecting host:port") + + // ErrNotFourOctets an error for the wrong number of octets after splitting a string + ErrNotFourOctets = errors.New("Wrong number of octets") +) + +// ParseIPToUint32 converts a string ip (e.g. "x.y.z.w") to an uint32 +func ParseIPToUint32(ip string) (uint32, error) { + if ip == "" { + return 0, ErrEmptyIP + } + + if ip == "localhost" { + return 127<<24 | 1, nil + } + + octets := strings.Split(ip, ".") + if len(octets) != 4 { + return 0, ErrNotFourOctets + } + + var intIP uint32 + for i := 0; i < 4; i++ { + octet, err := strconv.Atoi(octets[i]) + if err != nil { + return 0, err + } + intIP = (intIP << 8) | uint32(octet) + } + + return intIP, nil +} + +// ParsePort converts port number from string to uin16 +func ParsePort(portString string) (uint16, error) { + port, err := strconv.ParseUint(portString, 10, 16) + return uint16(port), err +} + +// PackIPAsUint32 packs an IPv4 as uint32 +func PackIPAsUint32(ip net.IP) uint32 { + if ipv4 := ip.To4(); ipv4 != nil { + return binary.BigEndian.Uint32(ipv4) + } + return 0 +} + +// TimeToMicrosecondsSinceEpochInt64 converts Go time.Time to a long +// representing time since epoch in microseconds, which is used expected +// in the Jaeger spans encoded as Thrift. +func TimeToMicrosecondsSinceEpochInt64(t time.Time) int64 { + // ^^^ Passing time.Time by value is faster than passing a pointer! + // BenchmarkTimeByValue-8 2000000000 1.37 ns/op + // BenchmarkTimeByPtr-8 2000000000 1.98 ns/op + + return t.UnixNano() / 1000 +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/utils/utils_test.go b/vendor/src/github.com/uber/jaeger-client-go/utils/utils_test.go new file mode 100644 index 00000000..1df8e0b3 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/utils/utils_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package utils + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetLocalIP(t *testing.T) { + ip, _ := HostIP() + assert.NotNil(t, ip, "assert we have an ip") +} + +func TestParseIPToUint32(t *testing.T) { + tests := []struct { + in string + out uint32 + err error + }{ + {"1.2.3.4", 1<<24 | 2<<16 | 3<<8 | 4, nil}, + {"127.0.0.1", 127<<24 | 1, nil}, + {"localhost", 127<<24 | 1, nil}, + {"127.xxx.0.1", 0, nil}, + {"", 0, ErrEmptyIP}, + {"hostname", 0, ErrNotFourOctets}, + } + + for _, test := range tests { + intIP, err := ParseIPToUint32(test.in) + if test.err != nil { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.out, intIP) + } + + } +} + +func TestParsePort(t *testing.T) { + tests := []struct { + in string + out uint16 + err bool + }{ + {"123", 123, false}, + {"77777", 0, true}, // too large for 16bit + {"bad-wolf", 0, true}, + } + for _, test := range tests { + p, err := ParsePort(test.in) + if test.err { + assert.Error(t, err) + } else { + assert.Equal(t, test.out, p) + } + } +} + +func TestPackIPAsUint32(t *testing.T) { + ipv6a := net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 1, 2, 3, 4} + ipv6b := net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") + assert.NotNil(t, ipv6a) + + tests := []struct { + in net.IP + out uint32 + }{ + {net.IPv4(1, 2, 3, 4), 1<<24 | 2<<16 | 3<<8 | 4}, + {ipv6a, 1<<24 | 2<<16 | 3<<8 | 4}, // IPv6 but convertible to IPv4 + {ipv6b, 0}, + } + for _, test := range tests { + ip := PackIPAsUint32(test.in) + assert.Equal(t, test.out, ip) + } +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin.go new file mode 100644 index 00000000..636952b7 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin.go @@ -0,0 +1,76 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "github.com/opentracing/opentracing-go" +) + +// ZipkinSpanFormat is an OpenTracing carrier format constant +const ZipkinSpanFormat = "zipkin-span-format" + +// ExtractableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type ExtractableZipkinSpan interface { + TraceID() uint64 + SpanID() uint64 + ParentID() uint64 + Flags() byte +} + +// InjectableZipkinSpan is a type of Carrier used for integration with Zipkin-aware +// RPC frameworks (like TChannel). It does not support baggage, only trace IDs. +type InjectableZipkinSpan interface { + SetTraceID(traceID uint64) + SetSpanID(spanID uint64) + SetParentID(parentID uint64) + SetFlags(flags byte) +} + +type zipkinPropagator struct { + tracer *Tracer +} + +func (p *zipkinPropagator) Inject( + ctx SpanContext, + abstractCarrier interface{}, +) error { + carrier, ok := abstractCarrier.(InjectableZipkinSpan) + if !ok { + return opentracing.ErrInvalidCarrier + } + + carrier.SetTraceID(ctx.TraceID().Low) // TODO this cannot work with 128bit IDs + carrier.SetSpanID(uint64(ctx.SpanID())) + carrier.SetParentID(uint64(ctx.ParentID())) + carrier.SetFlags(ctx.flags) + return nil +} + +func (p *zipkinPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) { + carrier, ok := abstractCarrier.(ExtractableZipkinSpan) + if !ok { + return emptyContext, opentracing.ErrInvalidCarrier + } + if carrier.TraceID() == 0 { + return emptyContext, opentracing.ErrSpanContextNotFound + } + var ctx SpanContext + ctx.traceID.Low = carrier.TraceID() + ctx.spanID = SpanID(carrier.SpanID()) + ctx.parentID = SpanID(carrier.ParentID()) + ctx.flags = carrier.Flags() + return ctx, nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin/README.md b/vendor/src/github.com/uber/jaeger-client-go/zipkin/README.md new file mode 100644 index 00000000..c6a4c027 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin/README.md @@ -0,0 +1,34 @@ +# Zipkin compatibility features + +## `NewZipkinB3HTTPHeaderPropagator()` + +Adds support for injecting and extracting Zipkin B3 Propagation HTTP headers, +for use with other Zipkin collectors. + +```go + +// ... +import ( + "github.com/uber/jaeger-client-go/zipkin" +) + +func main() { + // ... + zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() + injector := jaeger.TracerOptions.Injector(opentracing.HTTPHeaders, zipkinPropagator) + extractor := jaeger.TracerOptions.Extractor(opentracing.HTTPHeaders, zipkinPropagator) + + // Zipkin shares span ID between client and server spans; it must be enabled via the following option. + zipkinSharedRPCSpan := jaeger.TracerOptions.ZipkinSharedRPCSpan(true) + + // create Jaeger tracer + tracer, closer := jaeger.NewTracer( + "myService", + mySampler, // as usual + myReporter // as usual + injector, + extractor, + zipkinSharedRPCSpan, + ) +} +``` diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin/doc.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin/doc.go new file mode 100644 index 00000000..11357f8a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +// Package zipkin comprises Zipkin functionality for Zipkin compatiblity. +package zipkin diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation.go new file mode 100644 index 00000000..000c52f7 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation.go @@ -0,0 +1,95 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin + +import ( + "strconv" + "strings" + + opentracing "github.com/opentracing/opentracing-go" + + "github.com/uber/jaeger-client-go" +) + +// Propagator is an Injector and Extractor +type Propagator struct{} + +// NewZipkinB3HTTPHeaderPropagator creates a Propagator for extracting and injecting +// Zipkin HTTP B3 headers into SpanContexts. +func NewZipkinB3HTTPHeaderPropagator() Propagator { + return Propagator{} +} + +// Inject conforms to the Injector interface for decoding Zipkin HTTP B3 headers +func (p Propagator) Inject( + sc jaeger.SpanContext, + abstractCarrier interface{}, +) error { + textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter) + if !ok { + return opentracing.ErrInvalidCarrier + } + + // TODO this needs to change to support 128bit IDs + textMapWriter.Set("x-b3-traceid", strconv.FormatUint(sc.TraceID().Low, 16)) + if sc.ParentID() != 0 { + textMapWriter.Set("x-b3-parentspanid", strconv.FormatUint(uint64(sc.ParentID()), 16)) + } + textMapWriter.Set("x-b3-spanid", strconv.FormatUint(uint64(sc.SpanID()), 16)) + if sc.IsSampled() { + textMapWriter.Set("x-b3-sampled", "1") + } else { + textMapWriter.Set("x-b3-sampled", "0") + } + return nil +} + +// Extract conforms to the Extractor interface for encoding Zipkin HTTP B3 headers +func (p Propagator) Extract(abstractCarrier interface{}) (jaeger.SpanContext, error) { + textMapReader, ok := abstractCarrier.(opentracing.TextMapReader) + if !ok { + return jaeger.SpanContext{}, opentracing.ErrInvalidCarrier + } + var traceID uint64 + var spanID uint64 + var parentID uint64 + sampled := false + err := textMapReader.ForeachKey(func(rawKey, value string) error { + key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap + var err error + if key == "x-b3-traceid" { + traceID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-parentspanid" { + parentID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-spanid" { + spanID, err = strconv.ParseUint(value, 16, 64) + } else if key == "x-b3-sampled" && value == "1" { + sampled = true + } + return err + }) + + if err != nil { + return jaeger.SpanContext{}, err + } + if traceID == 0 { + return jaeger.SpanContext{}, opentracing.ErrSpanContextNotFound + } + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, nil), nil +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation_test.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation_test.go new file mode 100644 index 00000000..8ef4f3cf --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin/propagation_test.go @@ -0,0 +1,113 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package zipkin + +import ( + "testing" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-client-go" +) + +var ( + rootSampled = newSpanContext(1, 2, 0, true) + nonRootSampled = newSpanContext(1, 2, 1, true) + nonRootNonSampled = newSpanContext(1, 2, 1, false) +) + +var ( + rootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-sampled": "1", + } + nonRootSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "1", + } + nonRootNonSampledHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "1", + "x-b3-spanid": "2", + "x-b3-parentspanid": "1", + "x-b3-sampled": "0", + } + invalidHeader = opentracing.TextMapCarrier{ + "x-b3-traceid": "jdkafhsd", + "x-b3-spanid": "afsdfsdf", + "x-b3-parentspanid": "hiagggdf", + "x-b3-sampled": "sdfgsdfg", + } +) + +var ( + propagator = NewZipkinB3HTTPHeaderPropagator() +) + +func newSpanContext(traceID, spanID, parentID uint64, sampled bool) jaeger.SpanContext { + return jaeger.NewSpanContext( + jaeger.TraceID{Low: traceID}, + jaeger.SpanID(spanID), + jaeger.SpanID(parentID), + sampled, + nil, + ) +} + +func TestExtractorInvalid(t *testing.T) { + _, err := propagator.Extract(invalidHeader) + assert.Error(t, err) +} + +func TestExtractorRootSampled(t *testing.T) { + ctx, err := propagator.Extract(rootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, rootSampled, ctx) +} + +func TestExtractorNonRootSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampled, ctx) +} + +func TestExtractorNonRootNonSampled(t *testing.T) { + ctx, err := propagator.Extract(nonRootNonSampledHeader) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampled, ctx) +} + +func TestInjectorRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(rootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, rootSampledHeader, hdr) +} + +func TestInjectorNonRootSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootSampledHeader, hdr) +} + +func TestInjectorNonRootNonSampled(t *testing.T) { + hdr := opentracing.TextMapCarrier{} + err := propagator.Inject(nonRootNonSampled, hdr) + assert.Nil(t, err) + assert.EqualValues(t, nonRootNonSampledHeader, hdr) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin_test.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin_test.go new file mode 100644 index 00000000..2d1d464e --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin_test.go @@ -0,0 +1,68 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "testing" + + "github.com/opentracing/opentracing-go/ext" + "github.com/stretchr/testify/assert" +) + +func TestZipkinPropagator(t *testing.T) { + tracer, tCloser := NewTracer("x", NewConstSampler(true), NewNullReporter(), TracerOptions.ZipkinSharedRPCSpan(true)) + defer tCloser.Close() + + carrier := &TestZipkinSpan{} + sp := tracer.StartSpan("y") + + // Note: we intentionally use string as format, as that's what TChannel would need to do + if err := tracer.Inject(sp.Context(), "zipkin-span-format", carrier); err != nil { + t.Fatalf("Inject failed: %+v", err) + } + sp1 := sp.(*Span) + assert.Equal(t, sp1.context.traceID, TraceID{Low: carrier.traceID}) + assert.Equal(t, sp1.context.spanID, SpanID(carrier.spanID)) + assert.Equal(t, sp1.context.parentID, SpanID(carrier.parentID)) + assert.Equal(t, sp1.context.flags, carrier.flags) + + sp2ctx, err := tracer.Extract("zipkin-span-format", carrier) + if err != nil { + t.Fatalf("Extract failed: %+v", err) + } + sp2 := tracer.StartSpan("x", ext.RPCServerOption(sp2ctx)) + sp3 := sp2.(*Span) + assert.Equal(t, sp1.context.traceID, sp3.context.traceID) + assert.Equal(t, sp1.context.spanID, sp3.context.spanID) + assert.Equal(t, sp1.context.parentID, sp3.context.parentID) + assert.Equal(t, sp1.context.flags, sp3.context.flags) +} + +// TestZipkinSpan is a mock-up of TChannel's internal Span struct +type TestZipkinSpan struct { + traceID uint64 + parentID uint64 + spanID uint64 + flags byte +} + +func (s TestZipkinSpan) TraceID() uint64 { return s.traceID } +func (s TestZipkinSpan) ParentID() uint64 { return s.parentID } +func (s TestZipkinSpan) SpanID() uint64 { return s.spanID } +func (s TestZipkinSpan) Flags() byte { return s.flags } +func (s *TestZipkinSpan) SetTraceID(traceID uint64) { s.traceID = traceID } +func (s *TestZipkinSpan) SetSpanID(spanID uint64) { s.spanID = spanID } +func (s *TestZipkinSpan) SetParentID(parentID uint64) { s.parentID = parentID } +func (s *TestZipkinSpan) SetFlags(flags byte) { s.flags = flags } diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span.go new file mode 100644 index 00000000..b2e9a3e6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span.go @@ -0,0 +1,325 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/opentracing/opentracing-go/ext" + + "github.com/uber/jaeger-client-go/internal/spanlog" + z "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +const ( + // maxAnnotationLength is the max length of byte array or string allowed in the annotations + maxAnnotationLength = 256 + + // Zipkin UI does not work well with non-string tag values + allowPackedNumbers = false +) + +var specialTagHandlers = map[string]func(*zipkinSpan, interface{}){ + string(ext.SpanKind): setSpanKind, + string(ext.PeerHostIPv4): setPeerIPv4, + string(ext.PeerPort): setPeerPort, + string(ext.PeerService): setPeerService, + TracerIPTagKey: removeTag, +} + +// BuildZipkinThrift builds thrift span based on internal span. +func BuildZipkinThrift(s *Span) *z.Span { + span := &zipkinSpan{Span: s} + span.handleSpecialTags() + parentID := int64(span.context.parentID) + var ptrParentID *int64 + if parentID != 0 { + ptrParentID = &parentID + } + timestamp := utils.TimeToMicrosecondsSinceEpochInt64(span.startTime) + duration := span.duration.Nanoseconds() / int64(time.Microsecond) + endpoint := &z.Endpoint{ + ServiceName: span.tracer.serviceName, + Ipv4: int32(span.tracer.hostIPv4)} + thriftSpan := &z.Span{ + TraceID: int64(span.context.traceID.Low), // TODO upgrade zipkin thrift and use TraceIdHigh + ID: int64(span.context.spanID), + ParentID: ptrParentID, + Name: span.operationName, + Timestamp: ×tamp, + Duration: &duration, + Debug: span.context.IsDebug(), + Annotations: buildAnnotations(span, endpoint), + BinaryAnnotations: buildBinaryAnnotations(span, endpoint)} + return thriftSpan +} + +func buildAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.Annotation { + // automatically adding 2 Zipkin CoreAnnotations + annotations := make([]*z.Annotation, 0, 2+len(span.logs)) + var startLabel, endLabel string + if span.spanKind == string(ext.SpanKindRPCClientEnum) { + startLabel, endLabel = z.CLIENT_SEND, z.CLIENT_RECV + } else if span.spanKind == string(ext.SpanKindRPCServerEnum) { + startLabel, endLabel = z.SERVER_RECV, z.SERVER_SEND + } + if !span.startTime.IsZero() && startLabel != "" { + start := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(span.startTime), + Value: startLabel, + Host: endpoint} + annotations = append(annotations, start) + if span.duration != 0 { + endTs := span.startTime.Add(span.duration) + end := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(endTs), + Value: endLabel, + Host: endpoint} + annotations = append(annotations, end) + } + } + for _, log := range span.logs { + anno := &z.Annotation{ + Timestamp: utils.TimeToMicrosecondsSinceEpochInt64(log.Timestamp), + Host: endpoint} + if content, err := spanlog.MaterializeWithJSON(log.Fields); err == nil { + anno.Value = truncateString(string(content)) + } else { + anno.Value = err.Error() + } + annotations = append(annotations, anno) + } + return annotations +} + +func buildBinaryAnnotations(span *zipkinSpan, endpoint *z.Endpoint) []*z.BinaryAnnotation { + // automatically adding local component or server/client address tag, and client version + annotations := make([]*z.BinaryAnnotation, 0, 2+len(span.tags)) + + if span.peerDefined() && span.isRPC() { + peer := z.Endpoint{ + Ipv4: span.peer.Ipv4, + Port: span.peer.Port, + ServiceName: span.peer.ServiceName} + label := z.CLIENT_ADDR + if span.isRPCClient() { + label = z.SERVER_ADDR + } + anno := &z.BinaryAnnotation{ + Key: label, + Value: []byte{1}, + AnnotationType: z.AnnotationType_BOOL, + Host: &peer} + annotations = append(annotations, anno) + } + if !span.isRPC() { + componentName := endpoint.ServiceName + for _, tag := range span.tags { + if tag.key == string(ext.Component) { + componentName = stringify(tag.value) + break + } + } + local := &z.BinaryAnnotation{ + Key: z.LOCAL_COMPONENT, + Value: []byte(componentName), + AnnotationType: z.AnnotationType_STRING, + Host: endpoint} + annotations = append(annotations, local) + } + for _, tag := range span.tags { + // "Special tags" are already handled by this point, we'd be double reporting the + // tags if we don't skip here + if _, ok := specialTagHandlers[tag.key]; ok { + continue + } + if anno := buildBinaryAnnotation(tag.key, tag.value, nil); anno != nil { + annotations = append(annotations, anno) + } + } + return annotations +} + +func buildBinaryAnnotation(key string, val interface{}, endpoint *z.Endpoint) *z.BinaryAnnotation { + bann := &z.BinaryAnnotation{Key: key, Host: endpoint} + if value, ok := val.(string); ok { + bann.Value = []byte(truncateString(value)) + bann.AnnotationType = z.AnnotationType_STRING + } else if value, ok := val.([]byte); ok { + if len(value) > maxAnnotationLength { + value = value[:maxAnnotationLength] + } + bann.Value = value + bann.AnnotationType = z.AnnotationType_BYTES + } else if value, ok := val.(int32); ok && allowPackedNumbers { + bann.Value = int32ToBytes(value) + bann.AnnotationType = z.AnnotationType_I32 + } else if value, ok := val.(int64); ok && allowPackedNumbers { + bann.Value = int64ToBytes(value) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(int); ok && allowPackedNumbers { + bann.Value = int64ToBytes(int64(value)) + bann.AnnotationType = z.AnnotationType_I64 + } else if value, ok := val.(bool); ok { + bann.Value = []byte{boolToByte(value)} + bann.AnnotationType = z.AnnotationType_BOOL + } else { + value := stringify(val) + bann.Value = []byte(truncateString(value)) + bann.AnnotationType = z.AnnotationType_STRING + } + return bann +} + +func stringify(value interface{}) string { + if s, ok := value.(string); ok { + return s + } + return fmt.Sprintf("%+v", value) +} + +func truncateString(value string) string { + // we ignore the problem of utf8 runes possibly being sliced in the middle, + // as it is rather expensive to iterate through each tag just to find rune + // boundaries. + if len(value) > maxAnnotationLength { + return value[:maxAnnotationLength] + } + return value +} + +func boolToByte(b bool) byte { + if b { + return 1 + } + return 0 +} + +// int32ToBytes converts int32 to bytes. +func int32ToBytes(i int32) []byte { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(i)) + return buf +} + +// int64ToBytes converts int64 to bytes. +func int64ToBytes(i int64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +type zipkinSpan struct { + *Span + + // peer points to the peer service participating in this span, + // e.g. the Client if this span is a server span, + // or Server if this span is a client span + peer struct { + Ipv4 int32 + Port int16 + ServiceName string + } + + // used to distinguish local vs. RPC Server vs. RPC Client spans + spanKind string +} + +func (s *zipkinSpan) handleSpecialTags() { + s.Lock() + defer s.Unlock() + if s.firstInProcess { + // append the process tags + s.tags = append(s.tags, s.tracer.tags...) + } + filteredTags := make([]Tag, 0, len(s.tags)) + for _, tag := range s.tags { + if handler, ok := specialTagHandlers[tag.key]; ok { + handler(s, tag.value) + } else { + filteredTags = append(filteredTags, tag) + } + } + s.tags = filteredTags +} + +func setSpanKind(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.spanKind = val + return + } + if val, ok := value.(ext.SpanKindEnum); ok { + s.spanKind = string(val) + } +} + +func setPeerIPv4(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if ip, err := utils.ParseIPToUint32(val); err == nil { + s.peer.Ipv4 = int32(ip) + return + } + } + if val, ok := value.(uint32); ok { + s.peer.Ipv4 = int32(val) + return + } + if val, ok := value.(int32); ok { + s.peer.Ipv4 = val + } +} + +func setPeerPort(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + if port, err := utils.ParsePort(val); err == nil { + s.peer.Port = int16(port) + return + } + } + if val, ok := value.(uint16); ok { + s.peer.Port = int16(val) + return + } + if val, ok := value.(int); ok { + s.peer.Port = int16(val) + } +} + +func setPeerService(s *zipkinSpan, value interface{}) { + if val, ok := value.(string); ok { + s.peer.ServiceName = val + } +} + +func removeTag(s *zipkinSpan, value interface{}) {} + +func (s *zipkinSpan) peerDefined() bool { + return s.peer.ServiceName != "" || s.peer.Ipv4 != 0 || s.peer.Port != 0 +} + +func (s *zipkinSpan) isRPC() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) || s.spanKind == string(ext.SpanKindRPCServerEnum) +} + +func (s *zipkinSpan) isRPCClient() bool { + s.RLock() + defer s.RUnlock() + return s.spanKind == string(ext.SpanKindRPCClientEnum) +} diff --git a/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go b/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go new file mode 100644 index 00000000..d14b4bee --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-client-go/zipkin_thrift_span_test.go @@ -0,0 +1,329 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package jaeger + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + "github.com/uber/jaeger-client-go/utils" +) + +func TestThriftFirstInProcessSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp1 := tracer.StartSpan("s1").(*Span) + sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) + sp2.Finish() + sp1.Finish() + + tests := []struct { + span *Span + wantTags bool + }{ + {sp1, true}, + {sp2, false}, + } + + for _, test := range tests { + var check func(assert.TestingT, interface{}, ...interface{}) bool + if test.wantTags { + check = assert.NotNil + } else { + check = assert.Nil + } + thriftSpan := BuildZipkinThrift(test.span) + version := findBinaryAnnotation(thriftSpan, JaegerClientVersionTagKey) + hostname := findBinaryAnnotation(thriftSpan, TracerHostnameTagKey) + check(t, version) + check(t, hostname) + } +} + +func TestThriftForceSampled(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(false), // sample nothing + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SamplingPriority.Set(sp, 1) + assert.True(t, sp.context.IsSampled()) + assert.True(t, sp.context.IsDebug()) + thriftSpan := BuildZipkinThrift(sp) + assert.True(t, thriftSpan.Debug) +} + +func TestThriftSpanLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + root := tracer.StartSpan("s1") + + someTime := time.Now().Add(-time.Minute) + someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) + + fields := func(fields ...log.Field) []log.Field { + return fields + } + tests := []struct { + fields []log.Field + logFunc func(sp opentracing.Span) + expected string + expectedTimestamp int64 + disableSampling bool + }{ + {fields: fields(log.String("event", "happened")), expected: "happened"}, + {fields: fields(log.String("something", "happened")), expected: `{"something":"happened"}`}, + {fields: fields(log.Bool("something", true)), expected: `{"something":"true"}`}, + {fields: fields(log.Int("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Int64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint32("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Uint64("something", 123)), expected: `{"something":"123"}`}, + {fields: fields(log.Float32("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Float64("something", 123)), expected: `{"something":"123.000000"}`}, + {fields: fields(log.Error(errors.New("drugs are baaad, m-k"))), + expected: `{"error":"drugs are baaad, m-k"}`}, + {fields: fields(log.Object("something", 123)), expected: `{"something":"123"}`}, + { + fields: fields(log.Lazy(func(fv log.Encoder) { + fv.EmitBool("something", true) + })), + expected: `{"something":"true"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogKV("non-even number of arguments") + }, + // this is a bit fragile, but ¯\_(ツ)_/¯ + expected: `{"error":"non-even keyValues len: 1","function":"LogKV"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEvent("something") + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.LogEventWithPayload("something", "payload") + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something"}) + }, + expected: "something", + }, + { + logFunc: func(sp opentracing.Span) { + sp.Log(opentracing.LogData{Event: "something", Payload: "payload"}) + }, + expected: `{"event":"something","payload":"payload"}`, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + LogRecords: []opentracing.LogRecord{ + { + Timestamp: someTime, + Fields: fields(log.String("event", "happened")), + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + }, + }, + }) + }, + expected: "happened", + expectedTimestamp: someTimeInt64, + }, + { + logFunc: func(sp opentracing.Span) { + sp.FinishWithOptions(opentracing.FinishOptions{ + BulkLogData: []opentracing.LogData{ + { + Timestamp: someTime, + Event: "happened", + Payload: "payload", + }, + }, + }) + }, + expected: `{"event":"happened","payload":"payload"}`, + expectedTimestamp: someTimeInt64, + }, + { + disableSampling: true, + fields: fields(log.String("event", "happened")), + expected: "", + }, + { + disableSampling: true, + logFunc: func(sp opentracing.Span) { + sp.LogKV("event", "something") + }, + expected: "", + }, + } + + for i, test := range tests { + testName := fmt.Sprintf("test-%02d", i) + sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) + if test.disableSampling { + ext.SamplingPriority.Set(sp, 0) + } + if test.logFunc != nil { + test.logFunc(sp) + } else if len(test.fields) > 0 { + sp.LogFields(test.fields...) + } + thriftSpan := BuildZipkinThrift(sp.(*Span)) + if test.disableSampling { + assert.Equal(t, 0, len(thriftSpan.Annotations), testName) + continue + } + assert.Equal(t, 1, len(thriftSpan.Annotations), testName) + assert.Equal(t, test.expected, thriftSpan.Annotations[0].Value, testName) + if test.expectedTimestamp != 0 { + assert.Equal(t, test.expectedTimestamp, thriftSpan.Annotations[0].Timestamp, testName) + } + } +} + +func TestThriftLocalComponentSpan(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + tests := []struct { + addComponentTag bool + wantAnnotation string + }{ + {false, "DOOP"}, // Without COMPONENT tag the value is the service name + {true, "c1"}, + } + + for _, test := range tests { + sp := tracer.StartSpan("s1").(*Span) + if test.addComponentTag { + ext.Component.Set(sp, "c1") + } + sp.Finish() + thriftSpan := BuildZipkinThrift(sp) + + anno := findBinaryAnnotation(thriftSpan, "lc") + require.NotNil(t, anno) + assert.EqualValues(t, test.wantAnnotation, anno.Value) + } +} + +func TestSpecialTags(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + ext.SpanKindRPCServer.Set(sp) + ext.PeerService.Set(sp, "peer") + ext.PeerPort.Set(sp, 80) + ext.PeerHostIPv4.Set(sp, 2130706433) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + // Special tags should not be copied over to binary annotations + assert.Nil(t, findBinaryAnnotation(thriftSpan, "span.kind")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.service")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.port")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.ipv4")) + assert.Nil(t, findBinaryAnnotation(thriftSpan, "ip")) + + anno := findBinaryAnnotation(thriftSpan, "ca") + assert.NotNil(t, anno) + assert.NotNil(t, anno.Host) + assert.EqualValues(t, 80, anno.Host.Port) + assert.EqualValues(t, 2130706433, anno.Host.Ipv4) + assert.EqualValues(t, "peer", anno.Host.ServiceName) + + assert.NotNil(t, findAnnotation(thriftSpan, "sr")) + assert.NotNil(t, findAnnotation(thriftSpan, "ss")) +} + +func TestBaggageLogs(t *testing.T) { + tracer, closer := NewTracer("DOOP", + NewConstSampler(true), + NewNullReporter()) + defer closer.Close() + + sp := tracer.StartSpan("s1").(*Span) + sp.SetBaggageItem("auth.token", "token") + ext.SpanKindRPCServer.Set(sp) + sp.Finish() + + thriftSpan := BuildZipkinThrift(sp) + assert.NotNil(t, findAnnotation(thriftSpan, `{"event":"baggage","key":"auth.token","value":"token"}`)) +} + +func findAnnotation(span *zipkincore.Span, name string) *zipkincore.Annotation { + for _, a := range span.Annotations { + if a.Value == name { + return a + } + } + return nil +} + +func findBinaryAnnotation(span *zipkincore.Span, name string) *zipkincore.BinaryAnnotation { + for _, a := range span.BinaryAnnotations { + if a.Key == name { + return a + } + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/counter.go b/vendor/src/github.com/uber/jaeger-lib/metrics/counter.go new file mode 100644 index 00000000..2a6a43ef --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/counter.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Counter tracks the number of times an event has occurred +type Counter interface { + // Inc adds the given value to the counter. + Inc(int64) +} + +// NullCounter counter that does nothing +var NullCounter Counter = nullCounter{} + +type nullCounter struct{} + +func (nullCounter) Inc(int64) {} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/factory.go new file mode 100644 index 00000000..a744a890 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/factory.go @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Factory creates new metrics +type Factory interface { + Counter(name string, tags map[string]string) Counter + Timer(name string, tags map[string]string) Timer + Gauge(name string, tags map[string]string) Gauge + + // Namespace returns a nested metrics factory. + Namespace(name string, tags map[string]string) Factory +} + +// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge. +var NullFactory Factory = nullFactory{} + +type nullFactory struct{} + +func (nullFactory) Counter(name string, tags map[string]string) Counter { return NullCounter } +func (nullFactory) Timer(name string, tags map[string]string) Timer { return NullTimer } +func (nullFactory) Gauge(name string, tags map[string]string) Gauge { return NullGauge } +func (nullFactory) Namespace(name string, tags map[string]string) Factory { return NullFactory } diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/gauge.go b/vendor/src/github.com/uber/jaeger-lib/metrics/gauge.go new file mode 100644 index 00000000..3c606391 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/gauge.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +// Gauge returns instantaneous measurements of something as an int64 value +type Gauge interface { + // Update the gauge to the value passed in. + Update(int64) +} + +// NullGauge gauge that does nothing +var NullGauge Gauge = nullGauge{} + +type nullGauge struct{} + +func (nullGauge) Update(int64) {} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go new file mode 100644 index 00000000..186c8777 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package expvar + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/expvar" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit expvar package. +// buckets is the number of buckets to be used in histograms. +func NewFactory(buckets int) xkit.Factory { + return factory{ + buckets: buckets, + } +} + +type factory struct { + buckets int +} + +func (f factory) Counter(name string) metrics.Counter { + return expvar.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return expvar.NewHistogram(name, f.buckets) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return expvar.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: false} +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go new file mode 100644 index 00000000..30a0d594 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/expvar/factory_test.go @@ -0,0 +1,43 @@ +package expvar + +import ( + "expvar" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCounter(t *testing.T) { + f := NewFactory(10) + assert.False(t, f.Capabilities().Tagging) + c := f.Counter("gokit_expvar_counter") + c.Add(42) + kv := findExpvar("gokit_expvar_counter") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestGauge(t *testing.T) { + f := NewFactory(10) + g := f.Gauge("gokit_expvar_gauge") + g.Set(42) + kv := findExpvar("gokit_expvar_gauge") + assert.Equal(t, "42", kv.Value.String()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory(10) + hist := f.Histogram("gokit_expvar_hist") + hist.Observe(10.5) + kv := findExpvar("gokit_expvar_hist.p50") + assert.Equal(t, "10.5", kv.Value.String()) +} + +func findExpvar(key string) *expvar.KeyValue { + var kv *expvar.KeyValue + expvar.Do(func(v expvar.KeyValue) { + if v.Key == key { + kv = &v + } + }) + return kv +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory.go new file mode 100644 index 00000000..d19c23c6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory.go @@ -0,0 +1,161 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package xkit + +import ( + kit "github.com/go-kit/kit/metrics" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory provides a unified interface for creating named metrics +// from various go-kit metrics implementations. +type Factory interface { + Counter(name string) kit.Counter + Gauge(name string) kit.Gauge + Histogram(name string) kit.Histogram + Capabilities() Capabilities +} + +// Capabilities describes capabilities of a specific metrics factory. +type Capabilities struct { + // Tagging indicates whether the factory has the capability for tagged metrics + Tagging bool +} + +// FactoryOption is a function that adjusts some parameters of the factory. +type FactoryOption func(*factory) + +// Wrap is used to create an adapter from xkit.Factory to metrics.Factory. +func Wrap(namespace string, f Factory, options ...FactoryOption) metrics.Factory { + factory := &factory{ + scope: namespace, + factory: f, + scopeSep: ".", + tagsSep: ".", + tagKVSep: "_", + } + for i := range options { + options[i](factory) + } + return factory +} + +// ScopeSeparator returns an option that overrides default scope separator. +func ScopeSeparator(scopeSep string) FactoryOption { + return func(f *factory) { + f.scopeSep = scopeSep + } +} + +// TagsSeparator returns an option that overrides default tags separator. +func TagsSeparator(tagsSep string) FactoryOption { + return func(f *factory) { + f.tagsSep = tagsSep + } +} + +type factory struct { + scope string + tags map[string]string + factory Factory + scopeSep string + tagsSep string + tagKVSep string +} + +func (f *factory) subScope(name string) string { + if f.scope == "" { + return name + } + if name == "" { + return f.scope + } + return f.scope + f.scopeSep + name +} + +// nameAndTagsList returns a name and tags list for the new metrics. +// The name is a concatenation of nom and the current factory scope. +// The tags list is a flattened list of passed tags merged with factory tags. +// If the underlying factory does not support tags, then the tags are +// transformed into a string and appended to the name. +func (f *factory) nameAndTagsList(nom string, tags map[string]string) (name string, tagsList []string) { + mergedTags := f.mergeTags(tags) + name = f.subScope(nom) + tagsList = f.tagsList(mergedTags) + if len(tagsList) == 0 || f.factory.Capabilities().Tagging { + return + } + name = metrics.GetKey(name, mergedTags, f.tagsSep, f.tagKVSep) + tagsList = nil + return +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + name, tagsList := f.nameAndTagsList(name, tags) + counter := f.factory.Counter(name) + if len(tagsList) > 0 { + counter = counter.With(tagsList...) + } + return NewCounter(counter) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + name, tagsList := f.nameAndTagsList(name, tags) + hist := f.factory.Histogram(name) + if len(tagsList) > 0 { + hist = hist.With(tagsList...) + } + return NewTimer(hist) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + name, tagsList := f.nameAndTagsList(name, tags) + gauge := f.factory.Gauge(name) + if len(tagsList) > 0 { + gauge = gauge.With(tagsList...) + } + return NewGauge(gauge) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + scope: f.subScope(name), + tags: f.mergeTags(tags), + factory: f.factory, + scopeSep: f.scopeSep, + tagsSep: f.tagsSep, + tagKVSep: f.tagKVSep, + } +} + +func (f *factory) tagsList(a map[string]string) []string { + ret := make([]string, 0, 2*len(a)) + for k, v := range a { + ret = append(ret, k, v) + } + return ret +} + +func (f *factory) mergeTags(tags map[string]string) map[string]string { + ret := make(map[string]string, len(f.tags)+len(tags)) + for k, v := range f.tags { + ret[k] = v + } + for k, v := range tags { + ret[k] = v + } + return ret +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go new file mode 100644 index 00000000..d5e7d0ee --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/factory_test.go @@ -0,0 +1,202 @@ +package xkit + +import ( + "sort" + "testing" + "time" + + kit "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +type genericFactory struct{} + +func (f genericFactory) Counter(name string) kit.Counter { return generic.NewCounter(name) } +func (f genericFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f genericFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f genericFactory) Capabilities() Capabilities { return Capabilities{Tagging: true} } + +// noTagsFactory is similar to genericFactory but overrides With() methods to no-op +type noTagsFactory struct{} + +func (f noTagsFactory) Counter(name string) kit.Counter { + return noTagsCounter{generic.NewCounter(name)} +} +func (f noTagsFactory) Gauge(name string) kit.Gauge { return generic.NewGauge(name) } +func (f noTagsFactory) Histogram(name string) kit.Histogram { return generic.NewHistogram(name, 10) } +func (f noTagsFactory) Capabilities() Capabilities { return Capabilities{Tagging: false} } + +type noTagsCounter struct { + counter *generic.Counter +} + +func (c noTagsCounter) Add(delta float64) { c.counter.Add(delta) } +func (c noTagsCounter) With(labelValues ...string) kit.Counter { return c } + +type noTagsGauge struct { + gauge *generic.Gauge +} + +func (g noTagsGauge) Set(value float64) { g.gauge.Set(value) } +func (g noTagsGauge) Add(delta float64) { g.gauge.Add(delta) } +func (g noTagsGauge) With(labelValues ...string) kit.Gauge { return g } + +type noTagsHistogram struct { + hist *generic.Histogram +} + +func (h noTagsHistogram) Observe(value float64) { h.hist.Observe(value) } +func (h noTagsHistogram) With(labelValues ...string) kit.Histogram { return h } + +type Tags map[string]string +type metricFunc func(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) + +type testCase struct { + f Factory + + prefix string + name string + tags Tags + + useNamespace bool + namespace string + namespaceTags Tags + options []FactoryOption + + expName string + expTags []string +} + +func TestFactoryScoping(t *testing.T) { + genericFactory := genericFactory{} + noTagsFactory := noTagsFactory{} + testSuites := []struct { + metricType string + metricFunc metricFunc + }{ + {"counter", testCounter}, + {"gauge", testGauge}, + {"timer", testTimer}, + } + for _, ts := range testSuites { + testSuite := ts // capture loop var + testCases := []testCase{ + {f: genericFactory, prefix: "x", name: "", expName: "x"}, + {f: genericFactory, prefix: "", name: "y", expName: "y"}, + {f: genericFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: genericFactory, prefix: "x", name: "z", expName: "x.z", tags: Tags{"a": "b"}, expTags: []string{"a", "b"}}, + { + f: genericFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: genericFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y", + expTags: []string{"a", "b"}, + }, + { + f: genericFactory, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w.z", + expTags: []string{"a", "b", "c", "d"}, + }, + {f: noTagsFactory, prefix: "x", name: "", expName: "x"}, + {f: noTagsFactory, prefix: "", name: "y", expName: "y"}, + {f: noTagsFactory, prefix: "x", name: "y", expName: "x.y"}, + {f: noTagsFactory, prefix: "x", name: "z", expName: "x.z.a_b", tags: Tags{"a": "b"}}, + { + f: noTagsFactory, + name: "x", + useNamespace: true, + namespace: "w", + expName: "w.x", + }, + { + f: noTagsFactory, + name: "y", + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"a": "b"}, + expName: "w.y.a_b", + }, + { + f: noTagsFactory, + options: []FactoryOption{ScopeSeparator(":"), TagsSeparator(":")}, + name: "z", + tags: Tags{"a": "b"}, + useNamespace: true, + namespace: "w", + namespaceTags: Tags{"c": "d"}, + expName: "w:z:a_b:c_d", + }, + } + for _, tc := range testCases { + testCase := tc // capture loop var + factoryName := "genericFactory" + if testCase.f == noTagsFactory { + factoryName = "noTagsFactory" + } + t.Run(factoryName+"_"+testSuite.metricType+"_"+testCase.expName, func(t *testing.T) { + f := Wrap(testCase.prefix, testCase.f, testCase.options...) + if testCase.useNamespace { + f = f.Namespace(testCase.namespace, testCase.namespaceTags) + } + name, labels := testSuite.metricFunc(t, testCase, f) + assert.Equal(t, testCase.expName, name()) + actualTags := labels() + sort.Strings(actualTags) + assert.Equal(t, testCase.expTags, actualTags) + }) + } + } +} + +func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + c := f.Counter(testCase.name, testCase.tags) + c.Inc(123) + kc := c.(*Counter).counter + var gc *generic.Counter + if c, ok := kc.(*generic.Counter); ok { + gc = c + } else { + gc = kc.(noTagsCounter).counter + } + assert.EqualValues(t, 123.0, gc.Value()) + name = func() string { return gc.Name } + labels = gc.LabelValues + return +} + +func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + g := f.Gauge(testCase.name, testCase.tags) + g.Update(123) + gg := g.(*Gauge).gauge.(*generic.Gauge) + assert.EqualValues(t, 123.0, gg.Value()) + name = func() string { return gg.Name } + labels = gg.LabelValues + return +} + +func testTimer(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { + tm := f.Timer(testCase.name, testCase.tags) + tm.Record(123 * time.Millisecond) + gt := tm.(*Timer).hist.(*generic.Histogram) + assert.InDelta(t, 0.123, gt.Quantile(0.9), 0.00001) + name = func() string { return gt.Name } + labels = gt.LabelValues + return +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go new file mode 100644 index 00000000..d9249321 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory.go @@ -0,0 +1,49 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package influx + +import ( + "github.com/go-kit/kit/metrics" + "github.com/go-kit/kit/metrics/influx" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +// NewFactory creates a new metrics factory using go-kit influx package. +func NewFactory(client *influx.Influx) xkit.Factory { + return factory{ + client: client, + } +} + +type factory struct { + client *influx.Influx +} + +func (f factory) Counter(name string) metrics.Counter { + return f.client.NewCounter(name) +} + +func (f factory) Histogram(name string) metrics.Histogram { + return f.client.NewHistogram(name) +} + +func (f factory) Gauge(name string) metrics.Gauge { + return f.client.NewGauge(name) +} + +func (f factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go new file mode 100644 index 00000000..f4dc71ca --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/influx/factory_test.go @@ -0,0 +1,87 @@ +package influx + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/go-kit/kit/metrics/influx" + influxdb "github.com/influxdata/influxdb/client/v2" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + c := wf.Counter("gokit.infl-counter", map[string]string{"label": "val1"}) + c.Inc(7) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-counter,label=val1 count=7") +} + +func TestGauge(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + g := wf.Gauge("gokit.infl-gauge", map[string]string{"x": "y"}) + g.Update(42) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-gauge,x=y value=42") +} + +func TestTimer(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + timer := wf.Timer("gokit.infl-timer", map[string]string{"x": "y"}) + timer.Record(time.Second * 1) + timer.Record(time.Second * 1) + timer.Record(time.Second * 10) + + assert.Contains(t, reportToString(in), "namespace.gokit.infl-timer,x=y p50=1,p90=10,p95=10,p99=10") +} + +func TestWrapperNamespaces(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + wf := xkit.Wrap("namespace", inf) + + wf = wf.Namespace("bar", map[string]string{"bar_tag": "bar_tag"}) + + c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + + assert.Contains(t, reportToString(in), "namespace.bar.gokit.prom-wrapped-counter,bar_tag=bar_tag,x=y count=42") +} + +func TestCapabilities(t *testing.T) { + in := influx.New(map[string]string{}, influxdb.BatchPointsConfig{}, log.NewNopLogger()) + inf := NewFactory(in) + + assert.True(t, inf.Capabilities().Tagging) +} + +func reportToString(in *influx.Influx) string { + client := &bufWriter{} + in.WriteTo(client) + return client.buf.String() +} + +type bufWriter struct { + buf bytes.Buffer +} + +func (w *bufWriter) Write(bp influxdb.BatchPoints) error { + for _, p := range bp.Points() { + fmt.Fprintf(&w.buf, p.String()+"\n") + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go new file mode 100644 index 00000000..3b55b8ba --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package xkit + +import ( + "time" + + kit "github.com/go-kit/kit/metrics" +) + +// Counter is an adapter from go-kit Counter to jaeger-lib Counter +type Counter struct { + counter kit.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter kit.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Add(float64(delta)) +} + +// Gauge is an adapter from go-kit Gauge to jaeger-lib Gauge +type Gauge struct { + gauge kit.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge kit.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Set(float64(value)) +} + +// Timer is an adapter from go-kit Histogram to jaeger-lib Timer +type Timer struct { + hist kit.Histogram +} + +// NewTimer creates a new Timer +func NewTimer(hist kit.Histogram) *Timer { + return &Timer{hist: hist} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.hist.Observe(delta.Seconds()) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go new file mode 100644 index 00000000..b62c08af --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/metrics_test.go @@ -0,0 +1,32 @@ +package xkit + +import ( + "testing" + "time" + + "github.com/go-kit/kit/metrics/generic" + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestCounter(t *testing.T) { + kitCounter := generic.NewCounter("abc") + var counter metrics.Counter = NewCounter(kitCounter) + counter.Inc(123) + assert.EqualValues(t, 123, kitCounter.Value()) +} + +func TestGauge(t *testing.T) { + kitGauge := generic.NewGauge("abc") + var gauge metrics.Gauge = NewGauge(kitGauge) + gauge.Update(123) + assert.EqualValues(t, 123, kitGauge.Value()) +} + +func TestTimer(t *testing.T) { + kitHist := generic.NewHistogram("abc", 10) + var timer metrics.Timer = NewTimer(kitHist) + timer.Record(100*time.Millisecond + 500*time.Microsecond) // 100.5 milliseconds + assert.EqualValues(t, 0.1005, kitHist.Quantile(0.9)) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go new file mode 100644 index 00000000..2f133c34 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory.go @@ -0,0 +1,82 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package prometheus + +import ( + "strings" + + "github.com/go-kit/kit/metrics" + kitprom "github.com/go-kit/kit/metrics/prometheus" + "github.com/prometheus/client_golang/prometheus" + + "github.com/uber/jaeger-lib/metrics/go-kit" +) + +var normalizer = strings.NewReplacer( + ".", "_", + "-", "_", +) + +// NewFactory creates a new metrics factory using go-kit prometheus package. +// buckets define the buckets into which histogram observations are counted. +// If buckets == nil, the default value prometheus.DefBuckets is used. +func NewFactory(namespace, subsystem string, buckets []float64) xkit.Factory { + return &factory{ + namespace: namespace, + subsystem: subsystem, + buckets: buckets, + } +} + +type factory struct { + namespace string + subsystem string + buckets []float64 +} + +func (f *factory) Counter(name string) metrics.Counter { + opts := prometheus.CounterOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewCounterFrom(opts, nil) +} + +func (f *factory) Histogram(name string) metrics.Histogram { + opts := prometheus.HistogramOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + Buckets: f.buckets, + } + return kitprom.NewHistogramFrom(opts, nil) +} + +func (f *factory) Gauge(name string) metrics.Gauge { + opts := prometheus.GaugeOpts{ + Namespace: f.namespace, + Subsystem: f.subsystem, + Name: normalizer.Replace(name), + Help: name, + } + return kitprom.NewGaugeFrom(opts, nil) +} + +func (f *factory) Capabilities() xkit.Capabilities { + return xkit.Capabilities{Tagging: true} +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go new file mode 100644 index 00000000..6aacfd29 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/go-kit/prometheus/factory_test.go @@ -0,0 +1,64 @@ +package prometheus + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + xkit "github.com/uber/jaeger-lib/metrics/go-kit" +) + +func TestCounter(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + assert.True(t, f.Capabilities().Tagging) + c := f.Counter("gokit.prom-counter") + c.Add(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func TestGauge(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + g := f.Gauge("gokit.prom-gauge") + g.Set(42) + m := findMetric(t, "namespace_subsystem_gokit_prom_gauge") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetGauge().GetValue()) +} + +func TestHistogram(t *testing.T) { + f := NewFactory("namespace", "subsystem", nil) + hist := f.Histogram("gokit.prom-hist") + hist.Observe(10.5) + m := findMetric(t, "namespace_subsystem_gokit_prom_hist") + require.NotNil(t, m) + assert.Equal(t, 10.5, m[0].GetHistogram().GetSampleSum()) +} + +func TestWrapper(t *testing.T) { + f := NewFactory("", "", nil) + wf := xkit.Wrap("foo", f, xkit.ScopeSeparator(":")) + wf = wf.Namespace("bar", nil) + c := wf.Counter("gokit.prom-wrapped-counter", nil) + // TODO tags currently do not work because Prometheus requires to predeclate tag keys. + // c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c.Inc(42) + m := findMetric(t, "foo:bar:gokit_prom_wrapped_counter") + require.NotNil(t, m) + assert.Equal(t, 42.0, m[0].GetCounter().GetValue()) +} + +func findMetric(t *testing.T, key string) []*dto.Metric { + nf, err := prometheus.DefaultGatherer.Gather() + require.NoError(t, err) + for _, nf := range nf { + if nf.GetName() == key { + return nf.GetMetric() + } + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/local.go b/vendor/src/github.com/uber/jaeger-lib/metrics/local.go new file mode 100644 index 00000000..8c362484 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/local.go @@ -0,0 +1,332 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/codahale/hdrhistogram" +) + +// This is intentionally very similar to github.com/codahale/metrics, the +// main difference being that counters/gauges are scoped to the provider +// rather than being global (to facilitate testing). + +// A LocalBackend is a metrics provider which aggregates data in-vm, and +// allows exporting snapshots to shove the data into a remote collector +type LocalBackend struct { + cm sync.Mutex + gm sync.Mutex + tm sync.Mutex + counters map[string]*int64 + gauges map[string]*int64 + timers map[string]*localBackendTimer + stop chan struct{} + wg sync.WaitGroup + TagsSep string + TagKVSep string +} + +// NewLocalBackend returns a new LocalBackend. The collectionInterval is the histogram +// time window for each timer. +func NewLocalBackend(collectionInterval time.Duration) *LocalBackend { + b := &LocalBackend{ + counters: make(map[string]*int64), + gauges: make(map[string]*int64), + timers: make(map[string]*localBackendTimer), + stop: make(chan struct{}), + TagsSep: "|", + TagKVSep: "=", + } + if collectionInterval == 0 { + // Use one histogram time window for all timers + return b + } + b.wg.Add(1) + go b.runLoop(collectionInterval) + return b +} + +// Clear discards accumulated stats +func (b *LocalBackend) Clear() { + b.cm.Lock() + defer b.cm.Unlock() + b.gm.Lock() + defer b.gm.Unlock() + b.tm.Lock() + defer b.tm.Unlock() + b.counters = make(map[string]*int64) + b.gauges = make(map[string]*int64) + b.timers = make(map[string]*localBackendTimer) +} + +func (b *LocalBackend) runLoop(collectionInterval time.Duration) { + defer b.wg.Done() + ticker := time.NewTicker(collectionInterval) + for { + select { + case <-ticker.C: + b.tm.Lock() + timers := make(map[string]*localBackendTimer, len(b.timers)) + for timerName, timer := range b.timers { + timers[timerName] = timer + } + b.tm.Unlock() + + for _, t := range timers { + t.Lock() + t.hist.Rotate() + t.Unlock() + } + case <-b.stop: + ticker.Stop() + return + } + } +} + +// IncCounter increments a counter value +func (b *LocalBackend) IncCounter(name string, tags map[string]string, delta int64) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + b.cm.Lock() + defer b.cm.Unlock() + counter := b.counters[name] + if counter == nil { + b.counters[name] = new(int64) + *b.counters[name] = delta + return + } + atomic.AddInt64(counter, delta) +} + +// UpdateGauge updates the value of a gauge +func (b *LocalBackend) UpdateGauge(name string, tags map[string]string, value int64) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + b.gm.Lock() + defer b.gm.Unlock() + gauge := b.gauges[name] + if gauge == nil { + b.gauges[name] = new(int64) + *b.gauges[name] = value + return + } + atomic.StoreInt64(gauge, value) +} + +// RecordTimer records a timing duration +func (b *LocalBackend) RecordTimer(name string, tags map[string]string, d time.Duration) { + name = GetKey(name, tags, b.TagsSep, b.TagKVSep) + timer := b.findOrCreateTimer(name) + timer.Lock() + timer.hist.Current.RecordValue(int64(d / time.Millisecond)) + timer.Unlock() +} + +func (b *LocalBackend) findOrCreateTimer(name string) *localBackendTimer { + b.tm.Lock() + defer b.tm.Unlock() + if t, ok := b.timers[name]; ok { + return t + } + + t := &localBackendTimer{ + hist: hdrhistogram.NewWindowed(5, 0, int64((5*time.Minute)/time.Millisecond), 1), + } + b.timers[name] = t + return t +} + +type localBackendTimer struct { + sync.Mutex + hist *hdrhistogram.WindowedHistogram +} + +var ( + percentiles = map[string]float64{ + "P50": 50, + "P75": 75, + "P90": 90, + "P95": 95, + "P99": 99, + "P999": 99.9, + } +) + +// Snapshot captures a snapshot of the current counter and gauge values +func (b *LocalBackend) Snapshot() (counters, gauges map[string]int64) { + b.cm.Lock() + defer b.cm.Unlock() + + counters = make(map[string]int64, len(b.counters)) + for name, value := range b.counters { + counters[name] = atomic.LoadInt64(value) + } + + b.gm.Lock() + defer b.gm.Unlock() + + gauges = make(map[string]int64, len(b.gauges)) + for name, value := range b.gauges { + gauges[name] = atomic.LoadInt64(value) + } + + b.tm.Lock() + timers := make(map[string]*localBackendTimer) + for timerName, timer := range b.timers { + timers[timerName] = timer + } + b.tm.Unlock() + + for timerName, timer := range timers { + timer.Lock() + hist := timer.hist.Merge() + timer.Unlock() + for name, q := range percentiles { + gauges[timerName+"."+name] = hist.ValueAtQuantile(q) + } + } + + return +} + +// Stop cleanly closes the background goroutine spawned by NewLocalBackend. +func (b *LocalBackend) Stop() { + close(b.stop) + b.wg.Wait() +} + +// GetKey converts name+tags into a single string of the form +// "name|tag1=value1|...|tagN=valueN", where tag names are +// sorted alphabetically. +func GetKey(name string, tags map[string]string, tagsSep string, tagKVSep string) string { + keys := make([]string, 0, len(tags)) + for k := range tags { + keys = append(keys, k) + } + sort.Strings(keys) + key := name + for _, k := range keys { + key = key + tagsSep + k + tagKVSep + tags[k] + } + return key +} + +type stats struct { + name string + tags map[string]string + localBackend *LocalBackend +} + +type localTimer struct { + stats +} + +func (l *localTimer) Record(d time.Duration) { + l.localBackend.RecordTimer(l.name, l.tags, d) +} + +type localCounter struct { + stats +} + +func (l *localCounter) Inc(delta int64) { + l.localBackend.IncCounter(l.name, l.tags, delta) +} + +type localGauge struct { + stats +} + +func (l *localGauge) Update(value int64) { + l.localBackend.UpdateGauge(l.name, l.tags, value) +} + +// LocalFactory stats factory that creates metrics that are stored locally +type LocalFactory struct { + *LocalBackend + namespace string + tags map[string]string +} + +// NewLocalFactory returns a new LocalMetricsFactory +func NewLocalFactory(collectionInterval time.Duration) *LocalFactory { + return &LocalFactory{ + LocalBackend: NewLocalBackend(collectionInterval), + } +} + +// appendTags adds the tags to the namespace tags and returns a combined map. +func (l *LocalFactory) appendTags(tags map[string]string) map[string]string { + newTags := make(map[string]string) + for k, v := range l.tags { + newTags[k] = v + } + for k, v := range tags { + newTags[k] = v + } + return newTags +} + +func (l *LocalFactory) newNamespace(name string) string { + if l.namespace == "" { + return name + } + return l.namespace + "." + name +} + +// Counter returns a local stats counter +func (l *LocalFactory) Counter(name string, tags map[string]string) Counter { + return &localCounter{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Timer returns a local stats timer. +func (l *LocalFactory) Timer(name string, tags map[string]string) Timer { + return &localTimer{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Gauge returns a local stats gauge. +func (l *LocalFactory) Gauge(name string, tags map[string]string) Gauge { + return &localGauge{ + stats{ + name: l.newNamespace(name), + tags: l.appendTags(tags), + localBackend: l.LocalBackend, + }, + } +} + +// Namespace returns a new namespace. +func (l *LocalFactory) Namespace(name string, tags map[string]string) Factory { + return &LocalFactory{ + namespace: l.newNamespace(name), + tags: l.appendTags(tags), + LocalBackend: l.LocalBackend, + } +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/local_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/local_test.go new file mode 100644 index 00000000..f4dede6b --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/local_test.go @@ -0,0 +1,116 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLocalMetrics(t *testing.T) { + tags := map[string]string{ + "x": "y", + } + + f := NewLocalFactory(0) + defer f.Stop() + f.Counter("my-counter", tags).Inc(4) + f.Counter("my-counter", tags).Inc(6) + f.Counter("my-counter", nil).Inc(6) + f.Counter("other-counter", nil).Inc(8) + f.Gauge("my-gauge", nil).Update(25) + f.Gauge("my-gauge", nil).Update(43) + f.Gauge("other-gauge", nil).Update(74) + f.Namespace("namespace", tags).Counter("my-counter", nil).Inc(7) + + timings := map[string][]time.Duration{ + "foo-latency": { + time.Second * 35, + time.Second * 6, + time.Millisecond * 576, + time.Second * 12, + }, + "bar-latency": { + time.Minute*4 + time.Second*34, + time.Minute*7 + time.Second*12, + time.Second * 625, + time.Second * 12, + }, + } + + for metric, timing := range timings { + for _, d := range timing { + f.Timer(metric, nil).Record(d) + } + } + + c, g := f.Snapshot() + require.NotNil(t, c) + require.NotNil(t, g) + + assert.Equal(t, map[string]int64{ + "my-counter|x=y": 10, + "my-counter": 6, + "other-counter": 8, + "namespace.my-counter|x=y": 7, + }, c) + + assert.Equal(t, map[string]int64{ + "bar-latency.P50": 278527, + "bar-latency.P75": 278527, + "bar-latency.P90": 442367, + "bar-latency.P95": 442367, + "bar-latency.P99": 442367, + "bar-latency.P999": 442367, + "foo-latency.P50": 6143, + "foo-latency.P75": 12287, + "foo-latency.P90": 36863, + "foo-latency.P95": 36863, + "foo-latency.P99": 36863, + "foo-latency.P999": 36863, + "my-gauge": 43, + "other-gauge": 74, + }, g) + + f.Clear() + c, g = f.Snapshot() + require.Empty(t, c) + require.Empty(t, g) +} + +func TestLocalMetricsInterval(t *testing.T) { + refreshInterval := time.Millisecond + const relativeCheckFrequency = 5 // check 5 times per refreshInterval + const maxChecks = 2 * relativeCheckFrequency + checkInterval := (refreshInterval * relativeCheckFrequency) / maxChecks + + f := NewLocalFactory(refreshInterval) + defer f.Stop() + + f.Timer("timer", nil).Record(1) + + f.tm.Lock() + timer := f.timers["timer"] + f.tm.Unlock() + assert.NotNil(t, timer) + + // timer.hist.Current is modified on every Rotate(), which is called by LocalBackend after every refreshInterval + getCurr := func() interface{} { + timer.Lock() + defer timer.Unlock() + return timer.hist.Current + } + + curr := getCurr() + + // wait for twice as long as the refresh interval + for i := 0; i < maxChecks; i++ { + time.Sleep(checkInterval) + + if getCurr() != curr { + return + } + } + t.Fail() +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/metrics.go b/vendor/src/github.com/uber/jaeger-lib/metrics/metrics.go new file mode 100644 index 00000000..0b97707b --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/metrics.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "fmt" + "reflect" + "strings" +) + +// Init initializes the passed in metrics and initializes its fields using the passed in factory. +func Init(metrics interface{}, factory Factory, globalTags map[string]string) { + if err := initMetrics(metrics, factory, globalTags); err != nil { + panic(err.Error()) + } +} + +// initMetrics uses reflection to initialize a struct containing metrics fields +// by assigning new Counter/Gauge/Timer values with the metric name retrieved +// from the `metric` tag and stats tags retrieved from the `tags` tag. +// +// Note: all fields of the struct must be exported, have a `metric` tag, and be +// of type Counter or Gauge or Timer. +func initMetrics(m interface{}, factory Factory, globalTags map[string]string) error { + // Allow user to opt out of reporting metrics by passing in nil. + if factory == nil { + factory = NullFactory + } + + counterPtrType := reflect.TypeOf((*Counter)(nil)).Elem() + gaugePtrType := reflect.TypeOf((*Gauge)(nil)).Elem() + timerPtrType := reflect.TypeOf((*Timer)(nil)).Elem() + + v := reflect.ValueOf(m).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + tags := make(map[string]string) + for k, v := range globalTags { + tags[k] = v + } + field := t.Field(i) + metric := field.Tag.Get("metric") + if metric == "" { + return fmt.Errorf("Field %s is missing a tag 'metric'", field.Name) + } + if tagString := field.Tag.Get("tags"); tagString != "" { + tagPairs := strings.Split(tagString, ",") + for _, tagPair := range tagPairs { + tag := strings.Split(tagPair, "=") + if len(tag) != 2 { + return fmt.Errorf( + "Field [%s]: Tag [%s] is not of the form key=value in 'tags' string [%s]", + field.Name, tagPair, tagString) + } + tags[tag[0]] = tag[1] + } + } + var obj interface{} + if field.Type.AssignableTo(counterPtrType) { + obj = factory.Counter(metric, tags) + } else if field.Type.AssignableTo(gaugePtrType) { + obj = factory.Gauge(metric, tags) + } else if field.Type.AssignableTo(timerPtrType) { + obj = factory.Timer(metric, tags) + } else { + return fmt.Errorf( + "Field %s is not a pointer to timer, gauge, or counter", + field.Name) + } + v.Field(i).Set(reflect.ValueOf(obj)) + } + return nil +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/metrics_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/metrics_test.go new file mode 100644 index 00000000..7d9226c6 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/metrics_test.go @@ -0,0 +1,89 @@ +package metrics + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestInitMetrics(t *testing.T) { + testMetrics := struct { + Gauge Gauge `metric:"gauge" tags:"1=one,2=two"` + Counter Counter `metric:"counter"` + Timer Timer `metric:"timer"` + }{} + + f := NewLocalFactory(0) + defer f.Stop() + + globalTags := map[string]string{"key": "value"} + + err := initMetrics(&testMetrics, f, globalTags) + assert.NoError(t, err) + + testMetrics.Gauge.Update(10) + testMetrics.Counter.Inc(5) + testMetrics.Timer.Record(time.Duration(time.Second * 35)) + + // wait for metrics + for i := 0; i < 1000; i++ { + c, _ := f.Snapshot() + if _, ok := c["counter"]; ok { + break + } + time.Sleep(1 * time.Millisecond) + } + + c, g := f.Snapshot() + + assert.EqualValues(t, 5, c["counter|key=value"]) + assert.EqualValues(t, 10, g["gauge|1=one|2=two|key=value"]) + assert.EqualValues(t, 36863, g["timer|key=value.P50"]) + + stopwatch := StartStopwatch(testMetrics.Timer) + stopwatch.Stop() + assert.True(t, 0 < stopwatch.ElapsedTime()) +} + +var ( + noMetricTag = struct { + NoMetricTag Counter + }{} + + badTags = struct { + BadTags Counter `metric:"counter" tags:"1=one,noValue"` + }{} + + invalidMetricType = struct { + InvalidMetricType int64 `metric:"counter"` + }{} +) + +func TestInitMetricsFailures(t *testing.T) { + assert.EqualError(t, initMetrics(&noMetricTag, nil, nil), "Field NoMetricTag is missing a tag 'metric'") + + assert.EqualError(t, initMetrics(&badTags, nil, nil), + "Field [BadTags]: Tag [noValue] is not of the form key=value in 'tags' string [1=one,noValue]") + + assert.EqualError(t, initMetrics(&invalidMetricType, nil, nil), + "Field InvalidMetricType is not a pointer to timer, gauge, or counter") +} + +func TestInitPanic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Error("The code did not panic") + } + }() + + Init(&noMetricTag, NullFactory, nil) +} + +func TestNullMetrics(t *testing.T) { + // This test is just for cover + NullFactory.Timer("name", nil).Record(0) + NullFactory.Counter("name", nil).Inc(0) + NullFactory.Gauge("name", nil).Update(0) + NullFactory.Namespace("name", nil).Gauge("name2", nil).Update(0) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi.go b/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi.go new file mode 100644 index 00000000..c3791b60 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi.go @@ -0,0 +1,107 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package multi + +import ( + "time" + + "github.com/uber/jaeger-lib/metrics" +) + +// Factory is a metrics factory that dispatches to multiple metrics backends. +type Factory struct { + factories []metrics.Factory +} + +// New creates a new multi.Factory that will dispatch to multiple metrics backends. +func New(factories ...metrics.Factory) *Factory { + return &Factory{ + factories: factories, + } +} + +type counter struct { + counters []metrics.Counter +} + +func (c *counter) Inc(delta int64) { + for _, counter := range c.counters { + counter.Inc(delta) + } +} + +// Counter implements metrics.Factory interface +func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { + counter := &counter{ + counters: make([]metrics.Counter, len(f.factories)), + } + for i, factory := range f.factories { + counter.counters[i] = factory.Counter(name, tags) + } + return counter +} + +type timer struct { + timers []metrics.Timer +} + +func (t *timer) Record(delta time.Duration) { + for _, timer := range t.timers { + timer.Record(delta) + } +} + +// Timer implements metrics.Factory interface +func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { + timer := &timer{ + timers: make([]metrics.Timer, len(f.factories)), + } + for i, factory := range f.factories { + timer.timers[i] = factory.Timer(name, tags) + } + return timer +} + +type gauge struct { + gauges []metrics.Gauge +} + +func (t *gauge) Update(value int64) { + for _, gauge := range t.gauges { + gauge.Update(value) + } +} + +// Gauge implements metrics.Factory interface +func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { + gauge := &gauge{ + gauges: make([]metrics.Gauge, len(f.factories)), + } + for i, factory := range f.factories { + gauge.gauges[i] = factory.Gauge(name, tags) + } + return gauge +} + +// Namespace implements metrics.Factory interface +func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { + newFactory := &Factory{ + factories: make([]metrics.Factory, len(f.factories)), + } + for i, factory := range f.factories { + newFactory.factories[i] = factory.Namespace(name, tags) + } + return newFactory +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi_test.go new file mode 100644 index 00000000..24ecb733 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/multi/multi_test.go @@ -0,0 +1,32 @@ +package multi + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" + "github.com/uber/jaeger-lib/metrics/testutils" +) + +var _ metrics.Factory = &Factory{} // API check + +func TestMultiFactory(t *testing.T) { + f1 := metrics.NewLocalFactory(time.Second) + f2 := metrics.NewLocalFactory(time.Second) + multi1 := New(f1, f2) + multi2 := multi1.Namespace("ns2", nil) + tags := map[string]string{"x": "y"} + multi2.Counter("counter", tags).Inc(42) + multi2.Gauge("gauge", tags).Update(42) + multi2.Timer("timer", tags).Record(42 * time.Millisecond) + + for _, f := range []*metrics.LocalFactory{f1, f2} { + testutils.AssertCounterMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.counter", Tags: tags, Value: 42}) + testutils.AssertGaugeMetrics(t, f, + testutils.ExpectedMetric{Name: "ns2.gauge", Tags: tags, Value: 42}) + _, g := f.Snapshot() + assert.EqualValues(t, 43, g["ns2.timer|x=y.P99"]) + } +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/stopwatch.go b/vendor/src/github.com/uber/jaeger-lib/metrics/stopwatch.go new file mode 100644 index 00000000..4a8abdb5 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/stopwatch.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "time" +) + +// StartStopwatch begins recording the executing time of an event, returning +// a Stopwatch that should be used to stop the recording the time for +// that event. Multiple events can be occurring simultaneously each +// represented by different active Stopwatches +func StartStopwatch(timer Timer) Stopwatch { + return Stopwatch{t: timer, start: time.Now()} +} + +// A Stopwatch tracks the execution time of a specific event +type Stopwatch struct { + t Timer + start time.Time +} + +// Stop stops executing of the stopwatch and records the amount of elapsed time +func (s Stopwatch) Stop() { + s.t.Record(s.ElapsedTime()) +} + +// ElapsedTime returns the amount of elapsed time (in time.Duration) +func (s Stopwatch) ElapsedTime() time.Duration { + return time.Since(s.start) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory.go b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory.go new file mode 100644 index 00000000..79237729 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory.go @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package tally + +import ( + "github.com/uber-go/tally" + + "github.com/uber/jaeger-lib/metrics" +) + +// Wrap takes a tally Scope and returns jaeger-lib metrics.Factory. +func Wrap(scope tally.Scope) metrics.Factory { + return &factory{ + tally: scope, + } +} + +// TODO implement support for tags if tally.Scope does not support them +type factory struct { + tally tally.Scope +} + +func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewCounter(scope.Counter(name)) +} + +func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewGauge(scope.Gauge(name)) +} + +func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { + scope := f.tally + if len(tags) > 0 { + scope = scope.Tagged(tags) + } + return NewTimer(scope.Timer(name)) +} + +func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { + return &factory{ + tally: f.tally.SubScope(name).Tagged(tags), + } +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory_test.go new file mode 100644 index 00000000..fbbbe20a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/factory_test.go @@ -0,0 +1,47 @@ +package tally + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/uber-go/tally" +) + +func TestFactory(t *testing.T) { + testScope := tally.NewTestScope("pre", map[string]string{"a": "b"}) + factory := Wrap(testScope).Namespace("fix", map[string]string{"c": "d"}) + counter := factory.Counter("counter", map[string]string{"x": "y"}) + counter.Inc(42) + gauge := factory.Gauge("gauge", map[string]string{"x": "y"}) + gauge.Update(42) + timer := factory.Timer("timer", map[string]string{"x": "y"}) + timer.Record(42 * time.Millisecond) + snapshot := testScope.Snapshot() + + // tally v3 includes tags in the name, so look + c := snapshot.Counters()["pre.fix.counter"] + if c == nil { + // tally v3 includes tags in the name. + c = snapshot.Counters()["pre.fix.counter+a=b,c=d,x=y"] + } + + g := snapshot.Gauges()["pre.fix.gauge"] + if g == nil { + g = snapshot.Gauges()["pre.fix.gauge+a=b,c=d,x=y"] + } + + h := snapshot.Timers()["pre.fix.timer"] + if h == nil { + h = snapshot.Timers()["pre.fix.timer+a=b,c=d,x=y"] + } + + expectedTags := map[string]string{"a": "b", "c": "d", "x": "y"} + assert.EqualValues(t, 42, c.Value()) + assert.EqualValues(t, expectedTags, c.Tags()) + assert.EqualValues(t, 42, g.Value()) + assert.EqualValues(t, expectedTags, g.Tags()) + assert.Equal(t, []time.Duration{42 * time.Millisecond}, h.Values()) + assert.EqualValues(t, expectedTags, h.Tags()) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/tally/metrics.go b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/metrics.go new file mode 100644 index 00000000..f8621c66 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/tally/metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package tally + +import ( + "time" + + "github.com/uber-go/tally" +) + +// Counter is an adapter from go-tally Counter to jaeger-lib Counter +type Counter struct { + counter tally.Counter +} + +// NewCounter creates a new Counter +func NewCounter(counter tally.Counter) *Counter { + return &Counter{counter: counter} +} + +// Inc adds the given value to the counter. +func (c *Counter) Inc(delta int64) { + c.counter.Inc(delta) +} + +// Gauge is an adapter from go-tally Gauge to jaeger-lib Gauge +type Gauge struct { + gauge tally.Gauge +} + +// NewGauge creates a new Gauge +func NewGauge(gauge tally.Gauge) *Gauge { + return &Gauge{gauge: gauge} +} + +// Update the gauge to the value passed in. +func (g *Gauge) Update(value int64) { + g.gauge.Update(float64(value)) +} + +// Timer is an adapter from go-tally Histogram to jaeger-lib Timer +type Timer struct { + timer tally.Timer +} + +// NewTimer creates a new Timer +func NewTimer(timer tally.Timer) *Timer { + return &Timer{timer: timer} +} + +// Record saves the time passed in. +func (t *Timer) Record(delta time.Duration) { + t.timer.Record(delta) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils.go b/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils.go new file mode 100644 index 00000000..66134b6c --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils.go @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/uber/jaeger-lib/metrics" +) + +// ExpectedMetric contains metrics under test. +type ExpectedMetric struct { + Name string + Tags map[string]string + Value int +} + +// TODO do something similar for Timers + +// AssertCounterMetrics checks if counter metrics exist. +func AssertCounterMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + counters, _ := f.Snapshot() + assertMetrics(t, counters, expectedMetrics...) +} + +// AssertGaugeMetrics checks if gauge metrics exist. +func AssertGaugeMetrics(t *testing.T, f *metrics.LocalFactory, expectedMetrics ...ExpectedMetric) { + _, gauges := f.Snapshot() + assertMetrics(t, gauges, expectedMetrics...) +} + +func assertMetrics(t *testing.T, actualMetrics map[string]int64, expectedMetrics ...ExpectedMetric) { + for _, expected := range expectedMetrics { + key := metrics.GetKey(expected.Name, expected.Tags, "|", "=") + assert.EqualValues(t, + expected.Value, + actualMetrics[key], + "expected metric name: %s, tags: %+v", expected.Name, expected.Tags, + ) + } +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go b/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go new file mode 100644 index 00000000..9ebd24e8 --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/testutils/testutils_test.go @@ -0,0 +1,17 @@ +package testutils + +import ( + "testing" + + "github.com/uber/jaeger-lib/metrics" +) + +func TestAssertMetrics(t *testing.T) { + f := metrics.NewLocalFactory(0) + tags := map[string]string{"key": "value"} + f.IncCounter("counter", tags, 1) + f.UpdateGauge("gauge", tags, 11) + + AssertCounterMetrics(t, f, ExpectedMetric{Name: "counter", Tags: tags, Value: 1}) + AssertGaugeMetrics(t, f, ExpectedMetric{Name: "gauge", Tags: tags, Value: 11}) +} diff --git a/vendor/src/github.com/uber/jaeger-lib/metrics/timer.go b/vendor/src/github.com/uber/jaeger-lib/metrics/timer.go new file mode 100644 index 00000000..e18d222a --- /dev/null +++ b/vendor/src/github.com/uber/jaeger-lib/metrics/timer.go @@ -0,0 +1,33 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// 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. + +package metrics + +import ( + "time" +) + +// Timer accumulates observations about how long some operation took, +// and also maintains a historgam of percentiles. +type Timer interface { + // Records the time passed in. + Record(time.Duration) +} + +// NullTimer timer that does nothing +var NullTimer Timer = nullTimer{} + +type nullTimer struct{} + +func (nullTimer) Record(time.Duration) {} diff --git a/vendor/src/github.com/uber/tchannel-go/CHANGELOG.md b/vendor/src/github.com/uber/tchannel-go/CHANGELOG.md new file mode 100644 index 00000000..75d602de --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/CHANGELOG.md @@ -0,0 +1,171 @@ +Changelog +========= + +# v1.7.0 (2017-08-04) + +* Cancel the context on incoming calls if the client connection is closed. +* Add `WithoutHeaders` to remove TChannel keys from a context. + +# v1.6.0 (2017-06-02) + +* Locks Apache Thrift to version 0.9.3, 0.10.0 to maintain backward-compatibility. +* Add `OnPeerStatusChanged` channel option to receive a notification each time + the number of available connections changes for any given peer. +* Set DiffServ (QoS) bit on outbound connections. +* Improve resilience of the frame parser. + +# v1.5.0 (2017-03-21) + +* Add `PeerList.Len` to expose the number of peers in the peer list. +* Add `PeerList.GetNew` to only return previously unselected peers. + +# v1.4.0 (2017-03-01) + +* Add version information to the channel's LocalPeerInfo. +* Add peers package for peer management utilities such as + consistent peer selection. +* Fix SetScoreStrategy not rescoring existing peers. (#583). + +# v1.3.0 (2017-02-01) + +* Exposes the channel's RootPeerList with `channel.RootPeers()`. +* Support Thrift namespaces for thrift-gen. + +# v1.2.3 (2017-01-19) + +* Improve error messages when an argument reader is closed without + reading the EOF. (#567) +* thrift: Fix an issue where we return `nil` if we expected a Thrift exception + but none was found (e.g., exception is from the future). (#566) +* Fix ListenIP selecting docker interfaces over physical networks. (#565) +* Fix for error when a Thrift payload has completed decoding and attempts + to close the argument reader without waiting until EOF. (#564) +* thrift-gen: Fix "namespace go" being ignored even though the Apache thrift + generated code was respecting it. (#559) + +# v1.2.2 (2016-12-21) + +* Add a unique channel ID for introspection (#548) +* Don't drop existing headers on a context when using Wrap(ctx) (#547) +* Setting response headers is not goroutine safe, allow using a child context + for parallel sub-requests (#549). +* Fix context cancellation not cancelling Dial attempts (#541) +* Expose local peer information on {Inbound,Outbound}Call (#537) +* Only select active connections for calls (#521) +* Add remote peer info to connection logger and introspection (#514) +* Treat hostPorts ending in ":0" in the init headers as ephemeral (#513) + +# v1.2.1 (2016-09-29) + +* Fix data race on headers when making concurrent calls using the same context. (#505) + +# v1.2.0 (2016-09-15) + +* Adds support for routing keys (the TChannel rk transport header). + +# v1.1.0 (2016-08-25) + +* Integrate OpenTracing for distributed tracing and context propagation. + As long as a Zipkin-style tracing is configured, TChannel frames still + send tracing information, and `CurrentSpan(ctx)` works as before. + All tracer configuration must be handled through OpenTracing. + (#426) +* Improve error messages when using the json package and the host:port + fails to connect. (#475) +* mockhyperbahn now using inbuilt TChannel relaying to implement in-process + forwarding. (#472) +* Drop go1.4 support and add support for go1.7. +* Pass thrift.Context to the thrift.Server's response callback (#465) + +# 1.0.9 (2016-07-20) + +* Expose meta endpoints on the "tchannel" service name. (#459) +* Add Go version and tchannel-go library version to introspection. (#457) +* Better handling of peers where dialed host:port doesn't match the remote + connection's reported host:port. (#452) +* Expose the number of connections on a channel. (#451) + +# 1.0.8 (2016-07-15) + +* Remove dependency on "testing" from "tchannel-go" introduced in v1.0.7. + +# 1.0.7 (2016-07-15) + +* Add CallOptions() to IncomingCall which can be used as the call option + when making outbound calls to proxy all transport headers. +* Add tracing information to all error frames generated by the library. +* Add GetHandlers for getting all registered methods on a subchannel. +* Fix peer score not being calculated when adding a new outbound connections +* Expose the peer information for outbound calls. +* Support a separate connection timeout from the context timeout, useful for + streaming calls where the stream timeout may be much longer than the + connection timeout. + +# 1.0.6 (2016-06-16) + +* Fix trace span encoding fields in the wrong order + +# 1.0.5 (2016-04-04) + +* Use `context.Context` storage for headers so `thrift.Context` and + `tchannel.ContextWithHeaders` can be passed to functions that use + `context.Context`, and have them retain headers. +* `thrift.Server` allows a custom factory to be used for `thrift.Context` + creation based on the underlying `context.Context` and headers map. +* Store goroutine stack traces on channel creation that can be accessed + via introspection. + +# 1.0.4 (2016-03-09) + +* Improve handling of network failures during pending calls. Previously, calls + would timeout, but now they fail as soon as the network failure is detected. +* Remove ephemeral peers with closed outbound connections. +* #233: Ensure errors returned from Thrift handlers have a non-nil value. +* #228: Add registered methods to the introspection output. +* Add ability to set a global handler for a SubChannel. + +# 1.0.3 (2016-02-15) + +* Improved performance when writing Thrift structs +* Make closing message exchanges less disruptive, changes a panic due to + closing a channel twice to an error log. +* Introspection now includes information about all channels created + in the current process. + +# 1.0.2 (2016-01-29) + +* Extend the `ContextBuilder` API to support setting the transport-level + routing delegate header. +* Set a timeout when making new outbound connections to avoid hanging. +* Fix for #196: Make the initial Hyperbahn advertise more tolerant of transient + timeouts. +* Assorted logging and test improvements. + +# 1.0.1 (2016-01-19) + +* Bug fix for #181: Shuffle peers on PeerList.Add to avoid biases in peer + selection. +* Peers can now be removed using PeerList.Remove. +* Add ErrorHandlerFunc to create raw handlers that return errors. +* Retries try to avoid previously selected hosts, rather than just the + host:port. +* Create an ArgReader interface (which is an alias for io.ReadCloser) for + symmetry with ArgWriter. +* Add ArgReadable and ArgWritable interfaces for the common methods between + calls and responses. +* Expose Thrift binary encoding methods (thrift.ReadStruct, thrift.WriteStruct, + thrift.ReadHeaders, thrift.WriteHeaders) so callers can easily send Thrift + payloads over the streaming interface. + +# 1.0.0 (2016-01-11) + +* First stable release. +* Support making calls with JSON, Thrift or raw payloads. +* Services use thrift-gen, and implement handlers with a `func(ctx, arg) (res, + error)` signature. +* Support retries. +* Peer selection (peer heap, prefer incoming strategy, for use with Hyperbahn). +* Graceful channel shutdown. +* TCollector trace reporter with sampling support. +* Metrics collection with StatsD. +* Thrift support, including includes. diff --git a/vendor/src/github.com/uber/tchannel-go/CONTRIBUTING.md b/vendor/src/github.com/uber/tchannel-go/CONTRIBUTING.md new file mode 100644 index 00000000..60f2846a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/CONTRIBUTING.md @@ -0,0 +1,44 @@ +Contributing +============ + +We'd love your help making tchannel-go great! + +## Getting Started + +TChannel uses [glide](https://github.com/Masterminds/glide) to manage +dependencies. +To get started: + +```bash +go get github.com/uber/tchannel-go +make install_glide +make # tests should pass +``` + +## Making A Change + +*Before making any significant changes, please [open an +issue](https://github.com/uber/tchannel-go/issues).* Discussing your proposed +changes ahead of time will make the contribution process smooth for everyone. + +Once we've discussed your changes and you've got your code ready, make sure +that tests are passing (`make test` or `make cover`) and open your PR! Your +pull request is most likely to be accepted if it: + +* Includes tests for new functionality. +* Follows the guidelines in [Effective + Go](https://golang.org/doc/effective_go.html) and the [Go team's common code + review comments](https://github.com/golang/go/wiki/CodeReviewComments). +* Has a [good commit + message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Cutting a Release + +* Send a pull request against dev including: + * update CHANGELOG.md (`scripts/changelog_halp.sh`) + * update version.go +* Send a pull request for dev into master +* `git tag -m v0.0.0 -a v0.0.0` +* `git push origin --tags` +* Copy CHANGELOG.md fragment into release notes on + https://github.com/uber/tchannel-go/releases diff --git a/vendor/src/github.com/uber/tchannel-go/LICENSE.md b/vendor/src/github.com/uber/tchannel-go/LICENSE.md new file mode 100644 index 00000000..b090ca4f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2015 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/github.com/uber/tchannel-go/Makefile b/vendor/src/github.com/uber/tchannel-go/Makefile new file mode 100644 index 00000000..607fa612 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/Makefile @@ -0,0 +1,158 @@ +export GO15VENDOREXPERIMENT=1 + +PATH := $(GOPATH)/bin:$(PATH) +EXAMPLES=./examples/bench/server ./examples/bench/client ./examples/ping ./examples/thrift ./examples/hyperbahn/echo-server +ALL_PKGS := $(shell glide nv) +PROD_PKGS := . ./http ./hyperbahn ./json ./peers ./pprof ./raw ./relay ./stats ./thrift $(EXAMPLES) +TEST_ARG ?= -race -v -timeout 5m +BUILD := ./build +THRIFT_GEN_RELEASE := ./thrift-gen-release +THRIFT_GEN_RELEASE_LINUX := $(THRIFT_GEN_RELEASE)/linux-x86_64 +THRIFT_GEN_RELEASE_DARWIN := $(THRIFT_GEN_RELEASE)/darwin-x86_64 +SRCS := $(foreach pkg,$(PKGS),$(wildcard $(pkg)/*.go)) + +PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]') +ARCH := $(shell uname -m) + +OLD_GOPATH := $(GOPATH) + +BIN := $(shell pwd)/.bin + +# Cross language test args +TEST_HOST=127.0.0.1 +TEST_PORT=0 + +-include crossdock/rules.mk + +all: test examples + +$(BIN)/thrift: + mkdir -p $(BIN) + scripts/install-thrift.sh $(BIN) + +packages_test: + go list -json ./... | jq -r '. | select ((.TestGoFiles | length) > 0) | .ImportPath' + +setup: + mkdir -p $(BUILD) + mkdir -p $(BUILD)/examples + mkdir -p $(THRIFT_GEN_RELEASE_LINUX) + mkdir -p $(THRIFT_GEN_RELEASE_DARWIN) + +# We want to remove `vendor` dir because thrift-gen tests don't work with it. +# However, glide install even with --cache-gopath option leaves GOPATH at HEAD, +# not at the desired versions from glide.lock, which are only applied to `vendor` +# dir. So we move `vendor` to a temp dir and prepend it to GOPATH. +# Note that glide itself is still executed against the original GOPATH. +install: + GOPATH=$(OLD_GOPATH) glide --debug install --cache --cache-gopath + +install_lint: + @echo "Installing golint, since we expect to lint" + GOPATH=$(OLD_GOPATH) go get -u -f github.com/golang/lint/golint + +install_glide: + # all we want is: GOPATH=$(OLD_GOPATH) go get -u github.com/Masterminds/glide + # but have to pin to 0.12.3 due to https://github.com/Masterminds/glide/issues/745 + GOPATH=$(OLD_GOPATH) go get -u github.com/Masterminds/glide && cd $(OLD_GOPATH)/src/github.com/Masterminds/glide && git checkout v0.12.3 && go install + +install_ci: $(BIN)/thrift install_glide install +ifdef CROSSDOCK + $(MAKE) install_docker_ci +endif + +install_test: + go test -i $(TEST_ARG) $(ALL_PKGS) + +help: + @egrep "^# target:" [Mm]akefile | sort - + +clean: + echo Cleaning build artifacts... + go clean + rm -rf $(BUILD) $(THRIFT_GEN_RELEASE) + echo + +fmt format: + echo Formatting Packages... + go fmt $(ALL_PKGS) + echo + +test_ci: +ifdef CROSSDOCK + $(MAKE) crossdock_ci +else + $(MAKE) test +endif + +test: clean setup install_test check_no_test_deps $(BIN)/thrift + @echo Testing packages: + PATH=$(BIN):$$PATH go test -parallel=4 $(TEST_ARG) $(ALL_PKGS) + @echo Running frame pool tests + PATH=$(BIN):$$PATH go test -run TestFramesReleased -stressTest $(TEST_ARG) + +check_no_test_deps: + ! go list -json $(PROD_PKGS) | jq -r .Deps[] | grep -e test -e mock + +benchmark: clean setup $(BIN)/thrift + echo Running benchmarks: + PATH=$(BIN)::$$PATH go test $(ALL_PKGS) -bench=. -cpu=1 -benchmem -run NONE + +cover_profile: clean setup $(BIN)/thrift + @echo Testing packages: + mkdir -p $(BUILD) + PATH=$(BIN)::$$PATH go test ./ $(TEST_ARG) -coverprofile=$(BUILD)/coverage.out + +cover: cover_profile + go tool cover -html=$(BUILD)/coverage.out + +cover_ci: + @echo "Uploading coverage" + $(MAKE) cover_profile + curl -s https://codecov.io/bash > $(BUILD)/codecov.bash + bash $(BUILD)/codecov.bash -f $(BUILD)/coverage.out + + +FILTER := grep -v -e '_string.go' -e '/gen-go/' -e '/mocks/' -e 'vendor/' +lint: + @echo "Running golint" + -golint $(ALL_PKGS) | $(FILTER) | tee lint.log + @echo "Running go vet" + -go vet $(PKGS) 2>&1 | fgrep -v -e "possible formatting directiv" -e "exit status" | tee -a lint.log + @echo "Verifying files are gofmt'd" + -gofmt -l . | $(FILTER) | tee -a lint.log + @echo "Checking for unresolved FIXMEs" + -git grep -i -n fixme | $(FILTER) | grep -v -e Makefile | tee -a lint.log + @[ ! -s lint.log ] + +thrift_example: thrift_gen + go build -o $(BUILD)/examples/thrift ./examples/thrift/main.go + +test_server: + ./build/examples/test_server --host ${TEST_HOST} --port ${TEST_PORT} + +examples: clean setup thrift_example + echo Building examples... + mkdir -p $(BUILD)/examples/ping $(BUILD)/examples/bench + go build -o $(BUILD)/examples/ping/pong ./examples/ping/main.go + go build -o $(BUILD)/examples/hyperbahn/echo-server ./examples/hyperbahn/echo-server/main.go + go build -o $(BUILD)/examples/bench/server ./examples/bench/server + go build -o $(BUILD)/examples/bench/client ./examples/bench/client + go build -o $(BUILD)/examples/bench/runner ./examples/bench/runner.go + go build -o $(BUILD)/examples/test_server ./examples/test_server + +thrift_gen: $(BIN)/thrift + go build -o $(BUILD)/thrift-gen ./thrift/thrift-gen + PATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile thrift/test.thrift --outputDir thrift/gen-go/ + PATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile examples/keyvalue/keyvalue.thrift --outputDir examples/keyvalue/gen-go + PATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile examples/thrift/example.thrift --outputDir examples/thrift/gen-go + PATH=$(BIN):$$PATH $(BUILD)/thrift-gen --generateThrift --inputFile hyperbahn/hyperbahn.thrift --outputDir hyperbahn/gen-go + +release_thrift_gen: clean setup + GOOS=linux GOARCH=amd64 go build -o $(THRIFT_GEN_RELEASE_LINUX)/thrift-gen ./thrift/thrift-gen + GOOS=darwin GOARCH=amd64 go build -o $(THRIFT_GEN_RELEASE_DARWIN)/thrift-gen ./thrift/thrift-gen + tar -czf thrift-gen-release.tar.gz $(THRIFT_GEN_RELEASE) + mv thrift-gen-release.tar.gz $(THRIFT_GEN_RELEASE)/ + +.PHONY: all help clean fmt format install install_ci install_lint install_glide release_thrift_gen packages_test check_no_test_deps test test_ci lint +.SILENT: all help clean fmt format test lint diff --git a/vendor/src/github.com/uber/tchannel-go/README.md b/vendor/src/github.com/uber/tchannel-go/README.md new file mode 100644 index 00000000..cf613183 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/README.md @@ -0,0 +1,67 @@ +# TChannel [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +[TChannel][tchan-spec] is a multiplexing and framing protocol for RPC calls. +tchannel-go is a Go implementation of the protocol, including client libraries +for [Hyperbahn][hyperbahn]. + +If you'd like to start by writing a small Thrift and TChannel service, check +out [this guide](guide/Thrift_Hyperbahn.md). For a less opinionated setup, see +the [contribution guidelines](CONTRIBUTING.md). + +## Overview + +TChannel is a network protocol that supports: + + * A request/response model, + * Multiplexing multiple requests across the same TCP socket, + * Out-of-order responses, + * Streaming requests and responses, + * Checksummed frames, + * Transport of arbitrary payloads, + * Easy implementation in many languages, and + * Redis-like performance. + +This protocol is intended to run on datacenter networks for inter-process +communication. + +## Protocol + +TChannel frames have a fixed-length header and 3 variable-length fields. The +underlying protocol does not assign meaning to these fields, but the included +client/server implementation uses the first field to represent a unique +endpoint or function name in an RPC model. The next two fields can be used for +arbitrary data. Some suggested way to use the 3 fields are: + +* URI path + HTTP method and headers as JSON + body, or +* Function name + headers + thrift/protobuf. + +Note, however, that the only encoding supported by TChannel is UTF-8. If you +want JSON, you'll need to stringify and parse outside of TChannel. + +This design supports efficient routing and forwarding: routers need to parse +the first or second field, but can forward the third field without parsing. + +There is no notion of client and server in this system. Every TChannel instance +is capable of making and receiving requests, and thus requires a unique port on +which to listen. This requirement may change in the future. + +See the [protocol specification][tchan-proto-spec] for more details. + +## Examples + + - [ping](examples/ping): A simple ping/pong example using raw TChannel. + - [thrift](examples/thrift): A Thrift server/client example. + - [keyvalue](examples/keyvalue): A keyvalue Thrift service with separate server and client binaries. + +
+This project is released under the [MIT License](LICENSE.md). + +[doc-img]: https://godoc.org/github.com/uber/tchannel-go?status.svg +[doc]: https://godoc.org/github.com/uber/tchannel-go +[ci-img]: https://travis-ci.org/uber/tchannel-go.svg?branch=master +[ci]: https://travis-ci.org/uber/tchannel-go +[cov-img]: https://coveralls.io/repos/uber/tchannel-go/badge.svg?branch=master&service=github +[cov]: https://coveralls.io/github/uber/tchannel-go?branch=master +[tchan-spec]: http://tchannel.readthedocs.org/en/latest/ +[tchan-proto-spec]: http://tchannel.readthedocs.org/en/latest/protocol/ +[hyperbahn]: https://github.com/uber/hyperbahn diff --git a/vendor/src/github.com/uber/tchannel-go/all_channels.go b/vendor/src/github.com/uber/tchannel-go/all_channels.go new file mode 100644 index 00000000..01d81403 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/all_channels.go @@ -0,0 +1,69 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "sync" +) + +// channelMap is used to ensure that applications don't create multiple channels with +// the same service name in a single process. +var channelMap = struct { + sync.Mutex + existing map[string][]*Channel +}{ + existing: make(map[string][]*Channel), +} + +func registerNewChannel(ch *Channel) { + serviceName := ch.ServiceName() + ch.createdStack = string(getStacks(false /* all */)) + ch.log.WithFields( + LogField{"channelPtr", fmt.Sprintf("%p", ch)}, + LogField{"createdStack", ch.createdStack}, + ).Info("Created new channel.") + + channelMap.Lock() + defer channelMap.Unlock() + + existing := channelMap.existing[serviceName] + channelMap.existing[serviceName] = append(existing, ch) +} + +func removeClosedChannel(ch *Channel) { + channelMap.Lock() + defer channelMap.Unlock() + + channels := channelMap.existing[ch.ServiceName()] + for i, v := range channels { + if v != ch { + continue + } + + // Replace current index with the last element, and truncate channels. + channels[i] = channels[len(channels)-1] + channels = channels[:len(channels)-1] + break + } + + channelMap.existing[ch.ServiceName()] = channels +} diff --git a/vendor/src/github.com/uber/tchannel-go/all_channels_test.go b/vendor/src/github.com/uber/tchannel-go/all_channels_test.go new file mode 100644 index 00000000..5f506b58 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/all_channels_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAllChannelsRegistered(t *testing.T) { + introspectOpts := &IntrospectionOptions{IncludeOtherChannels: true} + + ch1_1, err := NewChannel("ch1", nil) + require.NoError(t, err, "Channel create failed") + ch1_2, err := NewChannel("ch1", nil) + require.NoError(t, err, "Channel create failed") + ch2_1, err := NewChannel("ch2", nil) + require.NoError(t, err, "Channel create failed") + + state := ch1_1.IntrospectState(introspectOpts) + assert.Equal(t, 1, len(state.OtherChannels["ch1"])) + assert.Equal(t, 1, len(state.OtherChannels["ch2"])) + + ch1_2.Close() + + state = ch1_1.IntrospectState(introspectOpts) + assert.Equal(t, 0, len(state.OtherChannels["ch1"])) + assert.Equal(t, 1, len(state.OtherChannels["ch2"])) + + ch2_2, err := NewChannel("ch2", nil) + + state = ch1_1.IntrospectState(introspectOpts) + require.NoError(t, err, "Channel create failed") + assert.Equal(t, 0, len(state.OtherChannels["ch1"])) + assert.Equal(t, 2, len(state.OtherChannels["ch2"])) + + ch1_1.Close() + ch2_1.Close() + ch2_2.Close() + + state = ch1_1.IntrospectState(introspectOpts) + assert.Equal(t, 0, len(state.OtherChannels["ch1"])) + assert.Equal(t, 0, len(state.OtherChannels["ch2"])) +} diff --git a/vendor/src/github.com/uber/tchannel-go/arguments.go b/vendor/src/github.com/uber/tchannel-go/arguments.go new file mode 100644 index 00000000..f0d46a25 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/arguments.go @@ -0,0 +1,150 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bufio" + "encoding/json" + "io" + "io/ioutil" + + "github.com/uber/tchannel-go/internal/argreader" +) + +// ArgReader is the interface for the arg2 and arg3 streams on an +// OutboundCallResponse and an InboundCall +type ArgReader io.ReadCloser + +// ArgWriter is the interface for the arg2 and arg3 streams on an OutboundCall +// and an InboundCallResponse +type ArgWriter interface { + io.WriteCloser + + // Flush flushes the currently written bytes without waiting for the frame + // to be filled. + Flush() error +} + +// ArgWritable is an interface for providing arg2 and arg3 writer streams; +// implemented by reqResWriter e.g. OutboundCall and InboundCallResponse +type ArgWritable interface { + Arg2Writer() (ArgWriter, error) + Arg3Writer() (ArgWriter, error) +} + +// ArgReadable is an interface for providing arg2 and arg3 reader streams; +// implemented by reqResReader e.g. InboundCall and OutboundCallResponse. +type ArgReadable interface { + Arg2Reader() (ArgReader, error) + Arg3Reader() (ArgReader, error) +} + +// ArgReadHelper providers a simpler interface to reading arguments. +type ArgReadHelper struct { + reader ArgReader + err error +} + +// NewArgReader wraps the result of calling ArgXReader to provide a simpler +// interface for reading arguments. +func NewArgReader(reader ArgReader, err error) ArgReadHelper { + return ArgReadHelper{reader, err} +} + +func (r ArgReadHelper) read(f func() error) error { + if r.err != nil { + return r.err + } + if err := f(); err != nil { + return err + } + if err := argreader.EnsureEmpty(r.reader, "read arg"); err != nil { + return err + } + return r.reader.Close() +} + +// Read reads from the reader into the byte slice. +func (r ArgReadHelper) Read(bs *[]byte) error { + return r.read(func() error { + var err error + *bs, err = ioutil.ReadAll(r.reader) + return err + }) +} + +// ReadJSON deserializes JSON from the underlying reader into data. +func (r ArgReadHelper) ReadJSON(data interface{}) error { + return r.read(func() error { + // TChannel allows for 0 length values (not valid JSON), so we use a bufio.Reader + // to check whether data is of 0 length. + reader := bufio.NewReader(r.reader) + if _, err := reader.Peek(1); err == io.EOF { + // If the data is 0 length, then we don't try to read anything. + return nil + } else if err != nil { + return err + } + + d := json.NewDecoder(reader) + return d.Decode(data) + }) +} + +// ArgWriteHelper providers a simpler interface to writing arguments. +type ArgWriteHelper struct { + writer io.WriteCloser + err error +} + +// NewArgWriter wraps the result of calling ArgXWriter to provider a simpler +// interface for writing arguments. +func NewArgWriter(writer io.WriteCloser, err error) ArgWriteHelper { + return ArgWriteHelper{writer, err} +} + +func (w ArgWriteHelper) write(f func() error) error { + if w.err != nil { + return w.err + } + + if err := f(); err != nil { + return err + } + + return w.writer.Close() +} + +// Write writes the given bytes to the underlying writer. +func (w ArgWriteHelper) Write(bs []byte) error { + return w.write(func() error { + _, err := w.writer.Write(bs) + return err + }) +} + +// WriteJSON writes the given object as JSON. +func (w ArgWriteHelper) WriteJSON(data interface{}) error { + return w.write(func() error { + e := json.NewEncoder(w.writer) + return e.Encode(data) + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/arguments_test.go b/vendor/src/github.com/uber/tchannel-go/arguments_test.go new file mode 100644 index 00000000..377218f8 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/arguments_test.go @@ -0,0 +1,99 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "io" + "io/ioutil" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type bufferWithClose struct { + *bytes.Buffer + closed bool +} + +var _ io.WriteCloser = &bufferWithClose{} +var _ io.ReadCloser = &bufferWithClose{} + +func newWriter() *bufferWithClose { + return &bufferWithClose{bytes.NewBuffer(nil), false} +} + +func newReader(bs []byte) *bufferWithClose { + return &bufferWithClose{bytes.NewBuffer(bs), false} +} + +func (w *bufferWithClose) Close() error { + w.closed = true + return nil +} + +type testObject struct { + Name string `json:"name"` + Value int `json:"value"` +} + +func TestJSONInputOutput(t *testing.T) { + obj := testObject{Name: "Foo", Value: 20756} + + writer := newWriter() + require.Nil(t, NewArgWriter(writer, nil).WriteJSON(obj)) + assert.True(t, writer.closed) + assert.Equal(t, "{\"name\":\"Foo\",\"value\":20756}\n", writer.String()) + + reader := newReader(writer.Bytes()) + outObj := testObject{} + require.Nil(t, NewArgReader(reader, nil).ReadJSON(&outObj)) + + assert.True(t, reader.closed) + assert.Equal(t, "Foo", outObj.Name) + assert.Equal(t, 20756, outObj.Value) +} + +func TestReadNotEmpty(t *testing.T) { + // Note: The contents need to be larger than the default buffer size of bufio.NewReader. + r := bytes.NewReader([]byte("{}" + strings.Repeat("{}\n", 10000))) + + var data map[string]interface{} + reader := NewArgReader(ioutil.NopCloser(r), nil) + require.Error(t, reader.ReadJSON(&data), "Read should fail due to extra bytes") +} + +func BenchmarkArgReaderWriter(b *testing.B) { + obj := testObject{Name: "Foo", Value: 20756} + outObj := testObject{} + + for i := 0; i < b.N; i++ { + writer := newWriter() + NewArgWriter(writer, nil).WriteJSON(obj) + reader := newReader(writer.Bytes()) + NewArgReader(reader, nil).ReadJSON(&outObj) + } + + b.StopTimer() + assert.Equal(b, obj, outObj) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/benchclient/main.go b/vendor/src/github.com/uber/tchannel-go/benchmark/benchclient/main.go new file mode 100644 index 00000000..3cb67904 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/benchclient/main.go @@ -0,0 +1,112 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// benchclient is used to make requests to a specific server. +package main + +import ( + "bufio" + "flag" + "fmt" + "log" + "os" + "strconv" + "strings" + "time" + + "github.com/uber/tchannel-go/benchmark" +) + +var ( + serviceName = flag.String("service", "bench-server", "The benchmark server's service name") + timeout = flag.Duration("timeout", time.Second, "Timeout for each request") + requestSize = flag.Int("request-size", 10000, "The number of bytes of each request") + noLibrary = flag.Bool("no-library", false, "Whether to use the template based library instead of TChannel's client library") + numClients = flag.Int("num-clients", 1, "Number of concurrent clients to run in process") + noDurations = flag.Bool("no-durations", false, "Disable printing of latencies to stdout") +) + +func main() { + flag.Parse() + + opts := []benchmark.Option{ + benchmark.WithServiceName(*serviceName), + benchmark.WithTimeout(*timeout), + benchmark.WithRequestSize(*requestSize), + benchmark.WithNumClients(*numClients), + } + if *noLibrary { + opts = append(opts, benchmark.WithNoLibrary()) + } + + client := benchmark.NewClient(flag.Args(), opts...) + fmt.Println("bench-client started") + + rdr := bufio.NewScanner(os.Stdin) + for rdr.Scan() { + line := rdr.Text() + parts := strings.Split(line, " ") + var n int + var err error + if len(parts) >= 2 { + n, err = strconv.Atoi(parts[1]) + if err != nil { + log.Fatalf("unrecognized number %q: %v", parts[1], err) + } + } + + switch cmd := parts[0]; cmd { + case "warmup": + if err := client.Warmup(); err != nil { + log.Fatalf("warmup failed: %v", err) + } + fmt.Println("success") + continue + case "rcall": + makeCalls(n, client.RawCall) + case "tcall": + makeCalls(n, client.ThriftCall) + case "quit": + return + default: + log.Fatalf("unrecognized command: %v", line) + } + } + + if err := rdr.Err(); err != nil { + log.Fatalf("Reader failed: %v", err) + } +} + +func makeCalls(n int, f func(n int) ([]time.Duration, error)) { + durations, err := f(n) + if err != nil { + log.Fatalf("Call failed: %v", err) + } + if !*noDurations { + for i, d := range durations { + if i > 0 { + fmt.Printf(" ") + } + fmt.Printf("%v", d) + } + } + fmt.Println() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/benchserver/main.go b/vendor/src/github.com/uber/tchannel-go/benchmark/benchserver/main.go new file mode 100644 index 00000000..df675eb9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/benchserver/main.go @@ -0,0 +1,78 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// benchserver is used to receive requests for benchmarks. +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "log" + "os" + "strings" + + "github.com/uber/tchannel-go/benchmark" +) + +var ( + serviceName = flag.String("service", "bench-server", "The benchmark server's service name") + advertiseHosts = flag.String("advertise-hosts", "", "Comma-separated list of hosts to advertise to") +) + +func main() { + flag.Parse() + + var adHosts []string + if len(*advertiseHosts) > 0 { + adHosts = strings.Split(*advertiseHosts, ",") + } + + server := benchmark.NewServer( + benchmark.WithServiceName(*serviceName), + benchmark.WithAdvertiseHosts(adHosts), + ) + + fmt.Println(server.HostPort()) + + rdr := bufio.NewReader(os.Stdin) + for { + line, err := rdr.ReadString('\n') + if err != nil { + if err == io.EOF { + return + } + log.Fatalf("stdin read failed: %v", err) + } + + line = strings.TrimSuffix(line, "\n") + switch line { + case "count-raw": + fmt.Println(server.RawCalls()) + case "count-thrift": + fmt.Println(server.ThriftCalls()) + case "quit": + return + default: + log.Fatalf("unrecognized command: %v", line) + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/build_manager.go b/vendor/src/github.com/uber/tchannel-go/benchmark/build_manager.go new file mode 100644 index 00000000..ffda38c6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/build_manager.go @@ -0,0 +1,78 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "io/ioutil" + "os" + "os/exec" + "sync" +) + +type buildManager struct { + sync.RWMutex + builds map[string]*build +} + +type build struct { + once sync.Once + mainFile string + + binaryFile string + buildErr error +} + +func newBuildManager() *buildManager { + return &buildManager{ + builds: make(map[string]*build), + } +} + +func (m *buildManager) GoBinary(mainFile string) (string, error) { + m.Lock() + bld, ok := m.builds[mainFile] + if !ok { + bld = &build{mainFile: mainFile} + m.builds[mainFile] = bld + } + m.Unlock() + + bld.once.Do(bld.Build) + return bld.binaryFile, bld.buildErr +} + +func (b *build) Build() { + tempFile, err := ioutil.TempFile("", "bench") + if err != nil { + panic("Failed to create temp file: " + err.Error()) + } + tempFile.Close() + + buildCmd := exec.Command("go", "build", "-o", tempFile.Name(), b.mainFile) + buildCmd.Stdout = os.Stdout + buildCmd.Stderr = os.Stderr + if err := buildCmd.Run(); err != nil { + b.buildErr = err + return + } + + b.binaryFile = tempFile.Name() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/client_server_bench_test.go b/vendor/src/github.com/uber/tchannel-go/benchmark/client_server_bench_test.go new file mode 100644 index 00000000..3664751b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/client_server_bench_test.go @@ -0,0 +1,82 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "log" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func BenchmarkServer(b *testing.B) { + server := NewServer() + + client := NewClient([]string{server.HostPort()}, + WithExternalProcess(), + WithNoLibrary(), + WithNumClients(10), + WithNoDurations(), + WithTimeout(10*time.Second), + ) + + assert.NoError(b, client.Warmup(), "Warmup failed") + + b.ResetTimer() + started := time.Now() + _, err := client.RawCall(b.N) + total := time.Since(started) + assert.NoError(b, err, "client.RawCall failed") + + if n := server.RawCalls(); b.N > n { + b.Errorf("Server received %v calls, expected at least %v calls", n, b.N) + } + log.Printf("Calls: %v Duration: %v RPS: %.0f", b.N, total, float64(b.N)/total.Seconds()) +} + +func BenchmarkClient(b *testing.B) { + servers := make([]Server, 3) + serverHosts := make([]string, len(servers)) + for i := range servers { + servers[i] = NewServer( + WithExternalProcess(), + WithNoLibrary(), + ) + serverHosts[i] = servers[i].HostPort() + } + + // To saturate a single process, we need to have multiple clients. + client := NewClient(serverHosts, + WithNoChecking(), + WithNumClients(10), + ) + require.NoError(b, client.Warmup(), "Warmup failed") + + b.ResetTimer() + started := time.Now() + if _, err := client.RawCall(b.N); err != nil { + b.Fatalf("Call failed: %v", err) + } + total := time.Since(started) + log.Printf("Calls: %v Duration: %v RPS: %.0f", b.N, total, float64(b.N)/total.Seconds()) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/external_client.go b/vendor/src/github.com/uber/tchannel-go/benchmark/external_client.go new file mode 100644 index 00000000..3f26b829 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/external_client.go @@ -0,0 +1,100 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +// externalClient represents a benchmark client running out-of-process. +type externalClient struct { + *externalCmd + opts *options +} + +func newExternalClient(hosts []string, opts *options) Client { + benchArgs := []string{ + "--service", opts.svcName, + "--timeout", opts.timeout.String(), + "--request-size", strconv.Itoa(opts.reqSize), + "--num-clients", strconv.Itoa(opts.numClients), + } + if opts.noDurations { + benchArgs = append(benchArgs, "--no-durations") + } + if opts.noLibrary { + benchArgs = append(benchArgs, "--no-library") + } + benchArgs = append(benchArgs, hosts...) + + cmd, initial := newExternalCmd("benchclient/main.go", benchArgs) + if !strings.Contains(initial, "started") { + panic("bench-client did not start, got: " + initial) + } + + return &externalClient{cmd, opts} +} + +func (c *externalClient) Warmup() error { + out, err := c.writeAndRead("warmup") + if err != nil { + return err + } + if out != "success" { + return fmt.Errorf("warmup failed: %v", out) + } + return nil +} + +func (c *externalClient) callAndParse(cmd string) ([]time.Duration, error) { + out, err := c.writeAndRead(cmd) + if err != nil { + return nil, err + } + + if out == "" { + return nil, nil + } + + durationStrs := strings.Split(out, " ") + durations := make([]time.Duration, len(durationStrs)) + for i, s := range durationStrs { + d, err := time.ParseDuration(s) + if err != nil { + return nil, fmt.Errorf("calls failed: %v", out) + } + + durations[i] = d + } + + return durations, nil +} + +func (c *externalClient) RawCall(n int) ([]time.Duration, error) { + return c.callAndParse(fmt.Sprintf("rcall %v", n)) +} + +func (c *externalClient) ThriftCall(n int) ([]time.Duration, error) { + return c.callAndParse(fmt.Sprintf("tcall %v", n)) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/external_common.go b/vendor/src/github.com/uber/tchannel-go/benchmark/external_common.go new file mode 100644 index 00000000..e8c3c225 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/external_common.go @@ -0,0 +1,92 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "bufio" + "io" + "os" + "os/exec" +) + +var _bm = newBuildManager() + +// externalCmd handles communication with an external benchmark client. +type externalCmd struct { + cmd *exec.Cmd + stdoutOrig io.ReadCloser + stdout *bufio.Scanner + stdin io.WriteCloser +} + +func newExternalCmd(mainFile string, benchArgs []string) (*externalCmd, string) { + bin, err := _bm.GoBinary(BenchmarkDir + mainFile) + if err != nil { + panic("failed to compile " + mainFile + ": " + err.Error()) + } + + cmd := exec.Command(bin, benchArgs...) + cmd.Stderr = os.Stderr + stdout, err := cmd.StdoutPipe() + if err != nil { + panic("failed to create stdout: " + err.Error()) + } + + stdin, err := cmd.StdinPipe() + if err != nil { + panic("failed to create stdin: " + err.Error()) + } + + if err := cmd.Start(); err != nil { + panic("failed to start external process: " + err.Error()) + } + + stdoutScanner := bufio.NewScanner(stdout) + if !stdoutScanner.Scan() { + panic("failed to check if external process started: " + err.Error()) + } + + out := stdoutScanner.Text() + return &externalCmd{ + cmd: cmd, + stdin: stdin, + stdout: stdoutScanner, + stdoutOrig: stdout, + }, out +} + +func (c *externalCmd) writeAndRead(cmd string) (string, error) { + if _, err := io.WriteString(c.stdin, cmd+"\n"); err != nil { + return "", err + } + + if c.stdout.Scan() { + return c.stdout.Text(), nil + } + + return "", c.stdout.Err() +} + +func (c *externalCmd) Close() { + c.stdin.Close() + c.stdoutOrig.Close() + c.cmd.Process.Kill() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/external_server.go b/vendor/src/github.com/uber/tchannel-go/benchmark/external_server.go new file mode 100644 index 00000000..a4e678a5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/external_server.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "net" + "strconv" + "strings" +) + +// externalServer represents a benchmark server running out-of-process. +type externalServer struct { + *externalCmd + hostPort string + opts *options +} + +func newExternalServer(opts *options) Server { + benchArgs := []string{ + "--service", opts.svcName, + } + if len(opts.advertiseHosts) > 0 { + benchArgs = append(benchArgs, + "--advertise-hosts", strings.Join(opts.advertiseHosts, ",")) + } + + cmd, hostPortStr := newExternalCmd("benchserver/main.go", benchArgs) + if _, _, err := net.SplitHostPort(hostPortStr); err != nil { + panic("bench-server did not print host:port on startup: " + err.Error()) + } + + return &externalServer{cmd, hostPortStr, opts} +} + +func (s *externalServer) HostPort() string { + return s.hostPort +} + +func (s *externalServer) RawCalls() int { + return s.writeAndReadInt("count-raw") +} + +func (s *externalServer) ThriftCalls() int { + return s.writeAndReadInt("count-thrift") +} + +func (s *externalServer) writeAndReadInt(cmd string) int { + v, err := s.writeAndRead(cmd) + if err != nil { + panic(err) + } + + vInt, err := strconv.Atoi(v) + if err != nil { + panic(err) + } + + return vInt +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/frame_templates.go b/vendor/src/github.com/uber/tchannel-go/benchmark/frame_templates.go new file mode 100644 index 00000000..1e967759 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/frame_templates.go @@ -0,0 +1,150 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "bytes" + "encoding/binary" + "io" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" +) + +const ( + _idOffset = 4 /* size (2) + type (1) + reserved (1) */ + _idOffsetEnd = _idOffset + 4 /* length */ +) + +type frames struct { + outgoing [][]byte + incoming [][]byte +} + +func (f frames) duplicate() frames { + return frames{ + outgoing: deepCopyByteSlice(f.outgoing), + incoming: deepCopyByteSlice(f.incoming), + } +} + +func deepCopyByteSlice(bs [][]byte) [][]byte { + newBs := make([][]byte, len(bs)) + for i, b := range bs { + newBs[i] = make([]byte, len(b)) + copy(newBs[i], b) + } + return newBs +} + +func (f frames) writeInitReq(w io.Writer) error { + _, err := w.Write(f.outgoing[0]) + return err +} + +func (f frames) writeInitRes(w io.Writer) error { + _, err := w.Write(f.incoming[0]) + return err +} + +func (f frames) writeCallReq(id uint32, w io.Writer) (int, error) { + frames := f.outgoing[1:] + return f.writeMulti(id, w, frames) +} + +func (f frames) writeCallRes(id uint32, w io.Writer) (int, error) { + frames := f.incoming[1:] + return f.writeMulti(id, w, frames) +} + +func (f frames) writeMulti(id uint32, w io.Writer, frames [][]byte) (int, error) { + written := 0 + for _, f := range frames { + binary.BigEndian.PutUint32(f[_idOffset:_idOffsetEnd], id) + if _, err := w.Write(f); err != nil { + return written, err + } + written++ + } + + return written, nil +} + +func getRawCallFrames(timeout time.Duration, svcName string, reqSize int) frames { + var fs frames + modifier := func(fromClient bool, f *tchannel.Frame) *tchannel.Frame { + buf := &bytes.Buffer{} + if err := f.WriteOut(buf); err != nil { + panic(err) + } + + if fromClient { + fs.outgoing = append(fs.outgoing, buf.Bytes()) + } else { + fs.incoming = append(fs.incoming, buf.Bytes()) + } + + return f + } + + withNewServerClient(svcName, func(server, client *tchannel.Channel) { + testutils.RegisterEcho(server, nil) + + relay, err := NewTCPFrameRelay([]string{server.PeerInfo().HostPort}, modifier) + if err != nil { + panic(err) + } + defer relay.Close() + + args := &raw.Args{ + Arg2: getRequestBytes(reqSize), + Arg3: getRequestBytes(reqSize), + } + + ctx, cancel := tchannel.NewContext(timeout) + defer cancel() + + if _, _, _, err := raw.Call(ctx, client, relay.HostPort(), svcName, "echo", args.Arg2, args.Arg3); err != nil { + panic(err) + } + }) + + return fs +} + +func withNewServerClient(svcName string, f func(server, client *tchannel.Channel)) { + opts := testutils.NewOpts().SetServiceName(svcName) + server, err := testutils.NewServerChannel(opts) + if err != nil { + panic(err) + } + defer server.Close() + + client, err := testutils.NewClientChannel(opts) + if err != nil { + panic(err) + } + defer client.Close() + + f(server, client) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/interfaces.go b/vendor/src/github.com/uber/tchannel-go/benchmark/interfaces.go new file mode 100644 index 00000000..ffe6350e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/interfaces.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import "time" + +// BenchmarkDir should be set to the benchmark source directory. +var BenchmarkDir = "./" + +// Client is a benchmark client that can be used to call a benchmark server. +type Client interface { + // Warmup will create connections to all host:ports the client was created with. + Warmup() error + + // RawCall makes an echo call using raw. + RawCall(n int) ([]time.Duration, error) + + // ThriftCall makes an echo call using thrift. + ThriftCall(n int) ([]time.Duration, error) + + // Close closes the benchmark client. + Close() +} + +// inProcClient represents a client that is running in the same process. +// It adds methods to reduce allocations. +type inProcClient interface { + Client + + // RawCallBuffer will make n raw calls and store the latencies in the specified buffer. + RawCallBuffer(latencies []time.Duration) error + + // ThriftCallBuffer will make n thrift calls and store the latencies in the specified buffer. + ThriftCallBuffer(latencies []time.Duration) error +} + +// Server is a benchmark server that can receive requests. +type Server interface { + // HostPort returns the HostPort that the server is listening on. + HostPort() string + + // Close closes the benchmark server. + Close() + + // RawCalls returns the number of raw calls the server has received. + RawCalls() int + + // ThriftCalls returns the number of Thrift calls the server has received. + ThriftCalls() int +} + +// Relay represents a relay for benchmarking. +type Relay interface { + // HostPort is the host:port that the relay is listening on. + HostPort() string + + // Close clsoes the relay. + Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/internal_client.go b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_client.go new file mode 100644 index 00000000..06a07760 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_client.go @@ -0,0 +1,173 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "bytes" + "fmt" + "os" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" +) + +// internalClient represents a benchmark client. +type internalClient struct { + ch *tchannel.Channel + sc *tchannel.SubChannel + tClient gen.TChanSecondService + argStr string + argBytes []byte + checkResult bool + opts *options +} + +// NewClient returns a new Client that can make calls to a benchmark server. +func NewClient(hosts []string, optFns ...Option) Client { + opts := getOptions(optFns) + if opts.external { + return newExternalClient(hosts, opts) + } + if opts.numClients > 1 { + return newInternalMultiClient(hosts, opts) + } + return newClient(hosts, opts) +} + +func newClient(hosts []string, opts *options) inProcClient { + if opts.external || opts.numClients > 1 { + panic("newClient got options that should be handled by NewClient") + } + + if opts.noLibrary { + return newInternalTCPClient(hosts, opts) + } + return newInternalClient(hosts, opts) +} + +func newInternalClient(hosts []string, opts *options) inProcClient { + ch, err := tchannel.NewChannel(opts.svcName, &tchannel.ChannelOptions{ + Logger: tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn), + }) + if err != nil { + panic("failed to create channel: " + err.Error()) + } + for _, host := range hosts { + ch.Peers().Add(host) + } + thriftClient := thrift.NewClient(ch, opts.svcName, nil) + client := gen.NewTChanSecondServiceClient(thriftClient) + + return &internalClient{ + ch: ch, + sc: ch.GetSubChannel(opts.svcName), + tClient: client, + argBytes: getRequestBytes(opts.reqSize), + argStr: getRequestString(opts.reqSize), + opts: opts, + } +} + +func (c *internalClient) Warmup() error { + for _, peer := range c.ch.Peers().Copy() { + ctx, cancel := tchannel.NewContext(c.opts.timeout) + _, err := peer.GetConnection(ctx) + cancel() + + if err != nil { + return err + } + } + + return nil +} + +func (c *internalClient) makeCalls(latencies []time.Duration, f func() (time.Duration, error)) error { + for i := range latencies { + var err error + latencies[i], err = f() + if err != nil { + return err + } + } + return nil +} + +func (c *internalClient) RawCallBuffer(latencies []time.Duration) error { + return c.makeCalls(latencies, func() (time.Duration, error) { + ctx, cancel := tchannel.NewContext(c.opts.timeout) + defer cancel() + + started := time.Now() + rArg2, rArg3, _, err := raw.CallSC(ctx, c.sc, "echo", c.argBytes, c.argBytes) + duration := time.Since(started) + + if err != nil { + return 0, err + } + if c.checkResult { + if !bytes.Equal(rArg2, c.argBytes) || !bytes.Equal(rArg3, c.argBytes) { + fmt.Println("Arg2", rArg2, "Expect", c.argBytes) + fmt.Println("Arg3", rArg3, "Expect", c.argBytes) + panic("echo call returned wrong results") + } + } + return duration, nil + }) +} + +func (c *internalClient) RawCall(n int) ([]time.Duration, error) { + latencies := make([]time.Duration, n) + return latencies, c.RawCallBuffer(latencies) +} + +func (c *internalClient) ThriftCallBuffer(latencies []time.Duration) error { + return c.makeCalls(latencies, func() (time.Duration, error) { + ctx, cancel := thrift.NewContext(c.opts.timeout) + defer cancel() + + started := time.Now() + res, err := c.tClient.Echo(ctx, c.argStr) + duration := time.Since(started) + + if err != nil { + return 0, err + } + if c.checkResult { + if res != c.argStr { + panic("thrift Echo returned wrong result") + } + } + return duration, nil + }) +} + +func (c *internalClient) ThriftCall(n int) ([]time.Duration, error) { + latencies := make([]time.Duration, n) + return latencies, c.ThriftCallBuffer(latencies) +} + +func (c *internalClient) Close() { + c.ch.Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/internal_multi_client.go b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_multi_client.go new file mode 100644 index 00000000..405a372e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_multi_client.go @@ -0,0 +1,109 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "time" + + "github.com/uber/tchannel-go/testutils" +) + +type internalMultiClient struct { + clients []inProcClient +} + +func newInternalMultiClient(hosts []string, opts *options) Client { + clients := make([]inProcClient, opts.numClients) + opts.numClients = 1 + + for i := range clients { + clients[i] = newClient(hosts, opts) + } + + return &internalMultiClient{clients: clients} +} + +func (c *internalMultiClient) Warmup() error { + for _, c := range c.clients { + if err := c.Warmup(); err != nil { + return err + } + } + return nil +} + +func (c *internalMultiClient) Close() { + for _, client := range c.clients { + client.Close() + } +} + +func (c *internalMultiClient) RawCall(n int) ([]time.Duration, error) { + return c.makeCalls(n, func(c inProcClient) callFunc { + return c.RawCallBuffer + }) +} + +func (c *internalMultiClient) ThriftCall(n int) ([]time.Duration, error) { + return c.makeCalls(n, func(c inProcClient) callFunc { + return c.ThriftCallBuffer + }) +} + +type callFunc func([]time.Duration) error + +type clientToCallFunc func(c inProcClient) callFunc + +func (c *internalMultiClient) makeCalls(n int, f clientToCallFunc) ([]time.Duration, error) { + buckets := testutils.Buckets(n, len(c.clients)) + errCs := make([]chan error, len(c.clients)) + + var start int + latencies := make([]time.Duration, n) + for i := range c.clients { + calls := buckets[i] + + end := start + calls + errCs[i] = c.callUsingClient(latencies[start:end], f(c.clients[i])) + start = end + } + + for _, errC := range errCs { + if err := <-errC; err != nil { + return nil, err + } + } + + return latencies, nil +} + +func (c *internalMultiClient) callUsingClient(latencies []time.Duration, f callFunc) chan error { + errC := make(chan error, 1) + if len(latencies) == 0 { + errC <- nil + return errC + } + + go func() { + errC <- f(latencies) + }() + return errC +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/internal_server.go b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_server.go new file mode 100644 index 00000000..207e7d6d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_server.go @@ -0,0 +1,130 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "fmt" + "os" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/hyperbahn" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +// internalServer represents a benchmark server. +type internalServer struct { + ch *tchannel.Channel + opts *options + rawCalls atomic.Int64 + thriftCalls atomic.Int64 +} + +// NewServer returns a new Server that can recieve Thrift calls or raw calls. +func NewServer(optFns ...Option) Server { + opts := getOptions(optFns) + if opts.external { + return newExternalServer(opts) + } + + ch, err := tchannel.NewChannel(opts.svcName, &tchannel.ChannelOptions{ + Logger: tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn), + }) + if err != nil { + panic("failed to create channel: " + err.Error()) + } + if err := ch.ListenAndServe("127.0.0.1:0"); err != nil { + panic("failed to listen on port 0: " + err.Error()) + } + + s := &internalServer{ + ch: ch, + opts: opts, + } + + tServer := thrift.NewServer(ch) + tServer.Register(gen.NewTChanSecondServiceServer(handler{calls: &s.thriftCalls})) + ch.Register(raw.Wrap(rawHandler{calls: &s.rawCalls}), "echo") + + if len(opts.advertiseHosts) > 0 { + if err := s.Advertise(opts.advertiseHosts); err != nil { + panic("failed to advertise: " + err.Error()) + } + } + + return s +} + +// HostPort returns the host:port that the server is listening on. +func (s *internalServer) HostPort() string { + return s.ch.PeerInfo().HostPort +} + +// Advertise advertises with Hyperbahn. +func (s *internalServer) Advertise(hyperbahnHosts []string) error { + config := hyperbahn.Configuration{InitialNodes: hyperbahnHosts} + hc, err := hyperbahn.NewClient(s.ch, config, nil) + if err != nil { + panic("failed to setup Hyperbahn client: " + err.Error()) + } + return hc.Advertise() +} + +func (s *internalServer) Close() { + s.ch.Close() +} + +func (s *internalServer) RawCalls() int { + return int(s.rawCalls.Load()) +} + +func (s *internalServer) ThriftCalls() int { + return int(s.thriftCalls.Load()) +} + +type rawHandler struct { + calls *atomic.Int64 +} + +func (rawHandler) OnError(ctx context.Context, err error) { + fmt.Println("benchmark.Server error:", err) +} + +func (h rawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + h.calls.Inc() + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil +} + +type handler struct { + calls *atomic.Int64 +} + +func (h handler) Echo(ctx thrift.Context, arg1 string) (string, error) { + h.calls.Inc() + return arg1, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_client.go b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_client.go new file mode 100644 index 00000000..a538c948 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_client.go @@ -0,0 +1,172 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "fmt" + "math/rand" + "net" + "time" + + "github.com/uber/tchannel-go" +) + +// internalTCPClient represents a TCP client that makes +// TChannel calls using raw TCP packets. +type internalTCPClient struct { + host string + lastID uint32 + responseIDs chan uint32 + conn net.Conn + frames frames + opts *options +} + +func newInternalTCPClient(hosts []string, opts *options) inProcClient { + return &internalTCPClient{ + host: hosts[rand.Intn(len(hosts))], + responseIDs: make(chan uint32, 1000), + frames: getRawCallFrames(opts.timeout, opts.svcName, opts.reqSize), + lastID: 1, + opts: opts, + } +} + +func (c *internalTCPClient) Warmup() error { + conn, err := net.Dial("tcp", c.host) + if err != nil { + return err + } + + c.conn = conn + go c.readConn() + + if err := c.frames.writeInitReq(conn); err != nil { + panic(err) + } + + return nil +} + +func (c *internalTCPClient) readConn() { + defer close(c.responseIDs) + + wantFirstID := true + f := tchannel.NewFrame(tchannel.MaxFrameSize) + for { + err := f.ReadIn(c.conn) + if err != nil { + return + } + + if wantFirstID { + if f.Header.ID != 1 { + panic(fmt.Errorf("Expected first response ID to be 1, got %v", f.Header.ID)) + } + wantFirstID = false + continue + } + + c.responseIDs <- f.Header.ID + } +} + +type call struct { + id uint32 + started time.Time + numFrames int +} + +func (c *internalTCPClient) makeCalls(latencies []time.Duration, f func() (call, error)) error { + n := len(latencies) + calls := make(map[uint32]*call, n) + + for i := 0; i < n; i++ { + c, err := f() + if err != nil { + return err + } + + calls[c.id] = &c + } + + timer := time.NewTimer(c.opts.timeout) + + // Use the original underlying slice for latencies. + durations := latencies[:0] + for { + if len(calls) == 0 { + return nil + } + + timer.Reset(c.opts.timeout) + select { + case id, ok := <-c.responseIDs: + if !ok { + panic("expecting more calls, but connection is closed") + } + call, ok := calls[id] + if !ok { + panic(fmt.Errorf("received unexpected response frame: %v", id)) + } + + call.numFrames-- + if call.numFrames != 0 { + continue + } + durations = append(durations, time.Since(call.started)) + delete(calls, id) + case <-timer.C: + return tchannel.ErrTimeout + } + } +} + +func (c *internalTCPClient) RawCallBuffer(latencies []time.Duration) error { + return c.makeCalls(latencies, func() (call, error) { + c.lastID++ + + started := time.Now() + numFrames, err := c.frames.writeCallReq(c.lastID, c.conn) + if err != nil { + return call{}, err + } + + return call{c.lastID, started, numFrames}, nil + }) +} + +func (c *internalTCPClient) RawCall(n int) ([]time.Duration, error) { + latencies := make([]time.Duration, n) + return latencies, c.RawCallBuffer(latencies) +} + +func (c *internalTCPClient) ThriftCallBuffer(latencies []time.Duration) error { + panic("not yet implemented") +} + +func (c *internalTCPClient) ThriftCall(n int) ([]time.Duration, error) { + panic("not yet implemented") +} + +func (c *internalTCPClient) Close() { + c.conn.Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_server.go b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_server.go new file mode 100644 index 00000000..22a432d4 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/internal_tcp_server.go @@ -0,0 +1,124 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "log" + "net" + + "github.com/uber/tchannel-go" + + "github.com/uber-go/atomic" +) + +// internalTCPServer represents a TCP server responds to TChannel +// calls using raw TCP packets. +type internalTCPServer struct { + frames frames + ln net.Listener + opts *options + rawCalls atomic.Int64 +} + +func newInternalTCPServer(opts *options) Server { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + panic(err) + } + + s := &internalTCPServer{ + ln: ln, + frames: getRawCallFrames(opts.timeout, opts.svcName, opts.reqSize), + opts: opts, + } + go s.acceptLoop() + return s +} + +func (s *internalTCPServer) acceptLoop() { + for { + conn, err := s.ln.Accept() + if err, ok := err.(net.Error); ok && err.Temporary() { + continue + } + if err != nil { + return + } + + go s.handleConn(conn) + } +} + +func (s *internalTCPServer) handleConn(conn net.Conn) { + c := make(chan uint32, 1000) + defer close(c) + go s.writeResponses(conn, c) + + var lastID uint32 + + f := tchannel.NewFrame(tchannel.MaxFrameSize) + for { + if err := f.ReadIn(conn); err != nil { + return + } + + if f.Header.ID > lastID { + c <- f.Header.ID + lastID = f.Header.ID + } + } +} + +func (s *internalTCPServer) writeResponses(conn net.Conn, ids chan uint32) { + frames := s.frames.duplicate() + + for id := range ids { + if id == 1 { + if err := frames.writeInitRes(conn); err != nil { + log.Printf("writeInitRes failed: %v", err) + } + continue + } + + s.rawCalls.Inc() + if _, err := frames.writeCallRes(id, conn); err != nil { + log.Printf("writeCallRes failed: %v", err) + return + } + } +} + +func (s *internalTCPServer) HostPort() string { + return s.ln.Addr().String() +} + +func (s *internalTCPServer) RawCalls() int { + return int(s.rawCalls.Load()) +} + +func (s *internalTCPServer) ThriftCalls() int { + // Server does not support Thrift calls currently. + return 0 +} + +func (s *internalTCPServer) Close() { + s.ln.Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/matrix_test.go b/vendor/src/github.com/uber/tchannel-go/benchmark/matrix_test.go new file mode 100644 index 00000000..aaf0838c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/matrix_test.go @@ -0,0 +1,158 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "fmt" + "testing" + + "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// combinations will call f with every combination of selecting elements +// from a slice with the specified length. +// e.g. for 2, the callback would be: +// f(false, false) +// f(false, true) +// f(true, false) +// f(true, true) +func combinations(length int, f func([]bool)) { + cur := make([]bool, length) + toGenerate := (1 << uint(length)) + + f(cur) + for i := 0; i < toGenerate-1; i++ { + var digit int + for digit = length - 1; cur[digit]; digit-- { + cur[digit] = false + } + cur[digit] = true + f(cur) + } +} + +func TestCombinations(t *testing.T) { + tests := []struct { + length int + want [][]bool + }{ + { + length: 1, + want: [][]bool{{false}, {true}}, + }, + { + length: 2, + want: [][]bool{{false, false}, {false, true}, {true, false}, {true, true}}, + }, + } + + for _, tt := range tests { + var got [][]bool + recordCombs := func(comb []bool) { + copied := append([]bool(nil), comb...) + got = append(got, copied) + } + combinations(tt.length, recordCombs) + assert.Equal(t, tt.want, got, "Mismatch for combinations of length %v", tt.length) + } +} + +func selectOptions(options []Option, toSelect []bool) []Option { + var opts []Option + for i, v := range toSelect { + if v { + opts = append(opts, options[i]) + } + } + return opts +} + +func combineOpts(base, override []Option) []Option { + resultOpts := append([]Option(nil), base...) + return append(resultOpts, override...) +} + +func runSingleTest(t *testing.T, baseOpts, serverOpts, clientOpts []Option) { + serverOpts = combineOpts(baseOpts, serverOpts) + clientOpts = combineOpts(baseOpts, clientOpts) + + msgP := fmt.Sprintf("%+v: ", struct { + serverOpts options + clientOpts options + }{*(getOptions(serverOpts)), *(getOptions(clientOpts))}) + + server := NewServer(serverOpts...) + defer server.Close() + + client := NewClient([]string{server.HostPort()}, clientOpts...) + defer client.Close() + + require.NoError(t, client.Warmup(), msgP+"Client warmup failed") + + durations, err := client.RawCall(0) + require.NoError(t, err, msgP+"Call(0) failed") + assert.Equal(t, 0, len(durations), msgP+"Wrong number of calls") + + assert.Equal(t, 0, server.RawCalls(), msgP+"server.RawCalls mismatch") + assert.Equal(t, 0, server.ThriftCalls(), msgP+"server.ThriftCalls mismatch") + + expectCalls := 0 + for i := 1; i < 10; i *= 2 { + durations, err = client.RawCall(i) + require.NoError(t, err, msgP+"Call(%v) failed", i) + require.Equal(t, i, len(durations), msgP+"Wrong number of calls") + + expectCalls += i + require.Equal(t, expectCalls, server.RawCalls(), msgP+"server.RawCalls mismatch") + require.Equal(t, 0, server.ThriftCalls(), msgP+"server.ThriftCalls mismatch") + } +} + +func TestServerClientMatrix(t *testing.T) { + tests := [][]Option{ + {WithServiceName("other")}, + {WithRequestSize(tchannel.MaxFrameSize)}, + } + + // These options can be independently applied to the server or the client. + independentOpts := []Option{ + WithExternalProcess(), + WithNoLibrary(), + } + + // These options only apply to the client. + clientOnlyOpts := combineOpts(independentOpts, []Option{ + WithNumClients(5), + }) + + for _, tt := range tests { + combinations(len(independentOpts), func(serverSelect []bool) { + combinations(len(clientOnlyOpts), func(clientSelect []bool) { + serverOpts := selectOptions(independentOpts, serverSelect) + clientOpts := selectOptions(clientOnlyOpts, clientSelect) + runSingleTest(t, tt, serverOpts, clientOpts) + }) + }) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/options.go b/vendor/src/github.com/uber/tchannel-go/benchmark/options.go new file mode 100644 index 00000000..b04f4a92 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/options.go @@ -0,0 +1,126 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import "time" + +type options struct { + external bool + svcName string + noLibrary bool + + // Following options only make sense for clients. + noChecking bool + timeout time.Duration + reqSize int + numClients int + + // noDurations disables printing of durations to stdout. + // This only applies to clients running out-of-process. + noDurations bool + + // Following options only make sense for servers. + advertiseHosts []string +} + +// Option represents a Benchmark option. +type Option func(*options) + +// WithTimeout sets the timeout to use for each call. +func WithTimeout(timeout time.Duration) Option { + return func(opts *options) { + opts.timeout = timeout + } +} + +// WithRequestSize sets the request size for each call. +func WithRequestSize(reqSize int) Option { + return func(opts *options) { + opts.reqSize = reqSize + } +} + +// WithServiceName sets the service name of the benchmark server. +func WithServiceName(svcName string) Option { + return func(opts *options) { + opts.svcName = svcName + } +} + +// WithExternalProcess creates a separate process to host the server/client. +func WithExternalProcess() Option { + return func(opts *options) { + opts.external = true + } +} + +// WithNoLibrary uses the fast TCP-template based approach for generating +// TChannel frames rather than the TChannel client library. +func WithNoLibrary() Option { + return func(opts *options) { + opts.noLibrary = true + } +} + +// WithNoChecking disables result verification on the client side, which +// may slow down the client (as it compares all request bytes against the +// response bytes). +func WithNoChecking() Option { + return func(opts *options) { + opts.noChecking = true + } +} + +// WithNumClients sets the number of concurrent TChannel clients to use +// internally under a single benchmark.Client. This is used to generate +// generate a large amount of traffic, as a single TChannel client will +// not saturate a CPU since it will spend most of the time blocking and +// waiting for the remote side to respond. +func WithNumClients(numClients int) Option { + return func(opts *options) { + opts.numClients = numClients + } +} + +// WithNoDurations disables printing of latencies to standard out. +func WithNoDurations() Option { + return func(opts *options) { + opts.noDurations = true + } +} + +// WithAdvertiseHosts sets the hosts to advertise with on startup. +func WithAdvertiseHosts(hosts []string) Option { + return func(opts *options) { + opts.advertiseHosts = hosts + } +} + +func getOptions(optFns []Option) *options { + opts := &options{ + timeout: time.Second, + svcName: "bench-server", + } + for _, opt := range optFns { + opt(opts) + } + return opts +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/real_relay.go b/vendor/src/github.com/uber/tchannel-go/benchmark/real_relay.go new file mode 100644 index 00000000..ea1bddc5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/real_relay.go @@ -0,0 +1,81 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "errors" + "os" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/relay" + "github.com/uber/tchannel-go/relay/relaytest" + + "github.com/uber-go/atomic" +) + +type fixedHosts struct { + hosts map[string][]string + pickI atomic.Int32 +} + +func (fh *fixedHosts) Get(cf relay.CallFrame, _ *tchannel.Connection) (string, error) { + peers := fh.hosts[string(cf.Service())] + if len(peers) == 0 { + return "", errors.New("no peers") + } + + pickI := int(fh.pickI.Inc()-1) % len(peers) + return peers[pickI], nil +} + +type realRelay struct { + ch *tchannel.Channel + hosts *fixedHosts +} + +// NewRealRelay creates a TChannel relay. +func NewRealRelay(services map[string][]string) (Relay, error) { + hosts := &fixedHosts{hosts: services} + ch, err := tchannel.NewChannel("relay", &tchannel.ChannelOptions{ + RelayHost: relaytest.HostFunc(hosts.Get), + Logger: tchannel.NewLevelLogger(tchannel.NewLogger(os.Stderr), tchannel.LogLevelWarn), + }) + if err != nil { + return nil, err + } + + if err := ch.ListenAndServe("127.0.0.1:0"); err != nil { + return nil, err + } + + return &realRelay{ + ch: ch, + hosts: hosts, + }, nil +} + +func (r *realRelay) HostPort() string { + return r.ch.PeerInfo().HostPort +} + +func (r *realRelay) Close() { + r.ch.Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/req_bytes.go b/vendor/src/github.com/uber/tchannel-go/benchmark/req_bytes.go new file mode 100644 index 00000000..5d2af5cc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/req_bytes.go @@ -0,0 +1,39 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +func getRequestBytes(n int) []byte { + bs := make([]byte, n) + for i := range bs { + bs[i] = byte(i) + } + return bs +} + +func getRequestString(n int) string { + // TODO: we should replace this with base64 once we drop go1.4 support. + chars := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz") + bs := make([]byte, n) + for i := range bs { + bs[i] = chars[i%len(chars)] + } + return string(bs) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_bench_test.go b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_bench_test.go new file mode 100644 index 00000000..1a78c075 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_bench_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "io" + "io/ioutil" + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func echoServer(tb testing.TB) net.Listener { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(tb, err, "Listen failed") + + go func() { + conn, err := ln.Accept() + require.NoError(tb, err, "Accept failed") + + // Echo the connection back to itself. + io.Copy(conn, conn) + }() + + return ln +} + +func benchmarkClient(b *testing.B, dst string, reqSize int) { + req := getRequestBytes(reqSize) + totalExpected := b.N * reqSize + + conn, err := net.Dial("tcp", dst) + require.NoError(b, err, "Failed to connect to destination") + defer conn.Close() + + readerDone := make(chan struct{}) + go func() { + defer close(readerDone) + n, err := io.CopyN(ioutil.Discard, conn, int64(totalExpected)) + assert.NoError(b, err, "Expected %v response bytes, got %v", totalExpected, n) + }() + + b.SetBytes(int64(reqSize)) + for i := 0; i < b.N; i++ { + _, err := conn.Write(req) + require.NoError(b, err, "Write failed") + } + + <-readerDone +} + +func benchmarkTCPDirect(b *testing.B, reqSize int) { + ln := echoServer(b) + benchmarkClient(b, ln.Addr().String(), reqSize) +} + +func BenchmarkTCPDirect100Bytes(b *testing.B) { + benchmarkTCPDirect(b, 100) +} + +func BenchmarkTCPDirect1k(b *testing.B) { + benchmarkTCPDirect(b, 1024) +} + +func BenchmarkTCPDirect4k(b *testing.B) { + benchmarkTCPDirect(b, 4*1024) +} + +func benchmarkTCPRelay(b *testing.B, reqSize int) { + ln := echoServer(b) + + relay, err := NewTCPRawRelay([]string{ln.Addr().String()}) + require.NoError(b, err, "Relay failed") + defer relay.Close() + + benchmarkClient(b, relay.HostPort(), reqSize) +} + +func BenchmarkTCPRelay100Bytes(b *testing.B) { + benchmarkTCPRelay(b, 100) +} + +func BenchmarkTCPRelay1kBytes(b *testing.B) { + benchmarkTCPRelay(b, 1024) +} + +func BenchmarkTCPRelay4k(b *testing.B) { + benchmarkTCPRelay(b, 4*1024) +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_frame_relay.go b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_frame_relay.go new file mode 100644 index 00000000..0680ce2b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_frame_relay.go @@ -0,0 +1,79 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "log" + "net" +) +import "github.com/uber/tchannel-go" + +type tcpFrameRelay struct { + *tcpRelay + modifier func(bool, *tchannel.Frame) *tchannel.Frame +} + +// NewTCPFrameRelay relays frames from one connection to another. It reads +// and writes frames using the TChannel frame functions. +func NewTCPFrameRelay(dests []string, modifier func(bool, *tchannel.Frame) *tchannel.Frame) (Relay, error) { + var err error + r := &tcpFrameRelay{modifier: modifier} + + r.tcpRelay, err = newTCPRelay(dests, r.handleConnFrameRelay) + if err != nil { + return nil, err + } + + return r, nil +} + +func (r *tcpFrameRelay) handleConnFrameRelay(fromClient bool, src, dst net.Conn) { + pool := tchannel.NewSyncFramePool() + frameCh := make(chan *tchannel.Frame, 100) + defer close(frameCh) + + go func() { + for f := range frameCh { + if err := f.WriteOut(dst); err != nil { + log.Printf("Failed to write out frame: %v", err) + return + } + pool.Release(f) + } + }() + + for { + f := pool.Get() + if err := f.ReadIn(src); err != nil { + return + } + + if r.modifier != nil { + f = r.modifier(fromClient, f) + } + + select { + case frameCh <- f: + default: + panic("frame buffer full") + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_raw_relay.go b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_raw_relay.go new file mode 100644 index 00000000..cc83b9cf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/benchmark/tcp_raw_relay.go @@ -0,0 +1,100 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmark + +import ( + "io" + "log" + "net" + + "github.com/uber-go/atomic" +) + +type tcpRelay struct { + destI atomic.Int32 + dests []string + ln net.Listener + handleConn func(fromClient bool, src, dst net.Conn) +} + +func newTCPRelay(dests []string, handleConn func(fromClient bool, src, dst net.Conn)) (*tcpRelay, error) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + + relay := &tcpRelay{ + dests: dests, + ln: ln, + handleConn: handleConn, + } + go relay.acceptLoop() + return relay, nil +} + +// NewTCPRawRelay creates a relay that just pipes data from one connection +// to another directly. +func NewTCPRawRelay(dests []string) (Relay, error) { + return newTCPRelay(dests, func(_ bool, src, dst net.Conn) { + io.Copy(src, dst) + }) +} + +func (r *tcpRelay) acceptLoop() { + for { + conn, err := r.ln.Accept() + if err, ok := err.(net.Error); ok && err.Temporary() { + continue + } + if err != nil { + return + } + + go r.handleIncoming(conn) + } +} + +func (r *tcpRelay) handleIncoming(src net.Conn) { + defer src.Close() + + dst, err := net.Dial("tcp", r.nextDestination()) + if err != nil { + log.Printf("Connection failed: %v", err) + return + } + defer dst.Close() + + go r.handleConn(true, src, dst) + r.handleConn(false, dst, src) +} + +func (r *tcpRelay) nextDestination() string { + i := int(r.destI.Inc()-1) % len(r.dests) + return r.dests[i] +} + +func (r *tcpRelay) HostPort() string { + return r.ln.Addr().String() +} + +func (r *tcpRelay) Close() { + r.ln.Close() +} diff --git a/vendor/src/github.com/uber/tchannel-go/calloptions.go b/vendor/src/github.com/uber/tchannel-go/calloptions.go new file mode 100644 index 00000000..9b8ec011 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/calloptions.go @@ -0,0 +1,93 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +// Format is the arg scheme used for a specific call. +type Format string + +// The list of formats supported by tchannel. +const ( + HTTP Format = "http" + JSON Format = "json" + Raw Format = "raw" + Thrift Format = "thrift" +) + +func (f Format) String() string { + return string(f) +} + +// CallOptions are options for a specific call. +type CallOptions struct { + // Format is arg scheme used for this call, sent in the "as" header. + // This header is only set if the Format is set. + Format Format + + // ShardKey determines where this call request belongs, used with ringpop applications. + ShardKey string + + // RequestState stores request state across retry attempts. + RequestState *RequestState + + // RoutingKey identifies the destined traffic group. Relays may favor the + // routing key over the service name to route the request to a specialized + // traffic group. + RoutingKey string + + // RoutingDelegate identifies a traffic group capable of routing a request + // to an instance of the intended service. + RoutingDelegate string + + // callerName can only be used when forwarding a request. It can only be set internally, + // e.g. by calling (*InboundCall).CallOptions() when forwarding a request + callerName string +} + +var defaultCallOptions = &CallOptions{} + +func (c *CallOptions) setHeaders(headers transportHeaders) { + headers[ArgScheme] = Raw.String() + c.overrideHeaders(headers) +} + +// overrideHeaders sets headers if the call options contains non-default values. +func (c *CallOptions) overrideHeaders(headers transportHeaders) { + if c.Format != "" { + headers[ArgScheme] = c.Format.String() + } + if c.ShardKey != "" { + headers[ShardKey] = c.ShardKey + } + if c.RoutingKey != "" { + headers[RoutingKey] = c.RoutingKey + } + if c.RoutingDelegate != "" { + headers[RoutingDelegate] = c.RoutingDelegate + } + if c.callerName != "" { + headers[CallerName] = c.callerName + } +} + +// setResponseHeaders copies some headers from the incoming call request to the response. +func setResponseHeaders(reqHeaders, respHeaders transportHeaders) { + respHeaders[ArgScheme] = reqHeaders[ArgScheme] +} diff --git a/vendor/src/github.com/uber/tchannel-go/calloptions_test.go b/vendor/src/github.com/uber/tchannel-go/calloptions_test.go new file mode 100644 index 00000000..a7a471cd --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/calloptions_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetHeaders(t *testing.T) { + tests := []struct { + format Format + routingDelegate string + routingKey string + expectedHeaders transportHeaders + }{ + { + // When no format is specified, Raw should be used by default. + format: "", + expectedHeaders: transportHeaders{ArgScheme: Raw.String()}, + }, + { + format: Thrift, + expectedHeaders: transportHeaders{ArgScheme: Thrift.String()}, + }, + { + format: JSON, + routingDelegate: "xpr", + expectedHeaders: transportHeaders{ + ArgScheme: JSON.String(), + RoutingDelegate: "xpr", + }, + }, + { + format: JSON, + routingKey: "canary", + expectedHeaders: transportHeaders{ + ArgScheme: JSON.String(), + RoutingKey: "canary", + }, + }, + } + + for _, tt := range tests { + callOpts := &CallOptions{ + Format: tt.format, + RoutingDelegate: tt.routingDelegate, + RoutingKey: tt.routingKey, + } + headers := make(transportHeaders) + callOpts.setHeaders(headers) + assert.Equal(t, tt.expectedHeaders, headers) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/channel.go b/vendor/src/github.com/uber/tchannel-go/channel.go new file mode 100644 index 00000000..6c2746ed --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/channel.go @@ -0,0 +1,787 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + "net" + "os" + "path/filepath" + "runtime" + "strings" + "sync" + "time" + + "github.com/uber/tchannel-go/tnet" + + "github.com/opentracing/opentracing-go" + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +var ( + errAlreadyListening = errors.New("channel already listening") + errInvalidStateForOp = errors.New("channel is in an invalid state for that method") + + // ErrNoServiceName is returned when no service name is provided when + // creating a new channel. + ErrNoServiceName = errors.New("no service name provided") +) + +const ephemeralHostPort = "0.0.0.0:0" + +// ChannelOptions are used to control parameters on a create a TChannel +type ChannelOptions struct { + // Default Connection options + DefaultConnectionOptions ConnectionOptions + + // The name of the process, for logging and reporting to peers + ProcessName string + + // OnPeerStatusChanged is an optional callback that receives a notification + // whenever the channel establishes a usable connection to a peer, or loses + // a connection to a peer. + OnPeerStatusChanged func(*Peer) + + // The logger to use for this channel + Logger Logger + + // The host:port selection implementation to use for relaying. This is an + // unstable API - breaking changes are likely. + RelayHost RelayHost + + // The list of service names that should be handled locally by this channel. + // This is an unstable API - breaking changes are likely. + RelayLocalHandlers []string + + // The maximum allowable timeout for relayed calls (longer timeouts are + // clamped to this value). Passing zero uses the default of 2m. + RelayMaxTimeout time.Duration + + // The reporter to use for reporting stats for this channel. + StatsReporter StatsReporter + + // TimeNow is a variable for overriding time.Now in unit tests. + // Note: This is not a stable part of the API and may change. + TimeNow func() time.Time + + // Tracer is an OpenTracing Tracer used to manage distributed tracing spans. + // If not set, opentracing.GlobalTracer() is used. + Tracer opentracing.Tracer + + // Handler is an alternate handler for all inbound requests, overriding the + // default handler that delegates to a subchannel. + Handler Handler +} + +// ChannelState is the state of a channel. +type ChannelState int + +const ( + // ChannelClient is a channel that can be used as a client. + ChannelClient ChannelState = iota + 1 + + // ChannelListening is a channel that is listening for new connnections. + ChannelListening + + // ChannelStartClose is a channel that has received a Close request. + // The channel is no longer listening, and all new incoming connections are rejected. + ChannelStartClose + + // ChannelInboundClosed is a channel that has drained all incoming connections, but may + // have outgoing connections. All incoming calls and new outgoing calls are rejected. + ChannelInboundClosed + + // ChannelClosed is a channel that has closed completely. + ChannelClosed +) + +//go:generate stringer -type=ChannelState + +// A Channel is a bi-directional connection to the peering and routing network. +// Applications can use a Channel to make service calls to remote peers via +// BeginCall, or to listen for incoming calls from peers. Applications that +// want to receive requests should call one of Serve or ListenAndServe +// TODO(prashant): Shutdown all subchannels + peers when channel is closed. +type Channel struct { + channelConnectionCommon + + chID uint32 + createdStack string + commonStatsTags map[string]string + connectionOptions ConnectionOptions + peers *PeerList + relayHost RelayHost + relayMaxTimeout time.Duration + handler Handler + onPeerStatusChanged func(*Peer) + + // mutable contains all the members of Channel which are mutable. + mutable struct { + sync.RWMutex // protects members of the mutable struct. + state ChannelState + peerInfo LocalPeerInfo // May be ephemeral if this is a client only channel + l net.Listener // May be nil if this is a client only channel + conns map[uint32]*Connection + } +} + +// channelConnectionCommon is the list of common objects that both use +// and can be copied directly from the channel to the connection. +type channelConnectionCommon struct { + log Logger + relayLocal map[string]struct{} + statsReporter StatsReporter + tracer opentracing.Tracer + subChannels *subChannelMap + timeNow func() time.Time +} + +// _nextChID is used to allocate unique IDs to every channel for debugging purposes. +var _nextChID atomic.Uint32 + +// Tracer returns the OpenTracing Tracer for this channel. If no tracer was provided +// in the configuration, returns opentracing.GlobalTracer(). Note that this approach +// allows opentracing.GlobalTracer() to be initialized _after_ the channel is created. +func (ccc channelConnectionCommon) Tracer() opentracing.Tracer { + if ccc.tracer != nil { + return ccc.tracer + } + return opentracing.GlobalTracer() +} + +// NewChannel creates a new Channel. The new channel can be used to send outbound requests +// to peers, but will not listen or handling incoming requests until one of ListenAndServe +// or Serve is called. The local service name should be passed to serviceName. +func NewChannel(serviceName string, opts *ChannelOptions) (*Channel, error) { + if serviceName == "" { + return nil, ErrNoServiceName + } + + if opts == nil { + opts = &ChannelOptions{} + } + + processName := opts.ProcessName + if processName == "" { + processName = fmt.Sprintf("%s[%d]", filepath.Base(os.Args[0]), os.Getpid()) + } + + logger := opts.Logger + if logger == nil { + logger = NullLogger + } + logger = logger.WithFields( + LogField{"service", serviceName}, + LogField{"process", processName}, + ) + + statsReporter := opts.StatsReporter + if statsReporter == nil { + statsReporter = NullStatsReporter + } + + timeNow := opts.TimeNow + if timeNow == nil { + timeNow = time.Now + } + + chID := _nextChID.Inc() + ch := &Channel{ + channelConnectionCommon: channelConnectionCommon{ + log: logger.WithFields( + LogField{"chID", chID}, + LogField{"service", serviceName}, + LogField{"process", processName}), + relayLocal: toStringSet(opts.RelayLocalHandlers), + statsReporter: statsReporter, + subChannels: &subChannelMap{}, + timeNow: timeNow, + tracer: opts.Tracer, + }, + chID: chID, + connectionOptions: opts.DefaultConnectionOptions.withDefaults(), + relayHost: opts.RelayHost, + relayMaxTimeout: validateRelayMaxTimeout(opts.RelayMaxTimeout, logger), + } + ch.peers = newRootPeerList(ch, opts.OnPeerStatusChanged).newChild() + + if opts.Handler != nil { + ch.handler = opts.Handler + } else { + ch.handler = channelHandler{ch} + } + + ch.mutable.peerInfo = LocalPeerInfo{ + PeerInfo: PeerInfo{ + ProcessName: processName, + HostPort: ephemeralHostPort, + IsEphemeral: true, + Version: PeerVersion{ + Language: "go", + LanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + TChannelVersion: VersionInfo, + }, + }, + ServiceName: serviceName, + } + ch.mutable.state = ChannelClient + ch.mutable.conns = make(map[uint32]*Connection) + ch.createCommonStats() + + // Register internal unless the root handler has been overridden, since + // Register will panic. + if opts.Handler == nil { + ch.registerInternal() + } + + registerNewChannel(ch) + + if opts.RelayHost != nil { + opts.RelayHost.SetChannel(ch) + } + return ch, nil +} + +// ConnectionOptions returns the channel's connection options. +func (ch *Channel) ConnectionOptions() *ConnectionOptions { + return &ch.connectionOptions +} + +// Serve serves incoming requests using the provided listener. +// The local peer info is set synchronously, but the actual socket listening is done in +// a separate goroutine. +func (ch *Channel) Serve(l net.Listener) error { + mutable := &ch.mutable + mutable.Lock() + defer mutable.Unlock() + + if mutable.l != nil { + return errAlreadyListening + } + mutable.l = tnet.Wrap(l) + + if mutable.state != ChannelClient { + return errInvalidStateForOp + } + mutable.state = ChannelListening + + mutable.peerInfo.HostPort = l.Addr().String() + mutable.peerInfo.IsEphemeral = false + ch.log = ch.log.WithFields(LogField{"hostPort", mutable.peerInfo.HostPort}) + + peerInfo := mutable.peerInfo + ch.log.WithFields( + LogField{"hostPort", peerInfo.HostPort}, + ).Info("Channel is listening.") + go ch.serve() + return nil +} + +// ListenAndServe listens on the given address and serves incoming requests. +// The port may be 0, in which case the channel will use an OS assigned port +// This method does not block as the handling of connections is done in a goroutine. +func (ch *Channel) ListenAndServe(hostPort string) error { + mutable := &ch.mutable + mutable.RLock() + + if mutable.l != nil { + mutable.RUnlock() + return errAlreadyListening + } + + l, err := net.Listen("tcp", hostPort) + if err != nil { + mutable.RUnlock() + return err + } + + mutable.RUnlock() + return ch.Serve(l) +} + +// Registrar is the base interface for registering handlers on either the base +// Channel or the SubChannel +type Registrar interface { + // ServiceName returns the service name that this Registrar is for. + ServiceName() string + + // Register registers a handler for ServiceName and the given method. + Register(h Handler, methodName string) + + // Logger returns the logger for this Registrar. + Logger() Logger + + // StatsReporter returns the stats reporter for this Registrar + StatsReporter() StatsReporter + + // StatsTags returns the tags that should be used. + StatsTags() map[string]string + + // Peers returns the peer list for this Registrar. + Peers() *PeerList +} + +// Register registers a handler for a method. +// +// The handler is registered with the service name used when the Channel was +// created. To register a handler with a different service name, obtain a +// SubChannel for that service with GetSubChannel, and Register a handler +// under that. You may also use SetHandler on a SubChannel to set up a +// catch-all Handler for that service. See the docs for SetHandler for more +// information. +// +// Register panics if the channel was constructed with an alternate root +// handler. +func (ch *Channel) Register(h Handler, methodName string) { + if _, ok := ch.handler.(channelHandler); !ok { + panic("can't register handler when channel configured with alternate root handler") + } + ch.GetSubChannel(ch.PeerInfo().ServiceName).Register(h, methodName) +} + +// PeerInfo returns the current peer info for the channel +func (ch *Channel) PeerInfo() LocalPeerInfo { + ch.mutable.RLock() + peerInfo := ch.mutable.peerInfo + ch.mutable.RUnlock() + + return peerInfo +} + +func (ch *Channel) createCommonStats() { + ch.commonStatsTags = map[string]string{ + "app": ch.mutable.peerInfo.ProcessName, + "service": ch.mutable.peerInfo.ServiceName, + } + host, err := os.Hostname() + if err != nil { + ch.log.WithFields(ErrField(err)).Info("Channel creation failed to get host.") + return + } + ch.commonStatsTags["host"] = host + // TODO(prashant): Allow user to pass extra tags (such as cluster, version). +} + +// GetSubChannel returns a SubChannel for the given service name. If the subchannel does not +// exist, it is created. +func (ch *Channel) GetSubChannel(serviceName string, opts ...SubChannelOption) *SubChannel { + sub, added := ch.subChannels.getOrAdd(serviceName, ch) + if added { + for _, opt := range opts { + opt(sub) + } + } + return sub +} + +// Peers returns the PeerList for the channel. +func (ch *Channel) Peers() *PeerList { + return ch.peers +} + +// RootPeers returns the root PeerList for the channel, which is the sole place +// new Peers are created. All children of the root list (including ch.Peers()) +// automatically re-use peers from the root list and create new peers in the +// root list. +func (ch *Channel) RootPeers() *RootPeerList { + return ch.peers.parent +} + +// BeginCall starts a new call to a remote peer, returning an OutboundCall that can +// be used to write the arguments of the call. +func (ch *Channel) BeginCall(ctx context.Context, hostPort, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) { + p := ch.RootPeers().GetOrAdd(hostPort) + return p.BeginCall(ctx, serviceName, methodName, callOptions) +} + +// serve runs the listener to accept and manage new incoming connections, blocking +// until the channel is closed. +func (ch *Channel) serve() { + acceptBackoff := 0 * time.Millisecond + + for { + netConn, err := ch.mutable.l.Accept() + if err != nil { + // Backoff from new accepts if this is a temporary error + if ne, ok := err.(net.Error); ok && ne.Temporary() { + if acceptBackoff == 0 { + acceptBackoff = 5 * time.Millisecond + } else { + acceptBackoff *= 2 + } + if max := 1 * time.Second; acceptBackoff > max { + acceptBackoff = max + } + ch.log.WithFields( + ErrField(err), + LogField{"backoff", acceptBackoff}, + ).Warn("Accept error, will wait and retry.") + time.Sleep(acceptBackoff) + continue + } else { + // Only log an error if this didn't happen due to a Close. + if ch.State() >= ChannelStartClose { + return + } + ch.log.WithFields(ErrField(err)).Fatal("Unrecoverable accept error, closing server.") + return + } + } + + acceptBackoff = 0 + + // Perform the connection handshake in a background goroutine. + go func() { + // Register the connection in the peer once the channel is set up. + events := connectionEvents{ + OnActive: ch.inboundConnectionActive, + OnCloseStateChange: ch.connectionCloseStateChange, + OnExchangeUpdated: ch.exchangeUpdated, + } + if _, err := ch.inboundHandshake(context.Background(), netConn, events); err != nil { + netConn.Close() + } + }() + } +} + +// Ping sends a ping message to the given hostPort and waits for a response. +func (ch *Channel) Ping(ctx context.Context, hostPort string) error { + peer := ch.RootPeers().GetOrAdd(hostPort) + conn, err := peer.GetConnection(ctx) + if err != nil { + return err + } + + return conn.ping(ctx) +} + +// Logger returns the logger for this channel. +func (ch *Channel) Logger() Logger { + return ch.log +} + +// StatsReporter returns the stats reporter for this channel. +func (ch *Channel) StatsReporter() StatsReporter { + return ch.statsReporter +} + +// StatsTags returns the common tags that should be used when reporting stats. +// It returns a new map for each call. +func (ch *Channel) StatsTags() map[string]string { + m := make(map[string]string) + for k, v := range ch.commonStatsTags { + m[k] = v + } + return m +} + +// ServiceName returns the serviceName that this channel was created for. +func (ch *Channel) ServiceName() string { + return ch.PeerInfo().ServiceName +} + +// Connect creates a new outbound connection to hostPort. +func (ch *Channel) Connect(ctx context.Context, hostPort string) (*Connection, error) { + switch state := ch.State(); state { + case ChannelClient, ChannelListening: + break + default: + ch.log.Debugf("Connect rejecting new connection as state is %v", state) + return nil, errInvalidStateForOp + } + + // The context timeout applies to the whole call, but users may want a lower + // connect timeout (e.g. for streams). + if params := getTChannelParams(ctx); params != nil && params.connectTimeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, params.connectTimeout) + defer cancel() + } + + events := connectionEvents{ + OnActive: ch.outboundConnectionActive, + OnCloseStateChange: ch.connectionCloseStateChange, + OnExchangeUpdated: ch.exchangeUpdated, + } + + if err := ctx.Err(); err != nil { + return nil, GetContextError(err) + } + + timeout := getTimeout(ctx) + tcpConn, err := dialContext(ctx, hostPort) + if err != nil { + if ne, ok := err.(net.Error); ok && ne.Timeout() { + ch.log.WithFields( + LogField{"remoteHostPort", hostPort}, + LogField{"timeout", timeout}, + ).Info("Outbound net.Dial timed out.") + err = ErrTimeout + } else if ctx.Err() == context.Canceled { + ch.log.WithFields( + LogField{"remoteHostPort", hostPort}, + ).Info("Outbound net.Dial was cancelled.") + err = GetContextError(ErrRequestCancelled) + } else { + ch.log.WithFields( + ErrField(err), + LogField{"remoteHostPort", hostPort}, + ).Info("Outbound net.Dial failed.") + } + return nil, err + } + + conn, err := ch.outboundHandshake(ctx, tcpConn, hostPort, events) + if conn != nil { + // It's possible that the connection we just created responds with a host:port + // that is not what we tried to connect to. E.g., we may have connected to + // 127.0.0.1:1234, but the returned host:port may be 10.0.0.1:1234. + // In this case, the connection won't be added to 127.0.0.1:1234 peer + // and so future calls to that peer may end up creating new connections. To + // avoid this issue, and to avoid clients being aware of any TCP relays, we + // add the connection to the intended peer. + if hostPort != conn.remotePeerInfo.HostPort { + conn.log.Debugf("Outbound connection host:port mismatch, adding to peer %v", conn.remotePeerInfo.HostPort) + ch.addConnectionToPeer(hostPort, conn, outbound) + } + } + + return conn, err +} + +// exchangeUpdated updates the peer heap. +func (ch *Channel) exchangeUpdated(c *Connection) { + if c.remotePeerInfo.HostPort == "" { + // Hostport is unknown until we get init resp. + return + } + + p, ok := ch.RootPeers().Get(c.remotePeerInfo.HostPort) + if !ok { + return + } + + ch.updatePeer(p) +} + +// updatePeer updates the score of the peer and update it's position in heap as well. +func (ch *Channel) updatePeer(p *Peer) { + ch.peers.onPeerChange(p) + ch.subChannels.updatePeer(p) + p.callOnUpdateComplete() +} + +// addConnection adds the connection to the channel's list of connection +// if the channel is in a valid state to accept this connection. It returns +// whether the connection was added. +func (ch *Channel) addConnection(c *Connection, direction connectionDirection) bool { + ch.mutable.Lock() + defer ch.mutable.Unlock() + + if c.readState() != connectionActive { + return false + } + + switch state := ch.mutable.state; state { + case ChannelClient, ChannelListening: + break + default: + return false + } + + ch.mutable.conns[c.connID] = c + return true +} + +func (ch *Channel) connectionActive(c *Connection, direction connectionDirection) { + c.log.Debugf("New active %v connection for peer %v", direction, c.remotePeerInfo.HostPort) + + if added := ch.addConnection(c, direction); !added { + // The channel isn't in a valid state to accept this connection, close the connection. + c.close(LogField{"reason", "new active connection on closing channel"}) + return + } + + ch.addConnectionToPeer(c.remotePeerInfo.HostPort, c, direction) +} + +func (ch *Channel) addConnectionToPeer(hostPort string, c *Connection, direction connectionDirection) { + p := ch.RootPeers().GetOrAdd(hostPort) + if err := p.addConnection(c, direction); err != nil { + c.log.WithFields( + LogField{"remoteHostPort", c.remotePeerInfo.HostPort}, + LogField{"direction", direction}, + ErrField(err), + ).Warn("Failed to add connection to peer.") + } + + ch.updatePeer(p) +} + +func (ch *Channel) inboundConnectionActive(c *Connection) { + ch.connectionActive(c, inbound) +} + +func (ch *Channel) outboundConnectionActive(c *Connection) { + ch.connectionActive(c, outbound) +} + +// removeClosedConn removes a connection if it's closed. +// Until a connection is fully closed, the channel must keep track of it. +func (ch *Channel) removeClosedConn(c *Connection) { + if c.readState() != connectionClosed { + return + } + + ch.mutable.Lock() + delete(ch.mutable.conns, c.connID) + ch.mutable.Unlock() +} + +func (ch *Channel) getMinConnectionState() connectionState { + minState := connectionClosed + for _, c := range ch.mutable.conns { + if s := c.readState(); s < minState { + minState = s + } + } + return minState +} + +// connectionCloseStateChange is called when a connection's close state changes. +func (ch *Channel) connectionCloseStateChange(c *Connection) { + ch.removeClosedConn(c) + if peer, ok := ch.RootPeers().Get(c.remotePeerInfo.HostPort); ok { + peer.connectionCloseStateChange(c) + ch.updatePeer(peer) + } + if c.outboundHP != "" && c.outboundHP != c.remotePeerInfo.HostPort { + // Outbound connections may be in multiple peers. + if peer, ok := ch.RootPeers().Get(c.outboundHP); ok { + peer.connectionCloseStateChange(c) + ch.updatePeer(peer) + } + } + + chState := ch.State() + if chState != ChannelStartClose && chState != ChannelInboundClosed { + return + } + + ch.mutable.RLock() + minState := ch.getMinConnectionState() + ch.mutable.RUnlock() + + var updateTo ChannelState + if minState >= connectionClosed { + updateTo = ChannelClosed + } else if minState >= connectionInboundClosed && chState == ChannelStartClose { + updateTo = ChannelInboundClosed + } + + var updatedToState ChannelState + if updateTo > 0 { + ch.mutable.Lock() + // Recheck the state as it's possible another goroutine changed the state + // from what we expected, and so we might make a stale change. + if ch.mutable.state == chState { + ch.mutable.state = updateTo + updatedToState = updateTo + } + ch.mutable.Unlock() + chState = updateTo + } + + c.log.Debugf("ConnectionCloseStateChange channel state = %v connection minState = %v", + chState, minState) + + if updatedToState == ChannelClosed { + ch.onClosed() + } +} + +func (ch *Channel) onClosed() { + removeClosedChannel(ch) + ch.log.Infof("Channel closed.") +} + +// Closed returns whether this channel has been closed with .Close() +func (ch *Channel) Closed() bool { + return ch.State() == ChannelClosed +} + +// State returns the current channel state. +func (ch *Channel) State() ChannelState { + ch.mutable.RLock() + state := ch.mutable.state + ch.mutable.RUnlock() + + return state +} + +// Close starts a graceful Close for the channel. This does not happen immediately: +// 1. This call closes the Listener and starts closing connections. +// 2. When all incoming connections are drained, the connection blocks new outgoing calls. +// 3. When all connections are drained, the channel's state is updated to Closed. +func (ch *Channel) Close() { + ch.Logger().Info("Channel.Close called.") + var connections []*Connection + var channelClosed bool + ch.mutable.Lock() + + if ch.mutable.l != nil { + ch.mutable.l.Close() + } + + ch.mutable.state = ChannelStartClose + if len(ch.mutable.conns) == 0 { + ch.mutable.state = ChannelClosed + channelClosed = true + } + for _, c := range ch.mutable.conns { + connections = append(connections, c) + } + ch.mutable.Unlock() + + for _, c := range connections { + c.close(LogField{"reason", "channel closing"}) + } + + if channelClosed { + ch.onClosed() + } +} + +// RelayHost returns the channel's RelayHost, if any. +func (ch *Channel) RelayHost() RelayHost { + return ch.relayHost +} + +func toStringSet(ss []string) map[string]struct{} { + set := make(map[string]struct{}, len(ss)) + for _, s := range ss { + set[s] = struct{}{} + } + return set +} diff --git a/vendor/src/github.com/uber/tchannel-go/channel_test.go b/vendor/src/github.com/uber/tchannel-go/channel_test.go new file mode 100644 index 00000000..0a375415 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/channel_test.go @@ -0,0 +1,184 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "io/ioutil" + "math" + "os" + "runtime" + "strings" + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func toMap(fields LogFields) map[string]interface{} { + m := make(map[string]interface{}) + for _, f := range fields { + m[f.Key] = f.Value + } + return m +} + +func TestNewChannel(t *testing.T) { + ch, err := NewChannel("svc", &ChannelOptions{ + ProcessName: "pname", + }) + require.NoError(t, err, "NewChannel failed") + + assert.Equal(t, LocalPeerInfo{ + ServiceName: "svc", + PeerInfo: PeerInfo{ + ProcessName: "pname", + HostPort: ephemeralHostPort, + IsEphemeral: true, + Version: PeerVersion{ + Language: "go", + LanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + TChannelVersion: VersionInfo, + }, + }, + }, ch.PeerInfo(), "Wrong local peer info") +} + +func TestLoggers(t *testing.T) { + ch, err := NewChannel("svc", &ChannelOptions{ + Logger: NewLogger(ioutil.Discard), + }) + require.NoError(t, err, "NewChannel failed") + defer ch.Close() + + peerInfo := ch.PeerInfo() + fields := toMap(ch.Logger().Fields()) + assert.Equal(t, peerInfo.ServiceName, fields["service"]) + + sc := ch.GetSubChannel("subch") + fields = toMap(sc.Logger().Fields()) + assert.Equal(t, peerInfo.ServiceName, fields["service"]) + assert.Equal(t, "subch", fields["subchannel"]) +} + +func TestStats(t *testing.T) { + ch, err := NewChannel("svc", &ChannelOptions{ + Logger: NewLogger(ioutil.Discard), + }) + require.NoError(t, err, "NewChannel failed") + defer ch.Close() + + hostname, err := os.Hostname() + require.NoError(t, err, "Hostname failed") + + peerInfo := ch.PeerInfo() + tags := ch.StatsTags() + assert.NotNil(t, ch.StatsReporter(), "StatsReporter missing") + assert.Equal(t, peerInfo.ProcessName, tags["app"], "app tag") + assert.Equal(t, peerInfo.ServiceName, tags["service"], "service tag") + assert.Equal(t, hostname, tags["host"], "hostname tag") + + sc := ch.GetSubChannel("subch") + subTags := sc.StatsTags() + assert.NotNil(t, sc.StatsReporter(), "StatsReporter missing") + for k, v := range tags { + assert.Equal(t, v, subTags[k], "subchannel missing tag %v", k) + } + assert.Equal(t, "subch", subTags["subchannel"], "subchannel tag missing") +} + +func TestRelayMaxTTL(t *testing.T) { + tests := []struct { + max time.Duration + expected time.Duration + }{ + {time.Second, time.Second}, + {-time.Second, _defaultRelayMaxTimeout}, + {0, _defaultRelayMaxTimeout}, + {time.Microsecond, _defaultRelayMaxTimeout}, + {math.MaxUint32 * time.Millisecond, math.MaxUint32 * time.Millisecond}, + {(math.MaxUint32 + 1) * time.Millisecond, _defaultRelayMaxTimeout}, + } + + for _, tt := range tests { + ch, err := NewChannel("svc", &ChannelOptions{ + RelayMaxTimeout: tt.max, + }) + assert.NoError(t, err, "Unexpected error when creating channel.") + assert.Equal(t, ch.relayMaxTimeout, tt.expected, "Unexpected max timeout on channel.") + } +} + +func TestIsolatedSubChannelsDontSharePeers(t *testing.T) { + ch, err := NewChannel("svc", &ChannelOptions{ + Logger: NewLogger(ioutil.Discard), + }) + require.NoError(t, err, "NewChannel failed") + defer ch.Close() + + sub := ch.GetSubChannel("svc-ringpop") + if ch.peers != sub.peers { + t.Log("Channel and subchannel don't share the same peer list.") + t.Fail() + } + + isolatedSub := ch.GetSubChannel("svc-shy-ringpop", Isolated) + if ch.peers == isolatedSub.peers { + t.Log("Channel and isolated subchannel share the same peer list.") + t.Fail() + } + + // Nobody knows about the peer. + assert.Nil(t, ch.peers.peersByHostPort["127.0.0.1:3000"]) + assert.Nil(t, sub.peers.peersByHostPort["127.0.0.1:3000"]) + assert.Nil(t, isolatedSub.peers.peersByHostPort["127.0.0.1:3000"]) + + // Uses of the parent channel should be reflected in the subchannel, but + // not the isolated subchannel. + ch.Peers().Add("127.0.0.1:3000") + assert.NotNil(t, ch.peers.peersByHostPort["127.0.0.1:3000"]) + assert.NotNil(t, sub.peers.peersByHostPort["127.0.0.1:3000"]) + assert.Nil(t, isolatedSub.peers.peersByHostPort["127.0.0.1:3000"]) +} + +func TestChannelTracerMethod(t *testing.T) { + mockTracer := mocktracer.New() + ch, err := NewChannel("svc", &ChannelOptions{ + Tracer: mockTracer, + }) + require.NoError(t, err) + defer ch.Close() + assert.Equal(t, mockTracer, ch.Tracer(), "expecting tracer passed at initialization") + + ch, err = NewChannel("svc", &ChannelOptions{}) + require.NoError(t, err) + defer ch.Close() + assert.EqualValues(t, opentracing.GlobalTracer(), ch.Tracer(), "expecting default tracer") + + // because ch.Tracer() function is doing dynamic lookup, we can change global tracer + origTracer := opentracing.GlobalTracer() + defer opentracing.InitGlobalTracer(origTracer) + + opentracing.InitGlobalTracer(mockTracer) + assert.Equal(t, mockTracer, ch.Tracer(), "expecting tracer set as global tracer") +} diff --git a/vendor/src/github.com/uber/tchannel-go/channel_utils_test.go b/vendor/src/github.com/uber/tchannel-go/channel_utils_test.go new file mode 100644 index 00000000..aa14af9a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/channel_utils_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" +) + +// NewServer creates a new server and returns the channel, service name, and host port. +func NewServer(t testing.TB, opts *testutils.ChannelOpts) (*Channel, string, string) { + ch := testutils.NewServer(t, opts) + peerInfo := ch.PeerInfo() + return ch, peerInfo.ServiceName, peerInfo.HostPort +} diff --git a/vendor/src/github.com/uber/tchannel-go/channelstate_string.go b/vendor/src/github.com/uber/tchannel-go/channelstate_string.go new file mode 100644 index 00000000..d34582c6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/channelstate_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=ChannelState; DO NOT EDIT + +package tchannel + +import "fmt" + +const _ChannelState_name = "ChannelClientChannelListeningChannelStartCloseChannelInboundClosedChannelClosed" + +var _ChannelState_index = [...]uint8{0, 13, 29, 46, 66, 79} + +func (i ChannelState) String() string { + i -= 1 + if i < 0 || i+1 >= ChannelState(len(_ChannelState_index)) { + return fmt.Sprintf("ChannelState(%d)", i+1) + } + return _ChannelState_name[_ChannelState_index[i]:_ChannelState_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/checksum.go b/vendor/src/github.com/uber/tchannel-go/checksum.go new file mode 100644 index 00000000..43e6d70b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/checksum.go @@ -0,0 +1,175 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "hash" + "hash/crc32" + "sync" +) + +var checksumPools [checksumCount]sync.Pool + +// A ChecksumType is a checksum algorithm supported by TChannel for checksumming call bodies +type ChecksumType byte + +const ( + // ChecksumTypeNone indicates no checksum is included in the message + ChecksumTypeNone ChecksumType = 0 + + // ChecksumTypeCrc32 indicates the message checksum is calculated using crc32 + ChecksumTypeCrc32 ChecksumType = 1 + + // ChecksumTypeFarmhash indicates the message checksum is calculated using Farmhash + ChecksumTypeFarmhash ChecksumType = 2 + + // ChecksumTypeCrc32C indicates the message checksum is calculated using crc32c + ChecksumTypeCrc32C ChecksumType = 3 + + checksumCount = 4 +) + +func init() { + crc32CastagnoliTable := crc32.MakeTable(crc32.Castagnoli) + + ChecksumTypeNone.pool().New = func() interface{} { + return nullChecksum{} + } + ChecksumTypeCrc32.pool().New = func() interface{} { + return newHashChecksum(ChecksumTypeCrc32, crc32.NewIEEE()) + } + ChecksumTypeCrc32C.pool().New = func() interface{} { + return newHashChecksum(ChecksumTypeCrc32C, crc32.New(crc32CastagnoliTable)) + } + + // TODO: Implement farm hash. + ChecksumTypeFarmhash.pool().New = func() interface{} { + return nullChecksum{} + } +} + +// ChecksumSize returns the size in bytes of the checksum calculation +func (t ChecksumType) ChecksumSize() int { + switch t { + case ChecksumTypeNone: + return 0 + case ChecksumTypeCrc32, ChecksumTypeCrc32C: + return crc32.Size + case ChecksumTypeFarmhash: + return 4 + default: + return 0 + } +} + +// pool returns the sync.Pool used to pool checksums for this type. +func (t ChecksumType) pool() *sync.Pool { + return &checksumPools[int(t)] +} + +// New creates a new Checksum of the given type +func (t ChecksumType) New() Checksum { + s := t.pool().Get().(Checksum) + s.Reset() + return s +} + +// Release puts a Checksum back in the pool. +func (t ChecksumType) Release(checksum Checksum) { + t.pool().Put(checksum) +} + +// A Checksum calculates a running checksum against a bytestream +type Checksum interface { + // TypeCode returns the type of this checksum + TypeCode() ChecksumType + + // Size returns the size of the calculated checksum + Size() int + + // Add adds bytes to the checksum calculation + Add(b []byte) []byte + + // Sum returns the current checksum value + Sum() []byte + + // Release puts a Checksum back in the pool. + Release() + + // Reset resets the checksum state to the default 0 value. + Reset() +} + +// No checksum +type nullChecksum struct{} + +// TypeCode returns the type of the checksum +func (c nullChecksum) TypeCode() ChecksumType { return ChecksumTypeNone } + +// Size returns the size of the checksum data, in the case the null checksum this is zero +func (c nullChecksum) Size() int { return 0 } + +// Add adds a byteslice to the checksum calculation +func (c nullChecksum) Add(b []byte) []byte { return nil } + +// Sum returns the current checksum calculation +func (c nullChecksum) Sum() []byte { return nil } + +// Release puts a Checksum back in the pool. +func (c nullChecksum) Release() { + c.TypeCode().Release(c) +} + +// Reset resets the checksum state to the default 0 value. +func (c nullChecksum) Reset() {} + +// Hash Checksum +type hashChecksum struct { + checksumType ChecksumType + hash hash.Hash + sumCache []byte +} + +func newHashChecksum(t ChecksumType, hash hash.Hash) *hashChecksum { + return &hashChecksum{ + checksumType: t, + hash: hash, + sumCache: make([]byte, 0, 4), + } +} + +// TypeCode returns the type of the checksum +func (h *hashChecksum) TypeCode() ChecksumType { return h.checksumType } + +// Size returns the size of the checksum data +func (h *hashChecksum) Size() int { return h.hash.Size() } + +// Add adds a byte slice to the checksum calculation +func (h *hashChecksum) Add(b []byte) []byte { h.hash.Write(b); return h.Sum() } + +// Sum returns the current value of the checksum calculation +func (h *hashChecksum) Sum() []byte { return h.hash.Sum(h.sumCache) } + +// Release puts a Checksum back in the pool. +func (h *hashChecksum) Release() { h.TypeCode().Release(h) } + +// Reset resets the checksum state to the default 0 value. +func (h *hashChecksum) Reset() { h.hash.Reset() } diff --git a/vendor/src/github.com/uber/tchannel-go/close_test.go b/vendor/src/github.com/uber/tchannel-go/close_test.go new file mode 100644 index 00000000..d32e0760 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/close_test.go @@ -0,0 +1,540 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "math/rand" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/goroutines" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +type channelState struct { + testServer *testutils.TestServer + closeCh chan struct{} + closed bool +} + +func makeCall(client *Channel, server *testutils.TestServer) error { + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, client, server.HostPort(), server.ServiceName(), "test", nil, nil) + return err +} + +func assertStateChangesTo(t *testing.T, ch *Channel, state ChannelState) { + var lastState ChannelState + require.True(t, testutils.WaitFor(time.Second, func() bool { + lastState = ch.State() + return lastState == state + }), "Channel state is %v expected %v", lastState, state) +} + +func TestCloseOnlyListening(t *testing.T) { + ch := testutils.NewServer(t, nil) + + // If there are no connections, then the channel should close immediately. + ch.Close() + assert.Equal(t, ChannelClosed, ch.State()) + assert.True(t, ch.Closed(), "Channel should be closed") +} + +func TestCloseNewClient(t *testing.T) { + ch := testutils.NewClient(t, nil) + + // If there are no connections, then the channel should close immediately. + ch.Close() + assert.Equal(t, ChannelClosed, ch.State()) + assert.True(t, ch.Closed(), "Channel should be closed") +} + +func TestCloseAfterTimeout(t *testing.T) { + // Disable log verfication since connections are closed after a timeout + // and the relay might still be reading/writing to the connection. + // TODO: Ideally, we only disable log verification on the relay. + opts := testutils.NewOpts().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + testHandler := onErrorTestHandler{newTestHandler(t), func(_ context.Context, err error) {}} + ts.Register(raw.Wrap(testHandler), "block") + + ctx, cancel := NewContext(100 * time.Millisecond) + defer cancel() + + // Make a call, wait for it to timeout. + clientCh := ts.NewClient(nil) + _, _, _, err := raw.Call(ctx, clientCh, ts.HostPort(), ts.ServiceName(), "block", nil, nil) + require.Equal(t, ErrTimeout, err, "Expected call to timeout") + + // The client channel should also close immediately. + clientCh.Close() + assertStateChangesTo(t, clientCh, ChannelClosed) + assert.True(t, clientCh.Closed(), "Channel should be closed") + + // Unblock the testHandler so that a goroutine isn't leaked. + <-testHandler.blockErr + }) +} + +func TestRaceExchangesWithClose(t *testing.T) { + var wg sync.WaitGroup + + ctx, cancel := NewContext(testutils.Timeout(70 * time.Millisecond)) + defer cancel() + + opts := testutils.NewOpts().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + server := ts.Server() + + gotCall := make(chan struct{}) + completeCall := make(chan struct{}) + testutils.RegisterFunc(server, "dummy", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{}, nil + }) + + testutils.RegisterEcho(server, func() { + close(gotCall) + <-completeCall + }) + + client := ts.NewClient(opts) + defer client.Close() + + callDone := make(chan struct{}) + go func() { + assert.NoError(t, testutils.CallEcho(client, ts.HostPort(), server.ServiceName(), &raw.Args{}), "Echo failed") + close(callDone) + }() + + // Wait until the server recieves a call, so it has an active inbound. + <-gotCall + + // Start a bunch of clients to trigger races between connecting and close. + var closed atomic.Bool + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + // We don't use ts.NewClient here to avoid data races. + c := testutils.NewClient(t, opts) + defer c.Close() + + if closed.Load() { + return + } + if err := c.Ping(ctx, ts.HostPort()); err != nil { + return + } + if closed.Load() { + return + } + raw.Call(ctx, c, ts.HostPort(), server.ServiceName(), "dummy", nil, nil) + }() + } + + // Now try to close the channel, it should block since there's active exchanges. + server.Close() + closed.Store(true) + assert.Equal(t, ChannelStartClose, ts.Server().State(), "Server should be in StartClose") + closed.Store(true) + + close(completeCall) + <-callDone + }) + + // Wait for all calls to complete + wg.Wait() +} + +// TestCloseStress ensures that once a Channel is closed, it cannot be reached. +func TestCloseStress(t *testing.T) { + CheckStress(t) + + const numHandlers = 5 + handler := &swapper{t} + var lock sync.RWMutex + var wg sync.WaitGroup + var channels []*channelState + + // Start numHandlers servers, and don't close the connections till they are signalled. + for i := 0; i < numHandlers; i++ { + wg.Add(1) + go func() { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(handler), "test") + + chState := &channelState{ + testServer: ts, + closeCh: make(chan struct{}), + } + + lock.Lock() + channels = append(channels, chState) + lock.Unlock() + wg.Done() + + // Wait for a close signal. + <-chState.closeCh + + // Lock until the connection is closed. + lock.Lock() + chState.closed = true + }) + }() + } + + // Wait till all the channels have been registered. + wg.Wait() + + // Start goroutines to make calls until the test has ended. + testEnded := make(chan struct{}) + for i := 0; i < 10; i++ { + go func() { + for { + select { + case <-testEnded: + return + default: + // Keep making requests till the test ends. + } + + // Get 2 random channels and make a call from one to the other. + lock.RLock() + chState1 := channels[rand.Intn(len(channels))] + chState2 := channels[rand.Intn(len(channels))] + if chState1 == chState2 { + lock.RUnlock() + continue + } + + // Grab a read lock to make sure channels aren't closed while we call. + ch1Closed := chState1.closed + ch2Closed := chState2.closed + err := makeCall(chState1.testServer.NewClient(nil), chState2.testServer) + lock.RUnlock() + if ch1Closed || ch2Closed { + assert.Error( + t, + err, + "Call from %v (%v) to %v (%v) should fail", + chState1.testServer.ServiceName(), + chState1.testServer.HostPort(), + chState2.testServer.ServiceName(), + chState2.testServer.HostPort(), + ) + } else { + assert.NoError( + t, + err, + "Call from %v (%v) to %v (%v) should not fail", + chState1.testServer.ServiceName(), + chState1.testServer.HostPort(), + chState2.testServer.ServiceName(), + chState2.testServer.HostPort(), + ) + } + } + }() + } + + // Kill connections till all of the connections are dead. + for i := 0; i < numHandlers; i++ { + time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) + channels[i].closeCh <- struct{}{} + } +} + +type closeSemanticsTest struct { + *testing.T + isolated bool +} + +func (t *closeSemanticsTest) makeServer(name string) (*Channel, chan struct{}) { + ch := testutils.NewServer(t.T, &testutils.ChannelOpts{ServiceName: name}) + + c := make(chan struct{}) + testutils.RegisterFunc(ch, "stream", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + <-c + return &raw.Res{}, nil + }) + testutils.RegisterFunc(ch, "call", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{}, nil + }) + return ch, c +} + +func (t *closeSemanticsTest) withNewClient(f func(ch *Channel)) { + ch := testutils.NewClient(t.T, &testutils.ChannelOpts{ServiceName: "client"}) + f(ch) + ch.Close() +} + +func (t *closeSemanticsTest) startCall(from *Channel, to *Channel, method string) (*OutboundCall, error) { + ctx, _ := NewContext(time.Second) + var call *OutboundCall + var err error + toPeer := to.PeerInfo() + if t.isolated { + sc := from.GetSubChannel(toPeer.ServiceName, Isolated) + sc.Peers().Add(toPeer.HostPort) + call, err = sc.BeginCall(ctx, method, nil) + } else { + call, err = from.BeginCall(ctx, toPeer.HostPort, toPeer.ServiceName, method, nil) + } + return call, err +} + +func (t *closeSemanticsTest) call(from *Channel, to *Channel) error { + call, err := t.startCall(from, to, "call") + if err == nil { + _, _, _, err = raw.WriteArgs(call, nil, nil) + } + return err +} + +func (t *closeSemanticsTest) callStream(from *Channel, to *Channel) <-chan struct{} { + c := make(chan struct{}) + + call, err := t.startCall(from, to, "stream") + require.NoError(t, err, "stream call failed to start") + require.NoError(t, NewArgWriter(call.Arg2Writer()).Write(nil), "write arg2") + require.NoError(t, NewArgWriter(call.Arg3Writer()).Write(nil), "write arg3") + + go func() { + var d []byte + assert.NoError(t, NewArgReader(call.Response().Arg2Reader()).Read(&d), "read arg2 from %v to %v", from.PeerInfo(), to.PeerInfo()) + assert.NoError(t, NewArgReader(call.Response().Arg3Reader()).Read(&d), "read arg3") + c <- struct{}{} + }() + + return c +} + +func (t *closeSemanticsTest) runTest() { + s1, s1C := t.makeServer("s1") + s2, s2C := t.makeServer("s2") + + // Make a call from s1 -> s2, and s2 -> s1 + call1 := t.callStream(s1, s2) + call2 := t.callStream(s2, s1) + + // s1 and s2 are both open, so calls to it should be successful. + t.withNewClient(func(ch *Channel) { + require.NoError(t, t.call(ch, s1), "failed to call s1") + require.NoError(t, t.call(ch, s2), "failed to call s2") + }) + require.NoError(t, t.call(s1, s2), "call s1 -> s2 failed") + require.NoError(t, t.call(s2, s1), "call s2 -> s1 failed") + + // Close s1, should no longer be able to call it. + s1.Close() + assert.Equal(t, ChannelStartClose, s1.State()) + + t.withNewClient(func(ch *Channel) { + assert.Error(t, t.call(ch, s1), "closed channel should not accept incoming calls") + require.NoError(t, t.call(ch, s2), + "closed channel with pending incoming calls should allow outgoing calls") + }) + + // Even an existing connection (e.g. from s2) should fail. + // TODO: this will fail until the peer is shared. + if !assert.Equal(t, ErrChannelClosed, t.call(s2, s1), + "closed channel should not accept incoming calls") { + t.Errorf("err %v", t.call(s2, s1)) + } + + require.Error(t, t.call(s1, s2), + "closed channel with pending incoming calls disallows outgoing calls") + + // Once the incoming connection is drained, outgoing calls should fail. + s1C <- struct{}{} + <-call2 + assertStateChangesTo(t.T, s1, ChannelInboundClosed) + require.Error(t, t.call(s1, s2), + "closed channel with no pending incoming calls should not allow outgoing calls") + + // Now the channel should be completely closed as there are no pending connections. + s2C <- struct{}{} + <-call1 + assertStateChangesTo(t.T, s1, ChannelClosed) + + // Close s2 so we don't leave any goroutines running. + s2.Close() +} + +func TestCloseSemantics(t *testing.T) { + // We defer the check as we want it to run after the SetTimeout clears the timeout. + defer goroutines.VerifyNoLeaks(t, nil) + defer testutils.SetTimeout(t, 2*time.Second)() + + ct := &closeSemanticsTest{t, false /* isolated */} + ct.runTest() +} + +func TestCloseSemanticsIsolated(t *testing.T) { + // We defer the check as we want it to run after the SetTimeout clears the timeout. + defer goroutines.VerifyNoLeaks(t, nil) + defer testutils.SetTimeout(t, 2*time.Second)() + + ct := &closeSemanticsTest{t, true /* isolated */} + ct.runTest() +} + +func TestCloseSingleChannel(t *testing.T) { + ch := testutils.NewServer(t, nil) + + var connected sync.WaitGroup + var completed sync.WaitGroup + blockCall := make(chan struct{}) + + testutils.RegisterFunc(ch, "echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + connected.Done() + <-blockCall + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil + }) + + for i := 0; i < 10; i++ { + connected.Add(1) + completed.Add(1) + go func() { + ctx, cancel := NewContext(time.Second) + defer cancel() + + peerInfo := ch.PeerInfo() + _, _, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "echo", nil, nil) + assert.NoError(t, err, "Call failed") + completed.Done() + }() + } + + // Wait for all calls to connect before triggerring the Close (so they do not fail). + connected.Wait() + ch.Close() + + // Unblock the calls, and wait for all the calls to complete. + close(blockCall) + completed.Wait() + + // Once all calls are complete, the channel should be closed. + assertStateChangesTo(t, ch, ChannelClosed) + goroutines.VerifyNoLeaks(t, nil) +} + +func TestCloseOneSide(t *testing.T) { + ch1 := testutils.NewServer(t, &testutils.ChannelOpts{ServiceName: "client"}) + ch2 := testutils.NewServer(t, &testutils.ChannelOpts{ServiceName: "server"}) + + connected := make(chan struct{}) + completed := make(chan struct{}) + blockCall := make(chan struct{}) + testutils.RegisterFunc(ch2, "echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + connected <- struct{}{} + <-blockCall + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil + }) + + go func() { + ctx, cancel := NewContext(time.Second) + defer cancel() + ch2Peer := ch2.PeerInfo() + _, _, _, err := raw.Call(ctx, ch1, ch2Peer.HostPort, ch2Peer.ServiceName, "echo", nil, nil) + assert.NoError(t, err, "Call failed") + completed <- struct{}{} + }() + + // Wait for connected before calling Close. + <-connected + ch1.Close() + + // Now unblock the call and wait for the call to complete. + close(blockCall) + <-completed + + // Once the call completes, the channel should be closed. + assertStateChangesTo(t, ch1, ChannelClosed) + + // We need to close all open TChannels before verifying blocked goroutines. + ch2.Close() + goroutines.VerifyNoLeaks(t, nil) +} + +// TestCloseSendError tests that system errors are not attempted to be sent when +// a connection is closed, and ensures there's no race conditions such as the error +// frame being added to the channel just as it is closed. +func TestCloseSendError(t *testing.T) { + var ( + closed atomic.Uint32 + counter atomic.Uint32 + ) + + opts := testutils.NewOpts().DisableLogVerification() + serverCh := testutils.NewServer(t, opts) + testutils.RegisterEcho(serverCh, func() { + if counter.Inc() > 10 { + // Close the server in a goroutine to possibly trigger more race conditions. + go func() { + closed.Inc() + serverCh.Close() + }() + } + }) + + clientCh := testutils.NewClient(t, opts) + + // Create a connection that will be shared. + require.NoError(t, testutils.Ping(clientCh, serverCh), "Ping from client to server failed") + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + time.Sleep(time.Duration(rand.Intn(1000)) * time.Microsecond) + err := testutils.CallEcho(clientCh, serverCh.PeerInfo().HostPort, serverCh.ServiceName(), nil) + if err != nil && closed.Load() == 0 { + t.Errorf("Call failed: %v", err) + } + wg.Done() + }() + } + + // Wait for all the goroutines to end + wg.Wait() + + clientCh.Close() + goroutines.VerifyNoLeaks(t, nil) +} diff --git a/vendor/src/github.com/uber/tchannel-go/codecov.yml b/vendor/src/github.com/uber/tchannel-go/codecov.yml new file mode 100644 index 00000000..15faf14e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/codecov.yml @@ -0,0 +1,15 @@ +coverage: + range: 75..100 + round: down + precision: 2 + + status: + project: + default: + enabled: yes + target: 85% + if_not_found: success + if_ci_failed: error +ignore: +- "*_string.go" + diff --git a/vendor/src/github.com/uber/tchannel-go/conn_leak_test.go b/vendor/src/github.com/uber/tchannel-go/conn_leak_test.go new file mode 100644 index 00000000..8496f0f8 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/conn_leak_test.go @@ -0,0 +1,98 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package tchannel_test + +import ( + "io/ioutil" + "runtime" + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/require" +) + +// This is a regression test for https://github.com/uber/tchannel-go/issues/643 +// We want to ensure that once a connection is closed, there are no references +// to the closed connection, and the GC frees the connection. +// We use `runtime.SetFinalizer` to detect whether the GC has freed the object. +// However, finalizers cannot be set on objects with circular references, +// so we cannot set a finalizer on the connection, but instead set a finalizer +// on a field of the connection which has the same lifetime. The connection +// logger is unique per connection and does not have circular references +// so we can use the logger, but need a pointer for `runtime.SetFinalizer`. +// loggerPtr is a Logger implementation that uses a pointer unlike other +// TChannel loggers. +type loggerPtr struct { + Logger +} + +func (l *loggerPtr) WithFields(fields ...LogField) Logger { + return &loggerPtr{l.Logger.WithFields(fields...)} +} + +func TestPeerConnectionLeaks(t *testing.T) { + // Disable log verification since we want to set our own logger. + opts := testutils.NewOpts().NoRelay().DisableLogVerification() + opts.Logger = &loggerPtr{NullLogger} + + connFinalized := make(chan struct{}) + setFinalizer := func(p *Peer, hostPort string) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + conn, err := p.GetConnection(ctx) + require.NoError(t, err, "Failed to get connection") + + runtime.SetFinalizer(conn.Logger(), func(interface{}) { + close(connFinalized) + }) + } + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s2Opts := testutils.NewOpts().SetServiceName("s2") + s2Opts.Logger = NewLogger(ioutil.Discard) + s2 := ts.NewServer(s2Opts) + + // Set a finalizer to detect when the connection from s1 -> s2 is freed. + peer := ts.Server().Peers().GetOrAdd(s2.PeerInfo().HostPort) + setFinalizer(peer, s2.PeerInfo().HostPort) + + // Close s2, so that the connection in s1 to s2 is released. + s2.Close() + closed := testutils.WaitFor(time.Second, s2.Closed) + require.True(t, closed, "s2 didn't close") + + // Trigger the GC which will call the finalizer, and ensure + // that the connection logger was finalized. + finalized := testutils.WaitFor(time.Second, func() bool { + runtime.GC() + select { + case <-connFinalized: + return true + default: + return false + } + }) + require.True(t, finalized, "Connection was not freed") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/connection.go b/vendor/src/github.com/uber/tchannel-go/connection.go new file mode 100644 index 00000000..b8c0cd65 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/connection.go @@ -0,0 +1,827 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + "io" + "math" + "net" + "sync" + "time" + + "github.com/uber/tchannel-go/tos" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +const ( + // CurrentProtocolVersion is the current version of the TChannel protocol + // supported by this stack + CurrentProtocolVersion = 0x02 + + // DefaultConnectTimeout is the default timeout used by net.Dial, if no timeout + // is specified in the context. + DefaultConnectTimeout = 5 * time.Second + + // defaultConnectionBufferSize is the default size for the connection's + // read and write channels. + defaultConnectionBufferSize = 512 +) + +// PeerVersion contains version related information for a specific peer. +// These values are extracted from the init headers. +type PeerVersion struct { + Language string `json:"language"` + LanguageVersion string `json:"languageVersion"` + TChannelVersion string `json:"tchannelVersion"` +} + +// PeerInfo contains information about a TChannel peer +type PeerInfo struct { + // The host and port that can be used to contact the peer, as encoded by net.JoinHostPort + HostPort string `json:"hostPort"` + + // The logical process name for the peer, used for only for logging / debugging + ProcessName string `json:"processName"` + + // IsEphemeral returns whether the remote host:port is ephemeral (e.g. not listening). + IsEphemeral bool `json:"isEphemeral"` + + // Version returns the version information for the remote peer. + Version PeerVersion `json:"version"` +} + +func (p PeerInfo) String() string { + return fmt.Sprintf("%s(%s)", p.HostPort, p.ProcessName) +} + +// IsEphemeralHostPort returns whether the connection is from an ephemeral host:port. +func (p PeerInfo) IsEphemeralHostPort() bool { + return p.IsEphemeral +} + +// LocalPeerInfo adds service name to the peer info, only required for the local peer. +type LocalPeerInfo struct { + PeerInfo + + // ServiceName is the service name for the local peer. + ServiceName string `json:"serviceName"` +} + +func (p LocalPeerInfo) String() string { + return fmt.Sprintf("%v: %v", p.ServiceName, p.PeerInfo) +} + +var ( + // ErrConnectionClosed is returned when a caller performs an method + // on a closed connection + ErrConnectionClosed = errors.New("connection is closed") + + // ErrSendBufferFull is returned when a message cannot be sent to the + // peer because the frame sending buffer has become full. Typically + // this indicates that the connection is stuck and writes have become + // backed up + ErrSendBufferFull = errors.New("connection send buffer is full, cannot send frame") + + // ErrConnectionNotReady is no longer used. + ErrConnectionNotReady = errors.New("connection is not yet ready") +) + +// errConnectionInvalidState is returned when the connection is in an unknown state. +type errConnectionUnknownState struct { + site string + state connectionState +} + +func (e errConnectionUnknownState) Error() string { + return fmt.Sprintf("connection is in unknown state: %v at %v", e.state, e.site) +} + +// ConnectionOptions are options that control the behavior of a Connection +type ConnectionOptions struct { + // The frame pool, allowing better management of frame buffers. Defaults to using raw heap. + FramePool FramePool + + // NOTE: This is deprecated and not used for anything. + RecvBufferSize int + + // The size of send channel buffers. Defaults to 512. + SendBufferSize int + + // The type of checksum to use when sending messages. + ChecksumType ChecksumType + + // ToS class name marked on outbound packets. + TosPriority tos.ToS +} + +// connectionEvents are the events that can be triggered by a connection. +type connectionEvents struct { + // OnActive is called when a connection becomes active. + OnActive func(c *Connection) + + // OnCloseStateChange is called when a connection that is closing changes state. + OnCloseStateChange func(c *Connection) + + // OnExchangeUpdated is called when a message exchange added or removed. + OnExchangeUpdated func(c *Connection) +} + +// Connection represents a connection to a remote peer. +type Connection struct { + channelConnectionCommon + + connID uint32 + opts ConnectionOptions + conn net.Conn + localPeerInfo LocalPeerInfo + remotePeerInfo PeerInfo + sendCh chan *Frame + stopCh chan struct{} + state connectionState + stateMut sync.RWMutex + inbound *messageExchangeSet + outbound *messageExchangeSet + handler Handler + nextMessageID atomic.Uint32 + events connectionEvents + commonStatsTags map[string]string + relay *Relayer + + // outboundHP is the host:port we used to create this outbound connection. + // It may not match remotePeerInfo.HostPort, in which case the connection is + // added to peers for both host:ports. For inbound connections, this is empty. + outboundHP string + + // closeNetworkCalled is used to avoid errors from being logged + // when this side closes a connection. + closeNetworkCalled atomic.Int32 + // stoppedExchanges is atomically set when exchanges are stopped due to error. + stoppedExchanges atomic.Uint32 + // pendingMethods is the number of methods running that may block closing of sendCh. + pendingMethods atomic.Int64 + // remotePeerAddress is used as a cache for remote peer address parsed into individual + // components that can be used to set peer tags on OpenTracing Span. + remotePeerAddress peerAddressComponents +} + +type peerAddressComponents struct { + port uint16 + ipv4 uint32 + ipv6 string + hostname string +} + +// _nextConnID is used to allocate unique IDs to every connection for debugging purposes. +var _nextConnID atomic.Uint32 + +type connectionState int + +const ( + // Connection is fully active + connectionActive connectionState = iota + 1 + + // Connection is starting to close; new incoming requests are rejected, outbound + // requests are allowed to proceed + connectionStartClose + + // Connection has finished processing all active inbound, and is + // waiting for outbound requests to complete or timeout + connectionInboundClosed + + // Connection is fully closed + connectionClosed +) + +//go:generate stringer -type=connectionState + +func getTimeout(ctx context.Context) time.Duration { + deadline, ok := ctx.Deadline() + if !ok { + return DefaultConnectTimeout + } + + return deadline.Sub(time.Now()) +} + +func (co ConnectionOptions) withDefaults() ConnectionOptions { + if co.ChecksumType == ChecksumTypeNone { + co.ChecksumType = ChecksumTypeCrc32 + } + if co.FramePool == nil { + co.FramePool = DefaultFramePool + } + if co.SendBufferSize <= 0 { + co.SendBufferSize = defaultConnectionBufferSize + } + return co +} + +func (ch *Channel) setConnectionTosPriority(tosPriority tos.ToS, c net.Conn) error { + tcpAddr, isTCP := c.RemoteAddr().(*net.TCPAddr) + if !isTCP { + return nil + } + + // Handle dual stack listeners and set Traffic Class. + var err error + switch ip := tcpAddr.IP; { + case ip.To16() != nil && ip.To4() == nil: + err = ipv6.NewConn(c).SetTrafficClass(int(tosPriority)) + case ip.To4() != nil: + err = ipv4.NewConn(c).SetTOS(int(tosPriority)) + } + return err +} + +func (ch *Channel) newConnection(conn net.Conn, initialID uint32, outboundHP string, remotePeer PeerInfo, remotePeerAddress peerAddressComponents, events connectionEvents) *Connection { + opts := ch.connectionOptions.withDefaults() + + connID := _nextConnID.Inc() + log := ch.log.WithFields(LogFields{ + {"connID", connID}, + {"localAddr", conn.LocalAddr()}, + {"remoteAddr", conn.RemoteAddr()}, + {"remoteHostPort", remotePeer.HostPort}, + {"remoteIsEphemeral", remotePeer.IsEphemeral}, + {"remoteProcess", remotePeer.ProcessName}, + }...) + if outboundHP != "" { + log = log.WithFields(LogFields{ + {"outboundHP", outboundHP}, + {"connectionDirection", outbound}, + }...) + } else { + log = log.WithFields(LogField{"connectionDirection", inbound}) + } + peerInfo := ch.PeerInfo() + + c := &Connection{ + channelConnectionCommon: ch.channelConnectionCommon, + + connID: connID, + conn: conn, + opts: opts, + state: connectionActive, + sendCh: make(chan *Frame, opts.SendBufferSize), + stopCh: make(chan struct{}), + localPeerInfo: peerInfo, + remotePeerInfo: remotePeer, + remotePeerAddress: remotePeerAddress, + outboundHP: outboundHP, + inbound: newMessageExchangeSet(log, messageExchangeSetInbound), + outbound: newMessageExchangeSet(log, messageExchangeSetOutbound), + handler: ch.handler, + events: events, + commonStatsTags: ch.commonStatsTags, + } + + if tosPriority := opts.TosPriority; tosPriority > 0 { + if err := ch.setConnectionTosPriority(tosPriority, conn); err != nil { + log.WithFields(ErrField(err)).Error("Failed to set ToS priority.") + } + } + + c.nextMessageID.Store(initialID) + c.log = log + c.inbound.onRemoved = c.checkExchanges + c.outbound.onRemoved = c.checkExchanges + c.inbound.onAdded = c.onExchangeAdded + c.outbound.onAdded = c.onExchangeAdded + + if ch.RelayHost() != nil { + c.relay = NewRelayer(ch, c) + } + + // Connections are activated as soon as they are created. + c.callOnActive() + + go c.readFrames(connID) + go c.writeFrames(connID) + return c +} + +func (c *Connection) onExchangeAdded() { + c.callOnExchangeChange() +} + +// IsActive returns whether this connection is in an active state. +func (c *Connection) IsActive() bool { + return c.readState() == connectionActive +} + +func (c *Connection) callOnActive() { + log := c.log + if remoteVersion := c.remotePeerInfo.Version; remoteVersion != (PeerVersion{}) { + log = log.WithFields(LogFields{ + {"remotePeerLanguage", remoteVersion.Language}, + {"remotePeerLanguageVersion", remoteVersion.LanguageVersion}, + {"remotePeerTChannelVersion", remoteVersion.TChannelVersion}, + }...) + } + log.Info("Created new active connection.") + + if f := c.events.OnActive; f != nil { + f(c) + } +} + +func (c *Connection) callOnCloseStateChange() { + if f := c.events.OnCloseStateChange; f != nil { + f(c) + } +} + +func (c *Connection) callOnExchangeChange() { + if f := c.events.OnExchangeUpdated; f != nil { + f(c) + } +} + +// ping sends a ping message and waits for a ping response. +func (c *Connection) ping(ctx context.Context) error { + if !c.pendingExchangeMethodAdd() { + // Connection is closed, no need to do anything. + return ErrInvalidConnectionState + } + defer c.pendingExchangeMethodDone() + + req := &pingReq{id: c.NextMessageID()} + mex, err := c.outbound.newExchange(ctx, c.opts.FramePool, req.messageType(), req.ID(), 1) + if err != nil { + return c.connectionError("create ping exchange", err) + } + defer c.outbound.removeExchange(req.ID()) + + if err := c.sendMessage(req); err != nil { + return c.connectionError("send ping", err) + } + + res := &pingRes{} + err = c.recvMessage(ctx, res, mex) + if err != nil { + return c.connectionError("receive pong", err) + } + + return nil +} + +// handlePingRes calls registered ping handlers. +func (c *Connection) handlePingRes(frame *Frame) bool { + if err := c.outbound.forwardPeerFrame(frame); err != nil { + c.log.WithFields(LogField{"response", frame.Header}).Warn("Unexpected ping response.") + return true + } + // ping req is waiting for this frame, and will release it. + return false +} + +// handlePingReq responds to the pingReq message with a pingRes. +func (c *Connection) handlePingReq(frame *Frame) { + if !c.pendingExchangeMethodAdd() { + // Connection is closed, no need to do anything. + return + } + defer c.pendingExchangeMethodDone() + + if state := c.readState(); state != connectionActive { + c.protocolError(frame.Header.ID, errConnNotActive{"ping on incoming", state}) + return + } + + pingRes := &pingRes{id: frame.Header.ID} + if err := c.sendMessage(pingRes); err != nil { + c.connectionError("send pong", err) + } +} + +// sendMessage sends a standalone message (typically a control message) +func (c *Connection) sendMessage(msg message) error { + frame := c.opts.FramePool.Get() + if err := frame.write(msg); err != nil { + c.opts.FramePool.Release(frame) + return err + } + + select { + case c.sendCh <- frame: + return nil + default: + return ErrSendBufferFull + } +} + +// recvMessage blocks waiting for a standalone response message (typically a +// control message) +func (c *Connection) recvMessage(ctx context.Context, msg message, mex *messageExchange) error { + frame, err := mex.recvPeerFrameOfType(msg.messageType()) + if err != nil { + if err, ok := err.(errorMessage); ok { + return err.AsSystemError() + } + return err + } + + err = frame.read(msg) + c.opts.FramePool.Release(frame) + return err +} + +// RemotePeerInfo returns the peer info for the remote peer. +func (c *Connection) RemotePeerInfo() PeerInfo { + return c.remotePeerInfo +} + +// NextMessageID reserves the next available message id for this connection +func (c *Connection) NextMessageID() uint32 { + return c.nextMessageID.Inc() +} + +// SendSystemError sends an error frame for the given system error. +func (c *Connection) SendSystemError(id uint32, span Span, err error) error { + frame := c.opts.FramePool.Get() + + if err := frame.write(&errorMessage{ + id: id, + errCode: GetSystemErrorCode(err), + tracing: span, + message: GetSystemErrorMessage(err), + }); err != nil { + + // This shouldn't happen - it means writing the errorMessage is broken. + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + LogField{"id", id}, + ErrField(err), + ).Warn("Couldn't create outbound frame.") + return fmt.Errorf("failed to create outbound error frame") + } + + // When sending errors, we hold the state rlock to ensure that sendCh is not closed + // as we are sending the frame. + return c.withStateRLock(func() error { + // Errors cannot be sent if the connection has been closed. + if c.state == connectionClosed { + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + LogField{"id", id}, + ).Info("Could not send error frame on closed connection.") + return fmt.Errorf("failed to send error frame, connection state %v", c.state) + } + + select { + case c.sendCh <- frame: // Good to go + return nil + default: // If the send buffer is full, log and return an error. + } + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + LogField{"id", id}, + ErrField(err), + ).Warn("Couldn't send outbound frame.") + return fmt.Errorf("failed to send error frame, buffer full") + }) +} + +func (c *Connection) logConnectionError(site string, err error) error { + errCode := ErrCodeNetwork + if err == io.EOF { + c.log.Debugf("Connection got EOF") + } else { + logger := c.log.WithFields( + LogField{"site", site}, + ErrField(err), + ) + if se, ok := err.(SystemError); ok && se.Code() != ErrCodeNetwork { + errCode = se.Code() + logger.Error("Connection error.") + } else { + logger.Info("Connection error.") + } + } + return NewWrappedSystemError(errCode, err) +} + +// connectionError handles a connection level error +func (c *Connection) connectionError(site string, err error) error { + var closeLogFields LogFields + if err == io.EOF { + closeLogFields = LogFields{{"reason", "network connection EOF"}} + } else { + closeLogFields = LogFields{ + {"reason", "connection error"}, + ErrField(err), + } + } + err = c.logConnectionError(site, err) + c.close(closeLogFields...) + + // On any connection error, notify the exchanges of this error. + if c.stoppedExchanges.CAS(0, 1) { + c.outbound.stopExchanges(err) + c.inbound.stopExchanges(err) + } + return err +} + +func (c *Connection) protocolError(id uint32, err error) error { + c.log.WithFields(ErrField(err)).Warn("Protocol error.") + sysErr := NewWrappedSystemError(ErrCodeProtocol, err) + c.SendSystemError(id, Span{}, sysErr) + // Don't close the connection until the error has been sent. + c.close( + LogField{"reason", "protocol error"}, + ErrField(err), + ) + + // On any connection error, notify the exchanges of this error. + if c.stoppedExchanges.CAS(0, 1) { + c.outbound.stopExchanges(sysErr) + c.inbound.stopExchanges(sysErr) + } + return sysErr +} + +// withStateLock performs an action with the connection state mutex locked +func (c *Connection) withStateLock(f func() error) error { + c.stateMut.Lock() + err := f() + c.stateMut.Unlock() + + return err +} + +// withStateRLock performs an action with the connection state mutex rlocked. +func (c *Connection) withStateRLock(f func() error) error { + c.stateMut.RLock() + err := f() + c.stateMut.RUnlock() + + return err +} + +func (c *Connection) readState() connectionState { + c.stateMut.RLock() + state := c.state + c.stateMut.RUnlock() + return state +} + +// readFrames is the loop that reads frames from the network connection and +// dispatches to the appropriate handler. Run within its own goroutine to +// prevent overlapping reads on the socket. Most handlers simply send the +// incoming frame to a channel; the init handlers are a notable exception, +// since we cannot process new frames until the initialization is complete. +func (c *Connection) readFrames(_ uint32) { + for { + frame := c.opts.FramePool.Get() + if err := frame.ReadIn(c.conn); err != nil { + if c.closeNetworkCalled.Load() == 0 { + c.connectionError("read frames", err) + } else { + c.log.Debugf("Ignoring error after connection was closed: %v", err) + } + c.opts.FramePool.Release(frame) + return + } + + var releaseFrame bool + if c.relay == nil { + releaseFrame = c.handleFrameNoRelay(frame) + } else { + releaseFrame = c.handleFrameRelay(frame) + } + if releaseFrame { + c.opts.FramePool.Release(frame) + } + } +} + +func (c *Connection) handleFrameRelay(frame *Frame) bool { + switch frame.Header.messageType { + case messageTypeCallReq, messageTypeCallReqContinue, messageTypeCallRes, messageTypeCallResContinue, messageTypeError: + if err := c.relay.Relay(frame); err != nil { + c.log.WithFields( + ErrField(err), + LogField{"header", frame.Header}, + LogField{"remotePeer", c.remotePeerInfo}, + ).Error("Failed to relay frame.") + } + return false + default: + return c.handleFrameNoRelay(frame) + } +} + +func (c *Connection) handleFrameNoRelay(frame *Frame) bool { + releaseFrame := true + + // call req and call res messages may not want the frame released immediately. + switch frame.Header.messageType { + case messageTypeCallReq: + releaseFrame = c.handleCallReq(frame) + case messageTypeCallReqContinue: + releaseFrame = c.handleCallReqContinue(frame) + case messageTypeCallRes: + releaseFrame = c.handleCallRes(frame) + case messageTypeCallResContinue: + releaseFrame = c.handleCallResContinue(frame) + case messageTypePingReq: + c.handlePingReq(frame) + case messageTypePingRes: + releaseFrame = c.handlePingRes(frame) + case messageTypeError: + releaseFrame = c.handleError(frame) + default: + // TODO(mmihic): Log and close connection with protocol error + c.log.WithFields( + LogField{"header", frame.Header}, + LogField{"remotePeer", c.remotePeerInfo}, + ).Error("Received unexpected frame.") + } + + return releaseFrame +} + +// writeFrames is the main loop that pulls frames from the send channel and +// writes them to the connection. +func (c *Connection) writeFrames(_ uint32) { + for { + select { + case f := <-c.sendCh: + if c.log.Enabled(LogLevelDebug) { + c.log.Debugf("Writing frame %s", f.Header) + } + + err := f.WriteOut(c.conn) + c.opts.FramePool.Release(f) + if err != nil { + c.connectionError("write frames", err) + return + } + case <-c.stopCh: + // If there are frames in sendCh, we want to drain them. + if len(c.sendCh) > 0 { + continue + } + // Close the network once we're no longer writing frames. + c.closeNetwork() + return + } + } +} + +// pendingExchangeMethodAdd returns whether the method that is trying to +// add a message exchange can continue. +func (c *Connection) pendingExchangeMethodAdd() bool { + return c.pendingMethods.Inc() > 0 +} + +// pendingExchangeMethodDone should be deferred by a method called +// pendingExchangeMessageAdd. +func (c *Connection) pendingExchangeMethodDone() { + c.pendingMethods.Dec() +} + +// closeSendCh waits till there are no other goroutines that may try to write +// to sendCh. +// We accept connID on the stack so can more easily debug panics or leaked goroutines. +func (c *Connection) closeSendCh(connID uint32) { + // Wait till all methods that may add exchanges are done running. + // When they are done, we set the value to a negative value which + // will ensure that if any other methods start that may add exchanges + // they will fail due to closed connection. + for !c.pendingMethods.CAS(0, math.MinInt32) { + time.Sleep(time.Millisecond) + } + + close(c.stopCh) +} + +// checkExchanges is called whenever an exchange is removed, and when Close is called. +func (c *Connection) checkExchanges() { + c.callOnExchangeChange() + + moveState := func(fromState, toState connectionState) bool { + err := c.withStateLock(func() error { + if c.state != fromState { + return errors.New("") + } + c.state = toState + return nil + }) + return err == nil + } + + var updated connectionState + if c.readState() == connectionStartClose { + if !c.relay.canClose() { + return + } + if c.inbound.count() == 0 && moveState(connectionStartClose, connectionInboundClosed) { + updated = connectionInboundClosed + } + // If there was no update to the state, there's no more processing to do. + if updated == 0 { + return + } + } + + if c.readState() == connectionInboundClosed { + // Safety check -- this should never happen since we already did the check + // when transitioning to connectionInboundClosed. + if !c.relay.canClose() { + c.relay.logger.Error("Relay can't close even though state is InboundClosed.") + return + } + + if c.outbound.count() == 0 && moveState(connectionInboundClosed, connectionClosed) { + updated = connectionClosed + } + } + + if updated != 0 { + // If the connection is closed, we can safely close the channel. + if updated == connectionClosed { + go c.closeSendCh(c.connID) + } + + c.log.WithFields( + LogField{"newState", updated}, + ).Debug("Connection state updated during shutdown.") + c.callOnCloseStateChange() + } +} + +func (c *Connection) close(fields ...LogField) error { + c.log.WithFields(fields...).Info("Connection closing.") + + // Update the state which will start blocking incoming calls. + if err := c.withStateLock(func() error { + switch c.state { + case connectionActive: + c.state = connectionStartClose + default: + return fmt.Errorf("connection must be Active to Close") + } + return nil + }); err != nil { + return err + } + + c.log.WithFields( + LogField{"newState", c.readState()}, + ).Debug("Connection state updated in Close.") + c.callOnCloseStateChange() + + // Check all in-flight requests to see whether we can transition the Close state. + c.checkExchanges() + + return nil +} + +// Close starts a graceful Close which will first reject incoming calls, reject outgoing calls +// before finally marking the connection state as closed. +func (c *Connection) Close() error { + return c.close(LogField{"reason", "user initiated"}) +} + +// closeNetwork closes the network connection and all network-related channels. +// This should only be done in response to a fatal connection or protocol +// error, or after all pending frames have been sent. +func (c *Connection) closeNetwork() { + // NB(mmihic): The sender goroutine will exit once the connection is + // closed; no need to close the send channel (and closing the send + // channel would be dangerous since other goroutine might be sending) + c.log.Debugf("Closing underlying network connection") + c.closeNetworkCalled.Inc() + if err := c.conn.Close(); err != nil { + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + ErrField(err), + ).Warn("Couldn't close connection to peer.") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/connection_bench_test.go b/vendor/src/github.com/uber/tchannel-go/connection_bench_test.go new file mode 100644 index 00000000..8ab5c80c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/connection_bench_test.go @@ -0,0 +1,197 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "runtime" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/streadway/quantile" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +const benchService = "bench-server" + +type benchmarkHandler struct{} + +func (h *benchmarkHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg2: args.Arg3, + Arg3: args.Arg2, + }, nil +} + +func (h *benchmarkHandler) OnError(ctx context.Context, err error) { +} + +type latencyTracker struct { + sync.Mutex + + started time.Time + estimator *quantile.Estimator +} + +func newLatencyTracker() *latencyTracker { + return &latencyTracker{ + estimator: quantile.New( + quantile.Unknown(0.01), + quantile.Known(0.50, 0.01), + quantile.Known(0.95, 0.001), + quantile.Known(0.99, 0.0005), + quantile.Known(1.0, 0.0005), + ), + started: time.Now(), + } +} + +func (lt *latencyTracker) addLatency(d time.Duration) { + lt.Lock() + lt.estimator.Add(float64(d)) + lt.Unlock() +} + +func (lt *latencyTracker) report(t testing.TB) { + duration := time.Since(lt.started) + lt.Lock() + t.Logf("%6v calls, %5.0f RPS (%v per call). Latency: Average: %v P95: %v P99: %v P100: %v", + lt.estimator.Samples(), + float64(lt.estimator.Samples())/float64(duration)*float64(time.Second), + duration/time.Duration(lt.estimator.Samples()), + time.Duration(lt.estimator.Get(0.50)), + time.Duration(lt.estimator.Get(0.95)), + time.Duration(lt.estimator.Get(0.99)), + time.Duration(lt.estimator.Get(1.0)), + ) + lt.Unlock() +} + +func setupServer(t testing.TB) *Channel { + serverCh := testutils.NewServer(t, testutils.NewOpts().SetServiceName("bench-server")) + handler := &benchmarkHandler{} + serverCh.Register(raw.Wrap(handler), "echo") + return serverCh +} + +type benchmarkConfig struct { + numCalls int + numServers int + numClients int + workersPerClient int + numBytes int +} + +func benchmarkCallsN(b *testing.B, c benchmarkConfig) { + var ( + clients []*Channel + servers []*Channel + ) + lt := newLatencyTracker() + + if c.numBytes == 0 { + c.numBytes = 100 + } + data := testutils.RandBytes(c.numBytes) + + // Set up clients and servers. + for i := 0; i < c.numServers; i++ { + servers = append(servers, setupServer(b)) + } + for i := 0; i < c.numClients; i++ { + clients = append(clients, testutils.NewClient(b, nil)) + for _, s := range servers { + clients[i].Peers().Add(s.PeerInfo().HostPort) + + // Initialize a connection + ctx, cancel := NewContext(50 * time.Millisecond) + assert.NoError(b, clients[i].Ping(ctx, s.PeerInfo().HostPort), "Initial ping failed") + cancel() + } + } + + // Make calls from clients to the servers + call := func(sc *SubChannel) { + ctx, cancel := NewContext(50 * time.Millisecond) + start := time.Now() + _, _, _, err := raw.CallSC(ctx, sc, "echo", nil, data) + duration := time.Since(start) + cancel() + if assert.NoError(b, err, "Call failed") { + lt.addLatency(duration) + } + } + + reqsLeft := testutils.Decrementor(c.numCalls) + clientWorker := func(client *Channel, clientNum, workerNum int) { + sc := client.GetSubChannel(benchService) + for reqsLeft.Single() { + call(sc) + } + } + clientRunner := func(client *Channel, clientNum int) { + testutils.RunN(c.workersPerClient, func(i int) { + clientWorker(client, clientNum, i) + }) + } + + lt = newLatencyTracker() + defer lt.report(b) + b.ResetTimer() + + testutils.RunN(c.numClients, func(i int) { + clientRunner(clients[i], i) + }) +} + +func BenchmarkCallsSerial(b *testing.B) { + benchmarkCallsN(b, benchmarkConfig{ + numCalls: b.N, + numServers: 1, + numClients: 1, + workersPerClient: 1, + }) +} + +func BenchmarkCallsConcurrentServer(b *testing.B) { + benchmarkCallsN(b, benchmarkConfig{ + numCalls: b.N, + numServers: 1, + numClients: runtime.GOMAXPROCS(0), + workersPerClient: 1, + }) +} + +func BenchmarkCallsConcurrentClient(b *testing.B) { + parallelism := runtime.GOMAXPROCS(0) + benchmarkCallsN(b, benchmarkConfig{ + numCalls: b.N, + numServers: parallelism, + numClients: 1, + workersPerClient: parallelism, + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/connection_direction.go b/vendor/src/github.com/uber/tchannel-go/connection_direction.go new file mode 100644 index 00000000..82f60547 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/connection_direction.go @@ -0,0 +1,41 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "fmt" + +type connectionDirection int + +const ( + inbound connectionDirection = iota + 1 + outbound +) + +func (d connectionDirection) String() string { + switch d { + case inbound: + return "inbound" + case outbound: + return "outbound" + default: + return fmt.Sprintf("connectionDirection(%v)", int(d)) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/connection_test.go b/vendor/src/github.com/uber/tchannel-go/connection_test.go new file mode 100644 index 00000000..6675e144 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/connection_test.go @@ -0,0 +1,1042 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "errors" + "fmt" + "io" + "net" + "runtime" + "strings" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/relay/relaytest" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/testreader" + "github.com/uber/tchannel-go/tos" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +// Values used in tests +var ( + testArg2 = []byte("Header in arg2") + testArg3 = []byte("Body in arg3") +) + +type testHandler struct { + sync.Mutex + + t testing.TB + format Format + caller string + blockErr chan error +} + +func newTestHandler(t testing.TB) *testHandler { + return &testHandler{t: t, blockErr: make(chan error)} +} + +func (h *testHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + h.Lock() + h.format = args.Format + h.caller = args.Caller + h.Unlock() + + assert.Equal(h.t, args.Caller, CurrentCall(ctx).CallerName()) + + switch args.Method { + case "block": + <-ctx.Done() + h.blockErr <- ctx.Err() + return &raw.Res{ + IsErr: true, + }, nil + case "echo": + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil + case "busy": + return &raw.Res{ + SystemErr: ErrServerBusy, + }, nil + case "app-error": + return &raw.Res{ + IsErr: true, + }, nil + } + return nil, errors.New("unknown method") +} + +func (h *testHandler) OnError(ctx context.Context, err error) { + stack := make([]byte, 4096) + runtime.Stack(stack, false /* all */) + h.t.Errorf("testHandler got error: %v stack:\n%s", err, stack) +} + +func writeFlushStr(w ArgWriter, d string) error { + if _, err := io.WriteString(w, d); err != nil { + return err + } + return w.Flush() +} + +func isTosPriority(c net.Conn, tosPriority tos.ToS) (bool, error) { + var connTosPriority int + var err error + + switch ip := c.RemoteAddr().(*net.TCPAddr).IP; { + case ip.To16() != nil && ip.To4() == nil: + connTosPriority, err = ipv6.NewConn(c).TrafficClass() + case ip.To4() != nil: + connTosPriority, err = ipv4.NewConn(c).TOS() + } + + return connTosPriority == int(tosPriority), err +} + +func TestRoundTrip(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + handler := newTestHandler(t) + ts.Register(raw.Wrap(handler), "echo") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + call, err := ts.Server().BeginCall(ctx, ts.HostPort(), ts.ServiceName(), "echo", &CallOptions{Format: JSON}) + require.NoError(t, err) + assert.NotEmpty(t, call.RemotePeer().HostPort) + assert.Equal(t, ts.Server().PeerInfo(), call.LocalPeer(), "Unexpected local peer") + + require.NoError(t, NewArgWriter(call.Arg2Writer()).Write(testArg2)) + require.NoError(t, NewArgWriter(call.Arg3Writer()).Write(testArg3)) + + var respArg2 []byte + require.NoError(t, NewArgReader(call.Response().Arg2Reader()).Read(&respArg2)) + assert.Equal(t, testArg2, []byte(respArg2)) + + var respArg3 []byte + require.NoError(t, NewArgReader(call.Response().Arg3Reader()).Read(&respArg3)) + assert.Equal(t, testArg3, []byte(respArg3)) + + assert.Equal(t, JSON, handler.format) + assert.Equal(t, ts.ServiceName(), handler.caller) + assert.Equal(t, JSON, call.Response().Format(), "response Format should match request Format") + }) +} + +func TestDefaultFormat(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + handler := newTestHandler(t) + ts.Register(raw.Wrap(handler), "echo") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + arg2, arg3, resp, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", testArg2, testArg3) + require.Nil(t, err) + + require.Equal(t, testArg2, arg2) + require.Equal(t, testArg3, arg3) + require.Equal(t, Raw, handler.format) + assert.Equal(t, Raw, resp.Format(), "response Format should match request Format") + }) +} + +func TestRemotePeer(t *testing.T) { + wantVersion := PeerVersion{ + Language: "go", + LanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + TChannelVersion: VersionInfo, + } + tests := []struct { + name string + remote func(*testutils.TestServer) *Channel + expectedFn func(*RuntimeState, *testutils.TestServer) PeerInfo + }{ + { + name: "ephemeral client", + remote: func(ts *testutils.TestServer) *Channel { return ts.NewClient(nil) }, + expectedFn: func(state *RuntimeState, ts *testutils.TestServer) PeerInfo { + return PeerInfo{ + HostPort: state.RootPeers[ts.HostPort()].OutboundConnections[0].LocalHostPort, + IsEphemeral: true, + ProcessName: state.LocalPeer.ProcessName, + Version: wantVersion, + } + }, + }, + { + name: "listening server", + remote: func(ts *testutils.TestServer) *Channel { return ts.NewServer(nil) }, + expectedFn: func(state *RuntimeState, ts *testutils.TestServer) PeerInfo { + return PeerInfo{ + HostPort: state.LocalPeer.HostPort, + IsEphemeral: false, + ProcessName: state.LocalPeer.ProcessName, + Version: wantVersion, + } + }, + }, + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + + for _, tt := range tests { + opts := testutils.NewOpts().SetServiceName("fake-service").NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + remote := tt.remote(ts) + defer remote.Close() + + gotPeer := make(chan PeerInfo, 1) + ts.RegisterFunc("test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + gotPeer <- CurrentCall(ctx).RemotePeer() + assert.Equal(t, ts.Server().PeerInfo(), CurrentCall(ctx).LocalPeer()) + return &raw.Res{}, nil + }) + + _, _, _, err := raw.Call(ctx, remote, ts.HostPort(), ts.Server().ServiceName(), "test", nil, nil) + assert.NoError(t, err, "%v: Call failed", tt.name) + expected := tt.expectedFn(remote.IntrospectState(nil), ts) + assert.Equal(t, expected, <-gotPeer, "%v: RemotePeer mismatch", tt.name) + }) + } +} + +func TestReuseConnection(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Since we're specifically testing that connections between hosts are re-used, + // we can't interpose a relay in this test. + s1Opts := testutils.NewOpts().SetServiceName("s1").NoRelay() + + testutils.WithTestServer(t, s1Opts, func(ts *testutils.TestServer) { + ch2 := ts.NewServer(&testutils.ChannelOpts{ServiceName: "s2"}) + hostPort2 := ch2.PeerInfo().HostPort + defer ch2.Close() + + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + ch2.Register(raw.Wrap(newTestHandler(t)), "echo") + + outbound, err := ts.Server().BeginCall(ctx, hostPort2, "s2", "echo", nil) + require.NoError(t, err) + outboundConn, outboundNetConn := OutboundConnection(outbound) + + // Try to make another call at the same time, should reuse the same connection. + outbound2, err := ts.Server().BeginCall(ctx, hostPort2, "s2", "echo", nil) + require.NoError(t, err) + outbound2Conn, _ := OutboundConnection(outbound) + assert.Equal(t, outboundConn, outbound2Conn) + + // Wait for the connection to be marked as active in ch2. + assert.True(t, testutils.WaitFor(time.Second, func() bool { + return ch2.IntrospectState(nil).NumConnections > 0 + }), "ch2 does not have any active connections") + + // When ch2 tries to call the test server, it should reuse the existing + // inbound connection the test server. Of course, this only works if the + // test server -> ch2 call wasn't relayed. + outbound3, err := ch2.BeginCall(ctx, ts.HostPort(), "s1", "echo", nil) + require.NoError(t, err) + _, outbound3NetConn := OutboundConnection(outbound3) + assert.Equal(t, outboundNetConn.RemoteAddr(), outbound3NetConn.LocalAddr()) + assert.Equal(t, outboundNetConn.LocalAddr(), outbound3NetConn.RemoteAddr()) + + // Ensure all calls can complete in parallel. + var wg sync.WaitGroup + for _, call := range []*OutboundCall{outbound, outbound2, outbound3} { + wg.Add(1) + go func(call *OutboundCall) { + defer wg.Done() + resp1, resp2, _, err := raw.WriteArgs(call, []byte("arg2"), []byte("arg3")) + require.NoError(t, err) + assert.Equal(t, resp1, []byte("arg2"), "result does match argument") + assert.Equal(t, resp2, []byte("arg3"), "result does match argument") + }(call) + } + wg.Wait() + }) +} + +func TestPing(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + clientCh := ts.NewClient(nil) + defer clientCh.Close() + require.NoError(t, clientCh.Ping(ctx, ts.HostPort())) + }) +} + +func TestBadRequest(t *testing.T) { + // ch will log an error when it receives a request for an unknown handler. + opts := testutils.NewOpts().AddLogFilter("Couldn't find handler.", 1) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "Noone", []byte("Headers"), []byte("Body")) + require.NotNil(t, err) + assert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err)) + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "Noone").Failed("bad-request").End() + ts.AssertRelayStats(calls) + }) +} + +func TestNoTimeout(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "Echo") + + ctx := context.Background() + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), "svc", "Echo", []byte("Headers"), []byte("Body")) + assert.Equal(t, ErrTimeoutRequired, err) + + ts.AssertRelayStats(relaytest.NewMockStats()) + }) +} + +func TestCancelled(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + ctx, cancel := NewContext(time.Second) + + // Make a call first to make sure we have a connection. + // We want to test the BeginCall path. + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", []byte("Headers"), []byte("Body")) + assert.NoError(t, err, "Call failed") + + // Now cancel the context. + cancel() + _, _, _, err = raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", []byte("Headers"), []byte("Body")) + assert.Equal(t, ErrRequestCancelled, err, "Unexpected error when making call with canceled context") + }) +} + +func TestNoServiceNaming(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), "", "Echo", []byte("Headers"), []byte("Body")) + assert.Equal(t, ErrNoServiceName, err) + + ts.AssertRelayStats(relaytest.NewMockStats()) + }) +} + +func TestServerBusy(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(ErrorHandlerFunc(func(ctx context.Context, call *InboundCall) error { + if _, err := raw.ReadArgs(call); err != nil { + return err + } + return ErrServerBusy + }), "busy") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "busy", []byte("Arg2"), []byte("Arg3")) + require.NotNil(t, err) + assert.Equal(t, ErrCodeBusy, GetSystemErrorCode(err), "err: %v", err) + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "busy").Failed("busy").End() + ts.AssertRelayStats(calls) + }) +} + +func TestUnexpectedHandlerError(t *testing.T) { + opts := testutils.NewOpts(). + AddLogFilter("Unexpected handler error", 1) + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ts.Register(ErrorHandlerFunc(func(ctx context.Context, call *InboundCall) error { + if _, err := raw.ReadArgs(call); err != nil { + return err + } + return fmt.Errorf("nope") + }), "nope") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "nope", []byte("Arg2"), []byte("Arg3")) + require.NotNil(t, err) + assert.Equal(t, ErrCodeUnexpected, GetSystemErrorCode(err), "err: %v", err) + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "nope").Failed("unexpected-error").End() + ts.AssertRelayStats(calls) + }) +} + +type onErrorTestHandler struct { + *testHandler + onError func(ctx context.Context, err error) +} + +func (h onErrorTestHandler) OnError(ctx context.Context, err error) { + h.onError(ctx, err) +} + +func TestTimeout(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + // onError may be called when the block call tries to write the call response. + onError := func(ctx context.Context, err error) { + assert.Equal(t, ErrTimeout, err, "onError err should be ErrTimeout") + assert.Equal(t, context.DeadlineExceeded, ctx.Err(), "Context should timeout") + } + testHandler := onErrorTestHandler{newTestHandler(t), onError} + ts.Register(raw.Wrap(testHandler), "block") + + ctx, cancel := NewContext(testutils.Timeout(15 * time.Millisecond)) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "block", []byte("Arg2"), []byte("Arg3")) + assert.Equal(t, ErrTimeout, err) + + // Verify the server-side receives an error from the context. + select { + case err := <-testHandler.blockErr: + assert.Equal(t, context.DeadlineExceeded, err, "Server should have received timeout") + case <-time.After(time.Second): + t.Errorf("Server did not receive call, may need higher timeout") + } + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "block").Failed("timeout").End() + ts.AssertRelayStats(calls) + }) +} + +func TestLargeMethod(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + largeMethod := testutils.RandBytes(16*1024 + 1) + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), string(largeMethod), nil, nil) + assert.Equal(t, ErrMethodTooLarge, err) + }) +} + +func TestLargeTimeout(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + + ctx, cancel := NewContext(1000 * time.Second) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", testArg2, testArg3) + assert.NoError(t, err, "Call failed") + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "echo").Succeeded().End() + ts.AssertRelayStats(calls) + }) +} + +func TestFragmentation(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + + arg2 := make([]byte, MaxFramePayloadSize*2) + for i := 0; i < len(arg2); i++ { + arg2[i] = byte('a' + (i % 10)) + } + + arg3 := make([]byte, MaxFramePayloadSize*3) + for i := 0; i < len(arg3); i++ { + arg3[i] = byte('A' + (i % 10)) + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + + respArg2, respArg3, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", arg2, arg3) + require.NoError(t, err) + assert.Equal(t, arg2, respArg2) + assert.Equal(t, arg3, respArg3) + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "echo").Succeeded().End() + ts.AssertRelayStats(calls) + }) +} + +func TestFragmentationSlowReader(t *testing.T) { + // Inbound forward will timeout and cause a warning log. + opts := testutils.NewOpts(). + AddLogFilter("Unable to forward frame", 1). + AddLogFilter("Connection error", 1) + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + startReading, handlerComplete := make(chan struct{}), make(chan struct{}) + handler := func(ctx context.Context, call *InboundCall) { + <-startReading + <-ctx.Done() + _, err := raw.ReadArgs(call) + assert.Error(t, err, "ReadArgs should fail since frames will be dropped due to slow reading") + close(handlerComplete) + } + + ts.Register(HandlerFunc(handler), "echo") + + arg2 := testutils.RandBytes(MaxFramePayloadSize * MexChannelBufferSize) + arg3 := testutils.RandBytes(MaxFramePayloadSize * (MexChannelBufferSize + 1)) + + ctx, cancel := NewContext(testutils.Timeout(30 * time.Millisecond)) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "echo", arg2, arg3) + assert.Error(t, err, "Call should timeout due to slow reader") + + close(startReading) + select { + case <-handlerComplete: + case <-time.After(testutils.Timeout(70 * time.Millisecond)): + t.Errorf("Handler not called, context timeout may be too low") + } + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "echo").Failed("timeout").End() + ts.AssertRelayStats(calls) + }) +} + +func TestWriteArg3AfterTimeout(t *testing.T) { + // The channel reads and writes during timeouts, causing warning logs. + opts := testutils.NewOpts().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + timedOut := make(chan struct{}) + + handler := func(ctx context.Context, call *InboundCall) { + _, err := raw.ReadArgs(call) + assert.NoError(t, err, "Read args failed") + response := call.Response() + assert.NoError(t, NewArgWriter(response.Arg2Writer()).Write(nil), "Write Arg2 failed") + writer, err := response.Arg3Writer() + assert.NoError(t, err, "Arg3Writer failed") + + for { + if _, err := writer.Write(testutils.RandBytes(4)); err != nil { + assert.Equal(t, err, ErrTimeout, "Handler should timeout") + close(timedOut) + return + } + runtime.Gosched() + } + } + ts.Register(HandlerFunc(handler), "call") + + ctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond)) + defer cancel() + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "call", nil, nil) + assert.Equal(t, err, ErrTimeout, "Call should timeout") + + // Wait for the write to complete, make sure there are no errors. + select { + case <-time.After(testutils.Timeout(60 * time.Millisecond)): + t.Errorf("Handler should have failed due to timeout") + case <-timedOut: + } + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "call").Failed("timeout").Succeeded().End() + ts.AssertRelayStats(calls) + }) +} + +func TestWriteErrorAfterTimeout(t *testing.T) { + // TODO: Make this test block at different points (e.g. before, during read/write). + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + timedOut := make(chan struct{}) + done := make(chan struct{}) + handler := func(ctx context.Context, call *InboundCall) { + <-ctx.Done() + <-timedOut + _, err := raw.ReadArgs(call) + assert.Equal(t, ErrTimeout, err, "Read args should fail with timeout") + response := call.Response() + assert.Equal(t, ErrTimeout, response.SendSystemError(ErrServerBusy), "SendSystemError should fail") + close(done) + } + ts.Register(HandlerFunc(handler), "call") + + ctx, cancel := NewContext(testutils.Timeout(30 * time.Millisecond)) + defer cancel() + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "call", nil, testutils.RandBytes(100000)) + assert.Equal(t, err, ErrTimeout, "Call should timeout") + close(timedOut) + + select { + case <-done: + case <-time.After(time.Second): + t.Errorf("Handler not called, timeout may be too low") + } + + calls := relaytest.NewMockStats() + calls.Add(ts.ServiceName(), ts.ServiceName(), "call").Failed("timeout").End() + ts.AssertRelayStats(calls) + }) +} + +func TestWriteAfterConnectionError(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Closing network connections can lead to warnings in many places. + // TODO: Relay is disabled due to https://github.com/uber/tchannel-go/issues/390 + // Enabling relay causes the test to be flaky. + opts := testutils.NewOpts().DisableLogVerification().NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + testutils.RegisterEcho(ts.Server(), nil) + server := ts.Server() + + call, err := server.BeginCall(ctx, ts.HostPort(), server.ServiceName(), "echo", nil) + require.NoError(t, err, "Call failed") + + w, err := call.Arg2Writer() + require.NoError(t, err, "Arg2Writer failed") + require.NoError(t, writeFlushStr(w, "initial"), "write initial failed") + + // Now close the underlying network connection, writes should fail. + _, conn := OutboundConnection(call) + conn.Close() + + // Writes should start failing pretty soon. + var writeErr error + for i := 0; i < 100; i++ { + if writeErr = writeFlushStr(w, "f"); writeErr != nil { + break + } + time.Sleep(time.Millisecond) + } + if assert.Error(t, writeErr, "Writes should fail after a connection is closed") { + assert.Equal(t, ErrCodeNetwork, GetSystemErrorCode(writeErr), "write should fail due to network error") + } + }) +} + +func TestReadTimeout(t *testing.T) { + // The error frame may fail to send since the connection closes before the handler sends it + // or the handler connection may be closed as it sends when the other side closes the conn. + opts := testutils.NewOpts(). + AddLogFilter("Couldn't send outbound error frame", 1). + AddLogFilter("Connection error", 1, "site", "read frames"). + AddLogFilter("Connection error", 1, "site", "write frames"). + AddLogFilter("simpleHandler OnError", 1, + "error", "failed to send error frame, connection state connectionClosed") + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + sn := ts.ServiceName() + calls := relaytest.NewMockStats() + + for i := 0; i < 10; i++ { + ctx, cancel := NewContext(time.Second) + handler := func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + defer cancel() + return nil, ErrRequestCancelled + } + ts.RegisterFunc("call", handler) + + _, _, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.ServiceName(), "call", nil, nil) + assert.Equal(t, err, ErrRequestCancelled, "Call should fail due to cancel") + calls.Add(sn, sn, "call").Failed("cancelled").End() + } + + ts.AssertRelayStats(calls) + }) +} + +func TestWriteTimeout(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ch := ts.Server() + ctx, cancel := NewContext(testutils.Timeout(15 * time.Millisecond)) + defer cancel() + + call, err := ch.BeginCall(ctx, ts.HostPort(), ch.ServiceName(), "call", nil) + require.NoError(t, err, "Call failed") + + writer, err := call.Arg2Writer() + require.NoError(t, err, "Arg2Writer failed") + + _, err = writer.Write([]byte{1}) + require.NoError(t, err, "Write initial bytes failed") + <-ctx.Done() + + _, err = io.Copy(writer, testreader.Looper([]byte{1})) + assert.Equal(t, ErrTimeout, err, "Write should fail with timeout") + + ts.AssertRelayStats(relaytest.NewMockStats()) + }) +} + +func TestGracefulClose(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ch2 := ts.NewServer(nil) + hp2 := ch2.PeerInfo().HostPort + defer ch2.Close() + + ctx, cancel := NewContext(time.Second) + defer cancel() + + assert.NoError(t, ts.Server().Ping(ctx, hp2), "Ping from ch1 -> ch2 failed") + assert.NoError(t, ch2.Ping(ctx, ts.HostPort()), "Ping from ch2 -> ch1 failed") + + // No stats for pings. + ts.AssertRelayStats(relaytest.NewMockStats()) + }) +} + +func TestNetDialTimeout(t *testing.T) { + // timeoutHostPort uses a blackholed address (RFC 6890) with a port + // reserved for documentation. This address should always cause a timeout. + const timeoutHostPort = "192.18.0.254:44444" + timeoutPeriod := testutils.Timeout(50 * time.Millisecond) + + client := testutils.NewClient(t, nil) + defer client.Close() + + started := time.Now() + ctx, cancel := NewContext(timeoutPeriod) + defer cancel() + + err := client.Ping(ctx, timeoutHostPort) + if !assert.Error(t, err, "Ping to blackhole address should fail") { + return + } + + if strings.Contains(err.Error(), "network is unreachable") { + t.Skipf("Skipping test, as network interface may not be available") + } + + d := time.Since(started) + assert.Equal(t, ErrTimeout, err, "Ping expected to fail with timeout") + assert.True(t, d >= timeoutPeriod, "Timeout should take more than %v, took %v", timeoutPeriod, d) +} + +func TestConnectTimeout(t *testing.T) { + opts := testutils.NewOpts().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + // Set up a relay that will delay the initial init req. + testComplete := make(chan struct{}) + + relayFunc := func(outgoing bool, f *Frame) *Frame { + select { + case <-time.After(testutils.Timeout(200 * time.Millisecond)): + return f + case <-testComplete: + // TODO: We should be able to forward the frame and have this test not fail. + // Currently, it fails since the sequence of events is: + // Server receives a TCP connection + // Channel.Close() is called on the server + // Server's TCP connection receives an init req + // Since we don't currently track pending connections, the open TCP connection is not closed, and + // we process the init req. This leaves an open connection at the end of the test. + return nil + } + } + relay, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc) + defer shutdown() + + // Make a call with a long timeout, but short connect timeout. + // We expect the call to fall almost immediately with ErrTimeout. + ctx, cancel := NewContextBuilder(2 * time.Second). + SetConnectTimeout(testutils.Timeout(100 * time.Millisecond)). + Build() + defer cancel() + + client := ts.NewClient(opts) + err := client.Ping(ctx, relay) + assert.Equal(t, ErrTimeout, err, "Ping should timeout due to timeout relay") + + // Note: we do not defer this, as we need to close(testComplete) before + // we call shutdown since shutdown waits for the relay to close, which + // is stuck waiting inside of our custom relay function. + close(testComplete) + }) +} + +func TestParallelConnectionAccepts(t *testing.T) { + opts := testutils.NewOpts().AddLogFilter("Failed during connection handshake", 1) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + testutils.RegisterEcho(ts.Server(), nil) + + // Start a connection attempt that should timeout. + conn, err := net.Dial("tcp", ts.HostPort()) + defer conn.Close() + require.NoError(t, err, "Dial failed") + + // When we try to make a call using a new client, it will require a + // new connection, and this verifies that the previous connection attempt + // and handshake do not impact the call. + client := ts.NewClient(nil) + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + }) +} + +func TestConnectionIDs(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + var inbound, outbound []uint32 + relayFunc := func(outgoing bool, f *Frame) *Frame { + if outgoing { + outbound = append(outbound, f.Header.ID) + } else { + inbound = append(inbound, f.Header.ID) + } + return f + } + relay, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc) + defer shutdown() + + ctx, cancel := NewContext(time.Second) + defer cancel() + + s2 := ts.NewServer(nil) + require.NoError(t, s2.Ping(ctx, relay), "Ping failed") + assert.Equal(t, []uint32{1, 2}, outbound, "Unexpected outbound IDs") + assert.Equal(t, []uint32{1, 2}, inbound, "Unexpected outbound IDs") + + // We want to reuse the same connection for the rest of the test which + // only makes sense when the relay is not used. + if ts.Relay() != nil { + return + } + + inbound = nil + outbound = nil + // We will reuse the inbound connection, but since the inbound connection + // hasn't originated any outbound requests, we'll use id 1. + require.NoError(t, ts.Server().Ping(ctx, s2.PeerInfo().HostPort), "Ping failed") + assert.Equal(t, []uint32{1}, outbound, "Unexpected outbound IDs") + assert.Equal(t, []uint32{1}, inbound, "Unexpected outbound IDs") + }) +} + +func TestTosPriority(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + opts := testutils.NewOpts().SetServiceName("s1").SetTosPriority(tos.Lowdelay) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + + outbound, err := ts.Server().BeginCall(ctx, ts.HostPort(), "s1", "echo", nil) + require.NoError(t, err, "BeginCall failed") + + _, outboundNetConn := OutboundConnection(outbound) + connTosPriority, err := isTosPriority(outboundNetConn, tos.Lowdelay) + require.NoError(t, err, "Checking TOS priority failed") + assert.Equal(t, connTosPriority, true) + _, _, _, err = raw.WriteArgs(outbound, []byte("arg2"), []byte("arg3")) + require.NoError(t, err, "Failed to write to outbound conn") + }) +} + +func TestPeerStatusChangeClientReduction(t *testing.T) { + sopts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, sopts, func(ts *testutils.TestServer) { + server := ts.Server() + testutils.RegisterEcho(server, nil) + changes := make(chan int, 2) + + copts := testutils.NewOpts().SetOnPeerStatusChanged(func(p *Peer) { + i, o := p.NumConnections() + assert.Equal(t, 0, i, "no inbound connections to client") + changes <- o + }) + + // Induce the creation of a connection from client to server. + client := ts.NewClient(copts) + require.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)) + assert.Equal(t, 1, <-changes, "event for first connection") + + // Re-use + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + + // Induce the destruction of a connection from the server to the client. + server.Close() + assert.Equal(t, 0, <-changes, "event for second disconnection") + + client.Close() + assert.Len(t, changes, 0, "unexpected peer status changes") + }) +} + +func TestPeerStatusChangeClient(t *testing.T) { + sopts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, sopts, func(ts *testutils.TestServer) { + server := ts.Server() + testutils.RegisterEcho(server, nil) + changes := make(chan int, 2) + + copts := testutils.NewOpts().SetOnPeerStatusChanged(func(p *Peer) { + i, o := p.NumConnections() + assert.Equal(t, 0, i, "no inbound connections to client") + changes <- o + }) + + // Induce the creation of a connection from client to server. + client := ts.NewClient(copts) + require.NoError(t, testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil)) + assert.Equal(t, 1, <-changes, "event for first connection") + + // Re-use + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + + // Induce the creation of a second connection from client to server. + pl := client.RootPeers() + p := pl.GetOrAdd(ts.HostPort()) + ctx := context.Background() + ctx, cancel := context.WithTimeout(ctx, testutils.Timeout(100*time.Millisecond)) + defer cancel() + _, err := p.Connect(ctx) + require.NoError(t, err) + assert.Equal(t, 2, <-changes, "event for second connection") + + // Induce the destruction of a connection from the server to the client. + server.Close() + <-changes // May be 1 or 0 depending on timing. + assert.Equal(t, 0, <-changes, "event for second disconnection") + + client.Close() + assert.Len(t, changes, 0, "unexpected peer status changes") + }) +} + +func TestPeerStatusChangeServer(t *testing.T) { + changes := make(chan int, 10) + sopts := testutils.NewOpts().NoRelay().SetOnPeerStatusChanged(func(p *Peer) { + i, o := p.NumConnections() + assert.Equal(t, 0, o, "no outbound connections from server") + changes <- i + }) + testutils.WithTestServer(t, sopts, func(ts *testutils.TestServer) { + server := ts.Server() + testutils.RegisterEcho(server, nil) + + copts := testutils.NewOpts() + for i := 0; i < 5; i++ { + client := ts.NewClient(copts) + + // Open + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + assert.Equal(t, 1, <-changes, "one event on new connection") + + // Re-use + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + assert.Len(t, changes, 0, "no new events on re-used connection") + + // Close + client.Close() + assert.Equal(t, 0, <-changes, "one event on lost connection") + } + }) + assert.Len(t, changes, 0, "unexpected peer status changes") +} + +func TestContextCanceledOnTCPClose(t *testing.T) { + // 1. Context canceled warning is expected as part of this test + // add log filter to ignore this error + // 2. We use our own relay in this test, so disable the relay + // that comes with the test server + opts := testutils.NewOpts().NoRelay().AddLogFilter("simpleHandler OnError", 1) + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + serverDoneC := make(chan struct{}) + callForwarded := make(chan struct{}) + + ts.RegisterFunc("test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + defer close(serverDoneC) + close(callForwarded) + <-ctx.Done() + assert.EqualError(t, ctx.Err(), "context canceled") + return &raw.Res{}, nil + }) + + // Set up a relay that can be used to terminate conns + // on both sides i.e. client and server + relayFunc := func(outgoing bool, f *Frame) *Frame { + return f + } + relayHostPort, shutdown := testutils.FrameRelay(t, ts.HostPort(), relayFunc) + + // Make a call with a long timeout. We shutdown the relay + // immediately after the server receives the call. Expected + // behavior is for both client/server to be done with the call + // immediately after relay shutsdown + ctx, cancel := NewContext(20 * time.Second) + defer cancel() + + clientCh := ts.NewClient(nil) + // initiate the call in a background routine and + // make it wait for the response + clientDoneC := make(chan struct{}) + go func() { + raw.Call(ctx, clientCh, relayHostPort, ts.ServiceName(), "test", nil, nil) + close(clientDoneC) + }() + + // wait for server to receive the call + select { + case <-callForwarded: + case <-time.After(2 * time.Second): + assert.Fail(t, "timed waiting for call to be forwarded") + } + + // now shutdown the relay to close conns + // on both sides + shutdown() + + // wait for both the client & server to be done + select { + case <-serverDoneC: + case <-time.After(2 * time.Second): + assert.Fail(t, "timed out waiting for server handler to exit") + } + + select { + case <-clientDoneC: + case <-time.After(2 * time.Second): + assert.Fail(t, "timed out waiting for client to exit") + } + + clientCh.Close() + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/connectionstate_string.go b/vendor/src/github.com/uber/tchannel-go/connectionstate_string.go new file mode 100644 index 00000000..10907493 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/connectionstate_string.go @@ -0,0 +1,17 @@ +// Code generated by "stringer -type=connectionState"; DO NOT EDIT + +package tchannel + +import "fmt" + +const _connectionState_name = "connectionActiveconnectionStartCloseconnectionInboundClosedconnectionClosed" + +var _connectionState_index = [...]uint8{0, 16, 36, 59, 75} + +func (i connectionState) String() string { + i -= 1 + if i < 0 || i >= connectionState(len(_connectionState_index)-1) { + return fmt.Sprintf("connectionState(%d)", i+1) + } + return _connectionState_name[_connectionState_index[i]:_connectionState_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/context.go b/vendor/src/github.com/uber/tchannel-go/context.go new file mode 100644 index 00000000..323ac21a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/context.go @@ -0,0 +1,124 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "time" + + "golang.org/x/net/context" +) + +const defaultTimeout = time.Second + +type contextKey int + +const ( + contextKeyTChannel contextKey = iota + contextKeyHeaders +) + +type tchannelCtxParams struct { + tracingDisabled bool + hideListeningOnOutbound bool + call IncomingCall + options *CallOptions + retryOptions *RetryOptions + connectTimeout time.Duration +} + +// IncomingCall exposes properties for incoming calls through the context. +type IncomingCall interface { + // CallerName returns the caller name from the CallerName transport header. + CallerName() string + + // ShardKey returns the shard key from the ShardKey transport header. + ShardKey() string + + // RoutingKey returns the routing key (referring to a traffic group) from + // RoutingKey transport header. + RoutingKey() string + + // RoutingDelegate returns the routing delegate from RoutingDelegate + // transport header. + RoutingDelegate() string + + // LocalPeer returns the local peer information. + LocalPeer() LocalPeerInfo + + // RemotePeer returns the caller's peer information. + // If the caller is an ephemeral peer, then the HostPort cannot be used to make new + // connections to the caller. + RemotePeer() PeerInfo + + // CallOptions returns the call options set for the incoming call. It can be useful + // if you are forwarding a request and wish to retain the CallerName(), which is not + // possible to set manually. + CallOptions() *CallOptions +} + +func getTChannelParams(ctx context.Context) *tchannelCtxParams { + if params, ok := ctx.Value(contextKeyTChannel).(*tchannelCtxParams); ok { + return params + } + return nil +} + +// NewContext returns a new root context used to make TChannel requests. +func NewContext(timeout time.Duration) (context.Context, context.CancelFunc) { + return NewContextBuilder(timeout).Build() +} + +// WrapContextForTest returns a copy of the given Context that is associated with the call. +// This should be used in units test only. +// NOTE: This method is deprecated. Callers should use NewContextBuilder().SetIncomingCallForTest. +func WrapContextForTest(ctx context.Context, call IncomingCall) context.Context { + getTChannelParams(ctx).call = call + return ctx +} + +// newIncomingContext creates a new context for an incoming call with the given span. +func newIncomingContext(call IncomingCall, timeout time.Duration) (context.Context, context.CancelFunc) { + return NewContextBuilder(timeout). + setIncomingCall(call). + Build() +} + +// CurrentCall returns the current incoming call, or nil if this is not an incoming call context. +func CurrentCall(ctx context.Context) IncomingCall { + if params := getTChannelParams(ctx); params != nil { + return params.call + } + return nil +} + +func currentCallOptions(ctx context.Context) *CallOptions { + if params := getTChannelParams(ctx); params != nil { + return params.options + } + return nil +} + +func isTracingDisabled(ctx context.Context) bool { + if params := getTChannelParams(ctx); params != nil { + return params.tracingDisabled + } + return false +} diff --git a/vendor/src/github.com/uber/tchannel-go/context_builder.go b/vendor/src/github.com/uber/tchannel-go/context_builder.go new file mode 100644 index 00000000..484aff80 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/context_builder.go @@ -0,0 +1,231 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "time" + + "golang.org/x/net/context" +) + +// ContextBuilder stores all TChannel-specific parameters that will +// be stored inside of a context. +type ContextBuilder struct { + // TracingDisabled disables trace reporting for calls using this context. + TracingDisabled bool + + // hideListeningOnOutbound disables sending the listening server's host:port + // when creating new outgoing connections. + hideListeningOnOutbound bool + + // replaceParentHeaders is set to true when SetHeaders() method is called. + // It forces headers from ParentContext to be ignored. When false, parent + // headers will be merged with headers accumulated by the builder. + replaceParentHeaders bool + + // If Timeout is zero, Build will default to defaultTimeout. + Timeout time.Duration + + // Headers are application headers that json/thrift will encode into arg2. + Headers map[string]string + + // CallOptions are TChannel call options for the specific call. + CallOptions *CallOptions + + // RetryOptions are the retry options for this call. + RetryOptions *RetryOptions + + // ConnectTimeout is the timeout for creating a TChannel connection. + ConnectTimeout time.Duration + + // ParentContext to build the new context from. If empty, context.Background() is used. + // The new (child) context inherits a number of properties from the parent context: + // - context fields, accessible via `ctx.Value(key)` + // - headers if parent is a ContextWithHeaders, unless replaced via SetHeaders() + ParentContext context.Context + + // Hidden fields: we do not want users outside of tchannel to set these. + incomingCall IncomingCall +} + +// NewContextBuilder returns a builder that can be used to create a Context. +func NewContextBuilder(timeout time.Duration) *ContextBuilder { + return &ContextBuilder{ + Timeout: timeout, + } +} + +// SetTimeout sets the timeout for the Context. +func (cb *ContextBuilder) SetTimeout(timeout time.Duration) *ContextBuilder { + cb.Timeout = timeout + return cb +} + +// AddHeader adds a single application header to the Context. +func (cb *ContextBuilder) AddHeader(key, value string) *ContextBuilder { + if cb.Headers == nil { + cb.Headers = map[string]string{key: value} + } else { + cb.Headers[key] = value + } + return cb +} + +// SetHeaders sets the application headers for this Context. +// If there is a ParentContext, its headers will be ignored after the call to this method. +func (cb *ContextBuilder) SetHeaders(headers map[string]string) *ContextBuilder { + cb.Headers = headers + cb.replaceParentHeaders = true + return cb +} + +// SetShardKey sets the ShardKey call option ("sk" transport header). +func (cb *ContextBuilder) SetShardKey(sk string) *ContextBuilder { + if cb.CallOptions == nil { + cb.CallOptions = new(CallOptions) + } + cb.CallOptions.ShardKey = sk + return cb +} + +// SetFormat sets the Format call option ("as" transport header). +func (cb *ContextBuilder) SetFormat(f Format) *ContextBuilder { + if cb.CallOptions == nil { + cb.CallOptions = new(CallOptions) + } + cb.CallOptions.Format = f + return cb +} + +// SetRoutingKey sets the RoutingKey call options ("rk" transport header). +func (cb *ContextBuilder) SetRoutingKey(rk string) *ContextBuilder { + if cb.CallOptions == nil { + cb.CallOptions = new(CallOptions) + } + cb.CallOptions.RoutingKey = rk + return cb +} + +// SetRoutingDelegate sets the RoutingDelegate call options ("rd" transport header). +func (cb *ContextBuilder) SetRoutingDelegate(rd string) *ContextBuilder { + if cb.CallOptions == nil { + cb.CallOptions = new(CallOptions) + } + cb.CallOptions.RoutingDelegate = rd + return cb +} + +// SetConnectTimeout sets the ConnectionTimeout for this context. +// The context timeout applies to the whole call, while the connect +// timeout only applies to creating a new connection. +func (cb *ContextBuilder) SetConnectTimeout(d time.Duration) *ContextBuilder { + cb.ConnectTimeout = d + return cb +} + +// HideListeningOnOutbound hides the host:port when creating new outbound +// connections. +func (cb *ContextBuilder) HideListeningOnOutbound() *ContextBuilder { + cb.hideListeningOnOutbound = true + return cb +} + +// DisableTracing disables tracing. +func (cb *ContextBuilder) DisableTracing() *ContextBuilder { + cb.TracingDisabled = true + return cb +} + +// SetIncomingCallForTest sets an IncomingCall in the context. +// This should only be used in unit tests. +func (cb *ContextBuilder) SetIncomingCallForTest(call IncomingCall) *ContextBuilder { + return cb.setIncomingCall(call) +} + +// SetRetryOptions sets RetryOptions in the context. +func (cb *ContextBuilder) SetRetryOptions(retryOptions *RetryOptions) *ContextBuilder { + cb.RetryOptions = retryOptions + return cb +} + +// SetTimeoutPerAttempt sets TimeoutPerAttempt in RetryOptions. +func (cb *ContextBuilder) SetTimeoutPerAttempt(timeoutPerAttempt time.Duration) *ContextBuilder { + if cb.RetryOptions == nil { + cb.RetryOptions = &RetryOptions{} + } + cb.RetryOptions.TimeoutPerAttempt = timeoutPerAttempt + return cb +} + +// SetParentContext sets the parent for the Context. +func (cb *ContextBuilder) SetParentContext(ctx context.Context) *ContextBuilder { + cb.ParentContext = ctx + return cb +} + +func (cb *ContextBuilder) setIncomingCall(call IncomingCall) *ContextBuilder { + cb.incomingCall = call + return cb +} + +func (cb *ContextBuilder) getHeaders() map[string]string { + if cb.ParentContext == nil || cb.replaceParentHeaders { + return cb.Headers + } + + parent, ok := cb.ParentContext.Value(contextKeyHeaders).(*headersContainer) + if !ok || len(parent.reqHeaders) == 0 { + return cb.Headers + } + + mergedHeaders := make(map[string]string, len(cb.Headers)+len(parent.reqHeaders)) + for k, v := range parent.reqHeaders { + mergedHeaders[k] = v + } + for k, v := range cb.Headers { + mergedHeaders[k] = v + } + return mergedHeaders +} + +// Build returns a ContextWithHeaders that can be used to make calls. +func (cb *ContextBuilder) Build() (ContextWithHeaders, context.CancelFunc) { + params := &tchannelCtxParams{ + options: cb.CallOptions, + call: cb.incomingCall, + retryOptions: cb.RetryOptions, + connectTimeout: cb.ConnectTimeout, + hideListeningOnOutbound: cb.hideListeningOnOutbound, + tracingDisabled: cb.TracingDisabled, + } + + parent := cb.ParentContext + if parent == nil { + parent = context.Background() + } else if headerCtx, ok := parent.(headerCtx); ok { + // Unwrap any headerCtx, since we'll be rewrapping anyway. + parent = headerCtx.Context + } + ctx, cancel := context.WithTimeout(parent, cb.Timeout) + + ctx = context.WithValue(ctx, contextKeyTChannel, params) + return WrapWithHeaders(ctx, cb.getHeaders()), cancel +} diff --git a/vendor/src/github.com/uber/tchannel-go/context_header.go b/vendor/src/github.com/uber/tchannel-go/context_header.go new file mode 100644 index 00000000..3a333af0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/context_header.go @@ -0,0 +1,121 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "golang.org/x/net/context" + +// ContextWithHeaders is a Context which contains request and response headers. +type ContextWithHeaders interface { + context.Context + + // Headers returns the call request headers. + Headers() map[string]string + + // ResponseHeaders returns the call response headers. + ResponseHeaders() map[string]string + + // SetResponseHeaders sets the given response headers on the context. + SetResponseHeaders(map[string]string) + + // Child creates a child context which stores headers separately from + // the parent context. + Child() ContextWithHeaders +} + +type headerCtx struct { + context.Context +} + +// headersContainer stores the headers, and is itself stored in the context under `contextKeyHeaders` +type headersContainer struct { + reqHeaders map[string]string + respHeaders map[string]string +} + +func (c headerCtx) headers() *headersContainer { + if h, ok := c.Value(contextKeyHeaders).(*headersContainer); ok { + return h + } + return nil +} + +// Headers gets application headers out of the context. +func (c headerCtx) Headers() map[string]string { + if h := c.headers(); h != nil { + return h.reqHeaders + } + return nil +} + +// ResponseHeaders returns the response headers. +func (c headerCtx) ResponseHeaders() map[string]string { + if h := c.headers(); h != nil { + return h.respHeaders + } + return nil +} + +// SetResponseHeaders sets the response headers. +func (c headerCtx) SetResponseHeaders(headers map[string]string) { + if h := c.headers(); h != nil { + h.respHeaders = headers + return + } + panic("SetResponseHeaders called on ContextWithHeaders not created via WrapWithHeaders") +} + +// Child creates a child context with a separate container for headers. +func (c headerCtx) Child() ContextWithHeaders { + var headersCopy headersContainer + if h := c.headers(); h != nil { + headersCopy = *h + } + + return Wrap(context.WithValue(c.Context, contextKeyHeaders, &headersCopy)) +} + +// Wrap wraps an existing context.Context into a ContextWithHeaders. +// If the underlying context has headers, they are preserved. +func Wrap(ctx context.Context) ContextWithHeaders { + hctx := headerCtx{Context: ctx} + if h := hctx.headers(); h != nil { + return hctx + } + + // If there is no header container, we should create an empty one. + return WrapWithHeaders(ctx, nil) +} + +// WrapWithHeaders returns a Context that can be used to make a call with request headers. +// If the parent `ctx` is already an instance of ContextWithHeaders, its existing headers +// will be ignored. In order to merge new headers with parent headers, use ContextBuilder. +func WrapWithHeaders(ctx context.Context, headers map[string]string) ContextWithHeaders { + h := &headersContainer{ + reqHeaders: headers, + } + newCtx := context.WithValue(ctx, contextKeyHeaders, h) + return headerCtx{Context: newCtx} +} + +// WithoutHeaders hides any TChannel headers from the given context. +func WithoutHeaders(ctx context.Context) context.Context { + return context.WithValue(context.WithValue(ctx, contextKeyTChannel, nil), contextKeyHeaders, nil) +} diff --git a/vendor/src/github.com/uber/tchannel-go/context_internal_test.go b/vendor/src/github.com/uber/tchannel-go/context_internal_test.go new file mode 100644 index 00000000..b27d3ca9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/context_internal_test.go @@ -0,0 +1,74 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package tchannel + +import ( + "testing" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestNewContextBuilderDisableTracing(t *testing.T) { + ctx, cancel := NewContextBuilder(time.Second). + DisableTracing().Build() + defer cancel() + + assert.True(t, isTracingDisabled(ctx), "Tracing should be disabled") +} + +func TestCurrentSpan(t *testing.T) { + ctx := context.Background() + span := CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + + tracer := mocktracer.New() + sp := tracer.StartSpan("test") + ctx = opentracing.ContextWithSpan(ctx, sp) + span = CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + assert.EqualValues(t, 0, span.TraceID(), "mock tracer is not Zipkin-compatible") + + tracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector)) + span = CurrentSpan(ctx) + require.NotNil(t, span, "CurrentSpan() should always return something") + assert.NotEqual(t, uint64(0), span.TraceID(), "mock tracer is now Zipkin-compatible") +} + +func TestContextWithoutHeadersKeyHeaders(t *testing.T) { + ctx := WrapWithHeaders(context.Background(), map[string]string{"k1": "v1"}) + assert.Equal(t, map[string]string{"k1": "v1"}, ctx.Headers()) + ctx2 := WithoutHeaders(ctx) + assert.Nil(t, ctx2.Value(contextKeyHeaders)) + _, ok := ctx2.(ContextWithHeaders) + assert.False(t, ok) +} + +func TestContextWithoutHeadersKeyTChannel(t *testing.T) { + ctx, _ := NewContextBuilder(time.Second).SetShardKey("s1").Build() + ctx2 := WithoutHeaders(ctx) + assert.Nil(t, ctx2.Value(contextKeyTChannel)) + _, ok := ctx2.(ContextWithHeaders) + assert.False(t, ok) +} diff --git a/vendor/src/github.com/uber/tchannel-go/context_test.go b/vendor/src/github.com/uber/tchannel-go/context_test.go new file mode 100644 index 00000000..34d3563e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/context_test.go @@ -0,0 +1,288 @@ +// Copyright (c) 2015 Uber Technologies, Inc. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/goroutines" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +var cn = "hello" + +func TestWrapContextForTest(t *testing.T) { + call := testutils.NewIncomingCall(cn) + ctx, cancel := NewContext(time.Second) + defer cancel() + actual := WrapContextForTest(ctx, call) + assert.Equal(t, call, CurrentCall(actual), "Incorrect call object returned.") +} + +func TestNewContextTimeoutZero(t *testing.T) { + ctx, cancel := NewContextBuilder(0).Build() + defer cancel() + + deadline, ok := ctx.Deadline() + assert.True(t, ok, "Context missing deadline") + assert.True(t, deadline.Sub(time.Now()) <= 0, "Deadline should be Now or earlier") +} + +func TestRoutingDelegatePropagates(t *testing.T) { + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + peerInfo := ch.PeerInfo() + testutils.RegisterFunc(ch, "test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg3: []byte(CurrentCall(ctx).RoutingDelegate()), + }, nil + }) + + ctx, cancel := NewContextBuilder(time.Second).Build() + defer cancel() + _, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, "", string(arg3), "Expected no routing delegate header") + + ctx, cancel = NewContextBuilder(time.Second).SetRoutingDelegate("xpr").Build() + defer cancel() + _, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, "xpr", string(arg3), "Expected routing delegate header to be set") + }) +} + +func TestRoutingKeyPropagates(t *testing.T) { + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + peerInfo := ch.PeerInfo() + testutils.RegisterFunc(ch, "test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg3: []byte(CurrentCall(ctx).RoutingKey()), + }, nil + }) + + ctx, cancel := NewContextBuilder(time.Second).Build() + defer cancel() + _, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, "", string(arg3), "Expected no routing key header") + + ctx, cancel = NewContextBuilder(time.Second).SetRoutingKey("canary").Build() + defer cancel() + _, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, "canary", string(arg3), "Expected routing key header to be set") + }) +} + +func TestShardKeyPropagates(t *testing.T) { + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + peerInfo := ch.PeerInfo() + testutils.RegisterFunc(ch, "test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg3: []byte(CurrentCall(ctx).ShardKey()), + }, nil + }) + + ctx, cancel := NewContextBuilder(time.Second).Build() + defer cancel() + _, arg3, _, err := raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, arg3, []byte("")) + + ctx, cancel = NewContextBuilder(time.Second). + SetShardKey("shard").Build() + defer cancel() + _, arg3, _, err = raw.Call(ctx, ch, peerInfo.HostPort, peerInfo.ServiceName, "test", nil, nil) + assert.NoError(t, err, "Call failed") + assert.Equal(t, string(arg3), "shard") + }) +} + +func TestCurrentCallWithNilResult(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + call := CurrentCall(ctx) + assert.Nil(t, call, "Should return nil.") +} + +func getParentContext(t *testing.T) ContextWithHeaders { + ctx := context.WithValue(context.Background(), "some key", "some value") + assert.Equal(t, "some value", ctx.Value("some key")) + + ctx1, _ := NewContextBuilder(time.Second). + SetParentContext(ctx). + AddHeader("header key", "header value"). + Build() + assert.Equal(t, "some value", ctx1.Value("some key")) + return ctx1 +} + +func TestContextBuilderParentContextNoHeaders(t *testing.T) { + ctx := getParentContext(t) + assert.Equal(t, map[string]string{"header key": "header value"}, ctx.Headers()) + assert.EqualValues(t, "some value", ctx.Value("some key"), "inherited from parent ctx") +} + +func TestContextBuilderParentContextMergeHeaders(t *testing.T) { + ctx := getParentContext(t) + ctx.Headers()["fixed header"] = "fixed value" + + // append header to parent + ctx2, _ := NewContextBuilder(time.Second). + SetParentContext(ctx). + AddHeader("header key 2", "header value 2"). + Build() + assert.Equal(t, map[string]string{ + "header key": "header value", // inherited + "fixed header": "fixed value", // inherited + "header key 2": "header value 2", // appended + }, ctx2.Headers()) + + // override parent header + ctx3, _ := NewContextBuilder(time.Second). + SetParentContext(ctx). + AddHeader("header key", "header value 2"). // override + Build() + + assert.Equal(t, map[string]string{ + "header key": "header value 2", // overwritten + "fixed header": "fixed value", // inherited + }, ctx3.Headers()) + + goroutines.VerifyNoLeaks(t, nil) +} + +func TestContextBuilderParentContextReplaceHeaders(t *testing.T) { + ctx := getParentContext(t) + ctx.Headers()["fixed header"] = "fixed value" + assert.Equal(t, map[string]string{ + "header key": "header value", + "fixed header": "fixed value", + }, ctx.Headers()) + + // replace headers with a new map + ctx2, _ := NewContextBuilder(time.Second). + SetParentContext(ctx). + SetHeaders(map[string]string{"header key": "header value 2"}). + Build() + assert.Equal(t, map[string]string{"header key": "header value 2"}, ctx2.Headers()) + + goroutines.VerifyNoLeaks(t, nil) +} + +func TestContextWrapWithHeaders(t *testing.T) { + headers1 := map[string]string{ + "k1": "v1", + } + ctx, _ := NewContextBuilder(time.Second). + SetHeaders(headers1). + Build() + assert.Equal(t, headers1, ctx.Headers(), "Headers mismatch after Build") + + headers2 := map[string]string{ + "k1": "v1", + } + ctx2 := WrapWithHeaders(ctx, headers2) + assert.Equal(t, headers2, ctx2.Headers(), "Headers mismatch after WrapWithHeaders") +} + +func TestContextWithHeadersAsContext(t *testing.T) { + var ctx context.Context = getParentContext(t) + assert.EqualValues(t, "some value", ctx.Value("some key"), "inherited from parent ctx") +} + +func TestContextBuilderParentContextSpan(t *testing.T) { + ctx := getParentContext(t) + assert.Equal(t, "some value", ctx.Value("some key")) + + ctx2, _ := NewContextBuilder(time.Second). + SetParentContext(ctx). + Build() + assert.Equal(t, "some value", ctx2.Value("some key"), "key/value propagated from parent ctx") + + goroutines.VerifyNoLeaks(t, nil) +} + +func TestContextWrapChild(t *testing.T) { + tests := []struct { + msg string + ctxFn func() ContextWithHeaders + wantHeaders map[string]string + wantValue interface{} + }{ + { + msg: "Basic context", + ctxFn: func() ContextWithHeaders { + ctxNoHeaders, _ := NewContextBuilder(time.Second).Build() + return ctxNoHeaders + }, + wantHeaders: nil, + wantValue: nil, + }, + { + msg: "Wrap basic context with value", + ctxFn: func() ContextWithHeaders { + ctxNoHeaders, _ := NewContextBuilder(time.Second).Build() + return Wrap(context.WithValue(ctxNoHeaders, "1", "2")) + }, + wantHeaders: nil, + wantValue: "2", + }, + { + msg: "Wrap context with headers and value", + ctxFn: func() ContextWithHeaders { + ctxWithHeaders, _ := NewContextBuilder(time.Second).AddHeader("h1", "v1").Build() + return Wrap(context.WithValue(ctxWithHeaders, "1", "2")) + }, + wantHeaders: map[string]string{"h1": "v1"}, + wantValue: "2", + }, + } + + for _, tt := range tests { + for _, child := range []bool{false, true} { + origCtx := tt.ctxFn() + ctx := origCtx + if child { + ctx = origCtx.Child() + } + + assert.Equal(t, tt.wantValue, ctx.Value("1"), "%v: Unexpected value", tt.msg) + assert.Equal(t, tt.wantHeaders, ctx.Headers(), "%v: Unexpected headers", tt.msg) + + respHeaders := map[string]string{"r": "v"} + ctx.SetResponseHeaders(respHeaders) + assert.Equal(t, respHeaders, ctx.ResponseHeaders(), "%v: Unexpected response headers", tt.msg) + + if child { + // If we're working with a child context, changes to response headers + // should not affect the original context. + assert.Nil(t, origCtx.ResponseHeaders(), "%v: Child modified original context's headers", tt.msg) + } + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/Dockerfile b/vendor/src/github.com/uber/tchannel-go/crossdock/Dockerfile new file mode 100644 index 00000000..7a26879f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/Dockerfile @@ -0,0 +1,4 @@ +FROM golang +ADD crossdock / +CMD ["/crossdock"] +EXPOSE 8080-8082 diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/api.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/api.go new file mode 100644 index 00000000..e229ab8a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/api.go @@ -0,0 +1,51 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +// Request instructs the server to call another server recursively if Downstream != nil, +// and return the results of the downstream call as well as the current tracing span it +// observes in its Context. +type Request struct { + ServerRole string `json:"serverRole"` + Downstream *Downstream `json:"downstream,omitempty"` +} + +// Downstream describes which downstream service to call recursively. +type Downstream struct { + ServiceName string `json:"serviceName"` + ServerRole string `json:"serverRole"` + Encoding string `json:"encoding"` + HostPort string `json:"hostPort"` + Downstream *Downstream `json:"downstream,omitempty"` +} + +// Response contains the span observed by the server and nested downstream response. +type Response struct { + Span *ObservedSpan `json:"span,omitempty"` + Downstream *Response `json:"downstream,omitempty"` +} + +// ObservedSpan describes the tracing span observed by the server +type ObservedSpan struct { + TraceID string `json:"traceId"` + Sampled bool `json:"sampled"` + Baggage string `json:"baggage"` +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior.go new file mode 100644 index 00000000..196fc491 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior.go @@ -0,0 +1,254 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +import ( + "fmt" + "strconv" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/crossdock/log" + + "github.com/crossdock/crossdock-go" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/utils" + "golang.org/x/net/context" +) + +// Different parameter keys and values used by the system +const ( + BehaviorName = "trace" +) + +// Behavior is an implementation of "trace behavior", that verifies +// that a tracing context and baggage are properly propagated through +// two level of servers. +type Behavior struct { + ServerPort string + Tracer opentracing.Tracer + ServiceToHost func(string) string + ch *tchannel.Channel + thriftCall DownstreamCall + jsonCall DownstreamCall +} + +// DownstreamCall is an encoding-agnostic abstraction of calling a downstream service. +type DownstreamCall func(ctx context.Context, target *Downstream) (*Response, error) + +// Register function adds JSON and Thrift handlers to the server channel ch +func (b *Behavior) Register(ch *tchannel.Channel) { + b.registerThrift(ch) + b.registerJSON(ch) +} + +// Run executes the trace behavior +func (b *Behavior) Run(t crossdock.T) { + logParams(t) + sampled, err := strconv.ParseBool(t.Param(sampledParam)) + if err != nil { + t.Fatalf("Malformed param %s: %s", sampledParam, err) + } + baggage := randomBaggage() + + level1 := &Request{ + ServerRole: RoleS1, + } + server1 := t.Param(server1NameParam) + + level2 := &Downstream{ + ServiceName: t.Param(server2NameParam), + ServerRole: RoleS2, + HostPort: fmt.Sprintf("%s:%s", + b.serviceToHost(t.Param(server2NameParam)), + b.ServerPort, + ), + Encoding: t.Param(server2EncodingParam), + } + level1.Downstream = level2 + + level3 := &Downstream{ + ServiceName: t.Param(server3NameParam), + ServerRole: RoleS3, + HostPort: fmt.Sprintf("%s:%s", + b.serviceToHost(t.Param(server3NameParam)), + b.ServerPort, + ), + Encoding: t.Param(server3EncodingParam), + } + level2.Downstream = level3 + + resp, err := b.startTrace(t, level1, sampled, baggage) + if err != nil { + t.Errorf("Failed to startTrace in S1(%s): %s", server1, err.Error()) + return + } + + log.Printf("Response: span=%+v, downstream=%+v", resp.Span, resp.Downstream) + traceID := resp.Span.TraceID + + require := crossdock.Require(t) + require.NotEmpty(traceID, "Trace ID should not be empty in S1(%s)", server1) + + if validateTrace(t, level1.Downstream, resp, server1, 1, traceID, sampled, baggage) { + t.Successf("trace checks out") + log.Println("PASS") + } else { + log.Println("FAIL") + } +} + +func logParams(t crossdock.T) { + keys := []string{ + sampledParam, + server1NameParam, + server2NameParam, + server2EncodingParam, + server3NameParam, + server3EncodingParam, + } + out := "Execute" + for _, key := range keys { + out = fmt.Sprintf("%s %s=%s", out, key, t.Param(key)) + } + log.Println(out) +} + +func (b *Behavior) serviceToHost(service string) string { + if b.ServiceToHost != nil { + return b.ServiceToHost(service) + } + return service +} + +func (b *Behavior) startTrace(t crossdock.T, req *Request, sampled bool, baggage string) (*Response, error) { + span := b.Tracer.StartSpan(req.ServerRole) + if sampled { + ext.SamplingPriority.Set(span, 1) + } + span.SetBaggageItem(BaggageKey, baggage) + defer span.Finish() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + ctx = opentracing.ContextWithSpan(ctx, span) + + return b.prepareResponse(ctx, t, req.Downstream) +} + +func validateTrace( + t crossdock.T, + target *Downstream, + resp *Response, + service string, + level int, + traceID string, + sampled bool, + baggage string, +) bool { + service = fmt.Sprintf("S%d(%s)", level, service) + checks := crossdock.Checks(t) + s := true + s = checks.Equal(traceID, resp.Span.TraceID, "Trace ID must match in %s", service) && s + s = checks.Equal(baggage, resp.Span.Baggage, "Baggage must match in %s", service) && s + s = checks.Equal(sampled, resp.Span.Sampled, "Sampled must match in %s", service) && s + if target != nil { + if resp.Downstream == nil { + t.Errorf("Should have downstream in S%d(%s)", level, service) + s = false + } else { + s = validateTrace(t, target.Downstream, resp.Downstream, + target.HostPort, level+1, traceID, sampled, baggage) && s + } + } else if resp.Downstream != nil { + s = checks.Nil(resp.Downstream, "Should not have downstream in %s", service) && s + } + return s +} + +func randomBaggage() string { + r := utils.NewRand(time.Now().UnixNano()) + n := uint64(r.Int63()) + return fmt.Sprintf("%x", n) +} + +func (b *Behavior) prepareResponse(ctx context.Context, t crossdock.T, reqDwn *Downstream) (*Response, error) { + log.Printf("prepareResponse: reqDwn=%v", reqDwn) + logSpan(ctx) + observedSpan, err := observeSpan(ctx) + if err != nil { + return nil, err + } + + resp := &Response{ + Span: observedSpan, + } + + if reqDwn != nil { + downstreamResp, err := b.callDownstream(ctx, reqDwn) + if err != nil { + if t != nil { + t.Errorf("Error when calling downstream %+v: %s", reqDwn, err) + } + return nil, err + } + resp.Downstream = downstreamResp + } + + return resp, nil +} + +func (b *Behavior) callDownstream(ctx context.Context, downstream *Downstream) (*Response, error) { + switch tchannel.Format(downstream.Encoding) { + case tchannel.JSON: + return b.jsonCall(ctx, downstream) + case tchannel.Thrift: + return b.thriftCall(ctx, downstream) + default: + return nil, errUnsupportedEncoding + } +} + +func observeSpan(ctx context.Context) (*ObservedSpan, error) { + span := opentracing.SpanFromContext(ctx) + if span == nil { + return nil, errNoSpanObserved + } + sc, ok := span.Context().(jaeger.SpanContext) + if !ok { + return &ObservedSpan{}, nil + } + observedSpan := &ObservedSpan{ + TraceID: fmt.Sprintf("%x", sc.TraceID()), + Sampled: sc.IsSampled(), + Baggage: span.BaggageItem(BaggageKey), + } + log.Printf("Observed span %+v", observedSpan) + return observedSpan, nil +} + +func logSpan(ctx context.Context) { + if span := opentracing.SpanFromContext(ctx); span != nil { + log.Printf("Span %s", span) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior_test.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior_test.go new file mode 100644 index 00000000..7d51f596 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/behavior_test.go @@ -0,0 +1,167 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +import ( + "fmt" + "net/url" + "strings" + "testing" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/crossdock/client" + "github.com/uber/tchannel-go/crossdock/common" + "github.com/uber/tchannel-go/crossdock/server" + + "github.com/crossdock/crossdock-go" + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-client-go" + "golang.org/x/net/context" +) + +func TestTraceBehavior(t *testing.T) { + tracer, tCloser := jaeger.NewTracer( + "crossdock", + jaeger.NewConstSampler(false), + jaeger.NewNullReporter()) + defer tCloser.Close() + + s := &server.Server{ + HostPort: "127.0.0.1:0", + Tracer: tracer, + } + err := s.Start() + require.NoError(t, err) + defer s.Close() + + behavior := &Behavior{ + ServerPort: s.Port(), + Tracer: tracer, + ServiceToHost: func(server string) string { return "localhost" }, + } + behavior.Register(s.Ch) + + c := &client.Client{ + ClientHostPort: "127.0.0.1:0", + Behaviors: crossdock.Behaviors{ + BehaviorName: behavior.Run, + }, + } + err = c.Start() + require.NoError(t, err) + defer c.Close() + + crossdock.Wait(t, c.URL(), 10) + + behaviors := []struct { + name string + axes map[string][]string + }{ + { + name: BehaviorName, + axes: map[string][]string{ + server1NameParam: {common.DefaultServiceName}, + sampledParam: {"true", "false"}, + server2NameParam: {common.DefaultServiceName}, + server2EncodingParam: {string(tchannel.JSON), string(tchannel.Thrift)}, + server3NameParam: {common.DefaultServiceName}, + server3EncodingParam: {string(tchannel.JSON), string(tchannel.Thrift)}, + }, + }, + } + + for _, bb := range behaviors { + for _, entry := range crossdock.Combinations(bb.axes) { + entryArgs := url.Values{} + for k, v := range entry { + entryArgs.Set(k, v) + } + // test via real HTTP call + crossdock.Call(t, c.URL(), bb.name, entryArgs) + } + } +} + +func TestNoSpanObserved(t *testing.T) { + _, err := observeSpan(context.Background()) + assert.Equal(t, errNoSpanObserved, err) +} + +func TestPrepareResponseErrors(t *testing.T) { + b := &Behavior{} + ctx := context.Background() + _, err := b.prepareResponse(ctx, nil, nil) + assert.Equal(t, errNoSpanObserved, err) + + span := opentracing.GlobalTracer().StartSpan("test") + ctx = opentracing.ContextWithSpan(ctx, span) + res, err := b.prepareResponse(ctx, nil, nil) + assert.NoError(t, err) + assert.Equal(t, "", res.Span.TraceID) + + _, err = b.prepareResponse(ctx, nil, &Downstream{ + Encoding: "invalid", + }) + assert.Equal(t, errUnsupportedEncoding, err) +} + +func TestTraceBehaviorRunErrors(t *testing.T) { + b := &Behavior{} + entries := crossdock.Run(crossdock.Params{ + sampledParam: "not a boolean", + }, b.Run) + assertFailedEntry(t, "Malformed param sampled", entries) + + params := crossdock.Params{ + server1NameParam: common.DefaultServiceName, + sampledParam: "true", + server2NameParam: common.DefaultServiceName, + server2EncodingParam: string(tchannel.JSON), + server3NameParam: common.DefaultServiceName, + server3EncodingParam: string(tchannel.JSON), + } + + b.Tracer = opentracing.GlobalTracer() + b.jsonCall = func(ctx context.Context, downstream *Downstream) (*Response, error) { + return nil, fmt.Errorf("made-up bad response") + } + entries = crossdock.Run(params, b.Run) + assertFailedEntry(t, "Failed to startTrace", entries) + + b.jsonCall = func(ctx context.Context, downstream *Downstream) (*Response, error) { + return &Response{ + Span: &ObservedSpan{}, + }, nil + } + entries = crossdock.Run(params, b.Run) + assertFailedEntry(t, "Trace ID should not be empty", entries) +} + +func assertFailedEntry(t *testing.T, expected string, entries []crossdock.Entry) { + require.True(t, len(entries) > 0, "Must have some entries %+v", entries) + entry := entries[len(entries)-1] + require.EqualValues(t, "failed", entry["status"], "Entries: %v", entries) + require.NotEmpty(t, entry["output"], "Entries: %v", entries) + output := entry["output"].(string) + assert.True(t, strings.Contains(output, expected), "Output must contain %s: %s", expected, output) +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/constants.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/constants.go new file mode 100644 index 00000000..9d882d45 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/constants.go @@ -0,0 +1,54 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +import "errors" + +const ( + // S1 instructions + sampledParam = "sampled" + server1NameParam = "s1name" + + // S1->S2 instructions + server2NameParam = "s2name" + server2EncodingParam = "s2encoding" + + // S2->S3 instructions + server3NameParam = "s3name" + server3EncodingParam = "s3encoding" + + // RoleS1 is the name of the role for server S1 + RoleS1 = "S1" + + // RoleS2 is the name of the role for server S2 + RoleS2 = "S2" + + // RoleS3 is the name of the role for server S3 + RoleS3 = "S3" + + // BaggageKey is the key used to pass baggage item + BaggageKey = "crossdock-baggage-key" +) + +var ( + errNoSpanObserved = errors.New("no span found in Context") + errUnsupportedEncoding = errors.New("unsupported encoding for downstream call") +) diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/json.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/json.go new file mode 100644 index 00000000..35118eb9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/json.go @@ -0,0 +1,65 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +import ( + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/crossdock/log" + "github.com/uber/tchannel-go/json" + + "golang.org/x/net/context" +) + +const jsonEndpoint = "trace" + +func (b *Behavior) registerJSON(ch *tchannel.Channel) { + handler := &jsonHandler{b: b, ch: ch} + json.Register(ch, json.Handlers{jsonEndpoint: handler.handleJSON}, handler.onError) + b.jsonCall = handler.callDownstream +} + +type jsonHandler struct { + ch *tchannel.Channel + b *Behavior +} + +func (h *jsonHandler) callDownstream(ctx context.Context, target *Downstream) (*Response, error) { + req := &Request{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + jctx := json.Wrap(ctx) + response := new(Response) + log.Printf("Calling JSON service %s (%s)", target.ServiceName, target.HostPort) + peer := h.ch.Peers().GetOrAdd(target.HostPort) + if err := json.CallPeer(jctx, peer, target.ServiceName, jsonEndpoint, req, response); err != nil { + return nil, err + } + return response, nil +} + +func (h *jsonHandler) handleJSON(ctx json.Context, req *Request) (*Response, error) { + return h.b.prepareResponse(ctx, nil, req.Downstream) +} + +func (h *jsonHandler) onError(ctx context.Context, err error) { + panic(err.Error()) +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/thrift.go b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/thrift.go new file mode 100644 index 00000000..9d86b66c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/behavior/trace/thrift.go @@ -0,0 +1,112 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package trace + +import ( + "encoding/json" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/crossdock/log" + "github.com/uber/tchannel-go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + + "golang.org/x/net/context" +) + +func (b *Behavior) registerThrift(ch *tchannel.Channel) { + handler := &thriftHandler{b: b, ch: ch} + server := thrift.NewServer(ch) + server.Register(gen.NewTChanSimpleServiceServer(handler)) + b.thriftCall = handler.callDownstream +} + +type thriftHandler struct { + gen.TChanSimpleService // leave nil so calls to unimplemented methods panic. + + ch *tchannel.Channel + b *Behavior +} + +func (h *thriftHandler) Call(ctx thrift.Context, arg *gen.Data) (*gen.Data, error) { + req, err := requestFromThrift(arg) + if err != nil { + return nil, err + } + res, err := h.b.prepareResponse(ctx, nil, req.Downstream) + if err != nil { + return nil, err + } + return responseToThrift(res) +} + +func (h *thriftHandler) callDownstream(ctx context.Context, target *Downstream) (*Response, error) { + req := &Request{ + ServerRole: target.ServerRole, + Downstream: target.Downstream, + } + opts := &thrift.ClientOptions{HostPort: target.HostPort} + thriftClient := thrift.NewClient(h.ch, target.ServiceName, opts) + serviceClient := gen.NewTChanSimpleServiceClient(thriftClient) + tReq, err := requestToThrift(req) + if err != nil { + return nil, err + } + + log.Printf("Calling Thrift service %s (%s)", target.ServiceName, target.HostPort) + tctx := thrift.Wrap(ctx) + res, err := serviceClient.Call(tctx, tReq) + if err != nil { + return nil, err + } + return responseFromThrift(res) +} + +func requestFromThrift(req *gen.Data) (*Request, error) { + var r Request + if err := json.Unmarshal([]byte(req.S2), &r); err != nil { + return nil, err + } + return &r, nil +} + +func requestToThrift(r *Request) (*gen.Data, error) { + jsonBytes, err := json.Marshal(r) + if err != nil { + return nil, err + } + return &gen.Data{S2: string(jsonBytes)}, nil +} + +func responseFromThrift(res *gen.Data) (*Response, error) { + var r Response + if err := json.Unmarshal([]byte(res.S2), &r); err != nil { + return nil, err + } + return &r, nil +} + +func responseToThrift(r *Response) (*gen.Data, error) { + jsonBytes, err := json.Marshal(r) + if err != nil { + return nil, err + } + return &gen.Data{S2: string(jsonBytes)}, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/client/client.go b/vendor/src/github.com/uber/tchannel-go/crossdock/client/client.go new file mode 100644 index 00000000..99b551bc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/client/client.go @@ -0,0 +1,84 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package client + +import ( + "fmt" + "net" + "net/http" + + "github.com/uber/tchannel-go/crossdock/common" + + "github.com/crossdock/crossdock-go" +) + +// Client is a controller for the tests +type Client struct { + ClientHostPort string + ServerPort string + listener net.Listener + mux *http.ServeMux + Behaviors crossdock.Behaviors +} + +// Start begins a Crossdock client in the background. +func (c *Client) Start() error { + if err := c.listen(); err != nil { + return err + } + go func() { + http.Serve(c.listener, c.mux) + }() + return nil +} + +// Listen initializes the server +func (c *Client) listen() error { + c.setDefaultPort(&c.ClientHostPort, ":"+common.DefaultClientPortHTTP) + c.setDefaultPort(&c.ServerPort, common.DefaultServerPort) + + c.mux = http.NewServeMux() // Using default mux creates problem in unit tests + c.mux.Handle("/", crossdock.Handler(c.Behaviors, true)) + + listener, err := net.Listen("tcp", c.ClientHostPort) + if err != nil { + return err + } + c.listener = listener + c.ClientHostPort = listener.Addr().String() // override in case it was ":0" + return nil +} + +// Close stops the client +func (c *Client) Close() error { + return c.listener.Close() +} + +// URL returns a URL that the client can be accessed on +func (c *Client) URL() string { + return fmt.Sprintf("http://%s/", c.ClientHostPort) +} + +func (c *Client) setDefaultPort(port *string, defaultPort string) { + if *port == "" { + *port = defaultPort + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/client/client_test.go b/vendor/src/github.com/uber/tchannel-go/crossdock/client/client_test.go new file mode 100644 index 00000000..39e5c552 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/client/client_test.go @@ -0,0 +1,76 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package client + +import ( + "net/url" + "testing" + + "github.com/crossdock/crossdock-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClientErrors(t *testing.T) { + c := &Client{ + ClientHostPort: "127.0.0.1:xxx", + } + assert.Error(t, c.Start()) +} + +func TestClientWithBehavior(t *testing.T) { + r := struct { + calledA bool + calledB bool + }{} + c := &Client{ + ClientHostPort: "127.0.0.1:0", + Behaviors: crossdock.Behaviors{ + "hello": func(t crossdock.T) { + if t.Param("param") == "a" { + t.Successf("good") + r.calledA = true + } + if t.Param("param") == "b" { + t.Skipf("not so good") + r.calledB = true + } + }, + }, + } + require.NoError(t, c.Start()) + defer c.Close() + crossdock.Wait(t, c.URL(), 10) + + axes := map[string][]string{ + "param": {"a", "b"}, + } + for _, entry := range crossdock.Combinations(axes) { + entryArgs := url.Values{} + for k, v := range entry { + entryArgs.Set(k, v) + } + // test via real HTTP call + crossdock.Call(t, c.URL(), "hello", entryArgs) + } + assert.True(t, r.calledA) + assert.True(t, r.calledB) +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/common/constants.go b/vendor/src/github.com/uber/tchannel-go/crossdock/common/constants.go new file mode 100644 index 00000000..ef55379d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/common/constants.go @@ -0,0 +1,32 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package common + +const ( + // DefaultClientPortHTTP is the port where the client (controller) runs + DefaultClientPortHTTP = "8080" + + // DefaultServerPort is the port of TChannel server + DefaultServerPort = "8081" + + // DefaultServiceName is the service name used by TChannel server + DefaultServiceName = "go" +) diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/docker-compose.yml b/vendor/src/github.com/uber/tchannel-go/crossdock/docker-compose.yml new file mode 100644 index 00000000..4f10a551 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/docker-compose.yml @@ -0,0 +1,30 @@ +version: '2' + +services: + crossdock: + image: crossdock/crossdock + links: + - go + - python + environment: + - WAIT_FOR=go + + - AXIS_CLIENT=go + - AXIS_S1NAME=go + - AXIS_SAMPLED=true,false + - AXIS_S2NAME=go,python + - AXIS_S2ENCODING=json,thrift + - AXIS_S3NAME=go,python + - AXIS_S3ENCODING=json,thrift + + - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2encoding,s3name,s3encoding + + go: + build: . + ports: + - "8080-8082" + + python: + image: tchannelhub/xdock-py + ports: + - "8080-8082" diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/log/logging.go b/vendor/src/github.com/uber/tchannel-go/crossdock/log/logging.go new file mode 100644 index 00000000..d12b7a54 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/log/logging.go @@ -0,0 +1,42 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package log + +import ( + stdlog "log" +) + +// Enabled controls logging +var Enabled = false + +// Println is the equivalent of standard log.Println gated by Enabled flag +func Println(msg string) { + if Enabled { + stdlog.Println(msg) + } +} + +// Printf is the equivalent of standard log.Printf gated by Enabled flag +func Printf(format string, v ...interface{}) { + if Enabled { + stdlog.Printf(format, v...) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/main.go b/vendor/src/github.com/uber/tchannel-go/crossdock/main.go new file mode 100644 index 00000000..c40e6eda --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/main.go @@ -0,0 +1,75 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "io" + + "github.com/uber/tchannel-go/crossdock/client" + "github.com/uber/tchannel-go/crossdock/log" + "github.com/uber/tchannel-go/crossdock/server" + + "github.com/crossdock/crossdock-go" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/tchannel-go/crossdock/behavior/trace" + "github.com/uber/tchannel-go/crossdock/common" +) + +func main() { + log.Enabled = true + + tracer, tCloser := initTracer() + defer tCloser.Close() + + server := &server.Server{Tracer: tracer} + if err := server.Start(); err != nil { + panic(err.Error()) + } + defer server.Close() + + behavior := &trace.Behavior{ + ServerPort: common.DefaultServerPort, + Tracer: tracer, + } + behavior.Register(server.Ch) + + client := &client.Client{ + Behaviors: crossdock.Behaviors{ + trace.BehaviorName: behavior.Run, + }, + } + + if err := client.Start(); err != nil { + panic(err.Error()) + } + defer client.Close() + + select {} +} + +func initTracer() (opentracing.Tracer, io.Closer) { + t, c := jaeger.NewTracer( + "crossdock-go", + jaeger.NewConstSampler(false), + jaeger.NewLoggingReporter(jaeger.StdLogger)) + return t, c +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/rules.mk b/vendor/src/github.com/uber/tchannel-go/crossdock/rules.mk new file mode 100644 index 00000000..71e97445 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/rules.mk @@ -0,0 +1,52 @@ +XDOCK_YAML=crossdock/docker-compose.yml + +.PHONY: crossdock-linux-bin +crossdock-linux-bin: + CGO_ENABLED=0 GOOS=linux time go build -a -installsuffix cgo -o crossdock/crossdock ./crossdock + +.PHONY: crossdock +crossdock: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill go + docker-compose -f $(XDOCK_YAML) rm -f go + docker-compose -f $(XDOCK_YAML) build go + docker-compose -f $(XDOCK_YAML) run crossdock + + +.PHONY: crossdock-fresh +crossdock-fresh: crossdock-linux-bin + docker-compose -f $(XDOCK_YAML) kill + docker-compose -f $(XDOCK_YAML) rm --force + docker-compose -f $(XDOCK_YAML) pull + docker-compose -f $(XDOCK_YAML) build + docker-compose -f $(XDOCK_YAML) run crossdock + +.PHONY: crossdock-logs +crossdock-logs: + docker-compose -f $(XDOCK_YAML) logs + +.PHONY: install_docker_ci +install_docker_ci: + @echo "Installing docker-compose $${DOCKER_COMPOSE_VERSION:?'DOCKER_COMPOSE_VERSION env not set'}" + sudo rm -f /usr/local/bin/docker-compose + curl -L https://github.com/docker/compose/releases/download/$${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + chmod +x docker-compose + sudo mv docker-compose /usr/local/bin + docker-compose version + +.PHONY: crossdock_ci +crossdock_ci: +ifdef CROSSDOCK + docker version + $(MAKE) crossdock +else + true +endif + +.PHONY: crossdock_logs_ci +crossdock_logs_ci: +ifdef CROSSDOCK + docker-compose -f $(XDOCK_YAML) logs +else + true +endif + diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/server/server.go b/vendor/src/github.com/uber/tchannel-go/crossdock/server/server.go new file mode 100644 index 00000000..80995f93 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/server/server.go @@ -0,0 +1,91 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package server + +import ( + "strings" + + "github.com/opentracing/opentracing-go" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/crossdock/common" + "github.com/uber/tchannel-go/crossdock/log" +) + +// Server implements S2-S3 servers +type Server struct { + HostPort string + Tracer opentracing.Tracer + Ch *tchannel.Channel +} + +// Start starts the test server called by the Client and other upstream servers. +func (s *Server) Start() error { + if s.HostPort == "" { + s.HostPort = ":" + common.DefaultServerPort + } + channelOpts := &tchannel.ChannelOptions{ + Tracer: s.Tracer, + } + ch, err := tchannel.NewChannel(common.DefaultServiceName, channelOpts) + if err != nil { + return err + } + + if err := ch.ListenAndServe(s.HostPort); err != nil { + return err + } + s.HostPort = ch.PeerInfo().HostPort // override in case it was ":0" + log.Printf("Started tchannel server at %s\n", s.HostPort) + s.Ch = ch + return nil +} + +// Close stops the server +func (s *Server) Close() { + s.Ch.Close() +} + +// Port returns the actual port the server listens to +func (s *Server) Port() string { + hostPortSplit := strings.Split(s.HostPort, ":") + port := hostPortSplit[len(hostPortSplit)-1] + return port +} diff --git a/vendor/src/github.com/uber/tchannel-go/crossdock/server/server_test.go b/vendor/src/github.com/uber/tchannel-go/crossdock/server/server_test.go new file mode 100644 index 00000000..ad7c4c99 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/crossdock/server/server_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package server + +import ( + "strconv" + "testing" + + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServer(t *testing.T) { + tracer := mocktracer.New() + + s := &Server{HostPort: "127.0.0.1:0", Tracer: tracer} + err := s.Start() + require.NoError(t, err) + defer s.Close() + + assert.Equal(t, tracer, s.Ch.Tracer()) + port, err := strconv.Atoi(s.Port()) + assert.NoError(t, err) + assert.True(t, port > 0) +} + +func TestServerErrors(t *testing.T) { + s := &Server{HostPort: "127.0.0.1:xxx"} + err := s.Start() + require.Error(t, err) +} diff --git a/vendor/src/github.com/uber/tchannel-go/deps_test.go b/vendor/src/github.com/uber/tchannel-go/deps_test.go new file mode 100644 index 00000000..6598c617 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/deps_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Our glide.yaml lists a set of directories in excludeDirs to avoid packages +// only used in testing from pulling in dependencies that should not affect +// package resolution for clients. +// However, we really want these directories to be part of test imports. Since +// glide does not provide a "testDirs" option, we add dependencies required +// for tests in this _test.go file. + +package tchannel_test + +import ( + "fmt" + "testing" + + jcg "github.com/uber/jaeger-client-go" + // why is this not automatically included from jaeger-client-go? + // _ "github.com/uber/jaeger-lib/metrics" +) + +func TestJaegerDeps(t *testing.T) { + m := jcg.Metrics{} + _ = m.SamplerUpdateFailure + fmt.Println("m", m) +} diff --git a/vendor/src/github.com/uber/tchannel-go/dial_16.go b/vendor/src/github.com/uber/tchannel-go/dial_16.go new file mode 100644 index 00000000..583c9e32 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/dial_16.go @@ -0,0 +1,34 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// +build !go1.7 + +package tchannel + +import ( + "net" + + "golang.org/x/net/context" +) + +func dialContext(ctx context.Context, hostPort string) (net.Conn, error) { + timeout := getTimeout(ctx) + return net.DialTimeout("tcp", hostPort, timeout) +} diff --git a/vendor/src/github.com/uber/tchannel-go/dial_17.go b/vendor/src/github.com/uber/tchannel-go/dial_17.go new file mode 100644 index 00000000..fb84a0ef --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/dial_17.go @@ -0,0 +1,33 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// +build go1.7 + +package tchannel + +import ( + "context" + "net" +) + +func dialContext(ctx context.Context, hostPort string) (net.Conn, error) { + d := net.Dialer{} + return d.DialContext(ctx, "tcp", hostPort) +} diff --git a/vendor/src/github.com/uber/tchannel-go/dial_17_test.go b/vendor/src/github.com/uber/tchannel-go/dial_17_test.go new file mode 100644 index 00000000..0e5fed49 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/dial_17_test.go @@ -0,0 +1,65 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// +build go1.7 + +package tchannel_test + +import ( + "strings" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/testutils" +) + +func TestNetDialCancelContext(t *testing.T) { + // timeoutHostPort uses a blackholed address (RFC 6890) with a port + // reserved for documentation. This address should always cause a timeout. + const timeoutHostPort = "192.18.0.254:44444" + timeoutPeriod := testutils.Timeout(50 * time.Millisecond) + + client := testutils.NewClient(t, nil) + defer client.Close() + + started := time.Now() + ctx, cancel := NewContext(time.Minute) + + go func() { + time.Sleep(timeoutPeriod) + cancel() + }() + + err := client.Ping(ctx, timeoutHostPort) + if !assert.Error(t, err, "Ping to blackhole address should fail") { + return + } + + if strings.Contains(err.Error(), "network is unreachable") { + t.Skipf("Skipping test, as network interface may not be available") + } + + d := time.Since(started) + assert.Equal(t, ErrCodeCancelled, GetSystemErrorCode(err), "Ping expected to fail with context cancelled") + assert.True(t, d < 2*timeoutPeriod, "Timeout should take less than %v, took %v", 2*timeoutPeriod, d) +} diff --git a/vendor/src/github.com/uber/tchannel-go/doc.go b/vendor/src/github.com/uber/tchannel-go/doc.go new file mode 100644 index 00000000..f393f3bc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/doc.go @@ -0,0 +1,26 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* +Package tchannel implements Go bindings for the TChannel protocol (https://github.com/uber/tchannel). + +A single Channel can be used for many concurrent requests to many hosts. +*/ +package tchannel diff --git a/vendor/src/github.com/uber/tchannel-go/errors.go b/vendor/src/github.com/uber/tchannel-go/errors.go new file mode 100644 index 00000000..a4083a3b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/errors.go @@ -0,0 +1,236 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + + "golang.org/x/net/context" +) + +const ( + // Message id for protocol level errors + invalidMessageID uint32 = 0xFFFFFFFF +) + +// A SystemErrCode indicates how a caller should handle a system error returned from a peer +type SystemErrCode byte + +//go:generate stringer -type=SystemErrCode + +const ( + // ErrCodeInvalid is an invalid error code, and should not be used + ErrCodeInvalid SystemErrCode = 0x00 + + // ErrCodeTimeout indicates the peer timed out. Callers can retry the request + // on another peer if the request is safe to retry. + ErrCodeTimeout SystemErrCode = 0x01 + + // ErrCodeCancelled indicates that the request was cancelled on the peer. Callers + // can retry the request on the same or another peer if the request is safe to retry + ErrCodeCancelled SystemErrCode = 0x02 + + // ErrCodeBusy indicates that the request was not dispatched because the peer + // was too busy to handle it. Callers can retry the request on another peer, and should + // reweight their connections to direct less traffic to this peer until it recovers. + ErrCodeBusy SystemErrCode = 0x03 + + // ErrCodeDeclined indicates that the request not dispatched because the peer + // declined to handle it, typically because the peer is not yet ready to handle it. + // Callers can retry the request on another peer, but should not reweight their connections + // and should continue to send traffic to this peer. + ErrCodeDeclined SystemErrCode = 0x04 + + // ErrCodeUnexpected indicates that the request failed for an unexpected reason, typically + // a crash or other unexpected handling. The request may have been processed before the failure; + // callers should retry the request on this or another peer only if the request is safe to retry + ErrCodeUnexpected SystemErrCode = 0x05 + + // ErrCodeBadRequest indicates that the request was malformed, and could not be processed. + // Callers should not bother to retry the request, as there is no chance it will be handled. + ErrCodeBadRequest SystemErrCode = 0x06 + + // ErrCodeNetwork indicates a network level error, such as a connection reset. + // Callers can retry the request if the request is safe to retry + ErrCodeNetwork SystemErrCode = 0x07 + + // ErrCodeProtocol indincates a fatal protocol error communicating with the peer. The connection + // will be terminated. + ErrCodeProtocol SystemErrCode = 0xFF +) + +var ( + // ErrServerBusy is a SystemError indicating the server is busy + ErrServerBusy = NewSystemError(ErrCodeBusy, "server busy") + + // ErrRequestCancelled is a SystemError indicating the request has been cancelled on the peer + ErrRequestCancelled = NewSystemError(ErrCodeCancelled, "request cancelled") + + // ErrTimeout is a SytemError indicating the request has timed out + ErrTimeout = NewSystemError(ErrCodeTimeout, "timeout") + + // ErrTimeoutRequired is a SystemError indicating that timeouts must be specified. + ErrTimeoutRequired = NewSystemError(ErrCodeBadRequest, "timeout required") + + // ErrChannelClosed is a SystemError indicating that the channel has been closed. + ErrChannelClosed = NewSystemError(ErrCodeDeclined, "closed channel") + + // ErrMethodTooLarge is a SystemError indicating that the method is too large. + ErrMethodTooLarge = NewSystemError(ErrCodeProtocol, "method too large") +) + +// MetricsKey is a string representation of the error code that's suitable for +// inclusion in metrics tags. +func (c SystemErrCode) MetricsKey() string { + switch c { + case ErrCodeInvalid: + // Shouldn't ever need this. + return "invalid" + case ErrCodeTimeout: + return "timeout" + case ErrCodeCancelled: + return "cancelled" + case ErrCodeBusy: + return "busy" + case ErrCodeDeclined: + return "declined" + case ErrCodeUnexpected: + return "unexpected-error" + case ErrCodeBadRequest: + return "bad-request" + case ErrCodeNetwork: + return "network-error" + case ErrCodeProtocol: + return "protocol-error" + default: + return c.String() + } +} + +func (c SystemErrCode) relayMetricsKey() string { + switch c { + case ErrCodeInvalid: + return "relay-invalid" + case ErrCodeTimeout: + return "relay-timeout" + case ErrCodeCancelled: + return "relay-cancelled" + case ErrCodeBusy: + return "relay-busy" + case ErrCodeDeclined: + return "relay-declined" + case ErrCodeUnexpected: + return "relay-unexpected-error" + case ErrCodeBadRequest: + return "relay-bad-request" + case ErrCodeNetwork: + return "relay-network-error" + case ErrCodeProtocol: + return "relay-protocol-error" + default: + return "relay-" + c.String() + } +} + +// A SystemError is a system-level error, containing an error code and message +// TODO(mmihic): Probably we want to hide this interface, and let application code +// just deal with standard raw errors. +type SystemError struct { + code SystemErrCode + msg string + wrapped error +} + +// NewSystemError defines a new SystemError with a code and message +func NewSystemError(code SystemErrCode, msg string, args ...interface{}) error { + return SystemError{code: code, msg: fmt.Sprintf(msg, args...)} +} + +// NewWrappedSystemError defines a new SystemError wrapping an existing error +func NewWrappedSystemError(code SystemErrCode, wrapped error) error { + if se, ok := wrapped.(SystemError); ok { + return se + } + + return SystemError{code: code, msg: fmt.Sprint(wrapped), wrapped: wrapped} +} + +// Error returns the code and message, conforming to the error interface +func (se SystemError) Error() string { + return fmt.Sprintf("tchannel error %v: %s", se.Code(), se.msg) +} + +// Wrapped returns the wrapped error +func (se SystemError) Wrapped() error { return se.wrapped } + +// Code returns the SystemError code, for sending to a peer +func (se SystemError) Code() SystemErrCode { + return se.code +} + +// Message returns the SystemError message. +func (se SystemError) Message() string { + return se.msg +} + +// GetContextError converts the context error to a tchannel error. +func GetContextError(err error) error { + if err == context.DeadlineExceeded { + return ErrTimeout + } + if err == context.Canceled { + return ErrRequestCancelled + } + return err +} + +// GetSystemErrorCode returns the code to report for the given error. If the error is a +// SystemError, we can get the code directly. Otherwise treat it as an unexpected error +func GetSystemErrorCode(err error) SystemErrCode { + if err == nil { + return ErrCodeInvalid + } + + if se, ok := err.(SystemError); ok { + return se.Code() + } + + return ErrCodeUnexpected +} + +// GetSystemErrorMessage returns the message to report for the given error. If the error is a +// SystemError, we can get the underlying message. Otherwise, use the Error() method. +func GetSystemErrorMessage(err error) string { + if se, ok := err.(SystemError); ok { + return se.Message() + } + + return err.Error() +} + +type errConnNotActive struct { + info string + state connectionState +} + +func (e errConnNotActive) Error() string { + return fmt.Sprintf("%v connection is not active: %v", e.info, e.state) +} diff --git a/vendor/src/github.com/uber/tchannel-go/errors_test.go b/vendor/src/github.com/uber/tchannel-go/errors_test.go new file mode 100644 index 00000000..3681b1cf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/errors_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "io" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestErrorMetricKeys(t *testing.T) { + codes := []SystemErrCode{ + ErrCodeInvalid, + ErrCodeTimeout, + ErrCodeCancelled, + ErrCodeBusy, + ErrCodeDeclined, + ErrCodeUnexpected, + ErrCodeBadRequest, + ErrCodeNetwork, + ErrCodeProtocol, + } + + // Metrics keys should be all lowercase letters and dashes. No spaces, + // underscores, or other characters. + expected := regexp.MustCompile(`^[[:lower:]-]+$`) + for _, c := range codes { + assert.True(t, expected.MatchString(c.MetricsKey()), "Expected metrics key for code %s to be well-formed.", c.String()) + } + + // Unexpected codes may have poorly-formed keys. + assert.Equal(t, "SystemErrCode(13)", SystemErrCode(13).MetricsKey(), "Expected invalid error codes to use a fallback metrics key format.") +} + +func TestInvalidError(t *testing.T) { + code := GetSystemErrorCode(nil) + assert.Equal(t, ErrCodeInvalid, code, "nil error should produce ErrCodeInvalid") +} + +func TestUnexpectedError(t *testing.T) { + code := GetSystemErrorCode(io.EOF) + assert.Equal(t, ErrCodeUnexpected, code, "non-tchannel SystemError should produce ErrCodeUnexpected") +} + +func TestSystemError(t *testing.T) { + code := GetSystemErrorCode(ErrTimeout) + assert.Equal(t, ErrCodeTimeout, code, "tchannel timeout error produces ErrCodeTimeout") +} + +func TestRelayMetricsKey(t *testing.T) { + for i := 0; i <= 256; i++ { + code := SystemErrCode(i) + assert.Equal(t, "relay-"+code.MetricsKey(), code.relayMetricsKey(), "Unexpected relay metrics key for %v", code) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/bench/client/client.go b/vendor/src/github.com/uber/tchannel-go/examples/bench/client/client.go new file mode 100644 index 00000000..63c4ced0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/bench/client/client.go @@ -0,0 +1,108 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "flag" + "log" + "net/http" + _ "net/http/pprof" + "runtime" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +var ( + hostPort = flag.String("hostPort", "localhost:12345", "listening socket of the bench server") + numGoroutines = flag.Int("numGo", 1, "The number of goroutines to spawn") + numOSThreads = flag.Int("numThreads", 1, "The number of OS threads to use (sets GOMAXPROCS)") + setBlockSize = flag.Int("setBlockSize", 4096, "The size in bytes of the data being set") + getToSetRatio = flag.Int("getToSetRatio", 1, "The number of Gets to do per Set call") + + // counter tracks the total number of requests completed in the past second. + counter atomic.Int64 +) + +func main() { + flag.Parse() + runtime.GOMAXPROCS(*numOSThreads) + + // Sets up a listener for pprof. + go func() { + log.Println(http.ListenAndServe("localhost:6061", nil)) + }() + + ch, err := tchannel.NewChannel("benchmark-client", nil) + if err != nil { + log.Fatalf("NewChannel failed: %v", err) + } + for i := 0; i < *numGoroutines; i++ { + go worker(ch) + } + + log.Printf("client config: %v workers on %v threads, setBlockSize %v, getToSetRatio %v", + *numGoroutines, *numOSThreads, *setBlockSize, *getToSetRatio) + requestCountReporter() +} + +func requestCountReporter() { + for { + time.Sleep(time.Second) + cur := counter.Swap(0) + log.Printf("%v requests", cur) + } +} + +func worker(ch *tchannel.Channel) { + data := make([]byte, *setBlockSize) + for { + if err := setRequest(ch, "key", string(data)); err != nil { + log.Fatalf("set failed: %v", err) + continue + } + counter.Inc() + + for i := 0; i < *getToSetRatio; i++ { + _, err := getRequest(ch, "key") + if err != nil { + log.Fatalf("get failed: %v", err) + } + counter.Inc() + } + } +} + +func setRequest(ch *tchannel.Channel, key, value string) error { + ctx, _ := context.WithTimeout(context.Background(), time.Second*10) + _, _, _, err := raw.Call(ctx, ch, *hostPort, "benchmark", "set", []byte(key), []byte(value)) + return err +} + +func getRequest(ch *tchannel.Channel, key string) (string, error) { + ctx, _ := context.WithTimeout(context.Background(), time.Second) + _, arg3, _, err := raw.Call(ctx, ch, *hostPort, "benchmark", "get", []byte(key), nil) + return string(arg3), err +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/bench/runner.go b/vendor/src/github.com/uber/tchannel-go/examples/bench/runner.go new file mode 100644 index 00000000..f929d794 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/bench/runner.go @@ -0,0 +1,128 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "log" + "net" + "os" + "os/exec" + "time" +) + +var ( + flagHostPort = flag.String("hostPort", "127.0.0.1:12345", "The host:port to run the benchmark on") + flagServerNumThreads = flag.Int("serverThreads", 1, "The number of OS threads to use for the server") + + flagServerBinary = flag.String("serverBinary", "./build/examples/bench/server", "Server binary location") + flagClientBinary = flag.String("clientBinary", "./build/examples/bench/client", "Client binary location") + + flagProfileAfter = flag.Duration("profileAfter", 0, + "Duration to wait before profiling. 0 disables profiling. Process is stopped after the profile.") + flagProfileSeconds = flag.Int("profileSeconds", 30, "The number of seconds to profile") + flagProfileStop = flag.Bool("profileStopProcess", true, "Whether to stop the benchmarks after profiling") +) + +func main() { + flag.Parse() + + server, err := runServer(*flagHostPort, *flagServerNumThreads) + if err != nil { + log.Fatalf("Server failed: %v", err) + } + defer server.Process.Kill() + + client, err := runClient(flag.Args()) + if err != nil { + log.Fatalf("Client failed: %v", err) + } + defer client.Process.Kill() + + if *flagProfileAfter != 0 { + go func() { + time.Sleep(*flagProfileAfter) + + // Profile the server and the client, which have pprof endpoints at :6060, and :6061 + p1, err := dumpProfile("localhost:6060", "server.pb.gz") + if err != nil { + log.Printf("Server profile failed: %v", err) + } + p2, err := dumpProfile("localhost:6061", "client.pb.gz") + if err != nil { + log.Printf("Client profile failed: %v", err) + } + if err := p1.Wait(); err != nil { + log.Printf("Server profile error: %v", err) + } + if err := p2.Wait(); err != nil { + log.Printf("Client profile error: %v", err) + } + + if !*flagProfileStop { + return + } + + server.Process.Signal(os.Interrupt) + client.Process.Signal(os.Interrupt) + + // After a while, kill the processes if we're still running. + time.Sleep(300 * time.Millisecond) + log.Printf("Still waiting for processes to stop, sending kill") + server.Process.Kill() + client.Process.Kill() + }() + } + + server.Wait() + client.Wait() +} + +func runServer(hostPort string, numThreads int) (*exec.Cmd, error) { + host, port, err := net.SplitHostPort(hostPort) + if err != nil { + return nil, err + } + + return runCmd(*flagServerBinary, + "--host", host, + "--port", port, + "--numThreads", fmt.Sprint(numThreads)) +} + +func runClient(clientArgs []string) (*exec.Cmd, error) { + return runCmd(*flagClientBinary, clientArgs...) +} + +func dumpProfile(baseHostPort, profileFile string) (*exec.Cmd, error) { + profileURL := fmt.Sprintf("http://%v/debug/pprof/profile", baseHostPort) + return runCmd("go", "tool", "pprof", "--proto", "--output="+profileFile, + fmt.Sprintf("--seconds=%v", *flagProfileSeconds), profileURL) +} + +func runCmd(cmdBinary string, args ...string) (*exec.Cmd, error) { + cmd := exec.Command(cmdBinary, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd, cmd.Start() +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/bench/server/server.go b/vendor/src/github.com/uber/tchannel-go/examples/bench/server/server.go new file mode 100644 index 00000000..24ad84f7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/bench/server/server.go @@ -0,0 +1,152 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "errors" + "flag" + "fmt" + "log" + "net/http" + _ "net/http/pprof" + "runtime" + "sync" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "golang.org/x/net/context" +) + +var ( + flagHost = flag.String("host", "localhost", "The hostname to listen on") + flagPort = flag.Int("port", 12345, "The base port to listen on") + flagInstances = flag.Int("instances", 1, "The number of instances to start") + flagOSThreads = flag.Int("numThreads", 1, "The number of OS threads to use (sets GOMAXPROCS)") +) + +func main() { + flag.Parse() + runtime.GOMAXPROCS(*flagOSThreads) + + // Sets up a listener for pprof. + go func() { + log.Printf("server pprof endpoint failed: %v", http.ListenAndServe("localhost:6060", nil)) + }() + + for i := 0; i < *flagInstances; i++ { + if err := setupServer(*flagHost, *flagPort, i); err != nil { + log.Fatalf("setupServer %v failed: %v", i, err) + } + } + + log.Printf("server config: %v threads listening on %v:%v", *flagOSThreads, *flagHost, *flagPort) + + // Listen indefinitely. + select {} +} + +func setupServer(host string, basePort, instanceNum int) error { + hostPort := fmt.Sprintf("%s:%v", host, basePort+instanceNum) + ch, err := tchannel.NewChannel("benchmark", &tchannel.ChannelOptions{ + ProcessName: fmt.Sprintf("benchmark-%v", instanceNum), + }) + if err != nil { + return fmt.Errorf("NewChannel failed: %v", err) + } + + handler := raw.Wrap(&kvHandler{vals: make(map[string]string)}) + ch.Register(handler, "ping") + ch.Register(handler, "get") + ch.Register(handler, "set") + + if err := ch.ListenAndServe(hostPort); err != nil { + return fmt.Errorf("ListenAndServe failed: %v", err) + } + + return nil +} + +type kvHandler struct { + sync.RWMutex + vals map[string]string +} + +func (h *kvHandler) WithLock(write bool, f func()) { + if write { + h.Lock() + } else { + h.RLock() + } + + f() + + if write { + h.Unlock() + } else { + h.RUnlock() + } +} + +func (h *kvHandler) Ping(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg2: []byte("pong"), + }, nil +} + +func (h *kvHandler) Get(ctx context.Context, args *raw.Args) (*raw.Res, error) { + var arg3 []byte + h.WithLock(false /* write */, func() { + arg3 = []byte(h.vals[string(args.Arg2)]) + }) + + return &raw.Res{ + Arg2: []byte(fmt.Sprint(len(arg3))), + Arg3: arg3, + }, nil +} + +func (h *kvHandler) Set(ctx context.Context, args *raw.Args) (*raw.Res, error) { + h.WithLock(true /* write */, func() { + h.vals[string(args.Arg2)] = string(args.Arg3) + }) + return &raw.Res{ + Arg2: []byte("ok"), + Arg3: []byte("really ok"), + }, nil +} + +func (h *kvHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + switch args.Method { + case "ping": + return h.Ping(ctx, args) + case "get": + return h.Get(ctx, args) + case "put": + return h.Set(ctx, args) + default: + return nil, errors.New("unknown method") + } +} + +func (h *kvHandler) OnError(ctx context.Context, err error) { + log.Fatalf("OnError %v", err) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/hyperbahn/echo-server/main.go b/vendor/src/github.com/uber/tchannel-go/examples/hyperbahn/echo-server/main.go new file mode 100644 index 00000000..e69d4565 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/hyperbahn/echo-server/main.go @@ -0,0 +1,106 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + "log" + "net" + "os" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/hyperbahn" + "github.com/uber/tchannel-go/raw" + + "golang.org/x/net/context" +) + +func main() { + tchan, err := tchannel.NewChannel("go-echo-server", nil) + if err != nil { + log.Fatalf("Failed to create channel: %v", err) + } + + listenIP, err := tchannel.ListenIP() + if err != nil { + log.Fatalf("Failed to get IP to listen on: %v", err) + } + + l, err := net.Listen("tcp", listenIP.String()+":61543") + if err != nil { + log.Fatalf("Could not listen: %v", err) + } + log.Printf("Listening on %v", l.Addr()) + + sc := tchan.GetSubChannel("go-echo-2") + tchan.Register(raw.Wrap(handler{""}), "echo") + sc.Register(raw.Wrap(handler{"subchannel:"}), "echo") + tchan.Serve(l) + + if len(os.Args[1:]) == 0 { + log.Fatalf("You must provide Hyperbahn nodes as arguments") + } + + // advertise service with Hyperbahn. + config := hyperbahn.Configuration{InitialNodes: os.Args[1:]} + client, err := hyperbahn.NewClient(tchan, config, &hyperbahn.ClientOptions{ + Handler: eventHandler{}, + Timeout: time.Second, + }) + if err != nil { + log.Fatalf("hyperbahn.NewClient failed: %v", err) + } + if err := client.Advertise(sc); err != nil { + log.Fatalf("Advertise failed: %v", err) + } + + // Server will keep running till Ctrl-C. + select {} +} + +type eventHandler struct{} + +func (eventHandler) On(event hyperbahn.Event) { + fmt.Printf("On(%v)\n", event) +} + +func (eventHandler) OnError(err error) { + fmt.Printf("OnError(%v)\n", err) +} + +type handler struct { + prefix string +} + +func (h handler) OnError(ctx context.Context, err error) { + log.Fatalf("OnError: %v", err) +} + +func (h handler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + arg2 := h.prefix + string(args.Arg2) + arg3 := h.prefix + string(args.Arg3) + + return &raw.Res{ + Arg2: []byte(arg2), + Arg3: []byte(arg3), + }, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/hypercat/main.go b/vendor/src/github.com/uber/tchannel-go/examples/hypercat/main.go new file mode 100644 index 00000000..e2f19473 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/hypercat/main.go @@ -0,0 +1,146 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "io" + "log" + "net" + "os" + "os/exec" + + "github.com/jessevdk/go-flags" + "github.com/uber/tchannel-go" + "golang.org/x/net/context" +) + +var options = struct { + ServiceName string `short:"s" long:"service" required:"true" description:"The TChannel/Hyperbahn service name"` + + // MethodName can be specified multiple times to listen on multiple methods. + MethodName []string `short:"o" long:"method" required:"true" description:"The method name to handle"` + + // HostPort can just be :port or port, in which case host defaults to tchannel's ListenIP. + HostPort string `short:"l" long:"hostPort" default:":0" description:"The port or host:port to listen on"` + + MaxConcurrency int `short:"m" long:"maxSpawn" default:"1" description:"The maximum number concurrent processes"` + + Cmd struct { + Command string `long:"command" description:"The command to execute" positional-arg-name:"command"` + Args []string `long:"args" description:"The arguments to pass to the command" positional-arg-name:"args"` + } `positional-args:"yes" required:"yes"` +}{} + +var running chan struct{} + +func parseArgs() { + var err error + if _, err = flags.Parse(&options); err != nil { + os.Exit(-1) + } + + // Convert host port to a real host port. + host, port, err := net.SplitHostPort(options.HostPort) + if err != nil { + port = options.HostPort + } + if host == "" { + hostIP, err := tchannel.ListenIP() + if err != nil { + log.Printf("could not get ListenIP: %v, defaulting to 127.0.0.1", err) + host = "127.0.0.1" + } else { + host = hostIP.String() + } + } + options.HostPort = host + ":" + port + + running = make(chan struct{}, options.MaxConcurrency) +} + +func main() { + parseArgs() + + ch, err := tchannel.NewChannel(options.ServiceName, nil) + if err != nil { + log.Fatalf("NewChannel failed: %v", err) + } + + for _, op := range options.MethodName { + ch.Register(tchannel.HandlerFunc(handler), op) + } + + if err := ch.ListenAndServe(options.HostPort); err != nil { + log.Fatalf("ListenAndServe failed: %v", err) + } + + peerInfo := ch.PeerInfo() + log.Printf("listening for %v:%v on %v", peerInfo.ServiceName, options.MethodName, peerInfo.HostPort) + select {} +} + +func onError(msg string, args ...interface{}) { + log.Fatalf(msg, args...) +} + +func handler(ctx context.Context, call *tchannel.InboundCall) { + running <- struct{}{} + defer func() { <-running }() + + var arg2 []byte + if err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil { + log.Fatalf("Arg2Reader failed: %v", err) + } + + arg3Reader, err := call.Arg3Reader() + if err != nil { + log.Fatalf("Arg3Reader failed: %v", err) + } + + response := call.Response() + if err := tchannel.NewArgWriter(response.Arg2Writer()).Write(nil); err != nil { + log.Fatalf("Arg2Writer failed: %v", err) + } + + arg3Writer, err := response.Arg3Writer() + if err != nil { + log.Fatalf("Arg3Writer failed: %v", err) + } + + if err := spawnProcess(arg3Reader, arg3Writer); err != nil { + log.Fatalf("spawnProcess failed: %v", err) + } + + if err := arg3Reader.Close(); err != nil { + log.Fatalf("Arg3Reader.Close failed: %v", err) + } + if err := arg3Writer.Close(); err != nil { + log.Fatalf("Arg3Writer.Close failed: %v", err) + } +} + +func spawnProcess(reader io.Reader, writer io.Writer) error { + cmd := exec.Command(options.Cmd.Command, options.Cmd.Args...) + cmd.Stdin = reader + cmd.Stdout = writer + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/README.md b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/README.md new file mode 100644 index 00000000..4776cb0a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/README.md @@ -0,0 +1,10 @@ +# Key-Value Store + +```bash +./build/examples/keyvalue/server +./build/examples/keyvalue/client +``` + +This example exposes a simple key-value store over TChannel using the Thrift +protocol. The client has an interactive CLI that can be used to make calls to +the server. diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/client/client.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/client/client.go new file mode 100644 index 00000000..18a4e716 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/client/client.go @@ -0,0 +1,160 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "strings" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue" + "github.com/uber/tchannel-go/hyperbahn" + "github.com/uber/tchannel-go/thrift" +) + +var curUser = "anonymous" + +func printHelp() { + fmt.Println("Usage:\n get [key]\n set [key] [value]") + fmt.Println(" user [newUser]\n clearAll") +} + +func main() { + // Create a TChannel. + ch, err := tchannel.NewChannel("keyvalue-client", nil) + if err != nil { + log.Fatalf("Failed to create tchannel: %v", err) + } + + // Set up Hyperbahn client. + config := hyperbahn.Configuration{InitialNodes: os.Args[1:]} + if len(config.InitialNodes) == 0 { + log.Fatalf("No Autobahn nodes to connect to given") + } + hyperbahn.NewClient(ch, config, nil) + + thriftClient := thrift.NewClient(ch, "keyvalue", nil) + client := keyvalue.NewTChanKeyValueClient(thriftClient) + adminClient := keyvalue.NewTChanAdminClient(thriftClient) + + // Read commands from the command line and execute them. + scanner := bufio.NewScanner(os.Stdin) + printHelp() + fmt.Printf("> ") + for scanner.Scan() { + parts := strings.Split(scanner.Text(), " ") + if parts[0] == "" { + continue + } + switch parts[0] { + case "help": + printHelp() + case "get": + if len(parts) < 2 { + printHelp() + break + } + get(client, parts[1]) + case "set": + if len(parts) < 3 { + printHelp() + break + } + set(client, parts[1], parts[2]) + case "user": + if len(parts) < 2 { + printHelp() + break + } + curUser = parts[1] + case "clearAll": + clear(adminClient) + default: + log.Printf("Unsupported command %q\n", parts[0]) + } + fmt.Print("> ") + } + scanner.Text() +} + +func get(client keyvalue.TChanKeyValue, key string) { + ctx, cancel := createContext() + defer cancel() + + val, err := client.Get(ctx, key) + if err != nil { + switch err := err.(type) { + case *keyvalue.InvalidKey: + log.Printf("Get %v failed: invalid key", key) + case *keyvalue.KeyNotFound: + log.Printf("Get %v failed: key not found", key) + default: + log.Printf("Get %v failed unexpectedly: %v", key, err) + } + return + } + + log.Printf("Get %v: %v", key, val) +} + +func set(client keyvalue.TChanKeyValue, key, value string) { + ctx, cancel := createContext() + defer cancel() + + if err := client.Set(ctx, key, value); err != nil { + switch err := err.(type) { + case *keyvalue.InvalidKey: + log.Printf("Set %v failed: invalid key", key) + default: + log.Printf("Set %v:%v failed unexpectedly: %#v", key, value, err) + } + return + } + + log.Printf("Set %v:%v succeeded with headers: %v", key, value, ctx.ResponseHeaders()) +} + +func clear(adminClient keyvalue.TChanAdmin) { + ctx, cancel := createContext() + defer cancel() + + if err := adminClient.ClearAll(ctx); err != nil { + switch err := err.(type) { + case *keyvalue.NotAuthorized: + log.Printf("You are not authorized to perform this method") + default: + log.Printf("ClearAll failed unexpectedly: %v", err) + } + return + } + + log.Printf("ClearAll completed, all keys cleared") +} + +func createContext() (thrift.Context, func()) { + ctx, cancel := thrift.NewContext(time.Second) + ctx = thrift.WithHeaders(ctx, map[string]string{"user": curUser}) + return ctx, cancel +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/admin.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/admin.go new file mode 100644 index 00000000..5d3b9052 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/admin.go @@ -0,0 +1,325 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package keyvalue + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Admin interface { + BaseService + + ClearAll() (err error) +} + +type AdminClient struct { + *BaseServiceClient +} + +func NewAdminClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AdminClient { + return &AdminClient{BaseServiceClient: NewBaseServiceClientFactory(t, f)} +} + +func NewAdminClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AdminClient { + return &AdminClient{BaseServiceClient: NewBaseServiceClientProtocol(t, iprot, oprot)} +} + +func (p *AdminClient) ClearAll() (err error) { + if err = p.sendClearAll(); err != nil { + return + } + return p.recvClearAll() +} + +func (p *AdminClient) sendClearAll() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("clearAll", thrift.CALL, p.SeqId); err != nil { + return + } + args := AdminClearAllArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *AdminClient) recvClearAll() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "clearAll" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "clearAll failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "clearAll failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error12 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error13 error + error13, err = error12.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error13 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "clearAll failed: invalid message type") + return + } + result := AdminClearAllResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.NotAuthorized != nil { + err = result.NotAuthorized + return + } + return +} + +type AdminProcessor struct { + *BaseServiceProcessor +} + +func NewAdminProcessor(handler Admin) *AdminProcessor { + self14 := &AdminProcessor{NewBaseServiceProcessor(handler)} + self14.AddToProcessorMap("clearAll", &adminProcessorClearAll{handler: handler}) + return self14 +} + +type adminProcessorClearAll struct { + handler Admin +} + +func (p *adminProcessorClearAll) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AdminClearAllArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("clearAll", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := AdminClearAllResult{} + var err2 error + if err2 = p.handler.ClearAll(); err2 != nil { + switch v := err2.(type) { + case *NotAuthorized: + result.NotAuthorized = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing clearAll: "+err2.Error()) + oprot.WriteMessageBegin("clearAll", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } + if err2 = oprot.WriteMessageBegin("clearAll", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +type AdminClearAllArgs struct { +} + +func NewAdminClearAllArgs() *AdminClearAllArgs { + return &AdminClearAllArgs{} +} + +func (p *AdminClearAllArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AdminClearAllArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("clearAll_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AdminClearAllArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AdminClearAllArgs(%+v)", *p) +} + +// Attributes: +// - NotAuthorized +type AdminClearAllResult struct { + NotAuthorized *NotAuthorized `thrift:"notAuthorized,1" db:"notAuthorized" json:"notAuthorized,omitempty"` +} + +func NewAdminClearAllResult() *AdminClearAllResult { + return &AdminClearAllResult{} +} + +var AdminClearAllResult_NotAuthorized_DEFAULT *NotAuthorized + +func (p *AdminClearAllResult) GetNotAuthorized() *NotAuthorized { + if !p.IsSetNotAuthorized() { + return AdminClearAllResult_NotAuthorized_DEFAULT + } + return p.NotAuthorized +} +func (p *AdminClearAllResult) IsSetNotAuthorized() bool { + return p.NotAuthorized != nil +} + +func (p *AdminClearAllResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AdminClearAllResult) ReadField1(iprot thrift.TProtocol) error { + p.NotAuthorized = &NotAuthorized{} + if err := p.NotAuthorized.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.NotAuthorized), err) + } + return nil +} + +func (p *AdminClearAllResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("clearAll_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *AdminClearAllResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetNotAuthorized() { + if err := oprot.WriteFieldBegin("notAuthorized", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:notAuthorized: ", p), err) + } + if err := p.NotAuthorized.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.NotAuthorized), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:notAuthorized: ", p), err) + } + } + return err +} + +func (p *AdminClearAllResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("AdminClearAllResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/baseservice.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/baseservice.go new file mode 100644 index 00000000..93ff7842 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/baseservice.go @@ -0,0 +1,367 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package keyvalue + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type BaseService interface { + HealthCheck() (r string, err error) +} + +type BaseServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewBaseServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaseServiceClient { + return &BaseServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewBaseServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaseServiceClient { + return &BaseServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +func (p *BaseServiceClient) HealthCheck() (r string, err error) { + if err = p.sendHealthCheck(); err != nil { + return + } + return p.recvHealthCheck() +} + +func (p *BaseServiceClient) sendHealthCheck() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("HealthCheck", thrift.CALL, p.SeqId); err != nil { + return + } + args := BaseServiceHealthCheckArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *BaseServiceClient) recvHealthCheck() (value string, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "HealthCheck" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "HealthCheck failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "HealthCheck failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "HealthCheck failed: invalid message type") + return + } + result := BaseServiceHealthCheckResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type BaseServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler BaseService +} + +func (p *BaseServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *BaseServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *BaseServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewBaseServiceProcessor(handler BaseService) *BaseServiceProcessor { + + self2 := &BaseServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self2.processorMap["HealthCheck"] = &baseServiceProcessorHealthCheck{handler: handler} + return self2 +} + +func (p *BaseServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x3.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x3 + +} + +type baseServiceProcessorHealthCheck struct { + handler BaseService +} + +func (p *baseServiceProcessorHealthCheck) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := BaseServiceHealthCheckArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("HealthCheck", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := BaseServiceHealthCheckResult{} + var retval string + var err2 error + if retval, err2 = p.handler.HealthCheck(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing HealthCheck: "+err2.Error()) + oprot.WriteMessageBegin("HealthCheck", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = &retval + } + if err2 = oprot.WriteMessageBegin("HealthCheck", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +type BaseServiceHealthCheckArgs struct { +} + +func NewBaseServiceHealthCheckArgs() *BaseServiceHealthCheckArgs { + return &BaseServiceHealthCheckArgs{} +} + +func (p *BaseServiceHealthCheckArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaseServiceHealthCheckArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("HealthCheck_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaseServiceHealthCheckArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaseServiceHealthCheckArgs(%+v)", *p) +} + +// Attributes: +// - Success +type BaseServiceHealthCheckResult struct { + Success *string `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewBaseServiceHealthCheckResult() *BaseServiceHealthCheckResult { + return &BaseServiceHealthCheckResult{} +} + +var BaseServiceHealthCheckResult_Success_DEFAULT string + +func (p *BaseServiceHealthCheckResult) GetSuccess() string { + if !p.IsSetSuccess() { + return BaseServiceHealthCheckResult_Success_DEFAULT + } + return *p.Success +} +func (p *BaseServiceHealthCheckResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *BaseServiceHealthCheckResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaseServiceHealthCheckResult) ReadField0(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + p.Success = &v + } + return nil +} + +func (p *BaseServiceHealthCheckResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("HealthCheck_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaseServiceHealthCheckResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRING, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteString(string(*p.Success)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.success (0) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *BaseServiceHealthCheckResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaseServiceHealthCheckResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/constants.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/constants.go new file mode 100644 index 00000000..1673651a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package keyvalue + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/keyvalue.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/keyvalue.go new file mode 100644 index 00000000..50fde411 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/keyvalue.go @@ -0,0 +1,833 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package keyvalue + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type KeyValue interface { + BaseService + + // Parameters: + // - Key + Get(key string) (r string, err error) + // Parameters: + // - Key + // - Value + Set(key string, value string) (err error) +} + +type KeyValueClient struct { + *BaseServiceClient +} + +func NewKeyValueClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *KeyValueClient { + return &KeyValueClient{BaseServiceClient: NewBaseServiceClientFactory(t, f)} +} + +func NewKeyValueClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *KeyValueClient { + return &KeyValueClient{BaseServiceClient: NewBaseServiceClientProtocol(t, iprot, oprot)} +} + +// Parameters: +// - Key +func (p *KeyValueClient) Get(key string) (r string, err error) { + if err = p.sendGet(key); err != nil { + return + } + return p.recvGet() +} + +func (p *KeyValueClient) sendGet(key string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Get", thrift.CALL, p.SeqId); err != nil { + return + } + args := KeyValueGetArgs{ + Key: key, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *KeyValueClient) recvGet() (value string, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Get" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Get failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Get failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error5 error + error5, err = error4.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error5 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Get failed: invalid message type") + return + } + result := KeyValueGetResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.NotFound != nil { + err = result.NotFound + return + } else if result.InvalidKey != nil { + err = result.InvalidKey + return + } + value = result.GetSuccess() + return +} + +// Parameters: +// - Key +// - Value +func (p *KeyValueClient) Set(key string, value string) (err error) { + if err = p.sendSet(key, value); err != nil { + return + } + return p.recvSet() +} + +func (p *KeyValueClient) sendSet(key string, value string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Set", thrift.CALL, p.SeqId); err != nil { + return + } + args := KeyValueSetArgs{ + Key: key, + Value: value, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *KeyValueClient) recvSet() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Set" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Set failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Set failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error7 error + error7, err = error6.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error7 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Set failed: invalid message type") + return + } + result := KeyValueSetResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.InvalidKey != nil { + err = result.InvalidKey + return + } + return +} + +type KeyValueProcessor struct { + *BaseServiceProcessor +} + +func NewKeyValueProcessor(handler KeyValue) *KeyValueProcessor { + self8 := &KeyValueProcessor{NewBaseServiceProcessor(handler)} + self8.AddToProcessorMap("Get", &keyValueProcessorGet{handler: handler}) + self8.AddToProcessorMap("Set", &keyValueProcessorSet{handler: handler}) + return self8 +} + +type keyValueProcessorGet struct { + handler KeyValue +} + +func (p *keyValueProcessorGet) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := KeyValueGetArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Get", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := KeyValueGetResult{} + var retval string + var err2 error + if retval, err2 = p.handler.Get(args.Key); err2 != nil { + switch v := err2.(type) { + case *KeyNotFound: + result.NotFound = v + case *InvalidKey: + result.InvalidKey = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Get: "+err2.Error()) + oprot.WriteMessageBegin("Get", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } else { + result.Success = &retval + } + if err2 = oprot.WriteMessageBegin("Get", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type keyValueProcessorSet struct { + handler KeyValue +} + +func (p *keyValueProcessorSet) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := KeyValueSetArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Set", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := KeyValueSetResult{} + var err2 error + if err2 = p.handler.Set(args.Key, args.Value); err2 != nil { + switch v := err2.(type) { + case *InvalidKey: + result.InvalidKey = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Set: "+err2.Error()) + oprot.WriteMessageBegin("Set", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } + if err2 = oprot.WriteMessageBegin("Set", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Key +type KeyValueGetArgs struct { + Key string `thrift:"key,1" db:"key" json:"key"` +} + +func NewKeyValueGetArgs() *KeyValueGetArgs { + return &KeyValueGetArgs{} +} + +func (p *KeyValueGetArgs) GetKey() string { + return p.Key +} +func (p *KeyValueGetArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *KeyValueGetArgs) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *KeyValueGetArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Get_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *KeyValueGetArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *KeyValueGetArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("KeyValueGetArgs(%+v)", *p) +} + +// Attributes: +// - Success +// - NotFound +// - InvalidKey +type KeyValueGetResult struct { + Success *string `thrift:"success,0" db:"success" json:"success,omitempty"` + NotFound *KeyNotFound `thrift:"notFound,1" db:"notFound" json:"notFound,omitempty"` + InvalidKey *InvalidKey `thrift:"invalidKey,2" db:"invalidKey" json:"invalidKey,omitempty"` +} + +func NewKeyValueGetResult() *KeyValueGetResult { + return &KeyValueGetResult{} +} + +var KeyValueGetResult_Success_DEFAULT string + +func (p *KeyValueGetResult) GetSuccess() string { + if !p.IsSetSuccess() { + return KeyValueGetResult_Success_DEFAULT + } + return *p.Success +} + +var KeyValueGetResult_NotFound_DEFAULT *KeyNotFound + +func (p *KeyValueGetResult) GetNotFound() *KeyNotFound { + if !p.IsSetNotFound() { + return KeyValueGetResult_NotFound_DEFAULT + } + return p.NotFound +} + +var KeyValueGetResult_InvalidKey_DEFAULT *InvalidKey + +func (p *KeyValueGetResult) GetInvalidKey() *InvalidKey { + if !p.IsSetInvalidKey() { + return KeyValueGetResult_InvalidKey_DEFAULT + } + return p.InvalidKey +} +func (p *KeyValueGetResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *KeyValueGetResult) IsSetNotFound() bool { + return p.NotFound != nil +} + +func (p *KeyValueGetResult) IsSetInvalidKey() bool { + return p.InvalidKey != nil +} + +func (p *KeyValueGetResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *KeyValueGetResult) ReadField0(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + p.Success = &v + } + return nil +} + +func (p *KeyValueGetResult) ReadField1(iprot thrift.TProtocol) error { + p.NotFound = &KeyNotFound{} + if err := p.NotFound.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.NotFound), err) + } + return nil +} + +func (p *KeyValueGetResult) ReadField2(iprot thrift.TProtocol) error { + p.InvalidKey = &InvalidKey{} + if err := p.InvalidKey.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.InvalidKey), err) + } + return nil +} + +func (p *KeyValueGetResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Get_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *KeyValueGetResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRING, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteString(string(*p.Success)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.success (0) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *KeyValueGetResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetNotFound() { + if err := oprot.WriteFieldBegin("notFound", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:notFound: ", p), err) + } + if err := p.NotFound.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.NotFound), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:notFound: ", p), err) + } + } + return err +} + +func (p *KeyValueGetResult) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetInvalidKey() { + if err := oprot.WriteFieldBegin("invalidKey", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:invalidKey: ", p), err) + } + if err := p.InvalidKey.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.InvalidKey), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:invalidKey: ", p), err) + } + } + return err +} + +func (p *KeyValueGetResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("KeyValueGetResult(%+v)", *p) +} + +// Attributes: +// - Key +// - Value +type KeyValueSetArgs struct { + Key string `thrift:"key,1" db:"key" json:"key"` + Value string `thrift:"value,2" db:"value" json:"value"` +} + +func NewKeyValueSetArgs() *KeyValueSetArgs { + return &KeyValueSetArgs{} +} + +func (p *KeyValueSetArgs) GetKey() string { + return p.Key +} + +func (p *KeyValueSetArgs) GetValue() string { + return p.Value +} +func (p *KeyValueSetArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *KeyValueSetArgs) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *KeyValueSetArgs) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Value = v + } + return nil +} + +func (p *KeyValueSetArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Set_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *KeyValueSetArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *KeyValueSetArgs) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) + } + if err := oprot.WriteString(string(p.Value)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) + } + return err +} + +func (p *KeyValueSetArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("KeyValueSetArgs(%+v)", *p) +} + +// Attributes: +// - InvalidKey +type KeyValueSetResult struct { + InvalidKey *InvalidKey `thrift:"invalidKey,1" db:"invalidKey" json:"invalidKey,omitempty"` +} + +func NewKeyValueSetResult() *KeyValueSetResult { + return &KeyValueSetResult{} +} + +var KeyValueSetResult_InvalidKey_DEFAULT *InvalidKey + +func (p *KeyValueSetResult) GetInvalidKey() *InvalidKey { + if !p.IsSetInvalidKey() { + return KeyValueSetResult_InvalidKey_DEFAULT + } + return p.InvalidKey +} +func (p *KeyValueSetResult) IsSetInvalidKey() bool { + return p.InvalidKey != nil +} + +func (p *KeyValueSetResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *KeyValueSetResult) ReadField1(iprot thrift.TProtocol) error { + p.InvalidKey = &InvalidKey{} + if err := p.InvalidKey.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.InvalidKey), err) + } + return nil +} + +func (p *KeyValueSetResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Set_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *KeyValueSetResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetInvalidKey() { + if err := oprot.WriteFieldBegin("invalidKey", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:invalidKey: ", p), err) + } + if err := p.InvalidKey.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.InvalidKey), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:invalidKey: ", p), err) + } + } + return err +} + +func (p *KeyValueSetResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("KeyValueSetResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/tchan-keyvalue.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/tchan-keyvalue.go new file mode 100644 index 00000000..f8fbdd02 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/tchan-keyvalue.go @@ -0,0 +1,381 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package keyvalue is generated code used to make or handle TChannel calls using Thrift. +package keyvalue + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanAdmin is the interface that defines the server handler and client interface. +type TChanAdmin interface { + TChanBaseService + + ClearAll(ctx thrift.Context) error +} + +// TChanKeyValue is the interface that defines the server handler and client interface. +type TChanKeyValue interface { + TChanBaseService + + Get(ctx thrift.Context, key string) (string, error) + Set(ctx thrift.Context, key string, value string) error +} + +// TChanBaseService is the interface that defines the server handler and client interface. +type TChanBaseService interface { + HealthCheck(ctx thrift.Context) (string, error) +} + +// Implementation of a client and service handler. + +type tchanAdminClient struct { + TChanBaseService + + thriftService string + client thrift.TChanClient +} + +func NewTChanAdminInheritedClient(thriftService string, client thrift.TChanClient) *tchanAdminClient { + return &tchanAdminClient{ + NewTChanBaseServiceInheritedClient(thriftService, client), + thriftService, + client, + } +} + +// NewTChanAdminClient creates a client that can be used to make remote calls. +func NewTChanAdminClient(client thrift.TChanClient) TChanAdmin { + return NewTChanAdminInheritedClient("Admin", client) +} + +func (c *tchanAdminClient) ClearAll(ctx thrift.Context) error { + var resp AdminClearAllResult + args := AdminClearAllArgs{} + success, err := c.client.Call(ctx, c.thriftService, "clearAll", &args, &resp) + if err == nil && !success { + switch { + case resp.NotAuthorized != nil: + err = resp.NotAuthorized + default: + err = fmt.Errorf("received no result or unknown exception for clearAll") + } + } + + return err +} + +type tchanAdminServer struct { + thrift.TChanServer + + handler TChanAdmin +} + +// NewTChanAdminServer wraps a handler for TChanAdmin so it can be +// registered with a thrift.Server. +func NewTChanAdminServer(handler TChanAdmin) thrift.TChanServer { + return &tchanAdminServer{ + NewTChanBaseServiceServer(handler), + handler, + } +} + +func (s *tchanAdminServer) Service() string { + return "Admin" +} + +func (s *tchanAdminServer) Methods() []string { + return []string{ + "clearAll", + + "HealthCheck", + } +} + +func (s *tchanAdminServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "clearAll": + return s.handleClearAll(ctx, protocol) + + case "HealthCheck": + return s.TChanServer.Handle(ctx, methodName, protocol) + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanAdminServer) handleClearAll(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req AdminClearAllArgs + var res AdminClearAllResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.ClearAll(ctx) + + if err != nil { + switch v := err.(type) { + case *NotAuthorized: + if v == nil { + return false, nil, fmt.Errorf("Handler for notAuthorized returned non-nil error type *NotAuthorized but nil value") + } + res.NotAuthorized = v + default: + return false, nil, err + } + } else { + } + + return err == nil, &res, nil +} + +type tchanKeyValueClient struct { + TChanBaseService + + thriftService string + client thrift.TChanClient +} + +func NewTChanKeyValueInheritedClient(thriftService string, client thrift.TChanClient) *tchanKeyValueClient { + return &tchanKeyValueClient{ + NewTChanBaseServiceInheritedClient(thriftService, client), + thriftService, + client, + } +} + +// NewTChanKeyValueClient creates a client that can be used to make remote calls. +func NewTChanKeyValueClient(client thrift.TChanClient) TChanKeyValue { + return NewTChanKeyValueInheritedClient("KeyValue", client) +} + +func (c *tchanKeyValueClient) Get(ctx thrift.Context, key string) (string, error) { + var resp KeyValueGetResult + args := KeyValueGetArgs{ + Key: key, + } + success, err := c.client.Call(ctx, c.thriftService, "Get", &args, &resp) + if err == nil && !success { + switch { + case resp.NotFound != nil: + err = resp.NotFound + case resp.InvalidKey != nil: + err = resp.InvalidKey + default: + err = fmt.Errorf("received no result or unknown exception for Get") + } + } + + return resp.GetSuccess(), err +} + +func (c *tchanKeyValueClient) Set(ctx thrift.Context, key string, value string) error { + var resp KeyValueSetResult + args := KeyValueSetArgs{ + Key: key, + Value: value, + } + success, err := c.client.Call(ctx, c.thriftService, "Set", &args, &resp) + if err == nil && !success { + switch { + case resp.InvalidKey != nil: + err = resp.InvalidKey + default: + err = fmt.Errorf("received no result or unknown exception for Set") + } + } + + return err +} + +type tchanKeyValueServer struct { + thrift.TChanServer + + handler TChanKeyValue +} + +// NewTChanKeyValueServer wraps a handler for TChanKeyValue so it can be +// registered with a thrift.Server. +func NewTChanKeyValueServer(handler TChanKeyValue) thrift.TChanServer { + return &tchanKeyValueServer{ + NewTChanBaseServiceServer(handler), + handler, + } +} + +func (s *tchanKeyValueServer) Service() string { + return "KeyValue" +} + +func (s *tchanKeyValueServer) Methods() []string { + return []string{ + "Get", + "Set", + + "HealthCheck", + } +} + +func (s *tchanKeyValueServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "Get": + return s.handleGet(ctx, protocol) + case "Set": + return s.handleSet(ctx, protocol) + + case "HealthCheck": + return s.TChanServer.Handle(ctx, methodName, protocol) + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanKeyValueServer) handleGet(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req KeyValueGetArgs + var res KeyValueGetResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Get(ctx, req.Key) + + if err != nil { + switch v := err.(type) { + case *KeyNotFound: + if v == nil { + return false, nil, fmt.Errorf("Handler for notFound returned non-nil error type *KeyNotFound but nil value") + } + res.NotFound = v + case *InvalidKey: + if v == nil { + return false, nil, fmt.Errorf("Handler for invalidKey returned non-nil error type *InvalidKey but nil value") + } + res.InvalidKey = v + default: + return false, nil, err + } + } else { + res.Success = &r + } + + return err == nil, &res, nil +} + +func (s *tchanKeyValueServer) handleSet(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req KeyValueSetArgs + var res KeyValueSetResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.Set(ctx, req.Key, req.Value) + + if err != nil { + switch v := err.(type) { + case *InvalidKey: + if v == nil { + return false, nil, fmt.Errorf("Handler for invalidKey returned non-nil error type *InvalidKey but nil value") + } + res.InvalidKey = v + default: + return false, nil, err + } + } else { + } + + return err == nil, &res, nil +} + +type tchanBaseServiceClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanBaseServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanBaseServiceClient { + return &tchanBaseServiceClient{ + thriftService, + client, + } +} + +// NewTChanBaseServiceClient creates a client that can be used to make remote calls. +func NewTChanBaseServiceClient(client thrift.TChanClient) TChanBaseService { + return NewTChanBaseServiceInheritedClient("baseService", client) +} + +func (c *tchanBaseServiceClient) HealthCheck(ctx thrift.Context) (string, error) { + var resp BaseServiceHealthCheckResult + args := BaseServiceHealthCheckArgs{} + success, err := c.client.Call(ctx, c.thriftService, "HealthCheck", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for HealthCheck") + } + } + + return resp.GetSuccess(), err +} + +type tchanBaseServiceServer struct { + handler TChanBaseService +} + +// NewTChanBaseServiceServer wraps a handler for TChanBaseService so it can be +// registered with a thrift.Server. +func NewTChanBaseServiceServer(handler TChanBaseService) thrift.TChanServer { + return &tchanBaseServiceServer{ + handler, + } +} + +func (s *tchanBaseServiceServer) Service() string { + return "baseService" +} + +func (s *tchanBaseServiceServer) Methods() []string { + return []string{ + "HealthCheck", + } +} + +func (s *tchanBaseServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "HealthCheck": + return s.handleHealthCheck(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanBaseServiceServer) handleHealthCheck(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req BaseServiceHealthCheckArgs + var res BaseServiceHealthCheckResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.HealthCheck(ctx) + + if err != nil { + return false, nil, err + } else { + res.Success = &r + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/ttypes.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/ttypes.go new file mode 100644 index 00000000..c559cf96 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue/ttypes.go @@ -0,0 +1,226 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package keyvalue + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - Key +type KeyNotFound struct { + Key string `thrift:"key,1" db:"key" json:"key"` +} + +func NewKeyNotFound() *KeyNotFound { + return &KeyNotFound{} +} + +func (p *KeyNotFound) GetKey() string { + return p.Key +} +func (p *KeyNotFound) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *KeyNotFound) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Key = v + } + return nil +} + +func (p *KeyNotFound) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("KeyNotFound"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *KeyNotFound) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) + } + if err := oprot.WriteString(string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) + } + return err +} + +func (p *KeyNotFound) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("KeyNotFound(%+v)", *p) +} + +func (p *KeyNotFound) Error() string { + return p.String() +} + +type InvalidKey struct { +} + +func NewInvalidKey() *InvalidKey { + return &InvalidKey{} +} + +func (p *InvalidKey) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *InvalidKey) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("InvalidKey"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *InvalidKey) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("InvalidKey(%+v)", *p) +} + +func (p *InvalidKey) Error() string { + return p.String() +} + +type NotAuthorized struct { +} + +func NewNotAuthorized() *NotAuthorized { + return &NotAuthorized{} +} + +func (p *NotAuthorized) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *NotAuthorized) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("NotAuthorized"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *NotAuthorized) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("NotAuthorized(%+v)", *p) +} + +func (p *NotAuthorized) Error() string { + return p.String() +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/keyvalue.thrift b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/keyvalue.thrift new file mode 100644 index 00000000..2f7e5922 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/keyvalue.thrift @@ -0,0 +1,29 @@ +service baseService { + string HealthCheck() +} + +exception KeyNotFound { + 1: string key +} + +exception InvalidKey {} + +service KeyValue extends baseService { + // If the key does not start with a letter, InvalidKey is returned. + // If the key does not exist, KeyNotFound is returned. + string Get(1: string key) throws ( + 1: KeyNotFound notFound + 2: InvalidKey invalidKey) + + // Set returns InvalidKey is an invalid key is sent. + void Set(1: string key, 2: string value) throws ( + 1: InvalidKey invalidKey + ) +} + +// Returned when the user is not authorized for the Admin service. +exception NotAuthorized {} + +service Admin extends baseService { + void clearAll() throws (1: NotAuthorized notAuthorized) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/server/server.go b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/server/server.go new file mode 100644 index 00000000..69615db6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/keyvalue/server/server.go @@ -0,0 +1,149 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + "log" + "os" + "sync" + "unicode" + "unicode/utf8" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/examples/keyvalue/gen-go/keyvalue" + "github.com/uber/tchannel-go/hyperbahn" + "github.com/uber/tchannel-go/pprof" + "github.com/uber/tchannel-go/thrift" +) + +func main() { + // Create a TChannel and register the Thrift handlers. + ch, err := tchannel.NewChannel("keyvalue", nil) + if err != nil { + log.Fatalf("Failed to create tchannel: %v", err) + } + + // Register both the KeyValue and Admin services. + // We can register multiple Thrift services on a single Hyperbahn service. + h := newKVHandler() + server := thrift.NewServer(ch) + server.Register(keyvalue.NewTChanKeyValueServer(h)) + server.Register(keyvalue.NewTChanAdminServer(h)) + pprof.Register(ch) + + // Listen for connections on the external interface so we can receive connections. + ip, err := tchannel.ListenIP() + if err != nil { + log.Fatalf("Failed to find IP to Listen on: %v", err) + } + // We use port 0 which asks the OS to assign any available port. + // Static port allocations are not necessary for services on Hyperbahn. + ch.ListenAndServe(fmt.Sprintf("%v:%v", ip, 0)) + + // Advertising registers this service instance with Hyperbahn so + // that Hyperbahn can route requests for "keyvalue" to us. + config := hyperbahn.Configuration{InitialNodes: os.Args[1:]} + if len(config.InitialNodes) > 0 { + client, err := hyperbahn.NewClient(ch, config, nil) + if err != nil { + log.Fatalf("hyperbahn.NewClient failed: %v", err) + } + if err := client.Advertise(); err != nil { + log.Fatalf("Hyperbahn advertise failed: %v", err) + } + } + + // The service is now started up, run it till we receive a ctrl-c. + log.Printf("KeyValue service has started on %v", ch.PeerInfo().HostPort) + select {} +} + +type kvHandler struct { + sync.RWMutex + vals map[string]string +} + +// NewKVHandler returns a new handler for the KeyValue service. +func newKVHandler() *kvHandler { + return &kvHandler{vals: make(map[string]string)} +} + +// Get returns the value stored for the given key. +func (h *kvHandler) Get(ctx thrift.Context, key string) (string, error) { + if err := isValidKey(key); err != nil { + return "", err + } + + h.RLock() + defer h.RUnlock() + + if val, ok := h.vals[key]; ok { + return val, nil + } + + return "", &keyvalue.KeyNotFound{Key: key} +} + +// Set sets the value for a given key. +func (h *kvHandler) Set(ctx thrift.Context, key, value string) error { + if err := isValidKey(key); err != nil { + return err + } + + h.Lock() + defer h.Unlock() + + h.vals[key] = value + // Example of how to use response headers. Normally, these values should be passed via result structs. + ctx.SetResponseHeaders(map[string]string{"count": fmt.Sprint(len(h.vals))}) + return nil +} + +// HealthCheck return the health status of this process. +func (h *kvHandler) HealthCheck(ctx thrift.Context) (string, error) { + return "OK", nil +} + +// ClearAll clears all the keys. +func (h *kvHandler) ClearAll(ctx thrift.Context) error { + if !isAdmin(ctx) { + return &keyvalue.NotAuthorized{} + } + + h.Lock() + defer h.Unlock() + + h.vals = make(map[string]string) + return nil +} + +func isValidKey(key string) error { + r, _ := utf8.DecodeRuneInString(key) + if !unicode.IsLetter(r) { + return &keyvalue.InvalidKey{} + } + return nil +} + +func isAdmin(ctx thrift.Context) bool { + return ctx.Headers()["user"] == "admin" +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/ping/README.md b/vendor/src/github.com/uber/tchannel-go/examples/ping/README.md new file mode 100644 index 00000000..25f1e637 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/ping/README.md @@ -0,0 +1,14 @@ +# Ping-Pong + +```bash +./build/examples/ping/pong +``` + +This example creates a client and server channel. The server channel registers +a `PingService` with a `ping` method, which takes request `Headers` and a `Ping` body +and returns the same `Headers` along with a `Pong` body. The client sends a ping +request to the server. + +Note that every instance is bidirectional, so the same channel can be used for +both sending and receiving requests to peers. New connections are initiated on +demand. diff --git a/vendor/src/github.com/uber/tchannel-go/examples/ping/main.go b/vendor/src/github.com/uber/tchannel-go/examples/ping/main.go new file mode 100644 index 00000000..98f867b0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/ping/main.go @@ -0,0 +1,119 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + "time" + + "golang.org/x/net/context" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/json" +) + +var log = tchannel.SimpleLogger + +// Ping is the ping request type. +type Ping struct { + Message string `json:"message"` +} + +// Pong is the ping response type. +type Pong Ping + +func pingHandler(ctx json.Context, ping *Ping) (*Pong, error) { + return &Pong{ + Message: fmt.Sprintf("ping %v", ping), + }, nil +} + +func pingOtherHandler(ctx json.Context, ping *Ping) (*Pong, error) { + return &Pong{ + Message: fmt.Sprintf("pingOther %v", ping), + }, nil +} + +func onError(ctx context.Context, err error) { + log.WithFields(tchannel.ErrField(err)).Fatal("onError handler triggered.") +} + +func listenAndHandle(s *tchannel.Channel, hostPort string) { + log.Infof("Service %s", hostPort) + + // If no error is returned, the listen was successful. Serving happens in the background. + if err := s.ListenAndServe(hostPort); err != nil { + log.WithFields( + tchannel.LogField{Key: "hostPort", Value: hostPort}, + tchannel.ErrField(err), + ).Fatal("Couldn't listen.") + } +} + +func main() { + // Create a new TChannel for handling requests + ch, err := tchannel.NewChannel("PingService", &tchannel.ChannelOptions{Logger: tchannel.SimpleLogger}) + if err != nil { + log.WithFields(tchannel.ErrField(err)).Fatal("Couldn't create new channel.") + } + + // Register a handler for the ping message on the PingService + json.Register(ch, json.Handlers{ + "ping": pingHandler, + }, onError) + + // Listen for incoming requests + listenAndHandle(ch, "127.0.0.1:10500") + + // Create a new TChannel for sending requests. + client, err := tchannel.NewChannel("ping-client", nil) + if err != nil { + log.WithFields(tchannel.ErrField(err)).Fatal("Couldn't create new client channel.") + } + + // Make a call to ourselves, with a timeout of 10s + ctx, cancel := json.NewContext(time.Second * 10) + defer cancel() + + peer := client.Peers().Add(ch.PeerInfo().HostPort) + + var pong Pong + if err := json.CallPeer(ctx, peer, "PingService", "ping", &Ping{"Hello World"}, &pong); err != nil { + log.WithFields(tchannel.ErrField(err)).Fatal("json.Call failed.") + } + + log.Infof("Received pong: %s", pong.Message) + + // Create a new subchannel for the top-level channel + subCh := ch.GetSubChannel("PingServiceOther") + + // Register a handler on the subchannel + json.Register(subCh, json.Handlers{ + "pingOther": pingOtherHandler, + }, onError) + + // Try to send a message to the Service:Method pair for the subchannel + if err := json.CallPeer(ctx, peer, "PingServiceOther", "pingOther", &Ping{"Hello Other World"}, &pong); err != nil { + log.WithFields(tchannel.ErrField(err)).Fatal("json.Call failed.") + } + + log.Infof("Received pong: %s", pong.Message) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/test_server/server.go b/vendor/src/github.com/uber/tchannel-go/examples/test_server/server.go new file mode 100644 index 00000000..a185ee78 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/test_server/server.go @@ -0,0 +1,71 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "log" + + "golang.org/x/net/context" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" +) + +var ( + flagHost = flag.String("host", "localhost", "The hostname to serve on") + flagPort = flag.Int("port", 0, "The port to listen on") +) + +type rawHandler struct{} + +func (rawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil +} + +func (rawHandler) OnError(ctx context.Context, err error) { + log.Fatalf("OnError: %v", err) +} + +func main() { + flag.Parse() + + ch, err := tchannel.NewChannel("test_as_raw", nil) + if err != nil { + log.Fatalf("NewChannel failed: %v", err) + } + + handler := raw.Wrap(rawHandler{}) + ch.Register(handler, "echo") + ch.Register(handler, "streaming_echo") + + hostPort := fmt.Sprintf("%s:%v", *flagHost, *flagPort) + if err := ch.ListenAndServe(hostPort); err != nil { + log.Fatalf("ListenAndServe failed: %v", err) + } + + fmt.Println("listening on", ch.PeerInfo().HostPort) + select {} +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/example.thrift b/vendor/src/github.com/uber/tchannel-go/examples/thrift/example.thrift new file mode 100644 index 00000000..3db5f537 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/example.thrift @@ -0,0 +1,18 @@ +struct HealthCheckRes { + 1: bool healthy, + 2: string msg, +} + +service Base { + void BaseCall() +} + +service First extends Base { + string Echo(1:string msg) + HealthCheckRes Healthcheck() + void AppError() +} + +service Second { + void Test() +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/base.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/base.go new file mode 100644 index 00000000..307a72fd --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/base.go @@ -0,0 +1,314 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package example + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Base interface { + BaseCall() (err error) +} + +type BaseClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewBaseClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *BaseClient { + return &BaseClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewBaseClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *BaseClient { + return &BaseClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +func (p *BaseClient) BaseCall() (err error) { + if err = p.sendBaseCall(); err != nil { + return + } + return p.recvBaseCall() +} + +func (p *BaseClient) sendBaseCall() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("BaseCall", thrift.CALL, p.SeqId); err != nil { + return + } + args := BaseBaseCallArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *BaseClient) recvBaseCall() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "BaseCall" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "BaseCall failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "BaseCall failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "BaseCall failed: invalid message type") + return + } + result := BaseBaseCallResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + return +} + +type BaseProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Base +} + +func (p *BaseProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *BaseProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *BaseProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewBaseProcessor(handler Base) *BaseProcessor { + + self2 := &BaseProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self2.processorMap["BaseCall"] = &baseProcessorBaseCall{handler: handler} + return self2 +} + +func (p *BaseProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x3.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x3 + +} + +type baseProcessorBaseCall struct { + handler Base +} + +func (p *baseProcessorBaseCall) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := BaseBaseCallArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("BaseCall", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := BaseBaseCallResult{} + var err2 error + if err2 = p.handler.BaseCall(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing BaseCall: "+err2.Error()) + oprot.WriteMessageBegin("BaseCall", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + if err2 = oprot.WriteMessageBegin("BaseCall", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +type BaseBaseCallArgs struct { +} + +func NewBaseBaseCallArgs() *BaseBaseCallArgs { + return &BaseBaseCallArgs{} +} + +func (p *BaseBaseCallArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaseBaseCallArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BaseCall_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaseBaseCallArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaseBaseCallArgs(%+v)", *p) +} + +type BaseBaseCallResult struct { +} + +func NewBaseBaseCallResult() *BaseBaseCallResult { + return &BaseBaseCallResult{} +} + +func (p *BaseBaseCallResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BaseBaseCallResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("BaseCall_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *BaseBaseCallResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BaseBaseCallResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/constants.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/constants.go new file mode 100644 index 00000000..eb9fb388 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package example + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/first.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/first.go new file mode 100644 index 00000000..d39b392b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/first.go @@ -0,0 +1,867 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package example + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type First interface { + Base + + // Parameters: + // - Msg + Echo(msg string) (r string, err error) + Healthcheck() (r *HealthCheckRes, err error) + AppError() (err error) +} + +type FirstClient struct { + *BaseClient +} + +func NewFirstClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *FirstClient { + return &FirstClient{BaseClient: NewBaseClientFactory(t, f)} +} + +func NewFirstClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *FirstClient { + return &FirstClient{BaseClient: NewBaseClientProtocol(t, iprot, oprot)} +} + +// Parameters: +// - Msg +func (p *FirstClient) Echo(msg string) (r string, err error) { + if err = p.sendEcho(msg); err != nil { + return + } + return p.recvEcho() +} + +func (p *FirstClient) sendEcho(msg string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Echo", thrift.CALL, p.SeqId); err != nil { + return + } + args := FirstEchoArgs{ + Msg: msg, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *FirstClient) recvEcho() (value string, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Echo" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Echo failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Echo failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error5 error + error5, err = error4.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error5 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Echo failed: invalid message type") + return + } + result := FirstEchoResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +func (p *FirstClient) Healthcheck() (r *HealthCheckRes, err error) { + if err = p.sendHealthcheck(); err != nil { + return + } + return p.recvHealthcheck() +} + +func (p *FirstClient) sendHealthcheck() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Healthcheck", thrift.CALL, p.SeqId); err != nil { + return + } + args := FirstHealthcheckArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *FirstClient) recvHealthcheck() (value *HealthCheckRes, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Healthcheck" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Healthcheck failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Healthcheck failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error7 error + error7, err = error6.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error7 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Healthcheck failed: invalid message type") + return + } + result := FirstHealthcheckResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +func (p *FirstClient) AppError() (err error) { + if err = p.sendAppError(); err != nil { + return + } + return p.recvAppError() +} + +func (p *FirstClient) sendAppError() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("AppError", thrift.CALL, p.SeqId); err != nil { + return + } + args := FirstAppErrorArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *FirstClient) recvAppError() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "AppError" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "AppError failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "AppError failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error8 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error9 error + error9, err = error8.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error9 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "AppError failed: invalid message type") + return + } + result := FirstAppErrorResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + return +} + +type FirstProcessor struct { + *BaseProcessor +} + +func NewFirstProcessor(handler First) *FirstProcessor { + self10 := &FirstProcessor{NewBaseProcessor(handler)} + self10.AddToProcessorMap("Echo", &firstProcessorEcho{handler: handler}) + self10.AddToProcessorMap("Healthcheck", &firstProcessorHealthcheck{handler: handler}) + self10.AddToProcessorMap("AppError", &firstProcessorAppError{handler: handler}) + return self10 +} + +type firstProcessorEcho struct { + handler First +} + +func (p *firstProcessorEcho) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := FirstEchoArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Echo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := FirstEchoResult{} + var retval string + var err2 error + if retval, err2 = p.handler.Echo(args.Msg); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Echo: "+err2.Error()) + oprot.WriteMessageBegin("Echo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = &retval + } + if err2 = oprot.WriteMessageBegin("Echo", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type firstProcessorHealthcheck struct { + handler First +} + +func (p *firstProcessorHealthcheck) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := FirstHealthcheckArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Healthcheck", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := FirstHealthcheckResult{} + var retval *HealthCheckRes + var err2 error + if retval, err2 = p.handler.Healthcheck(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Healthcheck: "+err2.Error()) + oprot.WriteMessageBegin("Healthcheck", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("Healthcheck", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type firstProcessorAppError struct { + handler First +} + +func (p *firstProcessorAppError) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := FirstAppErrorArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("AppError", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := FirstAppErrorResult{} + var err2 error + if err2 = p.handler.AppError(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing AppError: "+err2.Error()) + oprot.WriteMessageBegin("AppError", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + if err2 = oprot.WriteMessageBegin("AppError", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Msg +type FirstEchoArgs struct { + Msg string `thrift:"msg,1" db:"msg" json:"msg"` +} + +func NewFirstEchoArgs() *FirstEchoArgs { + return &FirstEchoArgs{} +} + +func (p *FirstEchoArgs) GetMsg() string { + return p.Msg +} +func (p *FirstEchoArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstEchoArgs) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Msg = v + } + return nil +} + +func (p *FirstEchoArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Echo_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstEchoArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("msg", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:msg: ", p), err) + } + if err := oprot.WriteString(string(p.Msg)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.msg (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:msg: ", p), err) + } + return err +} + +func (p *FirstEchoArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstEchoArgs(%+v)", *p) +} + +// Attributes: +// - Success +type FirstEchoResult struct { + Success *string `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewFirstEchoResult() *FirstEchoResult { + return &FirstEchoResult{} +} + +var FirstEchoResult_Success_DEFAULT string + +func (p *FirstEchoResult) GetSuccess() string { + if !p.IsSetSuccess() { + return FirstEchoResult_Success_DEFAULT + } + return *p.Success +} +func (p *FirstEchoResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *FirstEchoResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstEchoResult) ReadField0(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + p.Success = &v + } + return nil +} + +func (p *FirstEchoResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Echo_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstEchoResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRING, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteString(string(*p.Success)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.success (0) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *FirstEchoResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstEchoResult(%+v)", *p) +} + +type FirstHealthcheckArgs struct { +} + +func NewFirstHealthcheckArgs() *FirstHealthcheckArgs { + return &FirstHealthcheckArgs{} +} + +func (p *FirstHealthcheckArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstHealthcheckArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Healthcheck_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstHealthcheckArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstHealthcheckArgs(%+v)", *p) +} + +// Attributes: +// - Success +type FirstHealthcheckResult struct { + Success *HealthCheckRes `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewFirstHealthcheckResult() *FirstHealthcheckResult { + return &FirstHealthcheckResult{} +} + +var FirstHealthcheckResult_Success_DEFAULT *HealthCheckRes + +func (p *FirstHealthcheckResult) GetSuccess() *HealthCheckRes { + if !p.IsSetSuccess() { + return FirstHealthcheckResult_Success_DEFAULT + } + return p.Success +} +func (p *FirstHealthcheckResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *FirstHealthcheckResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstHealthcheckResult) ReadField0(iprot thrift.TProtocol) error { + p.Success = &HealthCheckRes{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *FirstHealthcheckResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Healthcheck_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstHealthcheckResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *FirstHealthcheckResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstHealthcheckResult(%+v)", *p) +} + +type FirstAppErrorArgs struct { +} + +func NewFirstAppErrorArgs() *FirstAppErrorArgs { + return &FirstAppErrorArgs{} +} + +func (p *FirstAppErrorArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstAppErrorArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("AppError_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstAppErrorArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstAppErrorArgs(%+v)", *p) +} + +type FirstAppErrorResult struct { +} + +func NewFirstAppErrorResult() *FirstAppErrorResult { + return &FirstAppErrorResult{} +} + +func (p *FirstAppErrorResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *FirstAppErrorResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("AppError_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *FirstAppErrorResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("FirstAppErrorResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/second.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/second.go new file mode 100644 index 00000000..cbd387ad --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/second.go @@ -0,0 +1,314 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package example + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Second interface { + Test() (err error) +} + +type SecondClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSecondClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SecondClient { + return &SecondClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSecondClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SecondClient { + return &SecondClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +func (p *SecondClient) Test() (err error) { + if err = p.sendTest(); err != nil { + return + } + return p.recvTest() +} + +func (p *SecondClient) sendTest() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Test", thrift.CALL, p.SeqId); err != nil { + return + } + args := SecondTestArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SecondClient) recvTest() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Test" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Test failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Test failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error12 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error13 error + error13, err = error12.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error13 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Test failed: invalid message type") + return + } + result := SecondTestResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + return +} + +type SecondProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Second +} + +func (p *SecondProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SecondProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SecondProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSecondProcessor(handler Second) *SecondProcessor { + + self14 := &SecondProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self14.processorMap["Test"] = &secondProcessorTest{handler: handler} + return self14 +} + +func (p *SecondProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x15 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x15.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x15 + +} + +type secondProcessorTest struct { + handler Second +} + +func (p *secondProcessorTest) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SecondTestArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Test", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SecondTestResult{} + var err2 error + if err2 = p.handler.Test(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Test: "+err2.Error()) + oprot.WriteMessageBegin("Test", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + if err2 = oprot.WriteMessageBegin("Test", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +type SecondTestArgs struct { +} + +func NewSecondTestArgs() *SecondTestArgs { + return &SecondTestArgs{} +} + +func (p *SecondTestArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SecondTestArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Test_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SecondTestArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SecondTestArgs(%+v)", *p) +} + +type SecondTestResult struct { +} + +func NewSecondTestResult() *SecondTestResult { + return &SecondTestResult{} +} + +func (p *SecondTestResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SecondTestResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Test_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SecondTestResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SecondTestResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/tchan-example.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/tchan-example.go new file mode 100644 index 00000000..02a55c13 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/tchan-example.go @@ -0,0 +1,366 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package example is generated code used to make or handle TChannel calls using Thrift. +package example + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanBase is the interface that defines the server handler and client interface. +type TChanBase interface { + BaseCall(ctx thrift.Context) error +} + +// TChanFirst is the interface that defines the server handler and client interface. +type TChanFirst interface { + TChanBase + + AppError(ctx thrift.Context) error + Echo(ctx thrift.Context, msg string) (string, error) + Healthcheck(ctx thrift.Context) (*HealthCheckRes, error) +} + +// TChanSecond is the interface that defines the server handler and client interface. +type TChanSecond interface { + Test(ctx thrift.Context) error +} + +// Implementation of a client and service handler. + +type tchanBaseClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanBaseInheritedClient(thriftService string, client thrift.TChanClient) *tchanBaseClient { + return &tchanBaseClient{ + thriftService, + client, + } +} + +// NewTChanBaseClient creates a client that can be used to make remote calls. +func NewTChanBaseClient(client thrift.TChanClient) TChanBase { + return NewTChanBaseInheritedClient("Base", client) +} + +func (c *tchanBaseClient) BaseCall(ctx thrift.Context) error { + var resp BaseBaseCallResult + args := BaseBaseCallArgs{} + success, err := c.client.Call(ctx, c.thriftService, "BaseCall", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for BaseCall") + } + } + + return err +} + +type tchanBaseServer struct { + handler TChanBase +} + +// NewTChanBaseServer wraps a handler for TChanBase so it can be +// registered with a thrift.Server. +func NewTChanBaseServer(handler TChanBase) thrift.TChanServer { + return &tchanBaseServer{ + handler, + } +} + +func (s *tchanBaseServer) Service() string { + return "Base" +} + +func (s *tchanBaseServer) Methods() []string { + return []string{ + "BaseCall", + } +} + +func (s *tchanBaseServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "BaseCall": + return s.handleBaseCall(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanBaseServer) handleBaseCall(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req BaseBaseCallArgs + var res BaseBaseCallResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.BaseCall(ctx) + + if err != nil { + return false, nil, err + } else { + } + + return err == nil, &res, nil +} + +type tchanFirstClient struct { + TChanBase + + thriftService string + client thrift.TChanClient +} + +func NewTChanFirstInheritedClient(thriftService string, client thrift.TChanClient) *tchanFirstClient { + return &tchanFirstClient{ + NewTChanBaseInheritedClient(thriftService, client), + thriftService, + client, + } +} + +// NewTChanFirstClient creates a client that can be used to make remote calls. +func NewTChanFirstClient(client thrift.TChanClient) TChanFirst { + return NewTChanFirstInheritedClient("First", client) +} + +func (c *tchanFirstClient) AppError(ctx thrift.Context) error { + var resp FirstAppErrorResult + args := FirstAppErrorArgs{} + success, err := c.client.Call(ctx, c.thriftService, "AppError", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for AppError") + } + } + + return err +} + +func (c *tchanFirstClient) Echo(ctx thrift.Context, msg string) (string, error) { + var resp FirstEchoResult + args := FirstEchoArgs{ + Msg: msg, + } + success, err := c.client.Call(ctx, c.thriftService, "Echo", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for Echo") + } + } + + return resp.GetSuccess(), err +} + +func (c *tchanFirstClient) Healthcheck(ctx thrift.Context) (*HealthCheckRes, error) { + var resp FirstHealthcheckResult + args := FirstHealthcheckArgs{} + success, err := c.client.Call(ctx, c.thriftService, "Healthcheck", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for Healthcheck") + } + } + + return resp.GetSuccess(), err +} + +type tchanFirstServer struct { + thrift.TChanServer + + handler TChanFirst +} + +// NewTChanFirstServer wraps a handler for TChanFirst so it can be +// registered with a thrift.Server. +func NewTChanFirstServer(handler TChanFirst) thrift.TChanServer { + return &tchanFirstServer{ + NewTChanBaseServer(handler), + handler, + } +} + +func (s *tchanFirstServer) Service() string { + return "First" +} + +func (s *tchanFirstServer) Methods() []string { + return []string{ + "AppError", + "Echo", + "Healthcheck", + + "BaseCall", + } +} + +func (s *tchanFirstServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "AppError": + return s.handleAppError(ctx, protocol) + case "Echo": + return s.handleEcho(ctx, protocol) + case "Healthcheck": + return s.handleHealthcheck(ctx, protocol) + + case "BaseCall": + return s.TChanServer.Handle(ctx, methodName, protocol) + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanFirstServer) handleAppError(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req FirstAppErrorArgs + var res FirstAppErrorResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.AppError(ctx) + + if err != nil { + return false, nil, err + } else { + } + + return err == nil, &res, nil +} + +func (s *tchanFirstServer) handleEcho(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req FirstEchoArgs + var res FirstEchoResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Echo(ctx, req.Msg) + + if err != nil { + return false, nil, err + } else { + res.Success = &r + } + + return err == nil, &res, nil +} + +func (s *tchanFirstServer) handleHealthcheck(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req FirstHealthcheckArgs + var res FirstHealthcheckResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Healthcheck(ctx) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} + +type tchanSecondClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanSecondInheritedClient(thriftService string, client thrift.TChanClient) *tchanSecondClient { + return &tchanSecondClient{ + thriftService, + client, + } +} + +// NewTChanSecondClient creates a client that can be used to make remote calls. +func NewTChanSecondClient(client thrift.TChanClient) TChanSecond { + return NewTChanSecondInheritedClient("Second", client) +} + +func (c *tchanSecondClient) Test(ctx thrift.Context) error { + var resp SecondTestResult + args := SecondTestArgs{} + success, err := c.client.Call(ctx, c.thriftService, "Test", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for Test") + } + } + + return err +} + +type tchanSecondServer struct { + handler TChanSecond +} + +// NewTChanSecondServer wraps a handler for TChanSecond so it can be +// registered with a thrift.Server. +func NewTChanSecondServer(handler TChanSecond) thrift.TChanServer { + return &tchanSecondServer{ + handler, + } +} + +func (s *tchanSecondServer) Service() string { + return "Second" +} + +func (s *tchanSecondServer) Methods() []string { + return []string{ + "Test", + } +} + +func (s *tchanSecondServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "Test": + return s.handleTest(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanSecondServer) handleTest(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req SecondTestArgs + var res SecondTestResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.Test(ctx) + + if err != nil { + return false, nil, err + } else { + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/ttypes.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/ttypes.go new file mode 100644 index 00000000..86bec21e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/gen-go/example/ttypes.go @@ -0,0 +1,143 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package example + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - Healthy +// - Msg +type HealthCheckRes struct { + Healthy bool `thrift:"healthy,1" db:"healthy" json:"healthy"` + Msg string `thrift:"msg,2" db:"msg" json:"msg"` +} + +func NewHealthCheckRes() *HealthCheckRes { + return &HealthCheckRes{} +} + +func (p *HealthCheckRes) GetHealthy() bool { + return p.Healthy +} + +func (p *HealthCheckRes) GetMsg() string { + return p.Msg +} +func (p *HealthCheckRes) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *HealthCheckRes) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Healthy = v + } + return nil +} + +func (p *HealthCheckRes) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Msg = v + } + return nil +} + +func (p *HealthCheckRes) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("HealthCheckRes"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *HealthCheckRes) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("healthy", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:healthy: ", p), err) + } + if err := oprot.WriteBool(bool(p.Healthy)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.healthy (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:healthy: ", p), err) + } + return err +} + +func (p *HealthCheckRes) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("msg", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:msg: ", p), err) + } + if err := oprot.WriteString(string(p.Msg)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.msg (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:msg: ", p), err) + } + return err +} + +func (p *HealthCheckRes) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("HealthCheckRes(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/examples/thrift/main.go b/vendor/src/github.com/uber/tchannel-go/examples/thrift/main.go new file mode 100644 index 00000000..f7280406 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/examples/thrift/main.go @@ -0,0 +1,186 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "bufio" + "errors" + "fmt" + "log" + "net" + "os" + "runtime" + "strings" + "time" + + tchannel "github.com/uber/tchannel-go" + gen "github.com/uber/tchannel-go/examples/thrift/gen-go/example" + "github.com/uber/tchannel-go/thrift" +) + +func main() { + var ( + listener net.Listener + err error + ) + + if listener, err = setupServer(); err != nil { + log.Fatalf("setupServer failed: %v", err) + } + + if err := runClient1("server", listener.Addr()); err != nil { + log.Fatalf("runClient1 failed: %v", err) + } + + if err := runClient2("server", listener.Addr()); err != nil { + log.Fatalf("runClient2 failed: %v", err) + } + + go listenConsole() + + // Run for 10 seconds, then stop + time.Sleep(time.Second * 10) +} + +func setupServer() (net.Listener, error) { + tchan, err := tchannel.NewChannel("server", optsFor("server")) + if err != nil { + return nil, err + } + + listener, err := net.Listen("tcp", ":0") + if err != nil { + return nil, err + } + + server := thrift.NewServer(tchan) + server.Register(gen.NewTChanFirstServer(&firstHandler{})) + server.Register(gen.NewTChanSecondServer(&secondHandler{})) + + // Serve will set the local peer info, and start accepting sockets in a separate goroutine. + tchan.Serve(listener) + return listener, nil +} + +func runClient1(hyperbahnService string, addr net.Addr) error { + tchan, err := tchannel.NewChannel("client1", optsFor("client1")) + if err != nil { + return err + } + tchan.Peers().Add(addr.String()) + tclient := thrift.NewClient(tchan, hyperbahnService, nil) + client := gen.NewTChanFirstClient(tclient) + + go func() { + for { + ctx, cancel := thrift.NewContext(time.Second) + res, err := client.Echo(ctx, "Hi") + log.Println("Echo(Hi) = ", res, ", err: ", err) + log.Println("AppError() = ", client.AppError(ctx)) + log.Println("BaseCall() = ", client.BaseCall(ctx)) + cancel() + time.Sleep(100 * time.Millisecond) + } + }() + return nil +} + +func runClient2(hyperbahnService string, addr net.Addr) error { + tchan, err := tchannel.NewChannel("client2", optsFor("client2")) + if err != nil { + return err + } + tchan.Peers().Add(addr.String()) + tclient := thrift.NewClient(tchan, hyperbahnService, nil) + client := gen.NewTChanSecondClient(tclient) + + go func() { + for { + ctx, cancel := thrift.NewContext(time.Second) + client.Test(ctx) + cancel() + time.Sleep(100 * time.Millisecond) + } + }() + return nil +} + +func listenConsole() { + rdr := bufio.NewReader(os.Stdin) + for { + line, _ := rdr.ReadString('\n') + switch strings.TrimSpace(line) { + case "s": + printStack() + default: + fmt.Println("Unrecognized command:", line) + } + } +} + +func printStack() { + buf := make([]byte, 10000) + runtime.Stack(buf, true /* all */) + fmt.Println("Stack:\n", string(buf)) +} + +type firstHandler struct{} + +func (h *firstHandler) Healthcheck(ctx thrift.Context) (*gen.HealthCheckRes, error) { + log.Printf("first: HealthCheck()\n") + return &gen.HealthCheckRes{ + Healthy: true, + Msg: "OK"}, nil +} + +func (h *firstHandler) BaseCall(ctx thrift.Context) error { + log.Printf("first: BaseCall()\n") + return nil +} + +func (h *firstHandler) Echo(ctx thrift.Context, msg string) (r string, err error) { + log.Printf("first: Echo(%v)\n", msg) + return msg, nil +} + +func (h *firstHandler) AppError(ctx thrift.Context) error { + log.Printf("first: AppError()\n") + return errors.New("app error") +} + +func (h *firstHandler) OneWay(ctx thrift.Context) error { + log.Printf("first: OneWay()\n") + return errors.New("OneWay error...won't be seen by client") +} + +type secondHandler struct{} + +func (h *secondHandler) Test(ctx thrift.Context) error { + log.Println("secondHandler: Test()") + return nil +} + +func optsFor(processName string) *tchannel.ChannelOptions { + return &tchannel.ChannelOptions{ + ProcessName: processName, + Logger: tchannel.NewLevelLogger(tchannel.SimpleLogger, tchannel.LogLevelWarn), + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/fragmentation_test.go b/vendor/src/github.com/uber/tchannel-go/fragmentation_test.go new file mode 100644 index 00000000..0ddc2dba --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/fragmentation_test.go @@ -0,0 +1,415 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "io" + "io/ioutil" + "sync" + "testing" + + "github.com/uber/tchannel-go/typed" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testFragmentHeaderSize = 1 /* flags */ + 1 /* checksum type */ + 4 /* CRC32 checksum */ + + testFragmentPayloadSize = 10 // enough room for a small payload + testFragmentSize = testFragmentHeaderSize + testFragmentPayloadSize +) + +func TestFragmentationEmptyArgs(t *testing.T) { + runFragmentationTest(t, []string{"", "", ""}, buffers([][]byte{{ + 0x0000, // flags + byte(ChecksumTypeCrc32), 0x0000, 0x0000, 0x0000, 0x0000, // empty checksum + 0x0000, 0x0000, // arg 1 (length no body) + 0x0000, 0x0000, // arg 2 (length no body) + 0x0000, 0x0000, // arg 3 (length no body) + }})) +} + +func TestFragmentationSingleFragment(t *testing.T) { + runFragmentationTest(t, []string{"A", "B", "C"}, buffers([][]byte{{ + 0x0000, // flags + byte(ChecksumTypeCrc32), 0xa3, 0x83, 0x3, 0x48, // CRC32 checksum + 0x0000, 0x0001, 'A', // arg 1 (length single character body) + 0x0000, 0x0001, 'B', // arg 2 (length single character body) + 0x0000, 0x0001, 'C', // arg 3 (length single character body) + }})) +} + +func TestFragmentationMultipleFragments(t *testing.T) { + runFragmentationTest(t, []string{"ABCDEFHIJKLM", "NOPQRZTUWXYZ", "012345678"}, buffers( + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0x98, 0x43, 0x9a, 0x45, // checksum + 0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'H', 'I'}}, // first 8 bytes of arg 1 + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0xaf, 0xb9, 0x9c, 0x98, // checksum + 0x0000, 0x0004, 'J', 'K', 'L', 'M', // remaining 4 bytes of arg 1 + 0x0000, 0x0002, 'N', 'O'}}, // all of arg 2 that fits (2 bytes) + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0x23, 0xae, 0x2f, 0x37, // checksum + 0x0000, 0x0008, 'P', 'Q', 'R', 'Z', 'T', 'U', 'W', 'X'}}, // more aarg 2 + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0xa2, 0x93, 0x74, 0xd8, // checksum + 0x0000, 0x0002, 'Y', 'Z', // last parts of arg 2 + 0x0000, 0x0004, '0', '1', '2', '3'}}, // first parts of arg 3 + [][]byte{{ + 0x0000, // no more fragments + byte(ChecksumTypeCrc32), 0xf3, 0x29, 0xbb, 0xd1, // checksum + 0x0000, 0x0005, '4', '5', '6', '7', '8'}}, + )) +} + +func TestFragmentationMiddleArgNearFragmentBoundary(t *testing.T) { + // This covers the case where an argument in the middle ends near the + // end of a fragment boundary, such that there is not enough room to + // put another argument in the fragment. In this case there should be + // an empty chunk for that argument in the next fragment + runFragmentationTest(t, []string{"ABCDEF", "NOPQ"}, buffers( + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0xbb, 0x76, 0xfe, 0x69, // CRC32 checksum + 0x0000, 0x0006, 'A', 'B', 'C', 'D', 'E', 'F'}}, // all of arg 1 + [][]byte{{ + 0x0000, // no more fragments + byte(ChecksumTypeCrc32), 0x5b, 0x3c, 0x54, 0xfe, // CRC32 checksum + 0x0000, 0x0000, // empty chunk indicating the end of arg 1 + 0x0000, 0x0004, 'N', 'O', 'P', 'Q'}}, // all of arg 2 + )) +} + +func TestFragmentationMiddleArgOnExactFragmentBoundary(t *testing.T) { + // This covers the case where an argument in the middle ends exactly at the end of a fragment. + // Again, there should be an empty chunk for that argument in the next fragment + runFragmentationTest(t, []string{"ABCDEFGH", "NOPQ"}, buffers( + [][]byte{{ + 0x0001, // has more fragments + byte(ChecksumTypeCrc32), 0x68, 0xdc, 0xb6, 0x1c, // CRC32 checksum + 0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}}, // all of arg 1 + [][]byte{{ + 0x0000, // no more fragments + byte(ChecksumTypeCrc32), 0x32, 0x66, 0xf, 0x25, // CRC32 checksum + 0x0000, 0x0000, // empty chunk indicating the end of arg 1 + 0x0000, 0x0004, 'N', 'O', 'P', 'Q'}}, // all of arg 2 + )) +} + +func TestFragmentationLastArgOnNearFragmentBoundary(t *testing.T) { + // Covers the case where the last argument ends near a fragment + // boundary. No new fragments should get created + runFragmentationTest(t, []string{"ABCDEF"}, buffers( + [][]byte{{ + 0x0000, // has more fragments + byte(ChecksumTypeCrc32), 0xbb, 0x76, 0xfe, 0x69, // CRC32 checksum + 0x0000, 0x0006, 'A', 'B', 'C', 'D', 'E', 'F'}}, // all of arg 1 + )) +} + +func TestFragmentationLastArgOnExactFragmentBoundary(t *testing.T) { + // Covers the case where the last argument ends exactly on a fragment + // boundary. No new fragments should get created + runFragmentationTest(t, []string{"ABCDEFGH"}, buffers( + [][]byte{{ + 0x0000, // has more fragments + byte(ChecksumTypeCrc32), 0x68, 0xdc, 0xb6, 0x1c, // CRC32 checksum + 0x0000, 0x0008, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}}, // all of arg 1 + )) +} + +func TestFragmentationWriterErrors(t *testing.T) { + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Write without starting argument + _, err := w.Write([]byte("foo")) + assert.Error(t, err) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // BeginArgument twice without starting argument + assert.NoError(t, w.BeginArgument(false /* last */)) + assert.Error(t, w.BeginArgument(false /* last */)) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // BeginArgument after writing final argument + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello"))) + assert.Error(t, w.BeginArgument(false /* last */)) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Close without beginning argument + assert.Error(t, w.Close()) + }) +} + +func TestFragmentationReaderErrors(t *testing.T) { + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Read without starting argument + b := make([]byte, 10) + _, err := r.Read(b) + assert.Error(t, err) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Close without beginning argument + assert.Error(t, r.Close()) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // BeginArgument after reading final argument + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello"))) + + reader, err := r.ArgReader(true /* last */) + assert.NoError(t, err) + + var arg []byte + assert.NoError(t, NewArgReader(reader, nil).Read(&arg)) + assert.Equal(t, "hello", string(arg)) + assert.Error(t, r.BeginArgument(false /* last */)) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Sender sent final argument, but receiver thinks there is more + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello"))) + + reader, err := r.ArgReader(false /* last */) + assert.NoError(t, err) + + var arg []byte + assert.Error(t, NewArgReader(reader, nil).Read(&arg)) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Close without receiving all data in chunk + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello"))) + + assert.NoError(t, r.BeginArgument(true /* last */)) + b := make([]byte, 3) + _, err = r.Read(b) + assert.NoError(t, err) + assert.Equal(t, "hel", string(b)) + assert.Error(t, r.Close()) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // Close without receiving all fragments + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello world what's up"))) + + assert.NoError(t, r.BeginArgument(true /* last */)) + b := make([]byte, 8) + _, err = r.Read(b) + assert.NoError(t, err) + assert.Equal(t, "hello wo", string(b)) + assert.Error(t, r.Close()) + }) + + runFragmentationErrorTest(func(w *fragmentingWriter, r *fragmentingReader) { + // BeginArgument while argument is in process + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello world what's up"))) + assert.NoError(t, r.BeginArgument(false /* last */)) + assert.Error(t, r.BeginArgument(false /* last */)) + }) +} + +func TestFragmentationChecksumTypeErrors(t *testing.T) { + sendCh := make(fragmentChannel, 10) + recvCh := make(fragmentChannel, 10) + w := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New()) + r := newFragmentingReader(NullLogger, recvCh) + + // Write two fragments out + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello world what's up"))) + + // Intercept and change the checksum type between the first and second fragment + first := <-sendCh + recvCh <- first + + second := <-sendCh + second[1] = byte(ChecksumTypeCrc32C) + recvCh <- second + + // Attempt to read, should fail + reader, err := r.ArgReader(true /* last */) + assert.NoError(t, err) + + var arg []byte + assert.Error(t, NewArgReader(reader, nil).Read(&arg)) +} + +func TestFragmentationChecksumMismatch(t *testing.T) { + sendCh := make(fragmentChannel, 10) + recvCh := make(fragmentChannel, 10) + w := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New()) + r := newFragmentingReader(NullLogger, recvCh) + + // Write two fragments out + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + assert.NoError(t, NewArgWriter(writer, nil).Write([]byte("hello world this is two"))) + + // Intercept and change the checksum value in the second fragment + first := <-sendCh + recvCh <- first + + second := <-sendCh + second[2], second[3], second[4], second[5] = 0x01, 0x02, 0x03, 0x04 + recvCh <- second + + // Attempt to read, should fail due to mismatch between local checksum and peer supplied checksum + reader, err := r.ArgReader(true /* last */) + assert.NoError(t, err) + + _, err = io.Copy(ioutil.Discard, reader) + assert.Equal(t, errMismatchedChecksums, err) +} + +func runFragmentationErrorTest(f func(w *fragmentingWriter, r *fragmentingReader)) { + ch := make(fragmentChannel, 10) + w := newFragmentingWriter(NullLogger, ch, ChecksumTypeCrc32.New()) + r := newFragmentingReader(NullLogger, ch) + f(w, r) +} + +func runFragmentationTest(t *testing.T, args []string, expectedFragments [][]byte) { + sendCh := make(fragmentChannel, 10) + recvCh := make(fragmentChannel, 10) + + w := newFragmentingWriter(NullLogger, sendCh, ChecksumTypeCrc32.New()) + r := newFragmentingReader(NullLogger, recvCh) + + var fragments [][]byte + var actualArgs []string + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for fragment := range sendCh { + fragments = append(fragments, fragment) + recvCh <- fragment + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + + for i := 0; i < len(args)-1; i++ { + reader, err := r.ArgReader(false /* last */) + require.NoError(t, err) + + var arg []byte + require.NoError(t, NewArgReader(reader, nil).Read(&arg)) + actualArgs = append(actualArgs, string(arg)) + } + + reader, err := r.ArgReader(true /* last */) + require.NoError(t, err) + + var arg []byte + require.NoError(t, NewArgReader(reader, nil).Read(&arg)) + actualArgs = append(actualArgs, string(arg)) + }() + + for i := 0; i < len(args)-1; i++ { + writer, err := w.ArgWriter(false /* last */) + assert.NoError(t, err) + require.NoError(t, NewArgWriter(writer, nil).Write([]byte(args[i]))) + } + writer, err := w.ArgWriter(true /* last */) + assert.NoError(t, err) + require.NoError(t, NewArgWriter(writer, nil).Write([]byte(args[len(args)-1]))) + close(sendCh) + + wg.Wait() + + assert.Equal(t, args, actualArgs) + assert.Equal(t, len(expectedFragments), len(fragments), "incorrect number of fragments") + for i := 0; i < len(expectedFragments); i++ { + expectedFragment, fragment := expectedFragments[i], fragments[i] + assert.Equal(t, expectedFragment, fragment, "incorrect fragment %d", i) + } +} + +type fragmentChannel chan []byte + +func (ch fragmentChannel) newFragment(initial bool, checksum Checksum) (*writableFragment, error) { + wbuf := typed.NewWriteBuffer(make([]byte, testFragmentSize)) + fragment := new(writableFragment) + fragment.flagsRef = wbuf.DeferByte() + wbuf.WriteSingleByte(byte(checksum.TypeCode())) + fragment.checksumRef = wbuf.DeferBytes(checksum.Size()) + fragment.checksum = checksum + fragment.contents = wbuf + return fragment, wbuf.Err() +} + +func (ch fragmentChannel) flushFragment(fragment *writableFragment) error { + var buf bytes.Buffer + fragment.contents.FlushTo(&buf) + ch <- buf.Bytes() + return nil +} + +func (ch fragmentChannel) recvNextFragment(initial bool) (*readableFragment, error) { + rbuf := typed.NewReadBuffer(<-ch) + fragment := new(readableFragment) + fragment.onDone = func() {} + fragment.flags = rbuf.ReadSingleByte() + fragment.checksumType = ChecksumType(rbuf.ReadSingleByte()) + fragment.checksum = rbuf.ReadBytes(fragment.checksumType.ChecksumSize()) + fragment.contents = rbuf + return fragment, rbuf.Err() +} + +func (ch fragmentChannel) doneReading(unexpected error) {} +func (ch fragmentChannel) doneSending() {} + +func buffers(elements ...[][]byte) [][]byte { + var buffers [][]byte + for i := range elements { + buffers = append(buffers, bytes.Join(elements[i], []byte{})) + } + + return buffers +} diff --git a/vendor/src/github.com/uber/tchannel-go/fragmenting_reader.go b/vendor/src/github.com/uber/tchannel-go/fragmenting_reader.go new file mode 100644 index 00000000..129edbfa --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/fragmenting_reader.go @@ -0,0 +1,316 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "errors" + "io" + + "github.com/uber/tchannel-go/typed" +) + +var ( + errMismatchedChecksumTypes = errors.New("peer returned different checksum types between fragments") + errMismatchedChecksums = errors.New("different checksums between peer and local") + errChunkExceedsFragmentSize = errors.New("peer chunk size exceeds remaining data in fragment") + errAlreadyReadingArgument = errors.New("already reading argument") + errNotReadingArgument = errors.New("not reading argument") + errMoreDataInArgument = errors.New("closed argument reader when there is more data available to read") + errExpectedMoreArguments = errors.New("closed argument reader when there may be more data available to read") + errNoMoreFragments = errors.New("no more fragments") +) + +type readableFragment struct { + isDone bool + flags byte + checksumType ChecksumType + checksum []byte + contents *typed.ReadBuffer + onDone func() +} + +func (f *readableFragment) done() { + if f.isDone { + return + } + f.onDone() + f.isDone = true +} + +type fragmentReceiver interface { + // recvNextFragment returns the next received fragment, blocking until + // it's available or a deadline/cancel occurs + recvNextFragment(intial bool) (*readableFragment, error) + + // doneReading is called when the fragment receiver is finished reading all fragments. + // If an error frame is the last received frame, then doneReading is called with an error. + doneReading(unexpectedErr error) +} + +type fragmentingReadState int + +const ( + fragmentingReadStart fragmentingReadState = iota + fragmentingReadInArgument + fragmentingReadInLastArgument + fragmentingReadWaitingForArgument + fragmentingReadComplete +) + +func (s fragmentingReadState) isReadingArgument() bool { + return s == fragmentingReadInArgument || s == fragmentingReadInLastArgument +} + +type fragmentingReader struct { + logger Logger + state fragmentingReadState + remainingChunks [][]byte + curChunk []byte + hasMoreFragments bool + receiver fragmentReceiver + curFragment *readableFragment + checksum Checksum + err error +} + +func newFragmentingReader(logger Logger, receiver fragmentReceiver) *fragmentingReader { + return &fragmentingReader{ + logger: logger, + receiver: receiver, + hasMoreFragments: true, + } +} + +// The ArgReader will handle fragmentation as needed. Once the argument has +// been read, the ArgReader must be closed. +func (r *fragmentingReader) ArgReader(last bool) (ArgReader, error) { + if err := r.BeginArgument(last); err != nil { + return nil, err + } + return r, nil +} + +func (r *fragmentingReader) BeginArgument(last bool) error { + if r.err != nil { + return r.err + } + + switch { + case r.state.isReadingArgument(): + r.err = errAlreadyReadingArgument + return r.err + case r.state == fragmentingReadComplete: + r.err = errComplete + return r.err + } + + // We're guaranteed that either this is the first argument (in which + // case we need to get the first fragment and chunk) or that we have a + // valid curChunk (populated via Close) + if r.state == fragmentingReadStart { + if r.err = r.recvAndParseNextFragment(true); r.err != nil { + return r.err + } + } + + r.state = fragmentingReadInArgument + if last { + r.state = fragmentingReadInLastArgument + } + return nil +} + +func (r *fragmentingReader) Read(b []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + + if !r.state.isReadingArgument() { + r.err = errNotReadingArgument + return 0, r.err + } + + totalRead := 0 + for { + // Copy as much data as we can from the current chunk + n := copy(b, r.curChunk) + totalRead += n + r.curChunk = r.curChunk[n:] + b = b[n:] + + if len(b) == 0 { + // There was enough data in the current chunk to + // satisfy the read. Advance our place in the current + // chunk and be done + return totalRead, nil + } + + // There wasn't enough data in the current chunk to satisfy the + // current read. If there are more chunks in the current + // fragment, then we've reach the end of this argument. Return + // an io.EOF so functions like ioutil.ReadFully know to finish + if len(r.remainingChunks) > 0 { + return totalRead, io.EOF + } + + // Try to fetch more fragments. If there are no more + // fragments, then we've reached the end of the argument + if !r.hasMoreFragments { + return totalRead, io.EOF + } + + if r.err = r.recvAndParseNextFragment(false); r.err != nil { + return totalRead, r.err + } + } +} + +func (r *fragmentingReader) Close() error { + last := r.state == fragmentingReadInLastArgument + if r.err != nil { + return r.err + } + + if !r.state.isReadingArgument() { + r.err = errNotReadingArgument + return r.err + } + + if len(r.curChunk) > 0 { + // There was more data remaining in the chunk + r.err = errMoreDataInArgument + return r.err + } + + // Several possibilities here: + // 1. The caller thinks this is the last argument, but there are chunks in the current + // fragment or more fragments in this message + // - give them an error + // 2. The caller thinks this is the last argument, and there are no more chunks and no more + // fragments + // - the stream is complete + // 3. The caller thinks there are more arguments, and there are more chunks in this fragment + // - advance to the next chunk, this is the first chunk for the next argument + // 4. The caller thinks there are more arguments, and there are no more chunks in this fragment, + // but there are more fragments in the message + // - retrieve the next fragment, confirm it has an empty chunk (indicating the end of the + // current argument), advance to the next check (which is the first chunk for the next arg) + // 5. The caller thinks there are more arguments, but there are no more chunks or fragments available + // - give them an err + if last { + if len(r.remainingChunks) > 0 || r.hasMoreFragments { + // We expect more arguments + r.err = errExpectedMoreArguments + return r.err + } + + r.doneReading(nil) + r.curFragment.done() + r.curChunk = nil + r.state = fragmentingReadComplete + return nil + } + + r.state = fragmentingReadWaitingForArgument + + // If there are more chunks in this fragment, advance to the next chunk. This is the first chunk + // for the next argument + if len(r.remainingChunks) > 0 { + r.curChunk, r.remainingChunks = r.remainingChunks[0], r.remainingChunks[1:] + return nil + } + + // If there are no more chunks in this fragment, and no more fragments, we have an issue + if !r.hasMoreFragments { + r.err = errNoMoreFragments + return r.err + } + + // There are no more chunks in this fragments, but more fragments - get the next fragment + if r.err = r.recvAndParseNextFragment(false); r.err != nil { + return r.err + } + + return nil +} + +func (r *fragmentingReader) recvAndParseNextFragment(initial bool) error { + if r.err != nil { + return r.err + } + + if r.curFragment != nil { + r.curFragment.done() + } + + r.curFragment, r.err = r.receiver.recvNextFragment(initial) + if r.err != nil { + if err, ok := r.err.(errorMessage); ok { + // Serialized system errors are still reported (e.g. latency, trace reporting). + r.err = err.AsSystemError() + r.doneReading(r.err) + } + return r.err + } + + // Set checksum, or confirm new checksum is the same type as the prior checksum + if r.checksum == nil { + r.checksum = r.curFragment.checksumType.New() + } else if r.checksum.TypeCode() != r.curFragment.checksumType { + return errMismatchedChecksumTypes + } + + // Split fragment into underlying chunks + r.hasMoreFragments = (r.curFragment.flags & hasMoreFragmentsFlag) == hasMoreFragmentsFlag + r.remainingChunks = nil + for r.curFragment.contents.BytesRemaining() > 0 && r.curFragment.contents.Err() == nil { + chunkSize := r.curFragment.contents.ReadUint16() + if chunkSize > uint16(r.curFragment.contents.BytesRemaining()) { + return errChunkExceedsFragmentSize + } + chunkData := r.curFragment.contents.ReadBytes(int(chunkSize)) + r.remainingChunks = append(r.remainingChunks, chunkData) + r.checksum.Add(chunkData) + } + + if r.curFragment.contents.Err() != nil { + return r.curFragment.contents.Err() + } + + // Validate checksums + localChecksum := r.checksum.Sum() + if bytes.Compare(r.curFragment.checksum, localChecksum) != 0 { + r.err = errMismatchedChecksums + return r.err + } + + // Pull out the first chunk to act as the current chunk + r.curChunk, r.remainingChunks = r.remainingChunks[0], r.remainingChunks[1:] + return nil +} + +func (r *fragmentingReader) doneReading(err error) { + if r.checksum != nil { + r.checksum.Release() + } + r.receiver.doneReading(err) +} diff --git a/vendor/src/github.com/uber/tchannel-go/fragmenting_writer.go b/vendor/src/github.com/uber/tchannel-go/fragmenting_writer.go new file mode 100644 index 00000000..9a9df2eb --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/fragmenting_writer.go @@ -0,0 +1,296 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + + "github.com/uber/tchannel-go/typed" +) + +var ( + errAlreadyWritingArgument = errors.New("already writing argument") + errNotWritingArgument = errors.New("not writing argument") + errComplete = errors.New("last argument already sent") +) + +const ( + chunkHeaderSize = 2 // each chunk is a uint16 + hasMoreFragmentsFlag = 0x01 // flags indicating there are more fragments coming +) + +// A writableFragment is a fragment that can be written to, containing a buffer +// for contents, a running checksum, and placeholders for the fragment flags +// and final checksum value +type writableFragment struct { + flagsRef typed.ByteRef + checksumRef typed.BytesRef + checksum Checksum + contents *typed.WriteBuffer + frame interface{} +} + +// finish finishes the fragment, updating the final checksum and fragment flags +func (f *writableFragment) finish(hasMoreFragments bool) { + f.checksumRef.Update(f.checksum.Sum()) + if hasMoreFragments { + f.flagsRef.Update(hasMoreFragmentsFlag) + } else { + f.checksum.Release() + } +} + +// A writableChunk is a chunk of data within a fragment, representing the +// contents of an argument within that fragment +type writableChunk struct { + size uint16 + sizeRef typed.Uint16Ref + checksum Checksum + contents *typed.WriteBuffer +} + +// newWritableChunk creates a new writable chunk around a checksum and a buffer to hold data +func newWritableChunk(checksum Checksum, contents *typed.WriteBuffer) *writableChunk { + return &writableChunk{ + size: 0, + sizeRef: contents.DeferUint16(), + checksum: checksum, + contents: contents, + } +} + +// writeAsFits writes as many bytes from the given slice as fits into the chunk +func (c *writableChunk) writeAsFits(b []byte) int { + if len(b) > c.contents.BytesRemaining() { + b = b[:c.contents.BytesRemaining()] + } + + c.checksum.Add(b) + c.contents.WriteBytes(b) + + written := len(b) + c.size += uint16(written) + return written +} + +// finish finishes the chunk, updating its chunk size +func (c *writableChunk) finish() { + c.sizeRef.Update(c.size) +} + +// A fragmentSender allocates and sends outbound fragments to a target +type fragmentSender interface { + // newFragment allocates a new fragment + newFragment(initial bool, checksum Checksum) (*writableFragment, error) + + // flushFragment flushes the given fragment + flushFragment(f *writableFragment) error + + // doneSending is called when the fragment receiver is finished sending all fragments. + doneSending() +} + +type fragmentingWriterState int + +const ( + fragmentingWriteStart fragmentingWriterState = iota + fragmentingWriteInArgument + fragmentingWriteInLastArgument + fragmentingWriteWaitingForArgument + fragmentingWriteComplete +) + +func (s fragmentingWriterState) isWritingArgument() bool { + return s == fragmentingWriteInArgument || s == fragmentingWriteInLastArgument +} + +// A fragmentingWriter writes one or more arguments to an underlying stream, +// breaking them into fragments as needed, and applying an overarching +// checksum. It relies on an underlying fragmentSender, which creates and +// flushes the fragments as needed +type fragmentingWriter struct { + logger Logger + sender fragmentSender + checksum Checksum + curFragment *writableFragment + curChunk *writableChunk + state fragmentingWriterState + err error +} + +// newFragmentingWriter creates a new fragmenting writer +func newFragmentingWriter(logger Logger, sender fragmentSender, checksum Checksum) *fragmentingWriter { + return &fragmentingWriter{ + logger: logger, + sender: sender, + checksum: checksum, + state: fragmentingWriteStart, + } +} + +// ArgWriter returns an ArgWriter to write an argument. The ArgWriter will handle +// fragmentation as needed. Once the argument is written, the ArgWriter must be closed. +func (w *fragmentingWriter) ArgWriter(last bool) (ArgWriter, error) { + if err := w.BeginArgument(last); err != nil { + return nil, err + } + return w, nil +} + +// BeginArgument tells the writer that the caller is starting a new argument. +// Must not be called while an existing argument is in place +func (w *fragmentingWriter) BeginArgument(last bool) error { + if w.err != nil { + return w.err + } + + switch { + case w.state == fragmentingWriteComplete: + w.err = errComplete + return w.err + case w.state.isWritingArgument(): + w.err = errAlreadyWritingArgument + return w.err + } + + // If we don't have a fragment, request one + if w.curFragment == nil { + initial := w.state == fragmentingWriteStart + if w.curFragment, w.err = w.sender.newFragment(initial, w.checksum); w.err != nil { + return w.err + } + } + + // If there's no room in the current fragment, freak out. This will + // only happen due to an implementation error in the TChannel stack + // itself + if w.curFragment.contents.BytesRemaining() <= chunkHeaderSize { + panic(fmt.Errorf("attempting to begin an argument in a fragment with only %d bytes available", + w.curFragment.contents.BytesRemaining())) + } + + w.curChunk = newWritableChunk(w.checksum, w.curFragment.contents) + w.state = fragmentingWriteInArgument + if last { + w.state = fragmentingWriteInLastArgument + } + return nil +} + +// Write writes argument data, breaking it into fragments as needed +func (w *fragmentingWriter) Write(b []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if !w.state.isWritingArgument() { + w.err = errNotWritingArgument + return 0, w.err + } + + totalWritten := 0 + for { + bytesWritten := w.curChunk.writeAsFits(b) + totalWritten += bytesWritten + if bytesWritten == len(b) { + // The whole thing fit, we're done + return totalWritten, nil + } + + // There was more data than fit into the fragment, so flush the current fragment, + // start a new fragment and chunk, and continue writing + if w.err = w.Flush(); w.err != nil { + return totalWritten, w.err + } + + b = b[bytesWritten:] + } +} + +// Flush flushes the current fragment, and starts a new fragment and chunk. +func (w *fragmentingWriter) Flush() error { + w.curChunk.finish() + w.curFragment.finish(true) + if w.err = w.sender.flushFragment(w.curFragment); w.err != nil { + return w.err + } + + if w.curFragment, w.err = w.sender.newFragment(false, w.checksum); w.err != nil { + return w.err + } + + w.curChunk = newWritableChunk(w.checksum, w.curFragment.contents) + return nil +} + +// Close ends the current argument. +func (w *fragmentingWriter) Close() error { + last := w.state == fragmentingWriteInLastArgument + if w.err != nil { + return w.err + } + + if !w.state.isWritingArgument() { + w.err = errNotWritingArgument + return w.err + } + + w.curChunk.finish() + + // There are three possibilities here: + // 1. There are no more arguments + // flush with more_fragments=false, mark the stream as complete + // 2. There are more arguments, but we can't fit more data into this fragment + // flush with more_fragments=true, start new fragment, write empty chunk to indicate + // the current argument is complete + // 3. There are more arguments, and we can fit more data into this fragment + // update the chunk but leave the current fragment open + if last { + // No more arguments - flush this final fragment and mark ourselves complete + w.state = fragmentingWriteComplete + w.curFragment.finish(false) + w.err = w.sender.flushFragment(w.curFragment) + w.sender.doneSending() + return w.err + } + + w.state = fragmentingWriteWaitingForArgument + if w.curFragment.contents.BytesRemaining() > chunkHeaderSize { + // There's enough room in this fragment for the next argument's + // initial chunk, so we're done here + return nil + } + + // This fragment is full - flush and prepare for another argument + w.curFragment.finish(true) + if w.err = w.sender.flushFragment(w.curFragment); w.err != nil { + return w.err + } + + if w.curFragment, w.err = w.sender.newFragment(false, w.checksum); w.err != nil { + return w.err + } + + // Write an empty chunk to indicate this argument has ended + w.curFragment.contents.WriteUint16(0) + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame.go b/vendor/src/github.com/uber/tchannel-go/frame.go new file mode 100644 index 00000000..646cac1d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame.go @@ -0,0 +1,196 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "encoding/json" + "fmt" + "io" + "math" + + "github.com/uber/tchannel-go/typed" +) + +const ( + // MaxFrameSize is the total maximum size for a frame + MaxFrameSize = math.MaxUint16 + + // FrameHeaderSize is the size of the header element for a frame + FrameHeaderSize = 16 + + // MaxFramePayloadSize is the maximum size of the payload for a single frame + MaxFramePayloadSize = MaxFrameSize - FrameHeaderSize +) + +// FrameHeader is the header for a frame, containing the MessageType and size +type FrameHeader struct { + // The size of the frame including the header + size uint16 + + // The type of message represented by the frame + messageType messageType + + // Left empty + reserved1 byte + + // The id of the message represented by the frame + ID uint32 + + // Left empty + reserved [8]byte +} + +// SetPayloadSize sets the size of the frame payload +func (fh *FrameHeader) SetPayloadSize(size uint16) { + fh.size = size + FrameHeaderSize +} + +// PayloadSize returns the size of the frame payload +func (fh FrameHeader) PayloadSize() uint16 { + return fh.size - FrameHeaderSize +} + +// FrameSize returns the total size of the frame +func (fh FrameHeader) FrameSize() uint16 { + return fh.size +} + +func (fh FrameHeader) String() string { return fmt.Sprintf("%v[%d]", fh.messageType, fh.ID) } + +// MarshalJSON returns a `{"id":NNN, "msgType":MMM, "size":SSS}` representation +func (fh FrameHeader) MarshalJSON() ([]byte, error) { + s := struct { + ID uint32 `json:"id"` + MsgType messageType `json:"msgType"` + Size uint16 `json:"size"` + }{fh.ID, fh.messageType, fh.size} + return json.Marshal(s) +} + +func (fh *FrameHeader) read(r *typed.ReadBuffer) error { + fh.size = r.ReadUint16() + fh.messageType = messageType(r.ReadSingleByte()) + fh.reserved1 = r.ReadSingleByte() + fh.ID = r.ReadUint32() + r.ReadBytes(len(fh.reserved)) + return r.Err() +} + +func (fh *FrameHeader) write(w *typed.WriteBuffer) error { + w.WriteUint16(fh.size) + w.WriteSingleByte(byte(fh.messageType)) + w.WriteSingleByte(fh.reserved1) + w.WriteUint32(fh.ID) + w.WriteBytes(fh.reserved[:]) + return w.Err() +} + +// A Frame is a header and payload +type Frame struct { + buffer []byte // full buffer, including payload and header + headerBuffer []byte // slice referencing just the header + + // The header for the frame + Header FrameHeader + + // The payload for the frame + Payload []byte +} + +// NewFrame allocates a new frame with the given payload capacity +func NewFrame(payloadCapacity int) *Frame { + f := &Frame{} + f.buffer = make([]byte, payloadCapacity+FrameHeaderSize) + f.Payload = f.buffer[FrameHeaderSize:] + f.headerBuffer = f.buffer[:FrameHeaderSize] + return f +} + +// ReadIn reads the frame from the given io.Reader +func (f *Frame) ReadIn(r io.Reader) error { + var rbuf typed.ReadBuffer + rbuf.Wrap(f.headerBuffer) + + if _, err := rbuf.FillFrom(r, FrameHeaderSize); err != nil { + return err + } + + if err := f.Header.read(&rbuf); err != nil { + return err + } + switch payloadSize := f.Header.PayloadSize(); { + case payloadSize > MaxFramePayloadSize: + return fmt.Errorf("invalid frame size %v", f.Header.size) + case payloadSize > 0: + if _, err := io.ReadFull(r, f.SizedPayload()); err != nil { + return err + } + } + + return nil +} + +// WriteOut writes the frame to the given io.Writer +func (f *Frame) WriteOut(w io.Writer) error { + var wbuf typed.WriteBuffer + wbuf.Wrap(f.headerBuffer) + + if err := f.Header.write(&wbuf); err != nil { + return err + } + + fullFrame := f.buffer[:f.Header.FrameSize()] + if _, err := w.Write(fullFrame); err != nil { + return err + } + + return nil +} + +// SizedPayload returns the slice of the payload actually used, as defined by the header +func (f *Frame) SizedPayload() []byte { + return f.Payload[:f.Header.PayloadSize()] +} + +// messageType returns the message type. +func (f *Frame) messageType() messageType { + return f.Header.messageType +} + +func (f *Frame) write(msg message) error { + var wbuf typed.WriteBuffer + wbuf.Wrap(f.Payload[:]) + if err := msg.write(&wbuf); err != nil { + return err + } + + f.Header.ID = msg.ID() + f.Header.reserved1 = 0 + f.Header.messageType = msg.messageType() + f.Header.SetPayloadSize(uint16(wbuf.BytesWritten())) + return nil +} + +func (f *Frame) read(msg message) error { + var rbuf typed.ReadBuffer + rbuf.Wrap(f.SizedPayload()) + return msg.read(&rbuf) +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame_pool.go b/vendor/src/github.com/uber/tchannel-go/frame_pool.go new file mode 100644 index 00000000..53f62127 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame_pool.go @@ -0,0 +1,86 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "sync" + +// A FramePool is a pool for managing and re-using frames +type FramePool interface { + // Retrieves a new frame from the pool + Get() *Frame + + // Releases a frame back to the pool + Release(f *Frame) +} + +// DefaultFramePool uses the SyncFramePool. +var DefaultFramePool = NewSyncFramePool() + +// DisabledFramePool is a pool that uses the heap and relies on GC. +var DisabledFramePool = disabledFramePool{} + +type disabledFramePool struct{} + +func (p disabledFramePool) Get() *Frame { return NewFrame(MaxFramePayloadSize) } +func (p disabledFramePool) Release(f *Frame) {} + +type syncFramePool struct { + pool *sync.Pool +} + +// NewSyncFramePool returns a frame pool that uses a sync.Pool. +func NewSyncFramePool() FramePool { + return &syncFramePool{ + pool: &sync.Pool{New: func() interface{} { return NewFrame(MaxFramePayloadSize) }}, + } +} + +func (p syncFramePool) Get() *Frame { + return p.pool.Get().(*Frame) +} + +func (p syncFramePool) Release(f *Frame) { + p.pool.Put(f) +} + +type channelFramePool chan *Frame + +// NewChannelFramePool returns a frame pool backed by a channel that has a max capacity. +func NewChannelFramePool(capacity int) FramePool { + return channelFramePool(make(chan *Frame, capacity)) +} + +func (c channelFramePool) Get() *Frame { + select { + case frame := <-c: + return frame + default: + return NewFrame(MaxFramePayloadSize) + } +} + +func (c channelFramePool) Release(f *Frame) { + select { + case c <- f: + default: + // Too many frames in the channel, discard it. + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame_pool_b_test.go b/vendor/src/github.com/uber/tchannel-go/frame_pool_b_test.go new file mode 100644 index 00000000..31cac2df --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame_pool_b_test.go @@ -0,0 +1,83 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "math/rand" + "sync" + "testing" + + . "github.com/uber/tchannel-go" + + "github.com/uber-go/atomic" +) + +func benchmarkUsing(b *testing.B, pool FramePool) { + const numGoroutines = 1000 + const maxHoldFrames = 1000 + + var gotFrames atomic.Uint64 + + var wg sync.WaitGroup + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func() { + + for { + if gotFrames.Load() > uint64(b.N) { + break + } + + framesToHold := rand.Intn(maxHoldFrames) + gotFrames.Add(uint64(framesToHold)) + + frames := make([]*Frame, framesToHold) + for i := 0; i < framesToHold; i++ { + frames[i] = pool.Get() + } + + for i := 0; i < framesToHold; i++ { + pool.Release(frames[i]) + } + } + + wg.Done() + }() + } + + wg.Wait() +} + +func BenchmarkFramePoolDisabled(b *testing.B) { + benchmarkUsing(b, DisabledFramePool) +} + +func BenchmarkFramePoolSync(b *testing.B) { + benchmarkUsing(b, NewSyncFramePool()) +} + +func BenchmarkFramePoolChannel1000(b *testing.B) { + benchmarkUsing(b, NewChannelFramePool(1000)) +} + +func BenchmarkFramePoolChannel10000(b *testing.B) { + benchmarkUsing(b, NewChannelFramePool(10000)) +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame_pool_test.go b/vendor/src/github.com/uber/tchannel-go/frame_pool_test.go new file mode 100644 index 00000000..6d438aed --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame_pool_test.go @@ -0,0 +1,181 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +// This file contains functions for tests to access internal tchannel state. +// Since it has a _test.go suffix, it is only compiled with tests in this package. + +import ( + "bytes" + "io" + "math/rand" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/goroutines" + "github.com/uber/tchannel-go/testutils/testreader" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +type swapper struct { + t *testing.T +} + +func (s *swapper) OnError(ctx context.Context, err error) { + s.t.Errorf("OnError: %v", err) +} + +func (*swapper) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{ + Arg2: args.Arg3, + Arg3: args.Arg2, + }, nil +} + +func doPingAndCall(t *testing.T, clientCh *Channel, hostPort string) { + ctx, cancel := NewContext(time.Second * 5) + defer cancel() + + require.NoError(t, clientCh.Ping(ctx, hostPort)) + + const maxRandArg = 512 * 1024 + + arg2 := testutils.RandBytes(rand.Intn(maxRandArg)) + arg3 := testutils.RandBytes(rand.Intn(maxRandArg)) + resArg2, resArg3, _, err := raw.Call(ctx, clientCh, hostPort, "swap-server", "swap", arg2, arg3) + if !assert.NoError(t, err, "error during sendRecv") { + return + } + + // We expect the arguments to be swapped. + if bytes.Compare(arg3, resArg2) != 0 { + t.Errorf("returned arg2 does not match expected:\n got %v\n want %v", resArg2, arg3) + } + if bytes.Compare(arg2, resArg3) != 0 { + t.Errorf("returned arg2 does not match expected:\n got %v\n want %v", resArg3, arg2) + } +} + +func doErrorCall(t *testing.T, clientCh *Channel, hostPort string) { + ctx, cancel := NewContext(time.Second * 5) + defer cancel() + + _, _, _, err := raw.Call(ctx, clientCh, hostPort, "swap-server", "non-existent", nil, nil) + assert.Error(t, err, "Call to non-existent endpoint should fail") + assert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), "Error code mismatch") +} + +func TestFramesReleased(t *testing.T) { + CheckStress(t) + + defer testutils.SetTimeout(t, 30*time.Second)() + const ( + requestsPerGoroutine = 10 + numGoroutines = 10 + ) + + pool := NewRecordingFramePool() + opts := testutils.NewOpts(). + SetServiceName("swap-server"). + SetFramePool(pool). + AddLogFilter("Couldn't find handler.", 2*numGoroutines*requestsPerGoroutine) + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(&swapper{t}), "swap") + + clientOpts := testutils.NewOpts().SetFramePool(pool) + clientCh := ts.NewClient(clientOpts) + + // Create an active connection that can be shared by the goroutines by calling Ping. + ctx, cancel := NewContext(time.Second) + defer cancel() + require.NoError(t, clientCh.Ping(ctx, ts.HostPort())) + + var wg sync.WaitGroup + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + for i := 0; i < requestsPerGoroutine; i++ { + doPingAndCall(t, clientCh, ts.HostPort()) + doErrorCall(t, clientCh, ts.HostPort()) + } + }() + } + + wg.Wait() + }) + + // TODO: The goroutines.GetAll is to debug test failures in Travis. Remove this once + // we confirm that the test is not flaky. + stacks := goroutines.GetAll() + if unreleasedCount, isEmpty := pool.CheckEmpty(); isEmpty != "" || unreleasedCount > 0 { + t.Errorf("Frame pool has %v unreleased frames, errors:\n%v\nStacks:%v", + unreleasedCount, isEmpty, stacks) + } +} + +type dirtyFramePool struct{} + +func (p dirtyFramePool) Get() *Frame { + f := NewFrame(MaxFramePayloadSize) + reader := testreader.Looper([]byte{^byte(0)}) + io.ReadFull(reader, f.Payload) + return f +} + +func (p dirtyFramePool) Release(f *Frame) {} + +func TestDirtyFrameRequests(t *testing.T) { + argSizes := []int{25000, 50000, 75000} + + // Create the largest required random cache. + testutils.RandBytes(argSizes[len(argSizes)-1]) + + opts := testutils.NewOpts(). + SetServiceName("swap-server"). + SetFramePool(dirtyFramePool{}) + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(&swapper{t}), "swap") + + for _, argSize := range argSizes { + ctx, cancel := NewContext(time.Second) + defer cancel() + + arg2, arg3 := testutils.RandBytes(argSize), testutils.RandBytes(argSize) + res2, res3, _, err := raw.Call(ctx, ts.Server(), ts.HostPort(), ts.Server().ServiceName(), "swap", arg2, arg3) + if assert.NoError(t, err, "Call failed") { + assert.Equal(t, arg2, res3, "Result arg3 wrong") + assert.Equal(t, arg3, res2, "Result arg3 wrong") + } + } + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame_test.go b/vendor/src/github.com/uber/tchannel-go/frame_test.go new file mode 100644 index 00000000..56e177e0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame_test.go @@ -0,0 +1,283 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "io" + "math" + "testing" + "testing/iotest" + "testing/quick" + + "github.com/uber/tchannel-go/testutils/testreader" + "github.com/uber/tchannel-go/typed" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func fakeHeader() FrameHeader { + return FrameHeader{ + size: uint16(0xFF34), + messageType: messageTypeCallReq, + ID: 0xDEADBEEF, + } +} + +func TestFrameHeaderJSON(t *testing.T) { + fh := fakeHeader() + logged, err := json.Marshal(fh) + assert.NoError(t, err, "FrameHeader can't be marshalled to JSON") + assert.Equal( + t, + string(logged), + `{"id":3735928559,"msgType":3,"size":65332}`, + "FrameHeader didn't marshal to JSON as expected", + ) +} + +func TestFraming(t *testing.T) { + fh := fakeHeader() + wbuf := typed.NewWriteBufferWithSize(1024) + require.Nil(t, fh.write(wbuf)) + + var b bytes.Buffer + if _, err := wbuf.FlushTo(&b); err != nil { + require.Nil(t, err) + } + + rbuf := typed.NewReadBuffer(b.Bytes()) + + var fh2 FrameHeader + require.Nil(t, fh2.read(rbuf)) + + assert.Equal(t, fh, fh2) +} + +func TestPartialRead(t *testing.T) { + f := NewFrame(MaxFramePayloadSize) + f.Header.size = FrameHeaderSize + 2134 + f.Header.messageType = messageTypeCallReq + f.Header.ID = 0xDEADBEED + + // We set the full payload but only the first 2134 bytes should be written. + for i := 0; i < len(f.Payload); i++ { + val := (i * 37) % 256 + f.Payload[i] = byte(val) + } + buf := &bytes.Buffer{} + require.NoError(t, f.WriteOut(buf)) + assert.Equal(t, f.Header.size, uint16(buf.Len()), "frame size should match written bytes") + + // Read the data back, from a reader that fragments. + f2 := NewFrame(MaxFramePayloadSize) + require.NoError(t, f2.ReadIn(iotest.OneByteReader(buf))) + + // Ensure header and payload are the same. + require.Equal(t, f.Header, f2.Header, "frame headers don't match") + require.Equal(t, f.SizedPayload(), f2.SizedPayload(), "payload does not match") +} + +func TestEmptyPayload(t *testing.T) { + f := NewFrame(MaxFramePayloadSize) + m := &pingRes{id: 1} + require.NoError(t, f.write(m)) + + // Write out the frame. + buf := &bytes.Buffer{} + require.NoError(t, f.WriteOut(buf)) + assert.Equal(t, FrameHeaderSize, buf.Len()) + + // Read the frame from the buffer. + // net.Conn returns io.EOF if you try to read 0 bytes at the end. + // This is also simulated by the LimitedReader so we use that here. + require.NoError(t, f.ReadIn(&io.LimitedReader{R: buf, N: FrameHeaderSize})) +} + +func TestReservedBytes(t *testing.T) { + // Set up a frame with non-zero values + f := NewFrame(MaxFramePayloadSize) + reader := testreader.Looper([]byte{^byte(0)}) + io.ReadFull(reader, f.Payload) + f.Header.read(typed.NewReadBuffer(f.Payload)) + + m := &pingRes{id: 1} + f.write(m) + + buf := &bytes.Buffer{} + f.WriteOut(buf) + assert.Equal(t, + []byte{ + 0x0, 0x10, // size + 0xd1, // type + 0x0, // reserved should always be 0 + 0x0, 0x0, 0x0, 0x1, // id + 0x0, 0x0, 0x0, 0x0, // reserved should always be 0 + 0x0, 0x0, 0x0, 0x0, // reserved should always be 0 + }, + buf.Bytes(), "Unexpected bytes") +} + +func TestMessageType(t *testing.T) { + frame := NewFrame(MaxFramePayloadSize) + err := frame.write(&callReq{Service: "foo"}) + require.NoError(t, err, "Error writing message to frame.") + assert.Equal(t, messageTypeCallReq, frame.messageType(), "Failed to read message type from frame.") +} + +func TestFrameReadIn(t *testing.T) { + maxPayload := bytes.Repeat([]byte{1}, MaxFramePayloadSize) + tests := []struct { + msg string + bs []byte + wantFrameHeader FrameHeader + wantFramePayload []byte + wantErr string + }{ + { + msg: "frame with no payload", + bs: []byte{ + 0, 16 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + }, + wantFrameHeader: FrameHeader{ + size: 16, + messageType: 1, + reserved1: 2, + ID: 3, + // reserved: [8]byte{9, 8, 7, 6, 5, 4, 3, 2}, // currently ignored. + }, + wantFramePayload: []byte{}, + }, + { + msg: "frame with small payload", + bs: []byte{ + 0, 18 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + 100, 200, // payload + }, + wantFrameHeader: FrameHeader{ + size: 18, + messageType: 1, + reserved1: 2, + ID: 3, + // reserved: [8]byte{9, 8, 7, 6, 5, 4, 3, 2}, // currently ignored. + }, + wantFramePayload: []byte{100, 200}, + }, + { + msg: "frame with max size", + bs: append([]byte{ + math.MaxUint8, math.MaxUint8 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + }, maxPayload...), + wantFrameHeader: FrameHeader{ + size: math.MaxUint16, + messageType: 1, + reserved1: 2, + ID: 3, + // currently ignored. + // reserved: [8]byte{9, 8, 7, 6, 5, 4, 3, 2}, + }, + wantFramePayload: maxPayload, + }, + { + msg: "frame with 0 size", + bs: []byte{ + 0, 0 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + }, + wantErr: "invalid frame size 0", + }, + { + msg: "frame with size < HeaderSize", + bs: []byte{ + 0, 15 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + }, + wantErr: "invalid frame size 15", + }, + { + msg: "frame with partial header", + bs: []byte{ + 0, 16 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + // missing reserved bytes + }, + wantErr: "unexpected EOF", + }, + { + msg: "frame with partial payload", + bs: []byte{ + 0, 24 /* size */, 1 /* type */, 2 /* reserved */, 0, 0, 0, 3, /* id */ + 9, 8, 7, 6, 5, 4, 3, 2, // reserved + 1, 2, // partial payload + }, + wantErr: "unexpected EOF", + }, + } + + for _, tt := range tests { + f := DefaultFramePool.Get() + r := bytes.NewReader(tt.bs) + err := f.ReadIn(r) + if tt.wantErr != "" { + require.Error(t, err, tt.msg) + assert.Contains(t, err.Error(), tt.wantErr, tt.msg) + continue + } + + require.NoError(t, err, tt.msg) + assert.Equal(t, tt.wantFrameHeader, f.Header, "%v: header mismatch", tt.msg) + assert.Equal(t, tt.wantFramePayload, f.SizedPayload(), "%v: unexpected payload") + } +} + +func frameReadIn(bs []byte) (decoded bool) { + frame := DefaultFramePool.Get() + defer DefaultFramePool.Release(frame) + + defer func() { + if r := recover(); r != nil { + decoded = false + } + }() + frame.ReadIn(bytes.NewReader(bs)) + return true +} + +func TestQuickFrameReadIn(t *testing.T) { + // Try to read any set of bytes as a frame. + err := quick.Check(frameReadIn, &quick.Config{MaxCount: 10000}) + require.NoError(t, err, "Failed to fuzz test ReadIn") + + // Limit the search space to just headers. + err = quick.Check(func(size uint16, t byte, id uint32) bool { + bs := make([]byte, FrameHeaderSize) + binary.BigEndian.PutUint16(bs[0:2], size) + bs[2] = t + binary.BigEndian.PutUint32(bs[4:8], id) + return frameReadIn(bs) + }, &quick.Config{MaxCount: 10000}) + require.NoError(t, err, "Failed to fuzz test ReadIn") +} diff --git a/vendor/src/github.com/uber/tchannel-go/frame_utils_test.go b/vendor/src/github.com/uber/tchannel-go/frame_utils_test.go new file mode 100644 index 00000000..cd7eb375 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/frame_utils_test.go @@ -0,0 +1,149 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "runtime" + "strings" + "sync" + "unsafe" + + "github.com/prashantv/protectmem" +) + +type RecordingFramePool struct { + sync.Mutex + + allocations map[*Frame]string + badRelease []string +} + +type protectMemAllocs struct { + frameAlloc *protectmem.Allocation + bufferAlloc *protectmem.Allocation +} + +type ProtectMemFramePool struct { + sync.Mutex + + allocations map[*Frame]protectMemAllocs +} + +func NewRecordingFramePool() *RecordingFramePool { + return &RecordingFramePool{ + allocations: make(map[*Frame]string), + } +} + +func recordStack() string { + buf := make([]byte, 4096) + runtime.Stack(buf, false) + return string(buf) +} + +func (p *RecordingFramePool) Get() *Frame { + p.Lock() + defer p.Unlock() + + frame := NewFrame(MaxFramePayloadSize) + p.allocations[frame] = recordStack() + return frame +} + +func zeroOut(bs []byte) { + for i := range bs { + bs[i] = 0 + } +} + +func (p *RecordingFramePool) Release(f *Frame) { + // Make sure the payload is not used after this point by clearing the frame. + zeroOut(f.Payload) + f.Payload = nil + zeroOut(f.buffer) + f.buffer = nil + zeroOut(f.headerBuffer) + f.headerBuffer = nil + f.Header = FrameHeader{} + + p.Lock() + defer p.Unlock() + + if _, ok := p.allocations[f]; !ok { + p.badRelease = append(p.badRelease, "bad Release at "+recordStack()) + return + } + + delete(p.allocations, f) +} + +func (p *RecordingFramePool) CheckEmpty() (int, string) { + p.Lock() + defer p.Unlock() + + var badCalls []string + badCalls = append(badCalls, p.badRelease...) + for f, s := range p.allocations { + badCalls = append(badCalls, fmt.Sprintf("frame %p: %v not released, get from: %v", f, f.Header, s)) + } + return len(p.allocations), strings.Join(badCalls, "\n") +} + +// NewProtectMemFramePool creates a frame pool that ensures that released frames +// are not reused by removing all access to a frame once it's been released. +func NewProtectMemFramePool() FramePool { + return &ProtectMemFramePool{ + allocations: make(map[*Frame]protectMemAllocs), + } +} +func (p *ProtectMemFramePool) Get() *Frame { + frameAlloc := protectmem.Allocate(unsafe.Sizeof(Frame{})) + f := (*Frame)(frameAlloc.Ptr()) + + bufferAlloc := protectmem.AllocateSlice(&f.buffer, MaxFramePayloadSize) + f.buffer = f.buffer[:MaxFramePayloadSize] + f.Payload = f.buffer[FrameHeaderSize:] + f.headerBuffer = f.buffer[:FrameHeaderSize] + + p.Lock() + p.allocations[f] = protectMemAllocs{ + frameAlloc: frameAlloc, + bufferAlloc: bufferAlloc, + } + p.Unlock() + + return f +} + +func (p *ProtectMemFramePool) Release(f *Frame) { + p.Lock() + allocs, ok := p.allocations[f] + delete(p.allocations, f) + p.Unlock() + + if !ok { + panic(fmt.Errorf("released frame that was not allocated by pool: %v", f.Header)) + } + + allocs.bufferAlloc.Protect(protectmem.None) + allocs.frameAlloc.Protect(protectmem.None) +} diff --git a/vendor/src/github.com/uber/tchannel-go/glide.lock b/vendor/src/github.com/uber/tchannel-go/glide.lock new file mode 100644 index 00000000..1ceba0b6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/glide.lock @@ -0,0 +1,84 @@ +hash: d6093eb1a6d4c1eaa9f2ad996ebf07748b0b3a0413f4ab9b9435df1d255567a5 +updated: 2017-09-25T17:19:51.449837394-07:00 +imports: +- name: github.com/apache/thrift + version: b2a4d4ae21c789b689dd162deb819665567f481c + subpackages: + - lib/go/thrift +- name: github.com/cactus/go-statsd-client + version: 91c326c3f7bd20f0226d3d1c289dd9f8ce28d33d + subpackages: + - statsd +- name: github.com/opentracing/opentracing-go + version: 1949ddbfd147afd4d964a9f00b24eb291e0e7c38 + subpackages: + - ext + - log + - mocktracer +- name: github.com/samuel/go-thrift + version: e9042807f4f5bf47563df6992d3ea0857313e2be + subpackages: + - parser +- name: github.com/uber-go/atomic + version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf + subpackages: + - utils +- name: github.com/uber/jaeger-client-go + version: 3e3870040def0ebdaf65a003863fa64f5cb26139 + subpackages: + - internal/spanlog + - log + - thrift-gen/agent + - thrift-gen/jaeger + - thrift-gen/sampling + - thrift-gen/zipkincore + - utils +- name: golang.org/x/net + version: 8351a756f30f1297fe94bbf4b767ec589c6ea6d0 + subpackages: + - bpf + - context + - internal/iana + - internal/socket + - ipv4 + - ipv6 +testImports: +- name: github.com/bmizerany/perks + version: d9a9656a3a4b1c2864fdb44db2ef8619772d92aa + subpackages: + - quantile +- name: github.com/codahale/hdrhistogram + version: 3a0bb77429bd3a61596f5e8a3172445844342120 +- name: github.com/crossdock/crossdock-go + version: 049aabb0122b03bc9bd30cab8f3f91fb60166361 +- name: github.com/davecgh/go-spew + version: 346938d642f2ec3594ed81d874461961cd0faa76 + subpackages: + - spew +- name: github.com/jessevdk/go-flags + version: 96dc06278ce32a0e9d957d590bb987c81ee66407 + subpackages: + - ext + - mocktracer +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/prashantv/protectmem + version: d0cabd7ce64237b84414449086fa42810ae01a92 +- name: github.com/streadway/quantile + version: b0c588724d25ae13f5afb3d90efec0edc636432b +- name: github.com/stretchr/objx + version: cbeaeb16a013161a98496fad62933b1d21786672 +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - mock + - require +- name: github.com/uber/jaeger-lib + version: 21a3da6d66fe0e278072676fdc84cd4c9ccb9b67 + subpackages: + - metrics +- name: gopkg.in/yaml.v2 + version: eb3733d160e74a9c7e442f435eb3bea458e1d19f diff --git a/vendor/src/github.com/uber/tchannel-go/glide.yaml b/vendor/src/github.com/uber/tchannel-go/glide.yaml new file mode 100644 index 00000000..5901b719 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/glide.yaml @@ -0,0 +1,56 @@ +package: github.com/uber/tchannel-go +excludeDirs: +- benchmark +- crossdock +- examples +- relay/relaytest +- testutils +- thrift/mocks +import: +- package: github.com/apache/thrift + version: '>=0.9.3, <0.11.0' + subpackages: + - lib/go/thrift +- package: github.com/cactus/go-statsd-client + version: ^3 + subpackages: + - statsd +- package: github.com/samuel/go-thrift + subpackages: + - parser +- package: golang.org/x/net + subpackages: + - context + - ipv4 + - ipv6 +- package: github.com/uber-go/atomic + version: ^1 + subpackages: + - utils +- package: github.com/opentracing/opentracing-go + version: ^1 +- package: github.com/uber/jaeger-client-go + version: ^2.7 +testImport: +- package: github.com/jessevdk/go-flags + version: ^1 + subpackages: + - ext + - mocktracer +- package: github.com/stretchr/testify + version: ^1.1 + subpackages: + - assert + - mock + - require +- package: github.com/bmizerany/perks + subpackages: + - quantile +- package: github.com/davecgh/go-spew + version: ^1 + subpackages: + - spew +- package: github.com/prashantv/protectmem +- package: github.com/streadway/quantile +- package: gopkg.in/yaml.v2 +- package: github.com/crossdock/crossdock-go diff --git a/vendor/src/github.com/uber/tchannel-go/guide/Thrift_Hyperbahn.md b/vendor/src/github.com/uber/tchannel-go/guide/Thrift_Hyperbahn.md new file mode 100644 index 00000000..120b6cf1 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/guide/Thrift_Hyperbahn.md @@ -0,0 +1,252 @@ +# Set up a Go + Thrift + Hyperbahn Service + +The code matching this guide is [here](../examples/keyvalue). + +The TChannel+Thrift integration for Go uses code generated by thrift-gen. + +## Dependencies + +Make sure your [GOPATH is set up](http://golang.org/doc/code.html) before following this guide. + +You'll need to `go get` the following: +* github.com/uber/tchannel-go +* github.com/uber/tchannel-go/hyperbahn +* github.com/uber/tchannel-go/thrift +* github.com/uber/tchannel-go/thrift/thrift-gen + +Use [Godep](https://github.com/tools/godep) to manage dependencies, as the API is still in development and will change. + +This example will assume that the service is created in the following directory: +`$GOPATH/src/github.com/uber/tchannel-go/examples/keyvalue` + +You should use your own path and update your import paths accordingly. + +## Thrift service + +Create a [Thrift](https://thrift.apache.org/) file to define your service. For this guide, we'll use: + +`keyvalue.thrift`: +```thrift +service baseService { + string HealthCheck() +} + +exception KeyNotFound { + 1: string key +} + +exception InvalidKey {} + +service KeyValue extends baseService { + // If the key does not start with a letter, InvalidKey is returned. + // If the key does not exist, KeyNotFound is returned. + string Get(1: string key) throws ( + 1: KeyNotFound notFound + 2: InvalidKey invalidKey) + + // Set returns InvalidKey is an invalid key is sent. + void Set(1: string key, 2: string value) +} + +// Returned when the user is not authorized for the Admin service. +exception NotAuthorized {} + +service Admin extends baseService { + void clearAll() throws (1: NotAuthorized notAuthorized) +} +``` + +This Thrift specification defines two services: + * `KeyValue`: A simple string key-value store. + * `Admin`: Management for the key-value store. + +Both of these services inherit `baseService` and so inherit `HealthCheck`. + +The methods may return exceptions instead of the expected result, which are +also defined in the specification. + +Once you have defined your service, you should generate the Thrift service and +client libraries by running the following: + +```bash +cd $GOPATH/src/github.com/uber/tchannel-go/examples/keyvalue +thrift-gen --generateThrift --inputFile keyvalue.thrift +``` + +This runs the Thrift compiler, and then generates the service and client bindings. +You can run the commands manually as well: + +```bash +# Generate serialization/deserialization logic. +thrift -r --gen go:thrift_import=github.com/apache/thrift/lib/go/thrift keyvalue.thrift + +# Generate TChannel service interfaces in the same directory where Thrift generates code. +thrift-gen --inputFile "$THRIFTFILE" --outputFile "THRIFT_FILE_FOLDER/gen-go/thriftName/tchan-keyvalue.go" +``` + +## Go server + +To get the server ready, the following needs to be done: + +1. Create the TChannel which is the network layer protocol. +2. Create a handler to handle the methods defined in the Thrift definition, and register it with tchannel/thrift. +3. Create a Hyperbahn client and advertise your service with Hyperbahn. + +### Create a TChannel +Create a channel using [tchannel.NewChannel](http://godoc.org/github.com/uber/tchannel-go#NewChannel) and listen using [Channel.ListenAndServe](http://godoc.org/github.com/uber/tchannel-go#Channel.ListenAndServe). + +The address passed to Listen should be a remote IP that can be used for incoming connections from other machines. You can use [tchannel.ListenIP](http://godoc.org/github.com/uber/tchannel-go#ListenIP) which uses heuristics to determine a good remote IP. + +When creating a channel, you can pass additional [options](http://godoc.org/github.com/uber/tchannel-go#ChannelOptions). + +### Create and register Thrift handler + +Create a custom type with methods required by the Thrift generated interface. You can examine this interface by looking in `gen-go/keyvalue/tchan-keyvalue.go`. For example, the interface for our definition file looks like: +```go +type TChanAdmin interface { + HealthCheck(ctx thrift.Context) (string, error) + ClearAll(ctx thrift.Context) error +} + +type TChanKeyValue interface { + Get(ctx thrift.Context, key string) (string, error) + HealthCheck(ctx thrift.Context) (string, error) + Set(ctx thrift.Context, key string, value string) error +} +``` +Create an instance of your handler type, and then create a [thrift.Server](http://godoc.org/github.com/uber/tchannel-go/thrift#NewServer) and [register](http://godoc.org/github.com/uber/tchannel-go/thrift#Server.Register) your Thrift handler. You can register multiple Thrift services on the same `thrift.Server`. + +Each handler method is run in a new goroutine and so must be thread-safe. +Your handler methods can return two types of errors: + * Errors declared in the Thrift file (e.g. `KeyNotFound`). + * Unexpected errors. + +If you return an unexpected error, an error frame is sent over Thrift with the message. If there are known error cases, it is better to declare them in the Thrift file and return those explicitly, e.g.: + +```go + if value, ok := map[key]; ok { + return value, "" + } + // Return a Thrift exception if the key is not found. + return "", &keyvalue.KeyNotFound{Key: key} +``` + +### Advertise with Hyperbahn + +Create a Hyperbahn client using [hyperbahn.NewClient](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#NewClient) which requires a Hyperbahn configuration object that should be loaded from a configuration file for the current environment. You can also pass more [options](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#ClientOptions) when creating the client. + +Call [Advertise](http://godoc.org/github.com/uber/tchannel-go/hyperbahn#Client.Advertise) to advertise the service with Hyperbahn. + +### Serving + +Your service is now serving over Hyperbahn! You can test this by making a call using [tcurl](https://github.com/uber/tcurl): + +``` +node tcurl.js -p [HYPERBAHN-HOSTPORT] -t [DIR-TO-THRIFT] keyvalue KeyValue::Set -3 '{"key": "hello", "value": "world"}' +node tcurl.js -p [HYPERBAHN-HOSTPORT] -t [DIR-TO-THRIFT] keyvalue KeyValue::Get -3 '{"key": "hello"}' +``` + +Replace `[HYPERBAHN-HOSTPORT]` with the host:port of a Hyperbahn node, and `[DIR-TO-THRIFT]` with the directory where the .thrift file is stored. + +Your service can now be accessed from any language over Hyperbahn + TChannel! + +## Go client + +Note: The client implementation is still in active development. + +To make a client that talks, you need to: + +1. Create a TChannel (or re-use an existing TChannel) +2. Set up Hyperbahn +3. Create a Thrift+TChannel client. +4. Make remote calls using the Thrift client. + +### Create a TChannel + +TChannels are bi-directional and so the client uses the same method as the server code (tchannel.NewChannel) to create a TChannel. You do not need to call ListenAndServe on the channel. Even though the channel does not host a service, a serviceName is required +for TChannel. This serviceName should be unique to identify this client. + +You can use an existing TChannel which hosts a service to make client calls. + +### Set up Hyperbahn + +Similar to the server code, create a new Hyperbahn client using hyperbahn.NewClient. You do not +need to call Advertise, as the client does not have any services to advertise over Hyperbahn. + +If you have already set up an existing client for use with a server, then you do not +need to do anything further. + +### Create a Thrift client + +The Thrift client has two parts: + +1. The `thrift.TChanClient` which is configured to hit a specific Hyperbahn service. +2. A generated client which uses an underlying `thrift.TChanClient` to call methods for a specific Thrift service. + +To create a `thrift.TChanClient`, use `thrift.NewClient`. This client can then be used to create a generated client: +```go +thriftClient := thrift.NewClient(ch, "keyvalue", nil) +client := keyvalue.NewTChanKeyValueClient(thriftClient) +adminClient := keyvalue.NewTChanAdminClient(thriftClient) +``` + +### Make remote calls + +Method calls on the client make remote calls over TChannel. E.g. +```go +err := client.Set(ctx, "hello", "world") +val, err := client.Get(ctx, "hello") +// val = "world" +``` + +You must pass a context when making method calls which passes the deadline, tracing information, and application headers. A simple root context is: +```go +ctx, cancel := thrift.NewContext(time.Second) +``` + +All calls over TChannel are required to have a timeout, and tracing information. NewContext should only be used by edges, all other nodes should pass through the incoming Context. When you pass through a Context, you pass along the deadline, tracing information, and the headers. + +Note: Trace spans are automatically generated by TChannel, and the parent is set automatically from the current context's tracing span. + +## Headers + +Thrift + TChannel allows clients to send headers (a list of string key/value pairs) and servers can add response headers to any response. + +In Go, headers are attached to a context +before a call is made using [WithHeaders](http://godoc.org/github.com/uber/tchannel-go/thrift#WithHeaders): +```go +headers := map[string]string{"user": "prashant"} + +ctx, cancel := thrift.NewContext(time.Second) +ctx = thrift.WithHeaders(ctx) +``` + +The server can read these headers using [Headers](http://godoc.org/github.com/uber/tchannel-go/thrift#Context) and can set additional response headers using `SetResponseHeaders`: +```go +func (h *kvHandler) ClearAll(ctx thrift.Context) { + headers := ctx.Headers() + // Application logic + respHeaders := map[string]string{ + "count": 10, + } + ctx.SetResponseHeaders(respHeaders) +} +``` + +The client can read the response headers by calling `ctx.ResponseHeaders()` on the same context that was passed when making the call: + +```go +ctx := thrift.WithHeaders(thrift.NewContext(time.Second), headers) +err := adminClient.ClearAll() +// check error +responseHeaders := ctx.ResponseHeaders() +``` + +Headers should not be used to pass arguments to the method - the Thrift request/response structs should be used for this. + +## Limitations & Upcoming Changes + +TChannel's peer selection does not yet have a detailed health model for nodes, and selection +does not balance load across nodes. + +The thrift-gen autogenerated code is new, and may not support all Thrift features (E.g. annotations, includes, multiple files) diff --git a/vendor/src/github.com/uber/tchannel-go/handlers.go b/vendor/src/github.com/uber/tchannel-go/handlers.go new file mode 100644 index 00000000..4682f60b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/handlers.go @@ -0,0 +1,127 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "reflect" + "runtime" + "sync" + + "golang.org/x/net/context" +) + +// A Handler is an object that can be registered with a Channel to process +// incoming calls for a given service and method +type Handler interface { + // Handles an incoming call for service + Handle(ctx context.Context, call *InboundCall) +} + +// A HandlerFunc is an adapter to allow the use of ordinary functions as +// Channel handlers. If f is a function with the appropriate signature, then +// HandlerFunc(f) is a Handler object that calls f. +type HandlerFunc func(ctx context.Context, call *InboundCall) + +// Handle calls f(ctx, call) +func (f HandlerFunc) Handle(ctx context.Context, call *InboundCall) { f(ctx, call) } + +// An ErrorHandlerFunc is an adapter to allow the use of ordinary functions as +// Channel handlers, with error handling convenience. If f is a function with +// the appropriate signature, then ErrorHandlerFunc(f) is a Handler object that +// calls f. +type ErrorHandlerFunc func(ctx context.Context, call *InboundCall) error + +// Handle calls f(ctx, call) +func (f ErrorHandlerFunc) Handle(ctx context.Context, call *InboundCall) { + if err := f(ctx, call); err != nil { + if GetSystemErrorCode(err) == ErrCodeUnexpected { + call.log.WithFields(f.getLogFields()...).WithFields(ErrField(err)).Error("Unexpected handler error") + } + call.Response().SendSystemError(err) + } +} + +func (f ErrorHandlerFunc) getLogFields() LogFields { + ptr := reflect.ValueOf(f).Pointer() + handlerFunc := runtime.FuncForPC(ptr) // can't be nil + fileName, fileLine := handlerFunc.FileLine(ptr) + return LogFields{ + {"handlerFuncName", handlerFunc.Name()}, + {"handlerFuncFileName", fileName}, + {"handlerFuncFileLine", fileLine}, + } +} + +// Manages handlers +type handlerMap struct { + sync.RWMutex + + handlers map[string]Handler +} + +// Registers a handler +func (hmap *handlerMap) register(h Handler, method string) { + hmap.Lock() + defer hmap.Unlock() + + if hmap.handlers == nil { + hmap.handlers = make(map[string]Handler) + } + + hmap.handlers[method] = h +} + +// Finds the handler matching the given service and method. See https://github.com/golang/go/issues/3512 +// for the reason that method is []byte instead of a string +func (hmap *handlerMap) find(method []byte) Handler { + hmap.RLock() + handler := hmap.handlers[string(method)] + hmap.RUnlock() + + return handler +} + +func (hmap *handlerMap) Handle(ctx context.Context, call *InboundCall) { + c := call.conn + h := hmap.find(call.Method()) + if h == nil { + c.log.WithFields( + LogField{"serviceName", call.ServiceName()}, + LogField{"method", call.MethodString()}, + ).Error("Couldn't find handler.") + call.Response().SendSystemError( + NewSystemError(ErrCodeBadRequest, "no handler for service %q and method %q", call.ServiceName(), call.Method())) + return + } + + if c.log.Enabled(LogLevelDebug) { + c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Method(), c.remotePeerInfo) + } + h.Handle(ctx, call) +} + +// channelHandler is a Handler that wraps a Channel and delegates requests +// to SubChannels based on the inbound call's service name. +type channelHandler struct{ ch *Channel } + +func (c channelHandler) Handle(ctx context.Context, call *InboundCall) { + c.ch.GetSubChannel(call.ServiceName()).handler.Handle(ctx, call) +} diff --git a/vendor/src/github.com/uber/tchannel-go/handlers_test.go b/vendor/src/github.com/uber/tchannel-go/handlers_test.go new file mode 100644 index 00000000..938057f3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/handlers_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +type dummyHandler struct{} + +func (dummyHandler) Handle(ctx context.Context, call *InboundCall) {} + +func TestHandlers(t *testing.T) { + const ( + m1 = "m1" + m2 = "m2" + ) + var ( + hmap = &handlerMap{} + + h1 = &dummyHandler{} + h2 = &dummyHandler{} + + m1b = []byte(m1) + m2b = []byte(m2) + ) + + assert.Nil(t, hmap.find(m1b)) + assert.Nil(t, hmap.find(m2b)) + + hmap.register(h1, m1) + assert.Equal(t, h1, hmap.find(m1b)) + assert.Nil(t, hmap.find(m2b)) + + hmap.register(h2, m2) + assert.Equal(t, h1, hmap.find(m1b)) + assert.Equal(t, h2, hmap.find(m2b)) +} diff --git a/vendor/src/github.com/uber/tchannel-go/http/buf.go b/vendor/src/github.com/uber/tchannel-go/http/buf.go new file mode 100644 index 00000000..8e431488 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/http/buf.go @@ -0,0 +1,59 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package http + +import ( + "net/http" + + "github.com/uber/tchannel-go/typed" +) + +func writeHeaders(wb *typed.WriteBuffer, form http.Header) { + numHeadersDeferred := wb.DeferUint16() + numHeaders := uint16(0) + for k, values := range form { + for _, v := range values { + wb.WriteLen16String(k) + wb.WriteLen16String(v) + numHeaders++ + } + } + numHeadersDeferred.Update(numHeaders) +} + +func readHeaders(rb *typed.ReadBuffer, form http.Header) { + numHeaders := rb.ReadUint16() + for i := 0; i < int(numHeaders); i++ { + k := rb.ReadLen16String() + v := rb.ReadLen16String() + form[k] = append(form[k], v) + } +} + +func readVarintString(rb *typed.ReadBuffer) string { + length := rb.ReadUvarint() + return rb.ReadString(int(length)) +} + +func writeVarintString(wb *typed.WriteBuffer, s string) { + wb.WriteUvarint(uint64(len(s))) + wb.WriteString(s) +} diff --git a/vendor/src/github.com/uber/tchannel-go/http/buf_test.go b/vendor/src/github.com/uber/tchannel-go/http/buf_test.go new file mode 100644 index 00000000..67f2736c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/http/buf_test.go @@ -0,0 +1,69 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package http + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/typed" +) + +func TestHeaders(t *testing.T) { + tests := []http.Header{ + {}, + { + "K1": []string{"K1V1", "K1V2", "K1V3"}, + "K2": []string{"K2V2", "K2V2"}, + }, + } + + for _, tt := range tests { + buf := make([]byte, 1000) + wb := typed.NewWriteBuffer(buf) + writeHeaders(wb, tt) + + newHeaders := make(http.Header) + rb := typed.NewReadBuffer(buf) + readHeaders(rb, newHeaders) + assert.Equal(t, tt, newHeaders, "Headers mismatch") + } +} + +func TestVarintString(t *testing.T) { + tests := []string{ + "", + "short string", + testutils.RandString(1000), + } + + for _, tt := range tests { + buf := make([]byte, 2000) + wb := typed.NewWriteBuffer(buf) + writeVarintString(wb, tt) + + rb := typed.NewReadBuffer(buf) + got := readVarintString(rb) + assert.Equal(t, tt, got, "Varint string mismatch") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/http/http_test.go b/vendor/src/github.com/uber/tchannel-go/http/http_test.go new file mode 100644 index 00000000..00b925cc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/http/http_test.go @@ -0,0 +1,251 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package http + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "strings" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func dumpHandler(w http.ResponseWriter, r *http.Request) { + r.URL.Host = "test.local" + r.URL.Scheme = "http" + + // We cannot use httputil.DumpRequestOut as it prints the chunked encoding + // while we only care about the data that the reader would see. + dump := &bytes.Buffer{} + dump.WriteString(r.Method) + dump.WriteString(r.URL.String()) + dump.WriteString("\n") + + dump.WriteString("Headers: ") + dump.WriteString(fmt.Sprint(r.Form)) + dump.WriteString("\n") + + dump.WriteString("Body: ") + io.Copy(dump, r.Body) + dump.WriteString("\n") + + w.Header().Add("My-Header-1", "V1") + w.Header().Add("My-Header-1", "V2") + w.Header().Add("My-Header-2", "V3") + + w.Write([]byte("Dumped request:\n")) + w.Write(dump.Bytes()) +} + +func setupHTTP(t *testing.T, serveMux *http.ServeMux) (string, func()) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err, "net.Listen failed") + + go http.Serve(ln, serveMux) + httpAddr := ln.Addr().String() + return httpAddr, func() { ln.Close() } +} + +func setupTChan(t *testing.T, mux *http.ServeMux) (string, func()) { + ch := testutils.NewServer(t, testutils.NewOpts().SetServiceName("test")) + handler := func(ctx context.Context, call *tchannel.InboundCall) { + req, err := ReadRequest(call) + if !assert.NoError(t, err, "ReadRequest failed") { + return + } + + // Make the HTTP call using the default mux. + writer, finish := ResponseWriter(call.Response()) + mux.ServeHTTP(writer, req) + finish() + } + ch.Register(tchannel.HandlerFunc(handler), "http") + return ch.PeerInfo().HostPort, func() { ch.Close() } +} + +func setupProxy(t *testing.T, tchanAddr string) (string, func()) { + mux := http.NewServeMux() + mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // You get /proxy/host:port/rest/of/the/path + parts := strings.SplitN(r.URL.Path, "/", 4) + r.URL.Host = parts[2] + r.URL.Scheme = "http" + r.URL.Path = parts[3] + + ch := testutils.NewClient(t, nil) + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + + call, err := ch.BeginCall(ctx, tchanAddr, "test", "http", nil) + require.NoError(t, err, "BeginCall failed") + + require.NoError(t, WriteRequest(call, r), "WriteRequest failed") + resp, err := ReadResponse(call.Response()) + require.NoError(t, err, "Read response failed") + + for k, vs := range resp.Header { + for _, v := range vs { + w.Header().Add(k, v) + } + } + w.WriteHeader(resp.StatusCode) + + _, err = io.Copy(w, resp.Body) + assert.NoError(t, err, "io.Copy failed") + err = resp.Body.Close() + assert.NoError(t, err, "Close Response Body failed") + })) + return setupHTTP(t, mux) +} + +// setupServer sets up a HTTP handler and a TChannel handler . +func setupServer(t *testing.T) (string, string, func()) { + mux := http.NewServeMux() + mux.Handle("/", http.HandlerFunc(dumpHandler)) + + httpAddr, httpClose := setupHTTP(t, mux) + tchanAddr, tchanClose := setupTChan(t, mux) + + close := func() { + httpClose() + tchanClose() + } + return httpAddr, tchanAddr, close +} + +func makeHTTPCall(t *testing.T, req *http.Request) *http.Response { + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err, "HTTP request failed") + return resp +} + +func makeTChanCall(t *testing.T, tchanAddr string, req *http.Request) *http.Response { + ch := testutils.NewClient(t, nil) + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + + call, err := ch.BeginCall(ctx, tchanAddr, "test", "http", nil) + require.NoError(t, err, "BeginCall failed") + + require.NoError(t, WriteRequest(call, req), "WriteRequest failed") + resp, err := ReadResponse(call.Response()) + require.NoError(t, err, "Read response failed") + + return resp +} + +func compareResponseBasic(t *testing.T, testName string, resp1, resp2 *http.Response) { + resp1Body, err := ioutil.ReadAll(resp1.Body) + require.NoError(t, err, "Read response failed") + resp2Body, err := ioutil.ReadAll(resp2.Body) + require.NoError(t, err, "Read response failed") + + assert.Equal(t, resp1.Status, resp2.Status, "%v: Response status mismatch", testName) + assert.Equal(t, resp1.StatusCode, resp2.StatusCode, "%v: Response status code mismatch", testName) + assert.Equal(t, string(resp1Body), string(resp2Body), "%v: Response body mismatch", testName) +} + +func compareResponses(t *testing.T, testName string, resp1, resp2 *http.Response) { + resp1Bs, err := httputil.DumpResponse(resp1, true) + require.NoError(t, err, "Dump response") + resp2Bs, err := httputil.DumpResponse(resp2, true) + require.NoError(t, err, "Dump response") + assert.Equal(t, string(resp1Bs), string(resp2Bs), "%v: Response mismatch", testName) +} + +type requestTest struct { + name string + f func(string) *http.Request +} + +func getRequestTests(t *testing.T) []requestTest { + randBytes := testutils.RandBytes(40000) + return []requestTest{ + { + name: "get simple", + f: func(httpAddr string) *http.Request { + req, err := http.NewRequest("GET", fmt.Sprintf("http://%v/this/is/my?req=1&v=2&v&a&a", httpAddr), nil) + require.NoError(t, err, "NewRequest failed") + return req + }, + }, + { + name: "post simple", + f: func(httpAddr string) *http.Request { + body := strings.NewReader("This is a simple POST body") + req, err := http.NewRequest("POST", fmt.Sprintf("http://%v/post/path?v=1&b=3", httpAddr), body) + require.NoError(t, err, "NewRequest failed") + return req + }, + }, + { + name: "post random bytes", + f: func(httpAddr string) *http.Request { + body := bytes.NewReader(randBytes) + req, err := http.NewRequest("POST", fmt.Sprintf("http://%v/post/path?v=1&b=3", httpAddr), body) + require.NoError(t, err, "NewRequest failed") + return req + }, + }, + } +} + +func TestDirectRequests(t *testing.T) { + httpAddr, tchanAddr, finish := setupServer(t) + defer finish() + + tests := getRequestTests(t) + for _, tt := range tests { + resp1 := makeHTTPCall(t, tt.f(httpAddr)) + resp2 := makeTChanCall(t, tchanAddr, tt.f(httpAddr)) + compareResponseBasic(t, tt.name, resp1, resp2) + } +} + +func TestProxyRequests(t *testing.T) { + httpAddr, tchanAddr, finish := setupServer(t) + defer finish() + proxyAddr, finish := setupProxy(t, tchanAddr) + defer finish() + + tests := getRequestTests(t) + for _, tt := range tests { + resp1 := makeHTTPCall(t, tt.f(httpAddr)) + resp2 := makeHTTPCall(t, tt.f(proxyAddr+"/proxy/"+httpAddr)) + + // Delete the Date header since the calls are made at different times. + resp1.Header.Del("Date") + resp2.Header.Del("Date") + compareResponses(t, tt.name, resp1, resp2) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/http/request.go b/vendor/src/github.com/uber/tchannel-go/http/request.go new file mode 100644 index 00000000..1ce4ac2a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/http/request.go @@ -0,0 +1,85 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package http + +import ( + "io" + "net/http" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/typed" +) + +// WriteRequest writes a http.Request to the given writers. +func WriteRequest(call tchannel.ArgWritable, req *http.Request) error { + // TODO(prashant): Allow creating write buffers that let you grow the buffer underneath. + wb := typed.NewWriteBufferWithSize(10000) + wb.WriteLen8String(req.Method) + writeVarintString(wb, req.URL.String()) + writeHeaders(wb, req.Header) + + arg2Writer, err := call.Arg2Writer() + if err != nil { + return err + } + if _, err := wb.FlushTo(arg2Writer); err != nil { + return err + } + if err := arg2Writer.Close(); err != nil { + return err + } + + arg3Writer, err := call.Arg3Writer() + if err != nil { + return err + } + + if req.Body != nil { + if _, err = io.Copy(arg3Writer, req.Body); err != nil { + return err + } + } + return arg3Writer.Close() +} + +// ReadRequest reads a http.Request from the given readers. +func ReadRequest(call tchannel.ArgReadable) (*http.Request, error) { + var arg2 []byte + if err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil { + return nil, err + } + rb := typed.NewReadBuffer(arg2) + method := rb.ReadLen8String() + url := readVarintString(rb) + + r, err := http.NewRequest(method, url, nil) + if err != nil { + return nil, err + } + readHeaders(rb, r.Header) + + if err := rb.Err(); err != nil { + return nil, err + } + + r.Body, err = call.Arg3Reader() + return r, err +} diff --git a/vendor/src/github.com/uber/tchannel-go/http/response.go b/vendor/src/github.com/uber/tchannel-go/http/response.go new file mode 100644 index 00000000..1dea92e1 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/http/response.go @@ -0,0 +1,139 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package http + +import ( + "fmt" + "io" + "net/http" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/typed" +) + +// ReadResponse reads a http.Response from the given readers. +func ReadResponse(call tchannel.ArgReadable) (*http.Response, error) { + var arg2 []byte + if err := tchannel.NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil { + return nil, err + } + + rb := typed.NewReadBuffer(arg2) + statusCode := rb.ReadUint16() + message := readVarintString(rb) + + response := &http.Response{ + StatusCode: int(statusCode), + Status: fmt.Sprintf("%v %v", statusCode, message), + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + readHeaders(rb, response.Header) + if err := rb.Err(); err != nil { + return nil, err + } + + arg3Reader, err := call.Arg3Reader() + if err != nil { + return nil, err + } + + response.Body = arg3Reader + return response, nil +} + +type tchanResponseWriter struct { + headers http.Header + statusCode int + response tchannel.ArgWritable + arg3Writer io.WriteCloser + err error +} + +func newTChanResponseWriter(response tchannel.ArgWritable) *tchanResponseWriter { + return &tchanResponseWriter{ + headers: make(http.Header), + statusCode: http.StatusOK, + response: response, + } +} + +func (w *tchanResponseWriter) Header() http.Header { + return w.headers +} + +func (w *tchanResponseWriter) WriteHeader(statusCode int) { + w.statusCode = statusCode +} + +// writeHeaders writes out the HTTP headers as arg2, and creates the arg3 writer. +func (w *tchanResponseWriter) writeHeaders() { + // TODO(prashant): Allow creating write buffers that let you grow the buffer underneath. + wb := typed.NewWriteBufferWithSize(10000) + wb.WriteUint16(uint16(w.statusCode)) + writeVarintString(wb, http.StatusText(w.statusCode)) + writeHeaders(wb, w.headers) + + arg2Writer, err := w.response.Arg2Writer() + if err != nil { + w.err = err + return + } + if _, w.err = wb.FlushTo(arg2Writer); w.err != nil { + return + } + if w.err = arg2Writer.Close(); w.err != nil { + return + } + + w.arg3Writer, w.err = w.response.Arg3Writer() +} + +func (w *tchanResponseWriter) Write(bs []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if w.arg3Writer == nil { + w.writeHeaders() + } + if w.err != nil { + return 0, w.err + } + + return w.arg3Writer.Write(bs) +} + +func (w *tchanResponseWriter) finish() error { + if w.arg3Writer == nil || w.err != nil { + return w.err + } + return w.arg3Writer.Close() +} + +// ResponseWriter returns a http.ResponseWriter that will write to an underlying writer. +// It also returns a function that should be called once the handler has completed. +func ResponseWriter(response tchannel.ArgWritable) (http.ResponseWriter, func() error) { + responseWriter := newTChanResponseWriter(response) + return responseWriter, responseWriter.finish +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/advertise.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/advertise.go new file mode 100644 index 00000000..123ceb2a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/advertise.go @@ -0,0 +1,136 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package hyperbahn + +import ( + "fmt" + "math/rand" + "time" + + "github.com/uber/tchannel-go" +) + +const ( + // maxAdvertiseFailures is the number of consecutive advertise failures after + // which we give up and trigger an OnError event. + maxAdvertiseFailures = 5 + // advertiseInterval is the base time interval between advertisements. + advertiseInterval = 50 * time.Second + // advertiseFuzzInterval is the maximum fuzz period to add to advertiseInterval. + advertiseFuzzInterval = 20 * time.Second + // advertiseRetryInterval is the unfuzzed base duration to wait before retry on the first + // advertise failure. Successive retries will use 2 * previous base duration. + advertiseRetryInterval = 1 * time.Second +) + +// ErrAdvertiseFailed is triggered when advertise fails. +type ErrAdvertiseFailed struct { + // WillRetry is set to true if advertise will be retried. + WillRetry bool + // Cause is the underlying error returned from the advertise call. + Cause error +} + +func (e ErrAdvertiseFailed) Error() string { + return fmt.Sprintf("advertise failed, retry: %v, cause: %v", e.WillRetry, e.Cause) +} + +// fuzzInterval returns a fuzzed version of the interval based on FullJitter as described here: +// http://www.awsarchitectureblog.com/2015/03/backoff.html +func fuzzInterval(interval time.Duration) time.Duration { + return time.Duration(rand.Int63n(int64(interval))) +} + +// fuzzedAdvertiseInterval returns the time to sleep between successful advertisements. +func (c *Client) fuzzedAdvertiseInterval() time.Duration { + return advertiseInterval + fuzzInterval(advertiseFuzzInterval) +} + +// logFailedRegistrationRetry logs either a warning or info depending on the number of +// consecutiveFailures. If consecutiveFailures > maxAdvertiseFailures, then we log a warning. +func (c *Client) logFailedRegistrationRetry(errLogger tchannel.Logger, consecutiveFailures uint) { + logFn := errLogger.Info + if consecutiveFailures > maxAdvertiseFailures { + logFn = errLogger.Warn + } + + logFn("Hyperbahn client registration failed, will retry.") +} + +// advertiseLoop readvertises the service approximately every minute (with some fuzzing). +func (c *Client) advertiseLoop() { + sleepFor := c.fuzzedAdvertiseInterval() + consecutiveFailures := uint(0) + + for { + c.sleep(sleepFor) + if c.IsClosed() { + c.tchan.Logger().Infof("Hyperbahn client closed") + return + } + + if err := c.sendAdvertise(); err != nil { + consecutiveFailures++ + errLogger := c.tchan.Logger().WithFields(tchannel.ErrField(err)) + if consecutiveFailures >= maxAdvertiseFailures && c.opts.FailStrategy == FailStrategyFatal { + c.opts.Handler.OnError(ErrAdvertiseFailed{Cause: err, WillRetry: false}) + errLogger.Fatal("Hyperbahn client registration failed.") + } + + c.logFailedRegistrationRetry(errLogger, consecutiveFailures) + c.opts.Handler.OnError(ErrAdvertiseFailed{Cause: err, WillRetry: true}) + + // Even after many failures, cap backoff. + if consecutiveFailures < maxAdvertiseFailures { + sleepFor = fuzzInterval(advertiseRetryInterval * time.Duration(1<= maxAdvertiseFailures*2 { + close(doneTesting) + r.client.Close() + } + }) + // For the last failure, we assert that the handler was called and + // signal that the test is done. + + r.setAdvertiseSuccess() + require.NoError(t, r.client.Advertise()) + <-r.reqCh + + sleptFor := <-r.sleepArgs + checkAdvertiseInterval(t, sleptFor) + + // Even after maxRegistrationFailures failures to register with + // Hyperbahn, FailStrategyIgnore should keep retrying. + for i := 1; i <= maxAdvertiseFailures*2; i++ { + r.sleepBlock <- struct{}{} + r.setAdvertiseFailure() + <-r.reqCh + + sleptFor := <-r.sleepArgs + + // Make sure that we cap backoff at some reasonable duration, even + // after many retries. + if i <= maxAdvertiseFailures { + checkRetryInterval(t, sleptFor, i) + } else { + checkRetryInterval(t, sleptFor, maxAdvertiseFailures) + } + } + + r.sleepClose() + + // Wait for the handler to be called and the mock expectation to be recorded. + <-doneTesting + }) +} + +func checkAdvertiseInterval(t *testing.T, sleptFor time.Duration) { + assert.True(t, sleptFor >= advertiseInterval, + "advertise interval should be > advertiseInterval") + assert.True(t, sleptFor < advertiseInterval+advertiseFuzzInterval, + "advertise interval should be < advertiseInterval + advertiseFuzzInterval") +} + +func checkRetryInterval(t *testing.T, sleptFor time.Duration, retryNum int) { + maxRetryInterval := advertiseRetryInterval * time.Duration(1<= Event(len(_Event_index)) { + return fmt.Sprintf("Event(%d)", i) + } + return _Event_name[_Event_index[i]:_Event_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/events.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/events.go new file mode 100644 index 00000000..9d76298e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/events.go @@ -0,0 +1,51 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package hyperbahn + +// Event describes different events that Client can trigger. +type Event int + +const ( + // UnknownEvent should never be used. + UnknownEvent Event = iota + // SendAdvertise is triggered when the Hyperbahn client tries to advertise. + SendAdvertise + // Advertised is triggered when the initial advertisement for a service is successful. + Advertised + // Readvertised is triggered on periodic advertisements. + Readvertised +) + +//go:generate stringer -type=Event + +// Handler is the interface for handling Hyperbahn events and errors. +type Handler interface { + // On is called when events are triggered. + On(event Event) + // OnError is called when an error is detected. + OnError(err error) +} + +// nullHandler is the default Handler if nil is passed, so handlers can always be called. +type nullHandler struct{} + +func (nullHandler) On(event Event) {} +func (nullHandler) OnError(err error) {} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/constants.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/constants.go new file mode 100644 index 00000000..807c5d9e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package hyperbahn + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/hyperbahn.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/hyperbahn.go new file mode 100644 index 00000000..7fd1f2cd --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/hyperbahn.go @@ -0,0 +1,528 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package hyperbahn + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Hyperbahn interface { + // Parameters: + // - Query + Discover(query *DiscoveryQuery) (r *DiscoveryResult_, err error) +} + +type HyperbahnClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewHyperbahnClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *HyperbahnClient { + return &HyperbahnClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewHyperbahnClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *HyperbahnClient { + return &HyperbahnClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Query +func (p *HyperbahnClient) Discover(query *DiscoveryQuery) (r *DiscoveryResult_, err error) { + if err = p.sendDiscover(query); err != nil { + return + } + return p.recvDiscover() +} + +func (p *HyperbahnClient) sendDiscover(query *DiscoveryQuery) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("discover", thrift.CALL, p.SeqId); err != nil { + return + } + args := HyperbahnDiscoverArgs{ + Query: query, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *HyperbahnClient) recvDiscover() (value *DiscoveryResult_, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "discover" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "discover failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "discover failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error1 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error2 error + error2, err = error1.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error2 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "discover failed: invalid message type") + return + } + result := HyperbahnDiscoverResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.NoPeersAvailable != nil { + err = result.NoPeersAvailable + return + } else if result.InvalidServiceName != nil { + err = result.InvalidServiceName + return + } + value = result.GetSuccess() + return +} + +type HyperbahnProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Hyperbahn +} + +func (p *HyperbahnProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *HyperbahnProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *HyperbahnProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewHyperbahnProcessor(handler Hyperbahn) *HyperbahnProcessor { + + self3 := &HyperbahnProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self3.processorMap["discover"] = &hyperbahnProcessorDiscover{handler: handler} + return self3 +} + +func (p *HyperbahnProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x4.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x4 + +} + +type hyperbahnProcessorDiscover struct { + handler Hyperbahn +} + +func (p *hyperbahnProcessorDiscover) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := HyperbahnDiscoverArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("discover", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := HyperbahnDiscoverResult{} + var retval *DiscoveryResult_ + var err2 error + if retval, err2 = p.handler.Discover(args.Query); err2 != nil { + switch v := err2.(type) { + case *NoPeersAvailable: + result.NoPeersAvailable = v + case *InvalidServiceName: + result.InvalidServiceName = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing discover: "+err2.Error()) + oprot.WriteMessageBegin("discover", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("discover", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Query +type HyperbahnDiscoverArgs struct { + Query *DiscoveryQuery `thrift:"query,1,required" db:"query" json:"query"` +} + +func NewHyperbahnDiscoverArgs() *HyperbahnDiscoverArgs { + return &HyperbahnDiscoverArgs{} +} + +var HyperbahnDiscoverArgs_Query_DEFAULT *DiscoveryQuery + +func (p *HyperbahnDiscoverArgs) GetQuery() *DiscoveryQuery { + if !p.IsSetQuery() { + return HyperbahnDiscoverArgs_Query_DEFAULT + } + return p.Query +} +func (p *HyperbahnDiscoverArgs) IsSetQuery() bool { + return p.Query != nil +} + +func (p *HyperbahnDiscoverArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetQuery bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetQuery = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetQuery { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Query is not set")) + } + return nil +} + +func (p *HyperbahnDiscoverArgs) ReadField1(iprot thrift.TProtocol) error { + p.Query = &DiscoveryQuery{} + if err := p.Query.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Query), err) + } + return nil +} + +func (p *HyperbahnDiscoverArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("discover_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *HyperbahnDiscoverArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("query", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:query: ", p), err) + } + if err := p.Query.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Query), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:query: ", p), err) + } + return err +} + +func (p *HyperbahnDiscoverArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("HyperbahnDiscoverArgs(%+v)", *p) +} + +// Attributes: +// - Success +// - NoPeersAvailable +// - InvalidServiceName +type HyperbahnDiscoverResult struct { + Success *DiscoveryResult_ `thrift:"success,0" db:"success" json:"success,omitempty"` + NoPeersAvailable *NoPeersAvailable `thrift:"noPeersAvailable,1" db:"noPeersAvailable" json:"noPeersAvailable,omitempty"` + InvalidServiceName *InvalidServiceName `thrift:"invalidServiceName,2" db:"invalidServiceName" json:"invalidServiceName,omitempty"` +} + +func NewHyperbahnDiscoverResult() *HyperbahnDiscoverResult { + return &HyperbahnDiscoverResult{} +} + +var HyperbahnDiscoverResult_Success_DEFAULT *DiscoveryResult_ + +func (p *HyperbahnDiscoverResult) GetSuccess() *DiscoveryResult_ { + if !p.IsSetSuccess() { + return HyperbahnDiscoverResult_Success_DEFAULT + } + return p.Success +} + +var HyperbahnDiscoverResult_NoPeersAvailable_DEFAULT *NoPeersAvailable + +func (p *HyperbahnDiscoverResult) GetNoPeersAvailable() *NoPeersAvailable { + if !p.IsSetNoPeersAvailable() { + return HyperbahnDiscoverResult_NoPeersAvailable_DEFAULT + } + return p.NoPeersAvailable +} + +var HyperbahnDiscoverResult_InvalidServiceName_DEFAULT *InvalidServiceName + +func (p *HyperbahnDiscoverResult) GetInvalidServiceName() *InvalidServiceName { + if !p.IsSetInvalidServiceName() { + return HyperbahnDiscoverResult_InvalidServiceName_DEFAULT + } + return p.InvalidServiceName +} +func (p *HyperbahnDiscoverResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *HyperbahnDiscoverResult) IsSetNoPeersAvailable() bool { + return p.NoPeersAvailable != nil +} + +func (p *HyperbahnDiscoverResult) IsSetInvalidServiceName() bool { + return p.InvalidServiceName != nil +} + +func (p *HyperbahnDiscoverResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *HyperbahnDiscoverResult) ReadField0(iprot thrift.TProtocol) error { + p.Success = &DiscoveryResult_{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *HyperbahnDiscoverResult) ReadField1(iprot thrift.TProtocol) error { + p.NoPeersAvailable = &NoPeersAvailable{} + if err := p.NoPeersAvailable.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.NoPeersAvailable), err) + } + return nil +} + +func (p *HyperbahnDiscoverResult) ReadField2(iprot thrift.TProtocol) error { + p.InvalidServiceName = &InvalidServiceName{} + if err := p.InvalidServiceName.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.InvalidServiceName), err) + } + return nil +} + +func (p *HyperbahnDiscoverResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("discover_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *HyperbahnDiscoverResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *HyperbahnDiscoverResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetNoPeersAvailable() { + if err := oprot.WriteFieldBegin("noPeersAvailable", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:noPeersAvailable: ", p), err) + } + if err := p.NoPeersAvailable.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.NoPeersAvailable), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:noPeersAvailable: ", p), err) + } + } + return err +} + +func (p *HyperbahnDiscoverResult) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetInvalidServiceName() { + if err := oprot.WriteFieldBegin("invalidServiceName", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:invalidServiceName: ", p), err) + } + if err := p.InvalidServiceName.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.InvalidServiceName), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:invalidServiceName: ", p), err) + } + } + return err +} + +func (p *HyperbahnDiscoverResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("HyperbahnDiscoverResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/tchan-hyperbahn.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/tchan-hyperbahn.go new file mode 100644 index 00000000..a21a4f3a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/tchan-hyperbahn.go @@ -0,0 +1,122 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package hyperbahn is generated code used to make or handle TChannel calls using Thrift. +package hyperbahn + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanHyperbahn is the interface that defines the server handler and client interface. +type TChanHyperbahn interface { + Discover(ctx thrift.Context, query *DiscoveryQuery) (*DiscoveryResult_, error) +} + +// Implementation of a client and service handler. + +type tchanHyperbahnClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanHyperbahnInheritedClient(thriftService string, client thrift.TChanClient) *tchanHyperbahnClient { + return &tchanHyperbahnClient{ + thriftService, + client, + } +} + +// NewTChanHyperbahnClient creates a client that can be used to make remote calls. +func NewTChanHyperbahnClient(client thrift.TChanClient) TChanHyperbahn { + return NewTChanHyperbahnInheritedClient("Hyperbahn", client) +} + +func (c *tchanHyperbahnClient) Discover(ctx thrift.Context, query *DiscoveryQuery) (*DiscoveryResult_, error) { + var resp HyperbahnDiscoverResult + args := HyperbahnDiscoverArgs{ + Query: query, + } + success, err := c.client.Call(ctx, c.thriftService, "discover", &args, &resp) + if err == nil && !success { + switch { + case resp.NoPeersAvailable != nil: + err = resp.NoPeersAvailable + case resp.InvalidServiceName != nil: + err = resp.InvalidServiceName + default: + err = fmt.Errorf("received no result or unknown exception for discover") + } + } + + return resp.GetSuccess(), err +} + +type tchanHyperbahnServer struct { + handler TChanHyperbahn +} + +// NewTChanHyperbahnServer wraps a handler for TChanHyperbahn so it can be +// registered with a thrift.Server. +func NewTChanHyperbahnServer(handler TChanHyperbahn) thrift.TChanServer { + return &tchanHyperbahnServer{ + handler, + } +} + +func (s *tchanHyperbahnServer) Service() string { + return "Hyperbahn" +} + +func (s *tchanHyperbahnServer) Methods() []string { + return []string{ + "discover", + } +} + +func (s *tchanHyperbahnServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "discover": + return s.handleDiscover(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanHyperbahnServer) handleDiscover(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req HyperbahnDiscoverArgs + var res HyperbahnDiscoverResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Discover(ctx, req.Query) + + if err != nil { + switch v := err.(type) { + case *NoPeersAvailable: + if v == nil { + return false, nil, fmt.Errorf("Handler for noPeersAvailable returned non-nil error type *NoPeersAvailable but nil value") + } + res.NoPeersAvailable = v + case *InvalidServiceName: + if v == nil { + return false, nil, fmt.Errorf("Handler for invalidServiceName returned non-nil error type *InvalidServiceName but nil value") + } + res.InvalidServiceName = v + default: + return false, nil, err + } + } else { + res.Success = r + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/ttypes.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/ttypes.go new file mode 100644 index 00000000..d6eef808 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn/ttypes.go @@ -0,0 +1,771 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package hyperbahn + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - Message +// - ServiceName +type NoPeersAvailable struct { + Message string `thrift:"message,1,required" db:"message" json:"message"` + ServiceName string `thrift:"serviceName,2,required" db:"serviceName" json:"serviceName"` +} + +func NewNoPeersAvailable() *NoPeersAvailable { + return &NoPeersAvailable{} +} + +func (p *NoPeersAvailable) GetMessage() string { + return p.Message +} + +func (p *NoPeersAvailable) GetServiceName() string { + return p.ServiceName +} +func (p *NoPeersAvailable) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetMessage bool = false + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetMessage = true + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + issetServiceName = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetMessage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Message is not set")) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *NoPeersAvailable) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Message = v + } + return nil +} + +func (p *NoPeersAvailable) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *NoPeersAvailable) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("NoPeersAvailable"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *NoPeersAvailable) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("message", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:message: ", p), err) + } + if err := oprot.WriteString(string(p.Message)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.message (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:message: ", p), err) + } + return err +} + +func (p *NoPeersAvailable) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:serviceName: ", p), err) + } + return err +} + +func (p *NoPeersAvailable) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("NoPeersAvailable(%+v)", *p) +} + +func (p *NoPeersAvailable) Error() string { + return p.String() +} + +// Attributes: +// - Message +// - ServiceName +type InvalidServiceName struct { + Message string `thrift:"message,1,required" db:"message" json:"message"` + ServiceName string `thrift:"serviceName,2,required" db:"serviceName" json:"serviceName"` +} + +func NewInvalidServiceName() *InvalidServiceName { + return &InvalidServiceName{} +} + +func (p *InvalidServiceName) GetMessage() string { + return p.Message +} + +func (p *InvalidServiceName) GetServiceName() string { + return p.ServiceName +} +func (p *InvalidServiceName) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetMessage bool = false + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetMessage = true + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + issetServiceName = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetMessage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Message is not set")) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *InvalidServiceName) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Message = v + } + return nil +} + +func (p *InvalidServiceName) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *InvalidServiceName) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("InvalidServiceName"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *InvalidServiceName) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("message", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:message: ", p), err) + } + if err := oprot.WriteString(string(p.Message)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.message (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:message: ", p), err) + } + return err +} + +func (p *InvalidServiceName) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:serviceName: ", p), err) + } + return err +} + +func (p *InvalidServiceName) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("InvalidServiceName(%+v)", *p) +} + +func (p *InvalidServiceName) Error() string { + return p.String() +} + +// Attributes: +// - ServiceName +type DiscoveryQuery struct { + ServiceName string `thrift:"serviceName,1,required" db:"serviceName" json:"serviceName"` +} + +func NewDiscoveryQuery() *DiscoveryQuery { + return &DiscoveryQuery{} +} + +func (p *DiscoveryQuery) GetServiceName() string { + return p.ServiceName +} +func (p *DiscoveryQuery) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetServiceName = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) + } + return nil +} + +func (p *DiscoveryQuery) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.ServiceName = v + } + return nil +} + +func (p *DiscoveryQuery) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("DiscoveryQuery"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *DiscoveryQuery) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) + } + if err := oprot.WriteString(string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) + } + return err +} + +func (p *DiscoveryQuery) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("DiscoveryQuery(%+v)", *p) +} + +// Attributes: +// - Ipv4 +type IpAddress struct { + Ipv4 *int32 `thrift:"ipv4,1" db:"ipv4" json:"ipv4,omitempty"` +} + +func NewIpAddress() *IpAddress { + return &IpAddress{} +} + +var IpAddress_Ipv4_DEFAULT int32 + +func (p *IpAddress) GetIpv4() int32 { + if !p.IsSetIpv4() { + return IpAddress_Ipv4_DEFAULT + } + return *p.Ipv4 +} +func (p *IpAddress) CountSetFieldsIpAddress() int { + count := 0 + if p.IsSetIpv4() { + count++ + } + return count + +} + +func (p *IpAddress) IsSetIpv4() bool { + return p.Ipv4 != nil +} + +func (p *IpAddress) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *IpAddress) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ipv4 = &v + } + return nil +} + +func (p *IpAddress) Write(oprot thrift.TProtocol) error { + if c := p.CountSetFieldsIpAddress(); c != 1 { + return fmt.Errorf("%T write union: exactly one field must be set (%d set).", p, c) + } + if err := oprot.WriteStructBegin("IpAddress"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *IpAddress) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetIpv4() { + if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) + } + if err := oprot.WriteI32(int32(*p.Ipv4)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) + } + } + return err +} + +func (p *IpAddress) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("IpAddress(%+v)", *p) +} + +// Attributes: +// - IP +// - Port +type ServicePeer struct { + IP *IpAddress `thrift:"ip,1,required" db:"ip" json:"ip"` + Port int32 `thrift:"port,2,required" db:"port" json:"port"` +} + +func NewServicePeer() *ServicePeer { + return &ServicePeer{} +} + +var ServicePeer_IP_DEFAULT *IpAddress + +func (p *ServicePeer) GetIP() *IpAddress { + if !p.IsSetIP() { + return ServicePeer_IP_DEFAULT + } + return p.IP +} + +func (p *ServicePeer) GetPort() int32 { + return p.Port +} +func (p *ServicePeer) IsSetIP() bool { + return p.IP != nil +} + +func (p *ServicePeer) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetIP bool = false + var issetPort bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetIP = true + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + issetPort = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetIP { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field IP is not set")) + } + if !issetPort { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Port is not set")) + } + return nil +} + +func (p *ServicePeer) ReadField1(iprot thrift.TProtocol) error { + p.IP = &IpAddress{} + if err := p.IP.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.IP), err) + } + return nil +} + +func (p *ServicePeer) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Port = v + } + return nil +} + +func (p *ServicePeer) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ServicePeer"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ServicePeer) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ip", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ip: ", p), err) + } + if err := p.IP.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.IP), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ip: ", p), err) + } + return err +} + +func (p *ServicePeer) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("port", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) + } + if err := oprot.WriteI32(int32(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) + } + return err +} + +func (p *ServicePeer) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ServicePeer(%+v)", *p) +} + +// Attributes: +// - Peers +type DiscoveryResult_ struct { + Peers []*ServicePeer `thrift:"peers,1,required" db:"peers" json:"peers"` +} + +func NewDiscoveryResult_() *DiscoveryResult_ { + return &DiscoveryResult_{} +} + +func (p *DiscoveryResult_) GetPeers() []*ServicePeer { + return p.Peers +} +func (p *DiscoveryResult_) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetPeers bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetPeers = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetPeers { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Peers is not set")) + } + return nil +} + +func (p *DiscoveryResult_) ReadField1(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*ServicePeer, 0, size) + p.Peers = tSlice + for i := 0; i < size; i++ { + _elem0 := &ServicePeer{} + if err := _elem0.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Peers = append(p.Peers, _elem0) + } + if err := iprot.ReadListEnd(); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *DiscoveryResult_) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("DiscoveryResult"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *DiscoveryResult_) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("peers", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:peers: ", p), err) + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Peers)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Peers { + if err := v.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:peers: ", p), err) + } + return err +} + +func (p *DiscoveryResult_) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("DiscoveryResult_(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/hyperbahn.thrift b/vendor/src/github.com/uber/tchannel-go/hyperbahn/hyperbahn.thrift new file mode 100644 index 00000000..892fa3cc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/hyperbahn.thrift @@ -0,0 +1,35 @@ +exception NoPeersAvailable { + 1: required string message + 2: required string serviceName +} + +exception InvalidServiceName { + 1: required string message + 2: required string serviceName +} + +struct DiscoveryQuery { + 1: required string serviceName +} + +union IpAddress { + 1: i32 ipv4 +} + +struct ServicePeer { + 1: required IpAddress ip + 2: required i32 port +} + +struct DiscoveryResult { + 1: required list peers +} + +service Hyperbahn { + DiscoveryResult discover( + 1: required DiscoveryQuery query + ) throws ( + 1: NoPeersAvailable noPeersAvailable + 2: InvalidServiceName invalidServiceName + ) +} \ No newline at end of file diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils.go new file mode 100644 index 00000000..8e9b7472 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils.go @@ -0,0 +1,45 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package hyperbahn + +import ( + "net" + "strconv" + + "github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn" +) + +// intToIP4 converts an integer IP representation into a 4-byte net.IP struct +func intToIP4(ip uint32) net.IP { + return net.IP{ + byte(ip >> 24 & 0xff), + byte(ip >> 16 & 0xff), + byte(ip >> 8 & 0xff), + byte(ip & 0xff), + } +} + +// servicePeerToHostPort converts a Hyperbahn ServicePeer into a hostPort string. +func servicePeerToHostPort(peer *hyperbahn.ServicePeer) string { + host := intToIP4(uint32(*peer.IP.Ipv4)).String() + port := strconv.Itoa(int(peer.Port)) + return net.JoinHostPort(host, port) +} diff --git a/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils_test.go b/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils_test.go new file mode 100644 index 00000000..90759a2b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/hyperbahn/utils_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package hyperbahn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIntToIP4(t *testing.T) { + tests := []struct { + ip uint32 + expected string + }{ + { + ip: 0, + expected: "0.0.0.0", + }, + { + ip: 0x01010101, + expected: "1.1.1.1", + }, + { + ip: 0x01030507, + expected: "1.3.5.7", + }, + { + ip: 0xFFFFFFFF, + expected: "255.255.255.255", + }, + } + + for _, tt := range tests { + got := intToIP4(tt.ip).String() + assert.Equal(t, tt.expected, got, "IP %v not converted correctly", tt.ip) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/inbound.go b/vendor/src/github.com/uber/tchannel-go/inbound.go new file mode 100644 index 00000000..87a80260 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/inbound.go @@ -0,0 +1,411 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" +) + +var errInboundRequestAlreadyActive = errors.New("inbound request is already active; possible duplicate client id") + +// handleCallReq handles an incoming call request, registering a message +// exchange to receive further fragments for that call, and dispatching it in +// another goroutine +func (c *Connection) handleCallReq(frame *Frame) bool { + now := c.timeNow() + switch state := c.readState(); state { + case connectionActive: + break + case connectionStartClose, connectionInboundClosed, connectionClosed: + c.SendSystemError(frame.Header.ID, callReqSpan(frame), ErrChannelClosed) + return true + default: + panic(fmt.Errorf("unknown connection state for call req: %v", state)) + } + + callReq := new(callReq) + callReq.id = frame.Header.ID + initialFragment, err := parseInboundFragment(c.opts.FramePool, frame, callReq) + if err != nil { + // TODO(mmihic): Probably want to treat this as a protocol error + c.log.WithFields( + LogField{"header", frame.Header}, + ErrField(err), + ).Error("Couldn't decode initial fragment.") + return true + } + + call := new(InboundCall) + call.conn = c + ctx, cancel := newIncomingContext(call, callReq.TimeToLive) + + if !c.pendingExchangeMethodAdd() { + // Connection is closed, no need to do anything. + return true + } + defer c.pendingExchangeMethodDone() + + mex, err := c.inbound.newExchange(ctx, c.opts.FramePool, callReq.messageType(), frame.Header.ID, mexChannelBufferSize) + if err != nil { + if err == errDuplicateMex { + err = errInboundRequestAlreadyActive + } + c.log.WithFields(LogField{"header", frame.Header}).Error("Couldn't register exchange.") + c.protocolError(frame.Header.ID, errInboundRequestAlreadyActive) + return true + } + + // Close may have been called between the time we checked the state and us creating the exchange. + if c.readState() != connectionActive { + mex.shutdown() + return true + } + + response := new(InboundCallResponse) + response.call = call + response.calledAt = now + response.timeNow = c.timeNow + response.span = c.extractInboundSpan(callReq) + if response.span != nil { + mex.ctx = opentracing.ContextWithSpan(mex.ctx, response.span) + } + response.mex = mex + response.conn = c + response.cancel = cancel + response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()}) + response.contents = newFragmentingWriter(response.log, response, initialFragment.checksumType.New()) + response.headers = transportHeaders{} + response.messageForFragment = func(initial bool) message { + if initial { + callRes := new(callRes) + callRes.Headers = response.headers + callRes.ResponseCode = responseOK + if response.applicationError { + callRes.ResponseCode = responseApplicationError + } + return callRes + } + + return new(callResContinue) + } + + call.mex = mex + call.initialFragment = initialFragment + call.serviceName = string(callReq.Service) + call.headers = callReq.Headers + call.response = response + call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()}) + call.messageForFragment = func(initial bool) message { return new(callReqContinue) } + call.contents = newFragmentingReader(call.log, call) + call.statsReporter = c.statsReporter + call.createStatsTags(c.commonStatsTags) + + response.statsReporter = c.statsReporter + response.commonStatsTags = call.commonStatsTags + + setResponseHeaders(call.headers, response.headers) + go c.dispatchInbound(c.connID, callReq.ID(), call, frame) + return false +} + +// handleCallReqContinue handles the continuation of a call request, forwarding +// it to the request channel for that request, where it can be pulled during +// defragmentation +func (c *Connection) handleCallReqContinue(frame *Frame) bool { + if err := c.inbound.forwardPeerFrame(frame); err != nil { + // If forward fails, it's due to a timeout. We can free this frame. + return true + } + return false +} + +// createStatsTags creates the common stats tags, if they are not already created. +func (call *InboundCall) createStatsTags(connectionTags map[string]string) { + call.commonStatsTags = map[string]string{ + "calling-service": call.CallerName(), + } + for k, v := range connectionTags { + call.commonStatsTags[k] = v + } +} + +// dispatchInbound ispatches an inbound call to the appropriate handler +func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall, frame *Frame) { + if call.log.Enabled(LogLevelDebug) { + call.log.Debugf("Received incoming call for %s from %s", call.ServiceName(), c.remotePeerInfo) + } + + if err := call.readMethod(); err != nil { + call.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + ErrField(err), + ).Error("Couldn't read method.") + c.opts.FramePool.Release(frame) + return + } + + call.commonStatsTags["endpoint"] = call.methodString + call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1) + if span := call.response.span; span != nil { + span.SetOperationName(call.methodString) + } + + // TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts. + go func() { + select { + case <-call.mex.ctx.Done(): + // checking if message exchange timedout or was cancelled + // only two possible errors at this step: + // context.DeadlineExceeded + // context.Canceled + if call.mex.ctx.Err() != nil { + call.mex.inboundExpired() + } + case <-call.mex.errCh.c: + if c.log.Enabled(LogLevelDebug) { + call.log.Debugf("Wait for timeout/cancellation interrupted by error: %v", call.mex.errCh.err) + } + // when an exchange errors out, mark the exchange as expired + // and call cancel so the server handler's context is canceled + // TODO: move the cancel to the parent context at connnection level + call.response.cancel() + call.mex.inboundExpired() + } + }() + + c.handler.Handle(call.mex.ctx, call) +} + +// An InboundCall is an incoming call from a peer +type InboundCall struct { + reqResReader + + conn *Connection + response *InboundCallResponse + serviceName string + method []byte + methodString string + headers transportHeaders + statsReporter StatsReporter + commonStatsTags map[string]string +} + +// ServiceName returns the name of the service being called +func (call *InboundCall) ServiceName() string { + return call.serviceName +} + +// Method returns the method being called +func (call *InboundCall) Method() []byte { + return call.method +} + +// MethodString returns the method being called as a string. +func (call *InboundCall) MethodString() string { + return call.methodString +} + +// Format the format of the request from the ArgScheme transport header. +func (call *InboundCall) Format() Format { + return Format(call.headers[ArgScheme]) +} + +// CallerName returns the caller name from the CallerName transport header. +func (call *InboundCall) CallerName() string { + return call.headers[CallerName] +} + +// ShardKey returns the shard key from the ShardKey transport header. +func (call *InboundCall) ShardKey() string { + return call.headers[ShardKey] +} + +// RoutingKey returns the routing key from the RoutingKey transport header. +func (call *InboundCall) RoutingKey() string { + return call.headers[RoutingKey] +} + +// RoutingDelegate returns the routing delegate from the RoutingDelegate transport header. +func (call *InboundCall) RoutingDelegate() string { + return call.headers[RoutingDelegate] +} + +// LocalPeer returns the local peer information for this call. +func (call *InboundCall) LocalPeer() LocalPeerInfo { + return call.conn.localPeerInfo +} + +// RemotePeer returns the remote peer information for this call. +func (call *InboundCall) RemotePeer() PeerInfo { + return call.conn.RemotePeerInfo() +} + +// CallOptions returns a CallOptions struct suitable for forwarding a request. +func (call *InboundCall) CallOptions() *CallOptions { + return &CallOptions{ + callerName: call.CallerName(), + Format: call.Format(), + ShardKey: call.ShardKey(), + RoutingDelegate: call.RoutingDelegate(), + RoutingKey: call.RoutingKey(), + } +} + +// Reads the entire method name (arg1) from the request stream. +func (call *InboundCall) readMethod() error { + var arg1 []byte + if err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil { + return call.failed(err) + } + + call.method = arg1 + call.methodString = string(arg1) + return nil +} + +// Arg2Reader returns an ArgReader to read the second argument. +// The ReadCloser must be closed once the argument has been read. +func (call *InboundCall) Arg2Reader() (ArgReader, error) { + return call.arg2Reader() +} + +// Arg3Reader returns an ArgReader to read the last argument. +// The ReadCloser must be closed once the argument has been read. +func (call *InboundCall) Arg3Reader() (ArgReader, error) { + return call.arg3Reader() +} + +// Response provides access to the InboundCallResponse object which can be used +// to write back to the calling peer +func (call *InboundCall) Response() *InboundCallResponse { + if call.err != nil { + // While reading Thrift, we cannot distinguish between malformed Thrift and other errors, + // and so we may try to respond with a bad request. We should ensure that the response + // is marked as failed if the request has failed so that we don't try to shutdown the exchange + // a second time. + call.response.err = call.err + } + return call.response +} + +func (call *InboundCall) doneReading(unexpected error) {} + +// An InboundCallResponse is used to send the response back to the calling peer +type InboundCallResponse struct { + reqResWriter + + call *InboundCall + cancel context.CancelFunc + // calledAt is the time the inbound call was routed to the application. + calledAt time.Time + timeNow func() time.Time + applicationError bool + systemError bool + headers transportHeaders + span opentracing.Span + statsReporter StatsReporter + commonStatsTags map[string]string +} + +// SendSystemError returns a system error response to the peer. The call is considered +// complete after this method is called, and no further data can be written. +func (response *InboundCallResponse) SendSystemError(err error) error { + if response.err != nil { + return response.err + } + // Fail all future attempts to read fragments + response.state = reqResWriterComplete + response.systemError = true + response.doneSending() + response.call.releasePreviousFragment() + + span := CurrentSpan(response.mex.ctx) + + return response.conn.SendSystemError(response.mex.msgID, *span, err) +} + +// SetApplicationError marks the response as being an application error. This method can +// only be called before any arguments have been sent to the calling peer. +func (response *InboundCallResponse) SetApplicationError() error { + if response.state > reqResWriterPreArg2 { + return response.failed(errReqResWriterStateMismatch{ + state: response.state, + expectedState: reqResWriterPreArg2, + }) + } + response.applicationError = true + return nil +} + +// Arg2Writer returns a WriteCloser that can be used to write the second argument. +// The returned writer must be closed once the write is complete. +func (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) { + if err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil { + return nil, err + } + return response.arg2Writer() +} + +// Arg3Writer returns a WriteCloser that can be used to write the last argument. +// The returned writer must be closed once the write is complete. +func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) { + return response.arg3Writer() +} + +// doneSending shuts down the message exchange for this call. +// For incoming calls, the last message is sending the call response. +func (response *InboundCallResponse) doneSending() { + // TODO(prashant): Move this to when the message is actually being sent. + now := response.timeNow() + + if span := response.span; span != nil { + if response.applicationError || response.systemError { + ext.Error.Set(span, true) + } + span.FinishWithOptions(opentracing.FinishOptions{FinishTime: now}) + } + + latency := now.Sub(response.calledAt) + response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency) + + if response.systemError { + // TODO(prashant): Report the error code type as per metrics doc and enable. + // response.statsReporter.IncCounter("inbound.calls.system-errors", response.commonStatsTags, 1) + } else if response.applicationError { + response.statsReporter.IncCounter("inbound.calls.app-errors", response.commonStatsTags, 1) + } else { + response.statsReporter.IncCounter("inbound.calls.success", response.commonStatsTags, 1) + } + + // Cancel the context since the response is complete. + response.cancel() + + // The message exchange is still open if there are no errors, call shutdown. + if response.err == nil { + response.mex.shutdown() + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/inbound_test.go b/vendor/src/github.com/uber/tchannel-go/inbound_test.go new file mode 100644 index 00000000..b0a76143 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/inbound_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "strings" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestActiveCallReq(t *testing.T) { + t.Skip("Test skipped due to unreliable way to test for protocol errors") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Note: This test cannot use log verification as the duplicate ID causes a log. + // It does not use a verified server, as it leaks a message exchange due to the + // modification of IDs in the relay. + opts := testutils.NewOpts().DisableLogVerification() + testutils.WithServer(t, opts, func(ch *Channel, hostPort string) { + gotCall := make(chan struct{}) + unblock := make(chan struct{}) + + testutils.RegisterFunc(ch, "blocked", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + gotCall <- struct{}{} + <-unblock + return &raw.Res{}, nil + }) + + relayFunc := func(outgoing bool, frame *Frame) *Frame { + if outgoing && frame.Header.ID == 3 { + frame.Header.ID = 2 + } + return frame + } + + relayHostPort, closeRelay := testutils.FrameRelay(t, hostPort, relayFunc) + defer closeRelay() + + firstComplete := make(chan struct{}) + go func() { + // This call will block until we close unblock. + raw.Call(ctx, ch, relayHostPort, ch.PeerInfo().ServiceName, "blocked", nil, nil) + close(firstComplete) + }() + + // Wait for the first call to be received by the server + <-gotCall + + // Make a new call, which should fail + _, _, _, err := raw.Call(ctx, ch, relayHostPort, ch.PeerInfo().ServiceName, "blocked", nil, nil) + assert.Error(t, err, "Expect error") + assert.True(t, strings.Contains(err.Error(), "already active"), + "expected already active error, got %v", err) + + close(unblock) + <-firstComplete + }) +} + +func TestInboundConnection(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Disable relay since relays hide host:port on outbound calls. + opts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s2 := ts.NewServer(nil) + + ts.RegisterFunc("test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + c, _ := InboundConnection(CurrentCall(ctx)) + assert.Equal(t, s2.PeerInfo().HostPort, c.RemotePeerInfo().HostPort, "Unexpected host port") + return &raw.Res{}, nil + }) + + _, _, _, err := raw.Call(ctx, s2, ts.HostPort(), ts.ServiceName(), "test", nil, nil) + require.NoError(t, err, "Call failed") + }) +} + +func TestInboundConnection_CallOptions(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + testutils.WithTestServer(t, nil, func(server *testutils.TestServer) { + server.RegisterFunc("test", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + assert.Equal(t, "client", CurrentCall(ctx).CallerName(), "Expected caller name to be passed through") + return &raw.Res{}, nil + }) + + backendName := server.ServiceName() + + proxyCh := server.NewServer(&testutils.ChannelOpts{ServiceName: "proxy"}) + defer proxyCh.Close() + + subCh := proxyCh.GetSubChannel(backendName) + subCh.SetHandler(HandlerFunc(func(ctx context.Context, inbound *InboundCall) { + outbound, err := proxyCh.BeginCall(ctx, server.HostPort(), backendName, inbound.MethodString(), inbound.CallOptions()) + require.NoError(t, err, "Create outbound call failed") + arg2, arg3, _, err := raw.WriteArgs(outbound, []byte("hello"), []byte("world")) + require.NoError(t, err, "Write outbound call failed") + require.NoError(t, raw.WriteResponse(inbound.Response(), &raw.Res{ + Arg2: arg2, + Arg3: arg3, + }), "Write response failed") + })) + + clientCh := server.NewClient(&testutils.ChannelOpts{ + ServiceName: "client", + }) + defer clientCh.Close() + + _, _, _, err := raw.Call(ctx, clientCh, proxyCh.PeerInfo().HostPort, backendName, "test", nil, nil) + require.NoError(t, err, "Call through proxy failed") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/incoming_test.go b/vendor/src/github.com/uber/tchannel-go/incoming_test.go new file mode 100644 index 00000000..ea8c2f43 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/incoming_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" +) + +func TestPeersIncomingConnection(t *testing.T) { + newService := func(svcName string) (*Channel, string) { + ch, _, hostPort := NewServer(t, &testutils.ChannelOpts{ServiceName: svcName}) + return ch, hostPort + } + + opts := testutils.NewOpts().NoRelay() + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + doPing := func(ch *Channel) { + ctx, cancel := NewContext(time.Second) + defer cancel() + assert.NoError(t, ch.Ping(ctx, hostPort), "Ping failed") + } + + hyperbahnSC := ch.GetSubChannel("hyperbahn") + ringpopSC := ch.GetSubChannel("ringpop", Isolated) + + hyperbahn, hyperbahnHostPort := newService("hyperbahn") + defer hyperbahn.Close() + ringpop, ringpopHostPort := newService("ringpop") + defer ringpop.Close() + + doPing(hyperbahn) + doPing(ringpop) + + // The root peer list should contain all incoming connections. + rootPeers := ch.RootPeers().Copy() + assert.NotNil(t, rootPeers[hyperbahnHostPort], "missing hyperbahn peer") + assert.NotNil(t, rootPeers[ringpopHostPort], "missing ringpop peer") + + for _, sc := range []Registrar{ch, hyperbahnSC, ringpopSC} { + _, err := sc.Peers().Get(nil) + assert.Equal(t, ErrNoPeers, err, + "incoming connections should not be added to non-root peer list") + } + + // verify number of peers/connections on the client side + serverState := ch.IntrospectState(nil).RootPeers + serverHostPort := ch.PeerInfo().HostPort + + assert.Equal(t, len(serverState), 2, "Incorrect peer count") + for _, client := range []*Channel{ringpop, hyperbahn} { + clientPeerState := client.IntrospectState(nil).RootPeers + clientHostPort := client.PeerInfo().HostPort + assert.Equal(t, len(clientPeerState), 1, "Incorrect peer count") + assert.Equal(t, len(clientPeerState[serverHostPort].OutboundConnections), 1, "Incorrect outbound connection count") + assert.Equal(t, len(clientPeerState[serverHostPort].InboundConnections), 0, "Incorrect inbound connection count") + + assert.Equal(t, len(serverState[clientHostPort].InboundConnections), 1, "Incorrect inbound connection count") + assert.Equal(t, len(serverState[clientHostPort].OutboundConnections), 0, "Incorrect outbound connection count") + } + + // In future when connections send a service name, we should be able to + // check that a new connection containing a service name for an isolated + // subchannel is only added to the isolated subchannels' peers, but all + // other incoming connections are added to the shared peer list. + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/init_test.go b/vendor/src/github.com/uber/tchannel-go/init_test.go new file mode 100644 index 00000000..b7dcd0e9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/init_test.go @@ -0,0 +1,343 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "io" + "net" + "runtime" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func writeMessage(w io.Writer, msg message) error { + f := NewFrame(MaxFramePayloadSize) + if err := f.write(msg); err != nil { + return err + } + return f.WriteOut(w) +} + +func readFrame(r io.Reader) (*Frame, error) { + f := NewFrame(MaxFramePayloadSize) + return f, f.ReadIn(r) +} + +func TestUnexpectedInitReq(t *testing.T) { + tests := []struct { + name string + initMsg message + expectedError errorMessage + }{ + { + name: "bad version", + initMsg: &initReq{initMessage{id: 1, Version: 0x1, initParams: initParams{ + InitParamHostPort: "0.0.0.0:0", + InitParamProcessName: "test", + }}}, + expectedError: errorMessage{ + id: 1, + errCode: ErrCodeProtocol, + }, + }, + { + name: "missing InitParamHostPort", + initMsg: &initReq{initMessage{id: 2, Version: CurrentProtocolVersion, initParams: initParams{ + InitParamProcessName: "test", + }}}, + expectedError: errorMessage{ + id: 2, + errCode: ErrCodeProtocol, + }, + }, + { + name: "missing InitParamProcessName", + initMsg: &initReq{initMessage{id: 3, Version: CurrentProtocolVersion, initParams: initParams{ + InitParamHostPort: "0.0.0.0:0", + }}}, + expectedError: errorMessage{ + id: 3, + errCode: ErrCodeProtocol, + }, + }, + { + name: "unexpected message type", + initMsg: &pingReq{ + id: 1, + }, + expectedError: errorMessage{ + id: 1, + errCode: ErrCodeProtocol, + }, + }, + } + + for _, tt := range tests { + ch, err := NewChannel("test", nil) + require.NoError(t, err) + defer ch.Close() + require.NoError(t, ch.ListenAndServe("127.0.0.1:0")) + hostPort := ch.PeerInfo().HostPort + + conn, err := net.Dial("tcp", hostPort) + require.NoError(t, err) + conn.SetReadDeadline(time.Now().Add(time.Second)) + + if !assert.NoError(t, writeMessage(conn, tt.initMsg), "write to conn failed") { + continue + } + + f, err := readFrame(conn) + if !assert.NoError(t, err, "read frame failed") { + continue + } + assert.Equal(t, messageTypeError, f.Header.messageType) + var errMsg errorMessage + if !assert.NoError(t, f.read(&errMsg), "parse frame to errorMessage") { + continue + } + assert.Equal(t, tt.expectedError.ID(), f.Header.ID, "test %v got bad ID", tt.name) + assert.Equal(t, tt.expectedError.errCode, errMsg.errCode, "test %v got bad code", tt.name) + assert.NoError(t, conn.Close(), "closing connection failed") + } +} + +func TestUnexpectedInitRes(t *testing.T) { + validParams := initParams{ + InitParamHostPort: "0.0.0.0:0", + InitParamProcessName: "tchannel-go.test", + } + tests := []struct { + msg message + errMsg string + }{ + { + msg: &initRes{initMessage{ + id: 1, + Version: CurrentProtocolVersion - 1, + initParams: validParams, + }}, + errMsg: "unsupported protocol version", + }, + { + msg: &initRes{initMessage{ + id: 1, + Version: CurrentProtocolVersion + 1, + initParams: validParams, + }}, + errMsg: "unsupported protocol version", + }, + { + msg: &initRes{initMessage{ + id: 1, + Version: CurrentProtocolVersion, + }}, + errMsg: "header host_port is required", + }, + { + msg: &initRes{initMessage{ + id: 1, + Version: CurrentProtocolVersion, + initParams: initParams{ + InitParamHostPort: "0.0.0.0:0", + }, + }}, + errMsg: "header process_name is required", + }, + } + + for _, tt := range tests { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err, "net.Listen failed") + defer ln.Close() + + done := make(chan struct{}) + go func() { + defer close(done) + ch, err := NewChannel("test", nil) + require.NoError(t, err) + defer ch.Close() + + ctx, cancel := NewContext(time.Second) + defer cancel() + _, err = ch.Peers().GetOrAdd(ln.Addr().String()).GetConnection(ctx) + if !assert.Error(t, err, "Expected GetConnection to fail") { + return + } + + assert.Equal(t, ErrCodeProtocol, GetSystemErrorCode(err), "Unexpected error code, got error: %v", err) + assert.Contains(t, err.Error(), tt.errMsg) + }() + + conn, err := ln.Accept() + require.NoError(t, err, "Failed to accept connection") + + // Read the frame and verify that it's an initReq. + f, err := readFrame(conn) + require.NoError(t, err, "read frame failed") + if !assert.Equal(t, messageTypeInitReq, f.messageType(), "Expected first message to be initReq") { + continue + } + + // Write out the specified initRes wait for the channel to get an error. + assert.NoError(t, writeMessage(conn, tt.msg), "write initRes failed") + <-done + } +} + +func TestHandleInitReqNewVersion(t *testing.T) { + ch, err := NewChannel("test", nil) + require.NoError(t, err) + defer ch.Close() + require.NoError(t, ch.ListenAndServe("127.0.0.1:0")) + hostPort := ch.PeerInfo().HostPort + + conn, err := net.Dial("tcp", hostPort) + require.NoError(t, err) + defer conn.Close() + conn.SetReadDeadline(time.Now().Add(time.Second)) + + initMsg := &initReq{initMessage{id: 1, Version: CurrentProtocolVersion + 3, initParams: initParams{ + InitParamHostPort: "0.0.0.0:0", + InitParamProcessName: "test", + }}} + require.NoError(t, writeMessage(conn, initMsg), "write to conn failed") + + // Verify we get an initRes back with the current protocol version. + f, err := readFrame(conn) + require.NoError(t, err, "expected frame with init res") + + var msg initRes + require.NoError(t, f.read(&msg), "could not read init res from frame") + if assert.Equal(t, messageTypeInitRes, f.Header.messageType, "expected initRes, got %v", f.Header.messageType) { + assert.Equal(t, initRes{ + initMessage: initMessage{ + Version: CurrentProtocolVersion, + initParams: initParams{ + InitParamHostPort: ch.PeerInfo().HostPort, + InitParamProcessName: ch.PeerInfo().ProcessName, + InitParamTChannelLanguage: "go", + InitParamTChannelLanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + InitParamTChannelVersion: VersionInfo, + }, + }, + }, msg, "unexpected init res") + } +} + +// TestHandleInitRes ensures that a Connection is ready to handle messages immediately +// after receiving an InitRes. +func TestHandleInitRes(t *testing.T) { + l := newListener(t) + listenerComplete := make(chan struct{}) + + go func() { + conn, err := l.Accept() + require.NoError(t, err, "l.Accept failed") + + // The connection should be kept open until the test has completed running. + defer conn.Close() + defer func() { listenerComplete <- struct{}{} }() + + f, err := readFrame(conn) + require.NoError(t, err, "readFrame failed") + assert.Equal(t, messageTypeInitReq, f.Header.messageType, "expected initReq message") + + var msg initReq + require.NoError(t, f.read(&msg), "read frame into initMsg failed") + initRes := initRes{msg.initMessage} + initRes.initMessage.id = f.Header.ID + require.NoError(t, writeMessage(conn, &initRes), "write initRes failed") + require.NoError(t, writeMessage(conn, &pingReq{noBodyMsg{}, 10}), "write pingReq failed") + + f, err = readFrame(conn) + require.NoError(t, err, "readFrame failed") + assert.Equal(t, messageTypePingRes, f.Header.messageType, "expected pingRes message") + }() + + ch, err := NewChannel("test-svc", nil) + require.NoError(t, err, "NewChannel failed") + defer ch.Close() + + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, err = ch.Peers().GetOrAdd(l.Addr().String()).GetConnection(ctx) + require.NoError(t, err, "GetConnection failed") + + <-listenerComplete +} + +func TestInitReqGetsError(t *testing.T) { + l := newListener(t) + listenerComplete := make(chan struct{}) + connectionComplete := make(chan struct{}) + go func() { + defer func() { listenerComplete <- struct{}{} }() + conn, err := l.Accept() + require.NoError(t, err, "l.Accept failed") + defer conn.Close() + + f, err := readFrame(conn) + require.NoError(t, err, "readFrame failed") + assert.Equal(t, messageTypeInitReq, f.Header.messageType, "expected initReq message") + err = writeMessage(conn, &errorMessage{ + id: f.Header.ID, + errCode: ErrCodeBadRequest, + message: "invalid host:port", + }) + assert.NoError(t, err, "Failed to write errorMessage") + // Wait till GetConnection returns before closing the connection. + <-connectionComplete + }() + + logOut := &bytes.Buffer{} + ch, err := NewChannel("test-svc", &ChannelOptions{Logger: NewLevelLogger(NewLogger(logOut), LogLevelWarn)}) + require.NoError(t, err, "NewClient failed") + defer ch.Close() + + ctx, cancel := NewContext(time.Second) + defer cancel() + + _, err = ch.Peers().GetOrAdd(l.Addr().String()).GetConnection(ctx) + expectedErr := NewSystemError(ErrCodeBadRequest, "invalid host:port") + assert.Equal(t, expectedErr, err, "Error mismatch") + assert.Contains(t, logOut.String(), + "[E] Failed during connection handshake.", + "Message should be logged") + assert.Contains(t, logOut.String(), + "tchannel error ErrCodeBadRequest: invalid host:port", + "Error should be logged") + close(connectionComplete) + + <-listenerComplete +} + +func newListener(t *testing.T) net.Listener { + l, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err, "Listen failed") + return l +} diff --git a/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty.go b/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty.go new file mode 100644 index 00000000..98240b85 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package argreader + +import ( + "fmt" + "io" + "sync" +) + +var _bufPool = sync.Pool{ + New: func() interface{} { + b := make([]byte, 128) + return &b + }, +} + +// EnsureEmpty ensures that the specified reader is empty. If the reader is +// not empty, it returns an error with the specified stage in the message. +func EnsureEmpty(r io.Reader, stage string) error { + buf := _bufPool.Get().(*[]byte) + defer _bufPool.Put(buf) + + n, err := r.Read(*buf) + if n > 0 { + return fmt.Errorf("found unexpected bytes after %s, found (upto 128 bytes): %x", stage, (*buf)[:n]) + } + if err == io.EOF { + return nil + } + return err +} diff --git a/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty_test.go b/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty_test.go new file mode 100644 index 00000000..123d84c0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/internal/argreader/empty_test.go @@ -0,0 +1,54 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package argreader + +import ( + "bytes" + "testing" + + "github.com/uber/tchannel-go/testutils/testreader" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEnsureEmptySuccess(t *testing.T) { + reader := bytes.NewReader(nil) + err := EnsureEmpty(reader, "success") + require.NoError(t, err, "ensureEmpty should succeed with empty reader") +} + +func TestEnsureEmptyHasBytes(t *testing.T) { + reader := bytes.NewReader([]byte{1, 2, 3}) + err := EnsureEmpty(reader, "T") + require.Error(t, err, "ensureEmpty should fail when there's bytes") + assert.Equal(t, err.Error(), "found unexpected bytes after T, found (upto 128 bytes): 010203") +} + +func TestEnsureEmptyError(t *testing.T) { + control, reader := testreader.ChunkReader() + control <- nil + close(control) + + err := EnsureEmpty(reader, "has bytes") + require.Error(t, err, "ensureEmpty should fail when there's an error") + assert.Equal(t, testreader.ErrUser, err, "Unexpected error") +} diff --git a/vendor/src/github.com/uber/tchannel-go/introspection.go b/vendor/src/github.com/uber/tchannel-go/introspection.go new file mode 100644 index 00000000..18d3448b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/introspection.go @@ -0,0 +1,516 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "encoding/json" + "runtime" + "sort" + "strconv" + "time" + + "golang.org/x/net/context" +) + +// IntrospectionOptions are the options used when introspecting the Channel. +type IntrospectionOptions struct { + // IncludeExchanges will include all the IDs in the message exchanges. + IncludeExchanges bool `json:"includeExchanges"` + + // IncludeEmptyPeers will include peers, even if they have no connections. + IncludeEmptyPeers bool `json:"includeEmptyPeers"` + + // IncludeTombstones will include tombstones when introspecting relays. + IncludeTombstones bool `json:"includeTombstones"` + + // IncludeOtherChannels will include basic information about other channels + // created in the same process as this channel. + IncludeOtherChannels bool `json:"includeOtherChannels"` +} + +// RuntimeVersion includes version information about the runtime and +// the tchannel library. +type RuntimeVersion struct { + GoVersion string `json:"goVersion"` + LibraryVersion string `json:"tchannelVersion"` +} + +// RuntimeState is a snapshot of the runtime state for a channel. +type RuntimeState struct { + ID uint32 `json:"id"` + + // CreatedStack is the stack for how this channel was created. + CreatedStack string `json:"createdStack"` + + // LocalPeer is the local peer information (service name, host-port, etc). + LocalPeer LocalPeerInfo `json:"localPeer"` + + // SubChannels contains information about any subchannels. + SubChannels map[string]SubChannelRuntimeState `json:"subChannels"` + + // RootPeers contains information about all the peers on this channel and their connections. + RootPeers map[string]PeerRuntimeState `json:"rootPeers"` + + // Peers is the list of shared peers for this channel. + Peers []SubPeerScore `json:"peers"` + + // NumConnections is the number of connections stored in the channel. + NumConnections int `json:"numConnections"` + + // Connections is the list of connection IDs in the channel + Connections []uint32 ` json:"connections"` + + // OtherChannels is information about any other channels running in this process. + OtherChannels map[string][]ChannelInfo `json:"otherChannels,omitEmpty"` + + // RuntimeVersion is the version information about the runtime and the library. + RuntimeVersion RuntimeVersion `json:"runtimeVersion"` +} + +// GoRuntimeStateOptions are the options used when getting Go runtime state. +type GoRuntimeStateOptions struct { + // IncludeGoStacks will include all goroutine stacks. + IncludeGoStacks bool `json:"includeGoStacks"` +} + +// ChannelInfo is the state of other channels in the same process. +type ChannelInfo struct { + ID uint32 `json:"id"` + CreatedStack string `json:"createdStack"` + LocalPeer LocalPeerInfo `json:"localPeer"` +} + +// GoRuntimeState is a snapshot of runtime stats from the runtime. +type GoRuntimeState struct { + MemStats runtime.MemStats `json:"memStats"` + NumGoroutines int `json:"numGoRoutines"` + NumCPU int `json:"numCPU"` + NumCGo int64 `json:"numCGo"` + GoStacks []byte `json:"goStacks,omitempty"` +} + +// SubChannelRuntimeState is the runtime state for a subchannel. +type SubChannelRuntimeState struct { + Service string `json:"service"` + Isolated bool `json:"isolated"` + // IsolatedPeers is the list of all isolated peers for this channel. + IsolatedPeers []SubPeerScore `json:"isolatedPeers,omitempty"` + Handler HandlerRuntimeState `json:"handler"` +} + +// HandlerRuntimeState TODO +type HandlerRuntimeState struct { + Type handlerType `json:"type"` + Methods []string `json:"methods,omitempty"` +} + +type handlerType string + +func (h handlerType) String() string { return string(h) } + +const ( + methodHandler handlerType = "methods" + overrideHandler = "overriden" +) + +// SubPeerScore show the runtime state of a peer with score. +type SubPeerScore struct { + HostPort string `json:"hostPort"` + Score uint64 `json:"score"` +} + +// ConnectionRuntimeState is the runtime state for a single connection. +type ConnectionRuntimeState struct { + ID uint32 `json:"id"` + ConnectionState string `json:"connectionState"` + LocalHostPort string `json:"localHostPort"` + RemoteHostPort string `json:"remoteHostPort"` + OutboundHostPort string `json:"outboundHostPort"` + RemotePeer PeerInfo `json:"remotePeer"` + InboundExchange ExchangeSetRuntimeState `json:"inboundExchange"` + OutboundExchange ExchangeSetRuntimeState `json:"outboundExchange"` + Relayer RelayerRuntimeState `json:"relayer"` +} + +// RelayerRuntimeState is the runtime state for a single relayer. +type RelayerRuntimeState struct { + Count int `json:"count"` + InboundItems RelayItemSetState `json:"inboundItems"` + OutboundItems RelayItemSetState `json:"outboundItems"` + MaxTimeout time.Duration `json:"maxTimeout"` +} + +// ExchangeSetRuntimeState is the runtime state for a message exchange set. +type ExchangeSetRuntimeState struct { + Name string `json:"name"` + Count int `json:"count"` + Exchanges map[string]ExchangeRuntimeState `json:"exchanges,omitempty"` +} + +// RelayItemSetState is the runtime state for a list of relay items. +type RelayItemSetState struct { + Name string `json:"name"` + Count int `json:"count"` + Items map[string]RelayItemState `json:"items,omitempty"` +} + +// ExchangeRuntimeState is the runtime state for a single message exchange. +type ExchangeRuntimeState struct { + ID uint32 `json:"id"` + MessageType messageType `json:"messageType"` +} + +// RelayItemState is the runtime state for a single relay item. +type RelayItemState struct { + ID uint32 `json:"id"` + RemapID uint32 `json:"remapID"` + DestinationConnectionID uint32 `json:"destinationConnectionID"` + Tomb bool `json:"tomb"` +} + +// PeerRuntimeState is the runtime state for a single peer. +type PeerRuntimeState struct { + HostPort string `json:"hostPort"` + OutboundConnections []ConnectionRuntimeState `json:"outboundConnections"` + InboundConnections []ConnectionRuntimeState `json:"inboundConnections"` + ChosenCount uint64 `json:"chosenCount"` + SCCount uint32 `json:"scCount"` +} + +// IntrospectState returns the RuntimeState for this channel. +// Note: this is purely for debugging and monitoring, and may slow down your Channel. +func (ch *Channel) IntrospectState(opts *IntrospectionOptions) *RuntimeState { + if opts == nil { + opts = &IntrospectionOptions{} + } + + ch.mutable.RLock() + numConns := len(ch.mutable.conns) + connIDs := make([]uint32, 0, numConns) + for id := range ch.mutable.conns { + connIDs = append(connIDs, id) + } + + ch.mutable.RUnlock() + + return &RuntimeState{ + ID: ch.chID, + CreatedStack: ch.createdStack, + LocalPeer: ch.PeerInfo(), + SubChannels: ch.subChannels.IntrospectState(opts), + RootPeers: ch.RootPeers().IntrospectState(opts), + Peers: ch.Peers().IntrospectList(opts), + NumConnections: numConns, + Connections: connIDs, + OtherChannels: ch.IntrospectOthers(opts), + RuntimeVersion: introspectRuntimeVersion(), + } +} + +// IntrospectOthers returns the ChannelInfo for all other channels in this process. +func (ch *Channel) IntrospectOthers(opts *IntrospectionOptions) map[string][]ChannelInfo { + if !opts.IncludeOtherChannels { + return nil + } + + channelMap.Lock() + defer channelMap.Unlock() + + states := make(map[string][]ChannelInfo) + for svc, channels := range channelMap.existing { + channelInfos := make([]ChannelInfo, 0, len(channels)) + for _, otherChan := range channels { + if ch == otherChan { + continue + } + channelInfos = append(channelInfos, otherChan.ReportInfo(opts)) + } + states[svc] = channelInfos + } + + return states +} + +// ReportInfo returns ChannelInfo for a channel. +func (ch *Channel) ReportInfo(opts *IntrospectionOptions) ChannelInfo { + return ChannelInfo{ + ID: ch.chID, + CreatedStack: ch.createdStack, + LocalPeer: ch.PeerInfo(), + } +} + +type containsPeerList interface { + Copy() map[string]*Peer +} + +func fromPeerList(peers containsPeerList, opts *IntrospectionOptions) map[string]PeerRuntimeState { + m := make(map[string]PeerRuntimeState) + for _, peer := range peers.Copy() { + peerState := peer.IntrospectState(opts) + if len(peerState.InboundConnections)+len(peerState.OutboundConnections) > 0 || opts.IncludeEmptyPeers { + m[peer.HostPort()] = peerState + } + } + return m +} + +// IntrospectState returns the runtime state of the +func (l *RootPeerList) IntrospectState(opts *IntrospectionOptions) map[string]PeerRuntimeState { + return fromPeerList(l, opts) +} + +// IntrospectState returns the runtime state of the subchannels. +func (subChMap *subChannelMap) IntrospectState(opts *IntrospectionOptions) map[string]SubChannelRuntimeState { + m := make(map[string]SubChannelRuntimeState) + subChMap.RLock() + for k, sc := range subChMap.subchannels { + state := SubChannelRuntimeState{ + Service: k, + Isolated: sc.Isolated(), + } + if state.Isolated { + state.IsolatedPeers = sc.Peers().IntrospectList(opts) + } + if hmap, ok := sc.handler.(*handlerMap); ok { + state.Handler.Type = methodHandler + methods := make([]string, 0, len(hmap.handlers)) + for k := range hmap.handlers { + methods = append(methods, k) + } + sort.Strings(methods) + state.Handler.Methods = methods + } else { + state.Handler.Type = overrideHandler + } + m[k] = state + } + subChMap.RUnlock() + return m +} + +func getConnectionRuntimeState(conns []*Connection, opts *IntrospectionOptions) []ConnectionRuntimeState { + connStates := make([]ConnectionRuntimeState, len(conns)) + + for i, conn := range conns { + connStates[i] = conn.IntrospectState(opts) + } + + return connStates +} + +// IntrospectState returns the runtime state for this peer. +func (p *Peer) IntrospectState(opts *IntrospectionOptions) PeerRuntimeState { + p.RLock() + defer p.RUnlock() + + return PeerRuntimeState{ + HostPort: p.hostPort, + InboundConnections: getConnectionRuntimeState(p.inboundConnections, opts), + OutboundConnections: getConnectionRuntimeState(p.outboundConnections, opts), + ChosenCount: p.chosenCount.Load(), + SCCount: p.scCount, + } +} + +// IntrospectState returns the runtime state for this connection. +func (c *Connection) IntrospectState(opts *IntrospectionOptions) ConnectionRuntimeState { + c.stateMut.RLock() + defer c.stateMut.RUnlock() + + state := ConnectionRuntimeState{ + ID: c.connID, + ConnectionState: c.state.String(), + LocalHostPort: c.conn.LocalAddr().String(), + RemoteHostPort: c.conn.RemoteAddr().String(), + OutboundHostPort: c.outboundHP, + RemotePeer: c.remotePeerInfo, + InboundExchange: c.inbound.IntrospectState(opts), + OutboundExchange: c.outbound.IntrospectState(opts), + } + if c.relay != nil { + state.Relayer = c.relay.IntrospectState(opts) + } + return state +} + +// IntrospectState returns the runtime state for this relayer. +func (r *Relayer) IntrospectState(opts *IntrospectionOptions) RelayerRuntimeState { + count := r.inbound.Count() + r.outbound.Count() + return RelayerRuntimeState{ + Count: count, + InboundItems: r.inbound.IntrospectState(opts, "inbound"), + OutboundItems: r.outbound.IntrospectState(opts, "outbound"), + MaxTimeout: r.maxTimeout, + } +} + +// IntrospectState returns the runtime state for this relayItems. +func (ri *relayItems) IntrospectState(opts *IntrospectionOptions, name string) RelayItemSetState { + ri.RLock() + defer ri.RUnlock() + + setState := RelayItemSetState{ + Name: name, + Count: ri.Count(), + } + if opts.IncludeExchanges { + setState.Items = make(map[string]RelayItemState, len(ri.items)) + for k, v := range ri.items { + if !opts.IncludeTombstones && v.tomb { + continue + } + state := RelayItemState{ + ID: k, + RemapID: v.remapID, + DestinationConnectionID: v.destination.conn.connID, + Tomb: v.tomb, + } + setState.Items[strconv.Itoa(int(k))] = state + } + } + + return setState +} + +// IntrospectState returns the runtime state for this messsage exchange set. +func (mexset *messageExchangeSet) IntrospectState(opts *IntrospectionOptions) ExchangeSetRuntimeState { + mexset.RLock() + setState := ExchangeSetRuntimeState{ + Name: mexset.name, + Count: len(mexset.exchanges), + } + + if opts.IncludeExchanges { + setState.Exchanges = make(map[string]ExchangeRuntimeState, len(mexset.exchanges)) + for k, v := range mexset.exchanges { + state := ExchangeRuntimeState{ + ID: k, + MessageType: v.msgType, + } + setState.Exchanges[strconv.Itoa(int(k))] = state + } + } + + mexset.RUnlock() + return setState +} + +func getStacks(all bool) []byte { + var buf []byte + for n := 4096; n < 10*1024*1024; n *= 2 { + buf = make([]byte, n) + stackLen := runtime.Stack(buf, all) + if stackLen < n { + return buf[:stackLen] + } + } + + // return the first 10MB of stacks if we have more than 10MB. + return buf +} +func (ch *Channel) handleIntrospection(arg3 []byte) interface{} { + var opts IntrospectionOptions + json.Unmarshal(arg3, &opts) + return ch.IntrospectState(&opts) +} + +// IntrospectList returns the list of peers (hostport, score) in this peer list. +func (l *PeerList) IntrospectList(opts *IntrospectionOptions) []SubPeerScore { + var peers []SubPeerScore + l.RLock() + for _, ps := range l.peerHeap.peerScores { + peers = append(peers, SubPeerScore{ + HostPort: ps.Peer.hostPort, + Score: ps.score, + }) + } + l.RUnlock() + + return peers +} + +// IntrospectNumConnections returns the number of connections returns the number +// of connections. Note: like other introspection APIs, this is not a stable API. +func (ch *Channel) IntrospectNumConnections() int { + ch.mutable.RLock() + numConns := len(ch.mutable.conns) + ch.mutable.RUnlock() + return numConns +} + +func handleInternalRuntime(arg3 []byte) interface{} { + var opts GoRuntimeStateOptions + json.Unmarshal(arg3, &opts) + + state := GoRuntimeState{ + NumGoroutines: runtime.NumGoroutine(), + NumCPU: runtime.NumCPU(), + NumCGo: runtime.NumCgoCall(), + } + runtime.ReadMemStats(&state.MemStats) + if opts.IncludeGoStacks { + state.GoStacks = getStacks(true /* all */) + } + + return state +} + +func introspectRuntimeVersion() RuntimeVersion { + return RuntimeVersion{ + GoVersion: runtime.Version(), + LibraryVersion: VersionInfo, + } +} + +// registerInternal registers the following internal handlers which return runtime state: +// _gometa_introspect: TChannel internal state. +// _gometa_runtime: Golang runtime stats. +func (ch *Channel) registerInternal() { + endpoints := []struct { + name string + handler func([]byte) interface{} + }{ + {"_gometa_introspect", ch.handleIntrospection}, + {"_gometa_runtime", handleInternalRuntime}, + } + + tchanSC := ch.GetSubChannel("tchannel") + for _, ep := range endpoints { + // We need ep in our closure. + ep := ep + handler := func(ctx context.Context, call *InboundCall) { + var arg2, arg3 []byte + if err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil { + return + } + if err := NewArgReader(call.Arg3Reader()).Read(&arg3); err != nil { + return + } + if err := NewArgWriter(call.Response().Arg2Writer()).Write(nil); err != nil { + return + } + NewArgWriter(call.Response().Arg3Writer()).WriteJSON(ep.handler(arg3)) + } + ch.Register(HandlerFunc(handler), ep.name) + tchanSC.Register(HandlerFunc(handler), ep.name) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/introspection_test.go b/vendor/src/github.com/uber/tchannel-go/introspection_test.go new file mode 100644 index 00000000..f6821c8c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/introspection_test.go @@ -0,0 +1,100 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/json" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Purpose of this test is to ensure introspection doesn't cause any panics +// and we have coverage of the introspection code. +func TestIntrospection(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + client := testutils.NewClient(t, nil) + defer client.Close() + + ctx, cancel := json.NewContext(time.Second) + defer cancel() + + var resp map[string]interface{} + peer := client.Peers().GetOrAdd(ts.HostPort()) + err := json.CallPeer(ctx, peer, ts.ServiceName(), "_gometa_introspect", map[string]interface{}{ + "includeExchanges": true, + "includeEmptyPeers": true, + "includeTombstones": true, + }, &resp) + require.NoError(t, err, "Call _gometa_introspect failed") + + err = json.CallPeer(ctx, peer, ts.ServiceName(), "_gometa_runtime", map[string]interface{}{ + "includeGoStacks": true, + }, &resp) + require.NoError(t, err, "Call _gometa_runtime failed") + + if !ts.HasRelay() { + // Try making the call on the "tchannel" service which is where meta handlers + // are registered. This will only work when we call it directly as the relay + // will not forward the tchannel service. + err = json.CallPeer(ctx, peer, "tchannel", "_gometa_runtime", map[string]interface{}{ + "includeGoStacks": true, + }, &resp) + require.NoError(t, err, "Call _gometa_runtime failed") + } + }) +} + +func TestIntrospectNumConnections(t *testing.T) { + // Disable the relay, since the relay does not maintain a 1:1 mapping betewen + // incoming connections vs outgoing connections. + opts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + assert.Equal(t, 0, ts.Server().IntrospectNumConnections(), "Expected no connection on new server") + + for i := 0; i < 10; i++ { + client := ts.NewClient(nil) + defer client.Close() + + require.NoError(t, client.Ping(ctx, ts.HostPort()), "Ping from new client failed") + assert.Equal(t, 1, client.IntrospectNumConnections(), "Client should have single connection") + assert.Equal(t, i+1, ts.Server().IntrospectNumConnections(), "Incorrect number of server connections") + } + + // Make sure that a closed connection will reduce NumConnections. + client := ts.NewClient(nil) + require.NoError(t, client.Ping(ctx, ts.HostPort()), "Ping from new client failed") + assert.Equal(t, 11, ts.Server().IntrospectNumConnections(), "Number of connections expected to increase") + + client.Close() + require.True(t, testutils.WaitFor(100*time.Millisecond, func() bool { + return ts.Server().IntrospectNumConnections() == 10 + }), "Closed connection did not get removed, num connections is %v", ts.Server().IntrospectNumConnections()) + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/call.go b/vendor/src/github.com/uber/tchannel-go/json/call.go new file mode 100644 index 00000000..65eb8baf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/call.go @@ -0,0 +1,172 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package json + +import ( + "fmt" + + "github.com/uber/tchannel-go" + + "golang.org/x/net/context" +) + +// ErrApplication is an application error which contains the object returned from the other side. +type ErrApplication map[string]interface{} + +func (e ErrApplication) Error() string { + return fmt.Sprintf("JSON call failed: %v", map[string]interface{}(e)) +} + +// Client is used to make JSON calls to other services. +type Client struct { + ch *tchannel.Channel + targetService string + hostPort string +} + +// ClientOptions are options used when creating a client. +type ClientOptions struct { + HostPort string +} + +// NewClient returns a json.Client used to make outbound JSON calls. +func NewClient(ch *tchannel.Channel, targetService string, opts *ClientOptions) *Client { + client := &Client{ + ch: ch, + targetService: targetService, + } + if opts != nil && opts.HostPort != "" { + client.hostPort = opts.HostPort + } + return client +} + +func makeCall(call *tchannel.OutboundCall, headers, arg3In, respHeaders, arg3Out, errorOut interface{}) (bool, string, error) { + if mapHeaders, ok := headers.(map[string]string); ok { + headers = tchannel.InjectOutboundSpan(call.Response(), mapHeaders) + } + if err := tchannel.NewArgWriter(call.Arg2Writer()).WriteJSON(headers); err != nil { + return false, "arg2 write failed", err + } + if err := tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(arg3In); err != nil { + return false, "arg3 write failed", err + } + + // Call Arg2Reader before checking application error. + if err := tchannel.NewArgReader(call.Response().Arg2Reader()).ReadJSON(respHeaders); err != nil { + return false, "arg2 read failed", err + } + + // If this is an error response, read the response into a map and return a jsonCallErr. + if call.Response().ApplicationError() { + if err := tchannel.NewArgReader(call.Response().Arg3Reader()).ReadJSON(errorOut); err != nil { + return false, "arg3 read error failed", err + } + return false, "", nil + } + + if err := tchannel.NewArgReader(call.Response().Arg3Reader()).ReadJSON(arg3Out); err != nil { + return false, "arg3 read failed", err + } + + return true, "", nil +} + +func (c *Client) startCall(ctx context.Context, method string, callOptions *tchannel.CallOptions) (*tchannel.OutboundCall, error) { + if c.hostPort != "" { + return c.ch.BeginCall(ctx, c.hostPort, c.targetService, method, callOptions) + } + + return c.ch.GetSubChannel(c.targetService).BeginCall(ctx, method, callOptions) +} + +// Call makes a JSON call, with retries. +func (c *Client) Call(ctx Context, method string, arg, resp interface{}) error { + var ( + headers = ctx.Headers() + + respHeaders map[string]string + respErr ErrApplication + errAt string + isOK bool + ) + + err := c.ch.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error { + respHeaders, respErr, isOK = nil, nil, false + errAt = "connect" + + call, err := c.startCall(ctx, method, &tchannel.CallOptions{ + Format: tchannel.JSON, + RequestState: rs, + }) + if err != nil { + return err + } + + isOK, errAt, err = makeCall(call, headers, arg, &respHeaders, resp, &respErr) + return err + }) + if err != nil { + // TODO: Don't lose the error type here. + return fmt.Errorf("%s: %v", errAt, err) + } + if !isOK { + return respErr + } + + return nil +} + +// TODO(prashantv): Clean up json.Call* interfaces. +func wrapCall(ctx Context, call *tchannel.OutboundCall, method string, arg, resp interface{}) error { + var respHeaders map[string]string + var respErr ErrApplication + isOK, errAt, err := makeCall(call, ctx.Headers(), arg, &respHeaders, resp, &respErr) + if err != nil { + return fmt.Errorf("%s: %v", errAt, err) + } + if !isOK { + return respErr + } + + ctx.SetResponseHeaders(respHeaders) + return nil +} + +// CallPeer makes a JSON call using the given peer. +func CallPeer(ctx Context, peer *tchannel.Peer, serviceName, method string, arg, resp interface{}) error { + call, err := peer.BeginCall(ctx, serviceName, method, &tchannel.CallOptions{Format: tchannel.JSON}) + if err != nil { + return err + } + + return wrapCall(ctx, call, method, arg, resp) +} + +// CallSC makes a JSON call using the given subchannel. +func CallSC(ctx Context, sc *tchannel.SubChannel, method string, arg, resp interface{}) error { + call, err := sc.BeginCall(ctx, method, &tchannel.CallOptions{Format: tchannel.JSON}) + if err != nil { + return err + } + + return wrapCall(ctx, call, method, arg, resp) +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/context.go b/vendor/src/github.com/uber/tchannel-go/json/context.go new file mode 100644 index 00000000..0788159c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/context.go @@ -0,0 +1,48 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package json + +import ( + "time" + + "github.com/uber/tchannel-go" + + "golang.org/x/net/context" +) + +// Context is a JSON Context which contains request and response headers. +type Context tchannel.ContextWithHeaders + +// NewContext returns a Context that can be used to make JSON calls. +func NewContext(timeout time.Duration) (Context, context.CancelFunc) { + ctx, cancel := tchannel.NewContext(timeout) + return tchannel.WrapWithHeaders(ctx, nil), cancel +} + +// Wrap returns a JSON Context that wraps around a Context. +func Wrap(ctx context.Context) Context { + return tchannel.WrapWithHeaders(ctx, nil) +} + +// WithHeaders returns a Context that can be used to make a call with request headers. +func WithHeaders(ctx context.Context, headers map[string]string) Context { + return tchannel.WrapWithHeaders(ctx, headers) +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/handler.go b/vendor/src/github.com/uber/tchannel-go/json/handler.go new file mode 100644 index 00000000..1b7b2c77 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/handler.go @@ -0,0 +1,177 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package json + +import ( + "fmt" + "reflect" + + "github.com/uber/tchannel-go" + + "github.com/opentracing/opentracing-go" + "golang.org/x/net/context" +) + +var ( + typeOfError = reflect.TypeOf((*error)(nil)).Elem() + typeOfContext = reflect.TypeOf((*Context)(nil)).Elem() +) + +// Handlers is the map from method names to handlers. +type Handlers map[string]interface{} + +// verifyHandler ensures that the given t is a function with the following signature: +// func(json.Context, *ArgType)(*ResType, error) +func verifyHandler(t reflect.Type) error { + if t.NumIn() != 2 || t.NumOut() != 2 { + return fmt.Errorf("handler should be of format func(json.Context, *ArgType) (*ResType, error)") + } + + isStructPtr := func(t reflect.Type) bool { + return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct + } + isMap := func(t reflect.Type) bool { + return t.Kind() == reflect.Map && t.Key().Kind() == reflect.String + } + validateArgRes := func(t reflect.Type, name string) error { + if !isStructPtr(t) && !isMap(t) { + return fmt.Errorf("%v should be a pointer to a struct, or a map[string]interface{}", name) + } + return nil + } + + if t.In(0) != typeOfContext { + return fmt.Errorf("arg0 should be of type json.Context") + } + if err := validateArgRes(t.In(1), "second argument"); err != nil { + return err + } + if err := validateArgRes(t.Out(0), "first return value"); err != nil { + return err + } + if !t.Out(1).AssignableTo(typeOfError) { + return fmt.Errorf("second return value should be an error") + } + + return nil +} + +type handler struct { + handler reflect.Value + argType reflect.Type + isArgMap bool + tracer func() opentracing.Tracer +} + +func toHandler(f interface{}) (*handler, error) { + hV := reflect.ValueOf(f) + if err := verifyHandler(hV.Type()); err != nil { + return nil, err + } + argType := hV.Type().In(1) + return &handler{handler: hV, argType: argType, isArgMap: argType.Kind() == reflect.Map}, nil +} + +// Register registers the specified methods specified as a map from method name to the +// JSON handler function. The handler functions should have the following signature: +// func(context.Context, *ArgType)(*ResType, error) +func Register(registrar tchannel.Registrar, funcs Handlers, onError func(context.Context, error)) error { + handlers := make(map[string]*handler) + + handler := tchannel.HandlerFunc(func(ctx context.Context, call *tchannel.InboundCall) { + h, ok := handlers[string(call.Method())] + if !ok { + onError(ctx, fmt.Errorf("call for unregistered method: %s", call.Method())) + return + } + + if err := h.Handle(ctx, call); err != nil { + onError(ctx, err) + } + }) + + for m, f := range funcs { + h, err := toHandler(f) + if err != nil { + return fmt.Errorf("%v cannot be used as a handler: %v", m, err) + } + h.tracer = func() opentracing.Tracer { + return tchannel.TracerFromRegistrar(registrar) + } + handlers[m] = h + registrar.Register(handler, m) + } + + return nil +} + +// Handle deserializes the JSON arguments and calls the underlying handler. +func (h *handler) Handle(tctx context.Context, call *tchannel.InboundCall) error { + var headers map[string]string + if err := tchannel.NewArgReader(call.Arg2Reader()).ReadJSON(&headers); err != nil { + return fmt.Errorf("arg2 read failed: %v", err) + } + tctx = tchannel.ExtractInboundSpan(tctx, call, headers, h.tracer()) + ctx := WithHeaders(tctx, headers) + + var arg3 reflect.Value + var callArg reflect.Value + if h.isArgMap { + arg3 = reflect.New(h.argType) + // New returns a pointer, but the method accepts the map directly. + callArg = arg3.Elem() + } else { + arg3 = reflect.New(h.argType.Elem()) + callArg = arg3 + } + if err := tchannel.NewArgReader(call.Arg3Reader()).ReadJSON(arg3.Interface()); err != nil { + return fmt.Errorf("arg3 read failed: %v", err) + } + + args := []reflect.Value{reflect.ValueOf(ctx), callArg} + results := h.handler.Call(args) + + res := results[0].Interface() + err := results[1].Interface() + // If an error was returned, we create an error arg3 to respond with. + if err != nil { + // TODO(prashantv): More consistent error handling between json/raw/thrift.. + if serr, ok := err.(tchannel.SystemError); ok { + return call.Response().SendSystemError(serr) + } + + call.Response().SetApplicationError() + // TODO(prashant): Allow client to customize the error in more ways. + res = struct { + Type string `json:"type"` + Message string `json:"message"` + }{ + Type: "error", + Message: err.(error).Error(), + } + } + + if err := tchannel.NewArgWriter(call.Response().Arg2Writer()).WriteJSON(ctx.ResponseHeaders()); err != nil { + return err + } + + return tchannel.NewArgWriter(call.Response().Arg3Writer()).WriteJSON(res) +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/json_test.go b/vendor/src/github.com/uber/tchannel-go/json/json_test.go new file mode 100644 index 00000000..cbaa3389 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/json_test.go @@ -0,0 +1,276 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package json + +import ( + "fmt" + "testing" + "time" + + "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +// ForwardArgs are the arguments specifying who to forward to (and the message to forward). +type ForwardArgs struct { + HeaderVal string + Service string + Method string + NextForward *ForwardArgs +} + +// Res is the final result. +type Res struct { + Result string +} + +type testHandler struct { + calls []string + callers []string + peer *tchannel.Peer + t *testing.T +} + +func (h *testHandler) forward(ctx Context, args *ForwardArgs) (*Res, error) { + headerVal := ctx.Headers()["hdr"] + ctx.SetResponseHeaders(map[string]string{"hdr": headerVal + "-resp"}) + h.calls = append(h.calls, "forward-"+headerVal) + h.callers = append(h.callers, tchannel.CurrentCall(ctx).CallerName()) + + if args.HeaderVal != "" { + ctx = WithHeaders(ctx, map[string]string{"hdr": args.HeaderVal}) + } + res := &Res{} + + if args.Method == "forward" { + if err := CallPeer(ctx, h.peer, args.Service, args.Method, args.NextForward, res); err != nil { + h.t.Errorf("forward->forward Call failed: %v", err) + return nil, err + } + assert.Equal(h.t, map[string]string{"hdr": args.HeaderVal + "-resp"}, ctx.ResponseHeaders()) + return res, nil + } + + if err := CallPeer(ctx, h.peer, args.Service, args.Method, nil, res); err != nil { + h.t.Errorf("forward->%v Call failed: %v", args.Method, err) + return nil, err + } + + return res, nil +} + +func (h *testHandler) leaf(ctx Context, _ *struct{}) (*Res, error) { + headerVal := ctx.Headers()["hdr"] + h.calls = append(h.calls, "leaf-"+headerVal) + h.callers = append(h.callers, tchannel.CurrentCall(ctx).CallerName()) + return &Res{"leaf called!"}, nil +} + +func (h *testHandler) onError(ctx context.Context, err error) { + h.t.Errorf("onError(%v)", err) +} + +func TestForwardChain(t *testing.T) { + servers := map[string]*struct { + channel *tchannel.Channel + handler *testHandler + otherPeer string + }{ + "serv1": {otherPeer: "serv2"}, + "serv2": {otherPeer: "serv3"}, + "serv3": {otherPeer: "serv1"}, + } + + // We want the following call graph: + // serv1.forward + // -> (1) serv2.forward + // -> (2) serv3.forward + // -> (3) serv1.forward + // -> (4) serv2.forward + // .... + // -> (11) serv3.leaf + rootArg := &ForwardArgs{} + curArg := rootArg + for i := 1; i <= 10; i++ { + service := fmt.Sprintf("serv%v", (i%3)+1) + + curArg.Method = "forward" + curArg.HeaderVal = fmt.Sprint(i) + curArg.Service = service + curArg.NextForward = &ForwardArgs{} + + curArg = curArg.NextForward + } + curArg.Service = "serv3" + curArg.HeaderVal = "11" + curArg.Method = "leaf" + + expectedCalls := map[string]struct { + calls []string + callers []string + }{ + "serv1": { + calls: []string{"forward-initial", "forward-3", "forward-6", "forward-9"}, + callers: []string{"serv3", "serv3", "serv3", "serv3"}, + }, + "serv2": { + calls: []string{"forward-1", "forward-4", "forward-7", "forward-10"}, + callers: []string{"serv1", "serv1", "serv1", "serv1"}, + }, + "serv3": { + calls: []string{"forward-2", "forward-5", "forward-8", "leaf-11"}, + callers: []string{"serv2", "serv2", "serv2", "serv2"}, + }, + } + + // Use the above data to setup the test and ensure the calls are made as expected. + for name, s := range servers { + var err error + s.channel, err = tchannel.NewChannel(name, nil) + require.NoError(t, err) + + s.handler = &testHandler{t: t} + require.NoError(t, Register(s.channel, Handlers{ + "forward": s.handler.forward, + "leaf": s.handler.leaf, + }, s.handler.onError)) + + require.NoError(t, s.channel.ListenAndServe("127.0.0.1:0")) + } + for _, s := range servers { + s.handler.peer = s.channel.Peers().Add(servers[s.otherPeer].channel.PeerInfo().HostPort) + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + ctx = WithHeaders(ctx, map[string]string{"hdr": "initial"}) + assert.Nil(t, tchannel.CurrentCall(ctx)) + + sc := servers["serv3"].channel.GetSubChannel("serv1") + resp := &Res{} + if assert.NoError(t, CallSC(ctx, sc, "forward", rootArg, resp)) { + assert.Equal(t, "leaf called!", resp.Result) + for s, expected := range expectedCalls { + assert.Equal(t, expected.calls, servers[s].handler.calls, "wrong calls for %v", s) + assert.Equal(t, expected.callers, servers[s].handler.callers, "wrong callers for %v", s) + } + } +} + +func TestHeadersForwarded(t *testing.T) { + ch, err := tchannel.NewChannel("svc", nil) + require.NoError(t, err) + + handler := &testHandler{t: t} + require.NoError(t, Register(ch, Handlers{ + "forward": handler.forward, + "leaf": handler.leaf, + }, handler.onError)) + assert.NoError(t, ch.ListenAndServe("127.0.0.1:0")) + + rootArg := &ForwardArgs{ + Service: "svc", + Method: "leaf", + HeaderVal: "", + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + ctx = WithHeaders(ctx, map[string]string{"hdr": "copy"}) + + assert.Nil(t, tchannel.CurrentCall(ctx)) + resp := &Res{} + handler.peer = ch.Peers().Add(ch.PeerInfo().HostPort) + if assert.NoError(t, CallPeer(ctx, handler.peer, "svc", "forward", rootArg, resp)) { + // Verify that the header is copied when ctx is not changed. + assert.Equal(t, handler.calls, []string{"forward-copy", "leaf-copy"}) + } +} + +func TestEmptyRequestHeader(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + ch, err := tchannel.NewChannel("server", nil) + require.NoError(t, err) + require.NoError(t, ch.ListenAndServe("127.0.0.1:0")) + + handler := func(ctx Context, _ *struct{}) (*struct{}, error) { + assert.Equal(t, map[string]string(nil), ctx.Headers()) + return nil, nil + } + onError := func(ctx context.Context, err error) { + t.Errorf("onError: %v", err) + } + require.NoError(t, Register(ch, Handlers{"handle": handler}, onError)) + + call, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, "server", "handle", &tchannel.CallOptions{ + Format: tchannel.JSON, + }) + require.NoError(t, err) + + require.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write(nil)) + require.NoError(t, tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(nil)) + + resp := call.Response() + var data interface{} + require.NoError(t, tchannel.NewArgReader(resp.Arg2Reader()).ReadJSON(&data)) + require.NoError(t, tchannel.NewArgReader(resp.Arg3Reader()).ReadJSON(&data)) +} + +func TestMapInputOutput(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + ch, err := tchannel.NewChannel("server", nil) + require.NoError(t, err) + require.NoError(t, ch.ListenAndServe("127.0.0.1:0")) + + handler := func(ctx Context, args map[string]interface{}) (map[string]interface{}, error) { + return args, nil + } + onError := func(ctx context.Context, err error) { + t.Errorf("onError: %v", err) + } + require.NoError(t, Register(ch, Handlers{"handle": handler}, onError)) + + call, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, "server", "handle", &tchannel.CallOptions{ + Format: tchannel.JSON, + }) + require.NoError(t, err) + + arg := map[string]interface{}{ + "v1": "value1", + "v2": 2.0, + "v3": map[string]interface{}{"k": "v", "k2": "v2"}, + } + require.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write(nil)) + require.NoError(t, tchannel.NewArgWriter(call.Arg3Writer()).WriteJSON(arg)) + + resp := call.Response() + var data interface{} + require.NoError(t, tchannel.NewArgReader(resp.Arg2Reader()).ReadJSON(&data)) + require.NoError(t, tchannel.NewArgReader(resp.Arg3Reader()).ReadJSON(&data)) + assert.Equal(t, arg, data.(map[string]interface{}), "result does not match arg") +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/retry_test.go b/vendor/src/github.com/uber/tchannel-go/json/retry_test.go new file mode 100644 index 00000000..a1832268 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/retry_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package json + +import ( + "strings" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRetryJSONCall(t *testing.T) { + ch := testutils.NewServer(t, nil) + ch.Peers().Add(ch.PeerInfo().HostPort) + + count := 0 + handler := func(ctx Context, req map[string]string) (map[string]string, error) { + count++ + if count > 4 { + return req, nil + } + return nil, tchannel.ErrServerBusy + } + Register(ch, Handlers{"test": handler}, nil) + + ctx, cancel := NewContext(time.Second) + defer cancel() + + client := NewClient(ch, ch.ServiceName(), nil) + + var res map[string]string + err := client.Call(ctx, "test", nil, &res) + assert.NoError(t, err, "Call should succeed") + assert.Equal(t, 5, count, "Handler should have been invoked 5 times") +} + +func TestRetryJSONNoConnect(t *testing.T) { + ch := testutils.NewClient(t, nil) + ch.Peers().Add("0.0.0.0:0") + + ctx, cancel := NewContext(time.Second) + defer cancel() + + var res map[string]interface{} + client := NewClient(ch, ch.ServiceName(), nil) + err := client.Call(ctx, "test", nil, &res) + require.Error(t, err, "Call should fail") + assert.True(t, strings.HasPrefix(err.Error(), "connect: "), "Error does not contain expected prefix: %v", err.Error()) +} diff --git a/vendor/src/github.com/uber/tchannel-go/json/tracing_test.go b/vendor/src/github.com/uber/tchannel-go/json/tracing_test.go new file mode 100644 index 00000000..bd230c19 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/json/tracing_test.go @@ -0,0 +1,68 @@ +package json_test + +import ( + "testing" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/json" + + . "github.com/uber/tchannel-go/testutils/testtracing" + "golang.org/x/net/context" +) + +// JSONHandler tests tracing over JSON encoding +type JSONHandler struct { + TraceHandler + t *testing.T +} + +func (h *JSONHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + jctx := json.Wrap(ctx) + response := new(TracingResponse) + peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort) + if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, response); err != nil { + return nil, err + } + return response, nil +} + +func (h *JSONHandler) callJSON(ctx json.Context, req *TracingRequest) (*TracingResponse, error) { + return h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + jctx := ctx.(json.Context) + peer := h.Ch.Peers().GetOrAdd(h.Ch.PeerInfo().HostPort) + childResp := new(TracingResponse) + if err := json.CallPeer(jctx, peer, h.Ch.PeerInfo().ServiceName, "call", req, childResp); err != nil { + return nil, err + } + return childResp, nil + }) +} + +func (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } + +func TestJSONTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.JSON, HeadersSupported: true}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + handler := &JSONHandler{TraceHandler: TraceHandler{Ch: ch}, t: t} + json.Register(ch, json.Handlers{"call": handler.callJSON}, handler.onError) + return handler.firstCall + }, + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: "", ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: "", ExpectedSpanCount: 0}, + }, + Mock: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6}, + }, + Jaeger: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6}, + }, + }, + } + suite.Run(t) +} diff --git a/vendor/src/github.com/uber/tchannel-go/largereq_test.go b/vendor/src/github.com/uber/tchannel-go/largereq_test.go new file mode 100644 index 00000000..7ec32741 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/largereq_test.go @@ -0,0 +1,70 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "bytes" + "log" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/require" +) + +func TestLargeRequest(t *testing.T) { + CheckStress(t) + + const ( + KB = 1024 + MB = 1024 * KB + GB = 1024 * MB + + maxRequestSize = 1 * GB + ) + + WithVerifiedServer(t, nil, func(serverCh *Channel, hostPort string) { + serverCh.Register(raw.Wrap(newTestHandler(t)), "echo") + + for reqSize := 2; reqSize <= maxRequestSize; reqSize *= 2 { + log.Printf("reqSize = %v", reqSize) + arg3 := testutils.RandBytes(reqSize) + arg2 := testutils.RandBytes(reqSize / 2) + + clientCh := testutils.NewClient(t, nil) + ctx, cancel := NewContext(time.Second * 30) + rArg2, rArg3, _, err := raw.Call(ctx, clientCh, hostPort, serverCh.PeerInfo().ServiceName, "echo", arg2, arg3) + require.NoError(t, err, "Call failed") + + if !bytes.Equal(arg2, rArg2) { + t.Errorf("echo arg2 mismatch") + } + if !bytes.Equal(arg3, rArg3) { + t.Errorf("echo arg3 mismatch") + } + cancel() + } + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/localip.go b/vendor/src/github.com/uber/tchannel-go/localip.go new file mode 100644 index 00000000..cda90fc8 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/localip.go @@ -0,0 +1,112 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "net" +) + +// scoreAddr scores how likely the given addr is to be a remote address and returns the +// IP to use when listening. Any address which receives a negative score should not be used. +// Scores are calculated as: +// -1 for any unknown IP addreseses. +// +300 for IPv4 addresses +// +100 for non-local addresses, extra +100 for "up" interaces. +func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) { + var ip net.IP + if netAddr, ok := addr.(*net.IPNet); ok { + ip = netAddr.IP + } else if netIP, ok := addr.(*net.IPAddr); ok { + ip = netIP.IP + } else { + return -1, nil + } + + var score int + if ip.To4() != nil { + score += 300 + } + if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() { + score += 100 + if iface.Flags&net.FlagUp != 0 { + score += 100 + } + } + if isLocalMacAddr(iface.HardwareAddr) { + score -= 50 + } + return score, ip +} + +func listenIP(interfaces []net.Interface) (net.IP, error) { + bestScore := -1 + var bestIP net.IP + // Select the highest scoring IP as the best IP. + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + // Skip this interface if there is an error. + continue + } + + for _, addr := range addrs { + score, ip := scoreAddr(iface, addr) + if score > bestScore { + bestScore = score + bestIP = ip + } + } + } + + if bestScore == -1 { + return nil, errors.New("no addresses to listen on") + } + + return bestIP, nil +} + +// ListenIP returns the IP to bind to in Listen. It tries to find an IP that can be used +// by other machines to reach this machine. +func ListenIP() (net.IP, error) { + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + return listenIP(interfaces) +} + +func mustParseMAC(s string) net.HardwareAddr { + addr, err := net.ParseMAC(s) + if err != nil { + panic(err) + } + return addr +} + +// If the first octet's second least-significant-bit is set, then it's local. +// https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local +func isLocalMacAddr(addr net.HardwareAddr) bool { + if len(addr) == 0 { + return false + } + return addr[0]&2 == 2 +} diff --git a/vendor/src/github.com/uber/tchannel-go/localip_test.go b/vendor/src/github.com/uber/tchannel-go/localip_test.go new file mode 100644 index 00000000..54a8a879 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/localip_test.go @@ -0,0 +1,102 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestScoreAddr(t *testing.T) { + ipv4 := net.ParseIP("10.0.1.2") + ipv6 := net.ParseIP("2001:db8:a0b:12f0::1") + + tests := []struct { + msg string + iface net.Interface + addr net.Addr + want int + wantIP net.IP + }{ + { + msg: "non-local up ipv4 IPNet address", + iface: net.Interface{Flags: net.FlagUp}, + addr: &net.IPNet{IP: ipv4}, + want: 500, + wantIP: ipv4, + }, + { + msg: "non-local up ipv4 IPAddr address", + iface: net.Interface{Flags: net.FlagUp}, + addr: &net.IPAddr{IP: ipv4}, + want: 500, + wantIP: ipv4, + }, + { + msg: "non-local up ipv4 IPAddr address, docker interface", + iface: net.Interface{ + Flags: net.FlagUp, + HardwareAddr: mustParseMAC("02:42:ac:11:56:af"), + }, + addr: &net.IPNet{IP: ipv4}, + want: 450, + wantIP: ipv4, + }, + { + msg: "non-local up ipv4 address, local MAC address", + iface: net.Interface{ + Flags: net.FlagUp, + HardwareAddr: mustParseMAC("02:42:9c:52:fc:86"), + }, + addr: &net.IPNet{IP: ipv4}, + want: 450, + wantIP: ipv4, + }, + { + msg: "non-local down ipv4 address", + iface: net.Interface{}, + addr: &net.IPNet{IP: ipv4}, + want: 400, + wantIP: ipv4, + }, + { + msg: "non-local down ipv6 address", + iface: net.Interface{}, + addr: &net.IPAddr{IP: ipv6}, + want: 100, + wantIP: ipv6, + }, + { + msg: "unknown address type", + iface: net.Interface{}, + addr: &net.UnixAddr{Name: "/tmp/socket"}, + want: -1, + }, + } + + for _, tt := range tests { + gotScore, gotIP := scoreAddr(tt.iface, tt.addr) + assert.Equal(t, tt.want, gotScore, tt.msg) + assert.Equal(t, tt.wantIP, gotIP, tt.msg) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/logger.go b/vendor/src/github.com/uber/tchannel-go/logger.go new file mode 100644 index 00000000..092bffe3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/logger.go @@ -0,0 +1,229 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "io" + "time" +) + +import ( + "os" +) + +// Logger provides an abstract interface for logging from TChannel. +// Applications can provide their own implementation of this interface to adapt +// TChannel logging to whatever logging library they prefer (stdlib log, +// logrus, go-logging, etc). The SimpleLogger adapts to the standard go log +// package. +type Logger interface { + // Enabled returns whether the given level is enabled. + Enabled(level LogLevel) bool + + // Fatal logs a message, then exits with os.Exit(1). + Fatal(msg string) + + // Error logs a message at error priority. + Error(msg string) + + // Warn logs a message at warning priority. + Warn(msg string) + + // Infof logs a message at info priority. + Infof(msg string, args ...interface{}) + + // Info logs a message at info priority. + Info(msg string) + + // Debugf logs a message at debug priority. + Debugf(msg string, args ...interface{}) + + // Debug logs a message at debug priority. + Debug(msg string) + + // Fields returns the fields that this logger contains. + Fields() LogFields + + // WithFields returns a logger with the current logger's fields and fields. + WithFields(fields ...LogField) Logger +} + +// LogField is a single field of additional information passed to the logger. +type LogField struct { + Key string + Value interface{} +} + +// ErrField wraps an error string as a LogField named "error" +func ErrField(err error) LogField { + return LogField{"error", err.Error()} +} + +// LogFields is a list of LogFields used to pass additional information to the logger. +type LogFields []LogField + +// NullLogger is a logger that emits nowhere +var NullLogger Logger = nullLogger{} + +type nullLogger struct { + fields LogFields +} + +func (nullLogger) Enabled(_ LogLevel) bool { return false } +func (nullLogger) Fatal(msg string) { os.Exit(1) } +func (nullLogger) Error(msg string) {} +func (nullLogger) Warn(msg string) {} +func (nullLogger) Infof(msg string, args ...interface{}) {} +func (nullLogger) Info(msg string) {} +func (nullLogger) Debugf(msg string, args ...interface{}) {} +func (nullLogger) Debug(msg string) {} +func (l nullLogger) Fields() LogFields { return l.fields } + +func (l nullLogger) WithFields(fields ...LogField) Logger { + newFields := make([]LogField, len(l.Fields())+len(fields)) + n := copy(newFields, l.Fields()) + copy(newFields[n:], fields) + return nullLogger{newFields} +} + +// SimpleLogger prints logging information to standard out. +var SimpleLogger = NewLogger(os.Stdout) + +type writerLogger struct { + writer io.Writer + fields LogFields +} + +const writerLoggerStamp = "15:04:05.000000" + +// NewLogger returns a Logger that writes to the given writer. +func NewLogger(writer io.Writer, fields ...LogField) Logger { + return &writerLogger{writer, fields} +} + +func (l writerLogger) Fatal(msg string) { + l.printfn("F", msg) + os.Exit(1) +} + +func (l writerLogger) Enabled(_ LogLevel) bool { return true } +func (l writerLogger) Error(msg string) { l.printfn("E", msg) } +func (l writerLogger) Warn(msg string) { l.printfn("W", msg) } +func (l writerLogger) Infof(msg string, args ...interface{}) { l.printfn("I", msg, args...) } +func (l writerLogger) Info(msg string) { l.printfn("I", msg) } +func (l writerLogger) Debugf(msg string, args ...interface{}) { l.printfn("D", msg, args...) } +func (l writerLogger) Debug(msg string) { l.printfn("D", msg) } +func (l writerLogger) printfn(prefix, msg string, args ...interface{}) { + fmt.Fprintf(l.writer, "%s [%s] %s tags: %v\n", time.Now().Format(writerLoggerStamp), prefix, fmt.Sprintf(msg, args...), l.fields) +} + +func (l writerLogger) Fields() LogFields { + return l.fields +} + +func (l writerLogger) WithFields(newFields ...LogField) Logger { + existingFields := l.Fields() + fields := make(LogFields, 0, len(existingFields)+1) + fields = append(fields, existingFields...) + fields = append(fields, newFields...) + return writerLogger{l.writer, fields} +} + +// LogLevel is the level of logging used by LevelLogger. +type LogLevel int + +// The minimum level that will be logged. e.g. LogLevelError only logs errors and fatals. +const ( + LogLevelAll LogLevel = iota + LogLevelDebug + LogLevelInfo + LogLevelWarn + LogLevelError + LogLevelFatal +) + +type levelLogger struct { + logger Logger + level LogLevel +} + +// NewLevelLogger returns a logger that only logs messages with a minimum of level. +func NewLevelLogger(logger Logger, level LogLevel) Logger { + return levelLogger{logger, level} +} + +func (l levelLogger) Enabled(level LogLevel) bool { + return l.level <= level +} + +func (l levelLogger) Fatal(msg string) { + if l.level <= LogLevelFatal { + l.logger.Fatal(msg) + } +} + +func (l levelLogger) Error(msg string) { + if l.level <= LogLevelError { + l.logger.Error(msg) + } +} + +func (l levelLogger) Warn(msg string) { + if l.level <= LogLevelWarn { + l.logger.Warn(msg) + } +} + +func (l levelLogger) Infof(msg string, args ...interface{}) { + if l.level <= LogLevelInfo { + l.logger.Infof(msg, args...) + } +} + +func (l levelLogger) Info(msg string) { + if l.level <= LogLevelInfo { + l.logger.Info(msg) + } +} + +func (l levelLogger) Debugf(msg string, args ...interface{}) { + if l.level <= LogLevelDebug { + l.logger.Debugf(msg, args...) + } +} + +func (l levelLogger) Debug(msg string) { + if l.level <= LogLevelDebug { + l.logger.Debug(msg) + } +} + +func (l levelLogger) Fields() LogFields { + return l.logger.Fields() +} + +func (l levelLogger) WithFields(fields ...LogField) Logger { + return levelLogger{ + logger: l.logger.WithFields(fields...), + level: l.level, + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/logger_test.go b/vendor/src/github.com/uber/tchannel-go/logger_test.go new file mode 100644 index 00000000..285516b7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/logger_test.go @@ -0,0 +1,158 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "bytes" + "errors" + "testing" + + . "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/assert" +) + +func field(k string, v interface{}) LogField { + return LogField{Key: k, Value: v} +} + +func TestErrField(t *testing.T) { + assert.Equal(t, field("error", "foo"), ErrField(errors.New("foo"))) +} + +func TestWriterLogger(t *testing.T) { + var buf bytes.Buffer + var bufLogger = NewLogger(&buf) + + debugf := func(logger Logger, msg string, args ...interface{}) { logger.Debugf(msg, args...) } + infof := func(logger Logger, msg string, args ...interface{}) { logger.Infof(msg, args...) } + + levels := []struct { + levelFunc func(logger Logger, msg string, args ...interface{}) + levelPrefix string + }{ + {debugf, "D"}, + {infof, "I"}, + } + + for _, level := range levels { + tagLogger1 := bufLogger.WithFields(field("key1", "value1")) + tagLogger2 := bufLogger.WithFields(field("key2", "value2"), field("key3", "value3")) + + verifyMsgAndPrefix := func(logger Logger) { + buf.Reset() + level.levelFunc(logger, "mes%v", "sage") + + out := buf.String() + assert.Contains(t, out, "message") + assert.Contains(t, out, "["+level.levelPrefix+"]") + } + + verifyMsgAndPrefix(bufLogger) + + verifyMsgAndPrefix(tagLogger1) + assert.Contains(t, buf.String(), "{key1 value1}") + assert.NotContains(t, buf.String(), "{key2 value2}") + assert.NotContains(t, buf.String(), "{key3 value3}") + + verifyMsgAndPrefix(tagLogger2) + assert.Contains(t, buf.String(), "{key2 value2}") + assert.Contains(t, buf.String(), "{key3 value3}") + assert.NotContains(t, buf.String(), "{key1 value1}") + } +} + +func TestWriterLoggerNoSubstitution(t *testing.T) { + var buf bytes.Buffer + var bufLogger = NewLogger(&buf) + + logDebug := func(logger Logger, msg string) { logger.Debug(msg) } + logInfo := func(logger Logger, msg string) { logger.Info(msg) } + logWarn := func(logger Logger, msg string) { logger.Warn(msg) } + logError := func(logger Logger, msg string) { logger.Error(msg) } + + levels := []struct { + levelFunc func(logger Logger, msg string) + levelPrefix string + }{ + {logDebug, "D"}, + {logInfo, "I"}, + {logWarn, "W"}, + {logError, "E"}, + } + + for _, level := range levels { + tagLogger1 := bufLogger.WithFields(field("key1", "value1")) + tagLogger2 := bufLogger.WithFields(field("key2", "value2"), field("key3", "value3")) + + verifyMsgAndPrefix := func(logger Logger) { + buf.Reset() + level.levelFunc(logger, "test-msg") + + out := buf.String() + assert.Contains(t, out, "test-msg") + assert.Contains(t, out, "["+level.levelPrefix+"]") + } + + verifyMsgAndPrefix(bufLogger) + + verifyMsgAndPrefix(tagLogger1) + assert.Contains(t, buf.String(), "{key1 value1}") + assert.NotContains(t, buf.String(), "{key2 value2}") + assert.NotContains(t, buf.String(), "{key3 value3}") + + verifyMsgAndPrefix(tagLogger2) + assert.Contains(t, buf.String(), "{key2 value2}") + assert.Contains(t, buf.String(), "{key3 value3}") + assert.NotContains(t, buf.String(), "{key1 value1}") + } +} + +func TestLevelLogger(t *testing.T) { + var buf bytes.Buffer + var bufLogger = NewLogger(&buf) + + expectedLines := map[LogLevel]int{ + LogLevelAll: 6, + LogLevelDebug: 6, + LogLevelInfo: 4, + LogLevelWarn: 2, + LogLevelError: 1, + LogLevelFatal: 0, + } + for level := LogLevelFatal; level >= LogLevelAll; level-- { + buf.Reset() + levelLogger := NewLevelLogger(bufLogger, level) + + for l := LogLevel(0); l <= LogLevelFatal; l++ { + assert.Equal(t, level <= l, levelLogger.Enabled(l), "levelLogger.Enabled(%v) at %v", l, level) + } + + levelLogger.Debug("debug") + levelLogger.Debugf("debu%v", "g") + levelLogger.Info("info") + levelLogger.Infof("inf%v", "o") + levelLogger.Warn("warn") + levelLogger.Error("error") + + assert.Equal(t, expectedLines[level], bytes.Count(buf.Bytes(), []byte{'\n'})) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/messages.go b/vendor/src/github.com/uber/tchannel-go/messages.go new file mode 100644 index 00000000..ecdef72f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/messages.go @@ -0,0 +1,336 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "time" + + "github.com/uber/tchannel-go/typed" +) + +// messageType defines a type of message +type messageType byte + +const ( + messageTypeInitReq messageType = 0x01 + messageTypeInitRes messageType = 0x02 + messageTypeCallReq messageType = 0x03 + messageTypeCallRes messageType = 0x04 + messageTypeCallReqContinue messageType = 0x13 + messageTypeCallResContinue messageType = 0x14 + messageTypePingReq messageType = 0xd0 + messageTypePingRes messageType = 0xd1 + messageTypeError messageType = 0xFF +) + +//go:generate stringer -type=messageType + +// message is the base interface for messages. Has an id and type, and knows +// how to read and write onto a binary stream +type message interface { + // ID returns the id of the message + ID() uint32 + + // messageType returns the type of the message + messageType() messageType + + // read reads the message from a binary stream + read(r *typed.ReadBuffer) error + + // write writes the message to a binary stream + write(w *typed.WriteBuffer) error +} + +type noBodyMsg struct{} + +func (noBodyMsg) read(r *typed.ReadBuffer) error { return nil } +func (noBodyMsg) write(w *typed.WriteBuffer) error { return nil } + +// initParams are parameters to an initReq/InitRes +type initParams map[string]string + +const ( + // InitParamHostPort contains the host and port of the peer process + InitParamHostPort = "host_port" + // InitParamProcessName contains the name of the peer process + InitParamProcessName = "process_name" + // InitParamTChannelLanguage contains the library language. + InitParamTChannelLanguage = "tchannel_language" + // InitParamTChannelLanguageVersion contains the language build/runtime version. + InitParamTChannelLanguageVersion = "tchannel_language_version" + // InitParamTChannelVersion contains the library version. + InitParamTChannelVersion = "tchannel_version" +) + +// initMessage is the base for messages in the initialization handshake +type initMessage struct { + id uint32 + Version uint16 + initParams initParams +} + +func (m *initMessage) read(r *typed.ReadBuffer) error { + m.Version = r.ReadUint16() + + m.initParams = initParams{} + np := r.ReadUint16() + for i := 0; i < int(np); i++ { + k := r.ReadLen16String() + v := r.ReadLen16String() + m.initParams[k] = v + } + + return r.Err() +} + +func (m *initMessage) write(w *typed.WriteBuffer) error { + w.WriteUint16(m.Version) + w.WriteUint16(uint16(len(m.initParams))) + + for k, v := range m.initParams { + w.WriteLen16String(k) + w.WriteLen16String(v) + } + + return w.Err() +} + +func (m *initMessage) ID() uint32 { + return m.id +} + +// An initReq contains context information sent from an initiating peer +type initReq struct { + initMessage +} + +func (m *initReq) messageType() messageType { return messageTypeInitReq } + +// An initRes contains context information returned to an initiating peer +type initRes struct { + initMessage +} + +func (m *initRes) messageType() messageType { return messageTypeInitRes } + +// TransportHeaderName is a type for transport header names. +type TransportHeaderName string + +func (cn TransportHeaderName) String() string { return string(cn) } + +// Known transport header keys for call requests. See protocol docs for more information. +const ( + // ArgScheme header specifies the format of the args. + ArgScheme TransportHeaderName = "as" + + // CallerName header specifies the name of the service making the call. + CallerName TransportHeaderName = "cn" + + // ClaimAtFinish header value is host:port specifying the instance to send a claim message + // to when response is being sent. + ClaimAtFinish TransportHeaderName = "caf" + + // ClaimAtStart header value is host:port specifying another instance to send a claim message + // to when work is started. + ClaimAtStart TransportHeaderName = "cas" + + // FailureDomain header describes a group of related requests to the same service that are + // likely to fail in the same way if they were to fail. + FailureDomain TransportHeaderName = "fd" + + // ShardKey header value is used by ringpop to deliver calls to a specific tchannel instance. + ShardKey TransportHeaderName = "sk" + + // RetryFlags header specifies whether retry policies. + RetryFlags TransportHeaderName = "re" + + // SpeculativeExecution header specifies the number of nodes on which to run the request. + SpeculativeExecution TransportHeaderName = "se" + + // RoutingDelegate header identifies an intermediate service which knows + // how to route the request to the intended recipient. + RoutingDelegate TransportHeaderName = "rd" + + // RoutingKey header identifies a traffic group containing instances of the + // requested service. A relay may use the routing key over the service if + // it knows about traffic groups. + RoutingKey TransportHeaderName = "rk" +) + +// transportHeaders are passed as part of a CallReq/CallRes +type transportHeaders map[TransportHeaderName]string + +func (ch transportHeaders) read(r *typed.ReadBuffer) { + nh := r.ReadSingleByte() + for i := 0; i < int(nh); i++ { + k := r.ReadLen8String() + v := r.ReadLen8String() + ch[TransportHeaderName(k)] = v + } +} + +func (ch transportHeaders) write(w *typed.WriteBuffer) { + w.WriteSingleByte(byte(len(ch))) + + for k, v := range ch { + w.WriteLen8String(k.String()) + w.WriteLen8String(v) + } +} + +// A callReq for service +type callReq struct { + id uint32 + TimeToLive time.Duration + Tracing Span + Headers transportHeaders + Service string +} + +func (m *callReq) ID() uint32 { return m.id } +func (m *callReq) messageType() messageType { return messageTypeCallReq } +func (m *callReq) read(r *typed.ReadBuffer) error { + m.TimeToLive = time.Duration(r.ReadUint32()) * time.Millisecond + m.Tracing.read(r) + m.Service = r.ReadLen8String() + m.Headers = transportHeaders{} + m.Headers.read(r) + return r.Err() +} + +func (m *callReq) write(w *typed.WriteBuffer) error { + w.WriteUint32(uint32(m.TimeToLive / time.Millisecond)) + m.Tracing.write(w) + w.WriteLen8String(m.Service) + m.Headers.write(w) + return w.Err() +} + +// A callReqContinue is continuation of a previous callReq +type callReqContinue struct { + noBodyMsg + id uint32 +} + +func (c *callReqContinue) ID() uint32 { return c.id } +func (c *callReqContinue) messageType() messageType { return messageTypeCallReqContinue } + +// ResponseCode to a CallReq +type ResponseCode byte + +const ( + responseOK ResponseCode = 0x00 + responseApplicationError ResponseCode = 0x01 +) + +// callRes is a response to a CallReq +type callRes struct { + id uint32 + ResponseCode ResponseCode + Tracing Span + Headers transportHeaders +} + +func (m *callRes) ID() uint32 { return m.id } +func (m *callRes) messageType() messageType { return messageTypeCallRes } + +func (m *callRes) read(r *typed.ReadBuffer) error { + m.ResponseCode = ResponseCode(r.ReadSingleByte()) + m.Tracing.read(r) + m.Headers = transportHeaders{} + m.Headers.read(r) + return r.Err() +} + +func (m *callRes) write(w *typed.WriteBuffer) error { + w.WriteSingleByte(byte(m.ResponseCode)) + m.Tracing.write(w) + m.Headers.write(w) + return w.Err() +} + +// callResContinue is a continuation of a previous CallRes +type callResContinue struct { + id uint32 +} + +func (c *callResContinue) ID() uint32 { return c.id } +func (c *callResContinue) messageType() messageType { return messageTypeCallResContinue } +func (c *callResContinue) read(r *typed.ReadBuffer) error { return nil } +func (c *callResContinue) write(w *typed.WriteBuffer) error { return nil } + +// An errorMessage is a system-level error response to a request or a protocol level error +type errorMessage struct { + id uint32 + errCode SystemErrCode + tracing Span + message string +} + +func (m *errorMessage) ID() uint32 { return m.id } +func (m *errorMessage) messageType() messageType { return messageTypeError } +func (m *errorMessage) read(r *typed.ReadBuffer) error { + m.errCode = SystemErrCode(r.ReadSingleByte()) + m.tracing.read(r) + m.message = r.ReadLen16String() + return r.Err() +} + +func (m *errorMessage) write(w *typed.WriteBuffer) error { + w.WriteSingleByte(byte(m.errCode)) + m.tracing.write(w) + w.WriteLen16String(m.message) + return w.Err() +} + +func (m errorMessage) AsSystemError() error { + // TODO(mmihic): Might be nice to return one of the well defined error types + return NewSystemError(m.errCode, m.message) +} + +// Error returns the error message from the converted +func (m errorMessage) Error() string { + return m.AsSystemError().Error() +} + +type pingReq struct { + noBodyMsg + id uint32 +} + +func (c *pingReq) ID() uint32 { return c.id } +func (c *pingReq) messageType() messageType { return messageTypePingReq } + +// pingRes is a ping response to a protocol level ping request. +type pingRes struct { + noBodyMsg + id uint32 +} + +func (c *pingRes) ID() uint32 { return c.id } +func (c *pingRes) messageType() messageType { return messageTypePingRes } + +func callReqSpan(f *Frame) Span { + rdr := typed.NewReadBuffer(f.Payload[_spanIndex : _spanIndex+_spanLength]) + var s Span + s.read(rdr) + return s +} diff --git a/vendor/src/github.com/uber/tchannel-go/messages_test.go b/vendor/src/github.com/uber/tchannel-go/messages_test.go new file mode 100644 index 00000000..81dbf615 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/messages_test.go @@ -0,0 +1,155 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "fmt" + "testing" + "time" + + "github.com/uber/tchannel-go/typed" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInitReq(t *testing.T) { + req := initReq{ + initMessage{ + id: 0xDEADBEEF, + Version: 0x02, + initParams: initParams{ + "lang": "en_US", + "tz": "GMT", + }, + }, + } + + assert.Equal(t, uint32(0xDEADBEEF), req.ID(), "ids do not match") + assert.Equal(t, messageTypeInitReq, req.messageType(), "types do not match") + assertRoundTrip(t, &req, &initReq{initMessage{id: 0xDEADBEEF}}) +} + +func TestInitRes(t *testing.T) { + res := initRes{ + initMessage{ + id: 0xDEADBEEF, + Version: 0x04, + initParams: initParams{ + "lang": "en_US", + "tz": "GMT", + }, + }, + } + + assert.Equal(t, uint32(0xDEADBEEF), res.ID(), "ids do not match") + assert.Equal(t, messageTypeInitRes, res.messageType(), "types do not match") + assertRoundTrip(t, &res, &initRes{initMessage{id: 0xDEADBEEF}}) +} + +func TestCallReq(t *testing.T) { + r := callReq{ + id: 0xDEADBEEF, + TimeToLive: time.Second * 45, + Tracing: Span{ + traceID: 294390430934, + parentID: 398348934, + spanID: 12762782, + flags: 0x01, + }, + Headers: transportHeaders{ + "r": "c", + "f": "d", + }, + Service: "udr", + } + + assert.Equal(t, uint32(0xDEADBEEF), r.ID()) + assert.Equal(t, messageTypeCallReq, r.messageType()) + assertRoundTrip(t, &r, &callReq{id: 0xDEADBEEF}) +} + +func TestCallReqContinue(t *testing.T) { + r := callReqContinue{ + id: 0xDEADBEEF, + } + + assert.Equal(t, uint32(0xDEADBEEF), r.ID()) + assert.Equal(t, messageTypeCallReqContinue, r.messageType()) + assertRoundTrip(t, &r, &callReqContinue{id: 0xDEADBEEF}) +} + +func TestCallRes(t *testing.T) { + r := callRes{ + id: 0xDEADBEEF, + ResponseCode: responseApplicationError, + Headers: transportHeaders{ + "r": "c", + "f": "d", + }, + Tracing: Span{ + traceID: 294390430934, + parentID: 398348934, + spanID: 12762782, + flags: 0x04, + }, + } + + assert.Equal(t, uint32(0xDEADBEEF), r.ID()) + assert.Equal(t, messageTypeCallRes, r.messageType()) + assertRoundTrip(t, &r, &callRes{id: 0xDEADBEEF}) +} + +func TestCallResContinue(t *testing.T) { + r := callResContinue{ + id: 0xDEADBEEF, + } + + assert.Equal(t, uint32(0xDEADBEEF), r.ID()) + assert.Equal(t, messageTypeCallResContinue, r.messageType()) + assertRoundTrip(t, &r, &callResContinue{id: 0xDEADBEEF}) +} + +func TestErrorMessage(t *testing.T) { + m := errorMessage{ + errCode: ErrCodeBusy, + message: "go away", + } + + assert.Equal(t, messageTypeError, m.messageType()) + assertRoundTrip(t, &m, &errorMessage{}) +} + +func assertRoundTrip(t *testing.T, expected message, actual message) { + w := typed.NewWriteBufferWithSize(1024) + require.Nil(t, expected.write(w), fmt.Sprintf("error writing message %v", expected.messageType())) + + var b bytes.Buffer + w.FlushTo(&b) + + r := typed.NewReadBufferWithSize(1024) + _, err := r.FillFrom(bytes.NewReader(b.Bytes()), len(b.Bytes())) + require.Nil(t, err) + require.Nil(t, actual.read(r), fmt.Sprintf("error reading message %v", expected.messageType())) + + assert.Equal(t, expected, actual, fmt.Sprintf("pre- and post-marshal %v do not match", expected.messageType())) +} diff --git a/vendor/src/github.com/uber/tchannel-go/messagetype_string.go b/vendor/src/github.com/uber/tchannel-go/messagetype_string.go new file mode 100644 index 00000000..dd6e33bb --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/messagetype_string.go @@ -0,0 +1,37 @@ +// generated by stringer -type=messageType; DO NOT EDIT + +package tchannel + +import "fmt" + +const ( + _messageType_name_0 = "messageTypeInitReqmessageTypeInitResmessageTypeCallReqmessageTypeCallRes" + _messageType_name_1 = "messageTypeCallReqContinuemessageTypeCallResContinue" + _messageType_name_2 = "messageTypePingReqmessageTypePingRes" + _messageType_name_3 = "messageTypeError" +) + +var ( + _messageType_index_0 = [...]uint8{0, 18, 36, 54, 72} + _messageType_index_1 = [...]uint8{0, 26, 52} + _messageType_index_2 = [...]uint8{0, 18, 36} + _messageType_index_3 = [...]uint8{0, 16} +) + +func (i messageType) String() string { + switch { + case 1 <= i && i <= 4: + i -= 1 + return _messageType_name_0[_messageType_index_0[i]:_messageType_index_0[i+1]] + case 19 <= i && i <= 20: + i -= 19 + return _messageType_name_1[_messageType_index_1[i]:_messageType_index_1[i+1]] + case 208 <= i && i <= 209: + i -= 208 + return _messageType_name_2[_messageType_index_2[i]:_messageType_index_2[i+1]] + case i == 255: + return _messageType_name_3 + default: + return fmt.Sprintf("messageType(%d)", i) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/mex.go b/vendor/src/github.com/uber/tchannel-go/mex.go new file mode 100644 index 00000000..403afaea --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/mex.go @@ -0,0 +1,503 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + "sync" + + "github.com/uber/tchannel-go/typed" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +var ( + errDuplicateMex = errors.New("multiple attempts to use the message id") + errMexShutdown = errors.New("mex has been shutdown") + errMexSetShutdown = errors.New("mexset has been shutdown") + errMexChannelFull = NewSystemError(ErrCodeBusy, "cannot send frame to message exchange channel") + errUnexpectedFrameType = errors.New("unexpected frame received") +) + +const ( + messageExchangeSetInbound = "inbound" + messageExchangeSetOutbound = "outbound" + + // mexChannelBufferSize is the size of the message exchange channel buffer. + mexChannelBufferSize = 2 +) + +type errNotifier struct { + c chan struct{} + err error + notified atomic.Int32 +} + +func newErrNotifier() errNotifier { + return errNotifier{c: make(chan struct{})} +} + +// Notify will store the error and notify all waiters on c that there's an error. +func (e *errNotifier) Notify(err error) error { + // The code should never try to Notify(nil). + if err == nil { + panic("cannot Notify with no error") + } + + // There may be some sort of race where we try to notify the mex twice. + if !e.notified.CAS(0, 1) { + return fmt.Errorf("cannot broadcast error: %v, already have: %v", err, e.err) + } + + e.err = err + close(e.c) + return nil +} + +// checkErr returns previously notified errors (if any). +func (e *errNotifier) checkErr() error { + select { + case <-e.c: + return e.err + default: + return nil + } +} + +// A messageExchange tracks this Connections's side of a message exchange with a +// peer. Each message exchange has a channel that can be used to receive +// frames from the peer, and a Context that can controls when the exchange has +// timed out or been cancelled. +type messageExchange struct { + recvCh chan *Frame + errCh errNotifier + ctx context.Context + msgID uint32 + msgType messageType + mexset *messageExchangeSet + framePool FramePool + + shutdownAtomic atomic.Uint32 + errChNotified atomic.Uint32 +} + +// checkError is called before waiting on the mex channels. +// It returns any existing errors (timeout, cancellation, connection errors). +func (mex *messageExchange) checkError() error { + if err := mex.ctx.Err(); err != nil { + return GetContextError(err) + } + + return mex.errCh.checkErr() +} + +// forwardPeerFrame forwards a frame from a peer to the message exchange, where +// it can be pulled by whatever application thread is handling the exchange +func (mex *messageExchange) forwardPeerFrame(frame *Frame) error { + // We want a very specific priority here: + // 1. Timeouts/cancellation (mex.ctx errors) + // 2. Whether recvCh has buffer space (non-blocking select over mex.recvCh) + // 3. Other mex errors (mex.errCh) + // Which is why we check the context error only (instead of mex.checkError). + // In the mex.errCh case, we do a non-blocking write to recvCh to prioritize it. + if err := mex.ctx.Err(); err != nil { + return GetContextError(err) + } + + select { + case mex.recvCh <- frame: + return nil + case <-mex.ctx.Done(): + // Note: One slow reader processing a large request could stall the connection. + // If we see this, we need to increase the recvCh buffer size. + return GetContextError(mex.ctx.Err()) + case <-mex.errCh.c: + // Select will randomly choose a case, but we want to prioritize + // sending a frame over the errCh. Try a non-blocking write. + select { + case mex.recvCh <- frame: + return nil + default: + } + return mex.errCh.err + } +} + +func (mex *messageExchange) checkFrame(frame *Frame) error { + if frame.Header.ID != mex.msgID { + mex.mexset.log.WithFields( + LogField{"msgId", mex.msgID}, + LogField{"header", frame.Header}, + ).Error("recvPeerFrame received msg with unexpected ID.") + return errUnexpectedFrameType + } + return nil +} + +// recvPeerFrame waits for a new frame from the peer, or until the context +// expires or is cancelled +func (mex *messageExchange) recvPeerFrame() (*Frame, error) { + // We have to check frames/errors in a very specific order here: + // 1. Timeouts/cancellation (mex.ctx errors) + // 2. Any pending frames (non-blocking select over mex.recvCh) + // 3. Other mex errors (mex.errCh) + // Which is why we check the context error only (instead of mex.checkError)e + // In the mex.errCh case, we do a non-blocking read from recvCh to prioritize it. + if err := mex.ctx.Err(); err != nil { + return nil, GetContextError(err) + } + + select { + case frame := <-mex.recvCh: + if err := mex.checkFrame(frame); err != nil { + return nil, err + } + return frame, nil + case <-mex.ctx.Done(): + return nil, GetContextError(mex.ctx.Err()) + case <-mex.errCh.c: + // Select will randomly choose a case, but we want to prioritize + // receiving a frame over errCh. Try a non-blocking read. + select { + case frame := <-mex.recvCh: + if err := mex.checkFrame(frame); err != nil { + return nil, err + } + return frame, nil + default: + } + return nil, mex.errCh.err + } +} + +// recvPeerFrameOfType waits for a new frame of a given type from the peer, failing +// if the next frame received is not of that type. +// If an error frame is returned, then the errorMessage is returned as the error. +func (mex *messageExchange) recvPeerFrameOfType(msgType messageType) (*Frame, error) { + frame, err := mex.recvPeerFrame() + if err != nil { + return nil, err + } + + switch frame.Header.messageType { + case msgType: + return frame, nil + + case messageTypeError: + // If we read an error frame, we can release it once we deserialize it. + defer mex.framePool.Release(frame) + + errMsg := errorMessage{ + id: frame.Header.ID, + } + var rbuf typed.ReadBuffer + rbuf.Wrap(frame.SizedPayload()) + if err := errMsg.read(&rbuf); err != nil { + return nil, err + } + return nil, errMsg + + default: + // TODO(mmihic): Should be treated as a protocol error + mex.mexset.log.WithFields( + LogField{"header", frame.Header}, + LogField{"expectedType", msgType}, + LogField{"expectedID", mex.msgID}, + ).Warn("Received unexpected frame.") + return nil, errUnexpectedFrameType + } +} + +// shutdown shuts down the message exchange, removing it from the message +// exchange set so that it cannot receive more messages from the peer. The +// receive channel remains open, however, in case there are concurrent +// goroutines sending to it. +func (mex *messageExchange) shutdown() { + // The reader and writer side can both hit errors and try to shutdown the mex, + // so we ensure that it's only shut down once. + if !mex.shutdownAtomic.CAS(0, 1) { + return + } + + if mex.errChNotified.CAS(0, 1) { + mex.errCh.Notify(errMexShutdown) + } + + mex.mexset.removeExchange(mex.msgID) +} + +// inboundExpired is called when an exchange is canceled or it times out, +// but a handler may still be running in the background. Since the handler may +// still write to the exchange, we cannot shutdown the exchange, but we should +// remove it from the connection's exchange list. +func (mex *messageExchange) inboundExpired() { + mex.mexset.expireExchange(mex.msgID) +} + +// A messageExchangeSet manages a set of active message exchanges. It is +// mainly used to route frames from a peer to the appropriate messageExchange, +// or to cancel or mark a messageExchange as being in error. Each Connection +// maintains two messageExchangeSets, one to manage exchanges that it has +// initiated (outbound), and another to manage exchanges that the peer has +// initiated (inbound). The message-type specific handlers are responsible for +// ensuring that their message exchanges are properly registered and removed +// from the corresponding exchange set. +type messageExchangeSet struct { + sync.RWMutex + + log Logger + name string + onRemoved func() + onAdded func() + sendChRefs sync.WaitGroup + + // maps are mutable, and are protected by the mutex. + exchanges map[uint32]*messageExchange + expiredExchanges map[uint32]struct{} + shutdown bool +} + +// newMessageExchangeSet creates a new messageExchangeSet with a given name. +func newMessageExchangeSet(log Logger, name string) *messageExchangeSet { + return &messageExchangeSet{ + name: name, + log: log.WithFields(LogField{"exchange", name}), + exchanges: make(map[uint32]*messageExchange), + expiredExchanges: make(map[uint32]struct{}), + } +} + +// addExchange adds an exchange, it must be called with the mexset locked. +func (mexset *messageExchangeSet) addExchange(mex *messageExchange) error { + if mexset.shutdown { + return errMexSetShutdown + } + + if _, ok := mexset.exchanges[mex.msgID]; ok { + return errDuplicateMex + } + + mexset.exchanges[mex.msgID] = mex + mexset.sendChRefs.Add(1) + return nil +} + +// newExchange creates and adds a new message exchange to this set +func (mexset *messageExchangeSet) newExchange(ctx context.Context, framePool FramePool, + msgType messageType, msgID uint32, bufferSize int) (*messageExchange, error) { + if mexset.log.Enabled(LogLevelDebug) { + mexset.log.Debugf("Creating new %s message exchange for [%v:%d]", mexset.name, msgType, msgID) + } + + mex := &messageExchange{ + msgType: msgType, + msgID: msgID, + ctx: ctx, + recvCh: make(chan *Frame, bufferSize), + errCh: newErrNotifier(), + mexset: mexset, + framePool: framePool, + } + + mexset.Lock() + addErr := mexset.addExchange(mex) + mexset.Unlock() + + if addErr != nil { + logger := mexset.log.WithFields( + LogField{"msgID", mex.msgID}, + LogField{"msgType", mex.msgType}, + LogField{"exchange", mexset.name}, + ) + if addErr == errMexSetShutdown { + logger.Warn("Attempted to create new mex after mexset shutdown.") + } else if addErr == errDuplicateMex { + logger.Warn("Duplicate msg ID for active and new mex.") + } + + return nil, addErr + } + + mexset.onAdded() + + // TODO(mmihic): Put into a deadline ordered heap so we can garbage collected expired exchanges + return mex, nil +} + +// deleteExchange will delete msgID, and return whether it was found or whether it was +// timed out. This method must be called with the lock. +func (mexset *messageExchangeSet) deleteExchange(msgID uint32) (found, timedOut bool) { + if _, found := mexset.exchanges[msgID]; found { + delete(mexset.exchanges, msgID) + return true, false + } + + if _, expired := mexset.expiredExchanges[msgID]; expired { + delete(mexset.expiredExchanges, msgID) + return false, true + } + + return false, false +} + +// removeExchange removes a message exchange from the set, if it exists. +// It decrements the sendChRefs wait group, signalling that this exchange no longer has +// any active goroutines that will try to send to sendCh. +func (mexset *messageExchangeSet) removeExchange(msgID uint32) { + if mexset.log.Enabled(LogLevelDebug) { + mexset.log.Debugf("Removing %s message exchange %d", mexset.name, msgID) + } + + mexset.Lock() + found, expired := mexset.deleteExchange(msgID) + mexset.Unlock() + + if !found && !expired { + mexset.log.WithFields( + LogField{"msgID", msgID}, + ).Error("Tried to remove exchange multiple times") + return + } + + // If the message exchange was found, then we perform clean up actions. + // These clean up actions can only be run once per exchange. + mexset.sendChRefs.Done() + mexset.onRemoved() +} + +// expireExchange is similar to removeExchange, however it does not decrement +// the sendChRefs wait group, since there could still be a handler running that +// will write to the send channel. +func (mexset *messageExchangeSet) expireExchange(msgID uint32) { + mexset.log.Debugf( + "Removing %s message exchange %d due to timeout or cancelation", + mexset.name, + msgID, + ) + + mexset.Lock() + // TODO(aniketp): explore if cancel can be called everytime we expire an exchange + found, expired := mexset.deleteExchange(msgID) + if found || expired { + // Record in expiredExchanges if we deleted the exchange. + mexset.expiredExchanges[msgID] = struct{}{} + } + mexset.Unlock() + + if expired { + mexset.log.WithFields(LogField{"msgID", msgID}).Info("Exchange expired already") + } + + mexset.onRemoved() +} + +// waitForSendCh waits for all goroutines with references to sendCh to complete. +func (mexset *messageExchangeSet) waitForSendCh() { + mexset.sendChRefs.Wait() +} + +func (mexset *messageExchangeSet) count() int { + mexset.RLock() + count := len(mexset.exchanges) + mexset.RUnlock() + + return count +} + +// forwardPeerFrame forwards a frame from the peer to the appropriate message +// exchange +func (mexset *messageExchangeSet) forwardPeerFrame(frame *Frame) error { + if mexset.log.Enabled(LogLevelDebug) { + mexset.log.Debugf("forwarding %s %s", mexset.name, frame.Header) + } + + mexset.RLock() + mex := mexset.exchanges[frame.Header.ID] + mexset.RUnlock() + + if mex == nil { + // This is ok since the exchange might have expired or been cancelled + mexset.log.WithFields( + LogField{"frameHeader", frame.Header.String()}, + LogField{"exchange", mexset.name}, + ).Info("Received frame for unknown message exchange.") + return nil + } + + if err := mex.forwardPeerFrame(frame); err != nil { + mexset.log.WithFields( + LogField{"frameHeader", frame.Header.String()}, + LogField{"frameSize", frame.Header.FrameSize()}, + LogField{"exchange", mexset.name}, + ErrField(err), + ).Info("Failed to forward frame.") + return err + } + + return nil +} + +// copyExchanges returns a copy of the exchanges if the exchange is active. +// The caller must lock the mexset. +func (mexset *messageExchangeSet) copyExchanges() (shutdown bool, exchanges map[uint32]*messageExchange) { + if mexset.shutdown { + return true, nil + } + + exchangesCopy := make(map[uint32]*messageExchange, len(mexset.exchanges)) + for k, mex := range mexset.exchanges { + exchangesCopy[k] = mex + } + + return false, exchangesCopy +} + +// stopExchanges stops all message exchanges to unblock all waiters on the mex. +// This should only be called on connection failures. +func (mexset *messageExchangeSet) stopExchanges(err error) { + if mexset.log.Enabled(LogLevelDebug) { + mexset.log.Debugf("stopping %v exchanges due to error: %v", mexset.count(), err) + } + + mexset.Lock() + shutdown, exchanges := mexset.copyExchanges() + mexset.shutdown = true + mexset.Unlock() + + if shutdown { + mexset.log.Debugf("mexset has already been shutdown") + return + } + + for _, mex := range exchanges { + // When there's a connection failure, we want to notify blocked callers that the + // call will fail, but we don't want to shutdown the exchange as only the + // arg reader/writer should shutdown the exchange. Otherwise, our guarantee + // on sendChRefs that there's no references to sendCh is violated since + // readers/writers could still have a reference to sendCh even though + // we shutdown the exchange and called Done on sendChRefs. + if mex.errChNotified.CAS(0, 1) { + mex.errCh.Notify(err) + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/mex_utils_test.go b/vendor/src/github.com/uber/tchannel-go/mex_utils_test.go new file mode 100644 index 00000000..65b21ba7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/mex_utils_test.go @@ -0,0 +1,76 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "strings" +) + +// CheckEmptyExchangesConn checks whether all exchanges for the given connection are empty. +// If there are exchanges, a string with information about leftover exchanges is returned. +func CheckEmptyExchangesConn(c *ConnectionRuntimeState) string { + var errors []string + checkExchange := func(e ExchangeSetRuntimeState) { + if e.Count > 0 { + errors = append(errors, fmt.Sprintf(" %v leftover %v exchanges", e.Name, e.Count)) + for _, v := range e.Exchanges { + errors = append(errors, fmt.Sprintf(" exchanges: %+v", v)) + } + } + } + checkExchange(c.InboundExchange) + checkExchange(c.OutboundExchange) + if len(errors) == 0 { + return "" + } + + return fmt.Sprintf("Connection %d has leftover exchanges:\n\t%v", c.ID, strings.Join(errors, "\n\t")) +} + +// CheckEmptyExchangesConns checks that all exchanges for the given connections are empty. +func CheckEmptyExchangesConns(connections []*ConnectionRuntimeState) string { + var errors []string + for _, c := range connections { + if v := CheckEmptyExchangesConn(c); v != "" { + errors = append(errors, v) + } + } + return strings.Join(errors, "\n") +} + +// CheckEmptyExchanges checks that all exchanges for the given channel are empty. +// +// TODO: Remove CheckEmptyExchanges and friends in favor of +// testutils.TestServer's verification. +func CheckEmptyExchanges(ch *Channel) string { + state := ch.IntrospectState(&IntrospectionOptions{IncludeExchanges: true}) + var connections []*ConnectionRuntimeState + for _, peer := range state.RootPeers { + for _, conn := range peer.InboundConnections { + connections = append(connections, &conn) + } + for _, conn := range peer.OutboundConnections { + connections = append(connections, &conn) + } + } + return CheckEmptyExchangesConns(connections) +} diff --git a/vendor/src/github.com/uber/tchannel-go/outbound.go b/vendor/src/github.com/uber/tchannel-go/outbound.go new file mode 100644 index 00000000..e5d3fbf9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/outbound.go @@ -0,0 +1,383 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "time" + + "github.com/uber/tchannel-go/typed" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" +) + +// maxMethodSize is the maximum size of arg1. +const maxMethodSize = 16 * 1024 + +// beginCall begins an outbound call on the connection +func (c *Connection) beginCall(ctx context.Context, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) { + now := c.timeNow() + + switch state := c.readState(); state { + case connectionActive: + break + case connectionStartClose, connectionInboundClosed, connectionClosed: + return nil, ErrConnectionClosed + default: + return nil, errConnectionUnknownState{"beginCall", state} + } + + deadline, ok := ctx.Deadline() + if !ok { + // This case is handled by validateCall, so we should + // never get here. + return nil, ErrTimeoutRequired + } + + // If the timeToLive is less than a millisecond, it will be encoded as 0 on + // the wire, hence we return a timeout immediately. + timeToLive := deadline.Sub(now) + if timeToLive < time.Millisecond { + return nil, ErrTimeout + } + + if err := ctx.Err(); err != nil { + return nil, GetContextError(err) + } + + if !c.pendingExchangeMethodAdd() { + // Connection is closed, no need to do anything. + return nil, ErrInvalidConnectionState + } + defer c.pendingExchangeMethodDone() + + requestID := c.NextMessageID() + mex, err := c.outbound.newExchange(ctx, c.opts.FramePool, messageTypeCallReq, requestID, mexChannelBufferSize) + if err != nil { + return nil, err + } + + // Close may have been called between the time we checked the state and us creating the exchange. + if state := c.readState(); state != connectionActive { + mex.shutdown() + return nil, ErrConnectionClosed + } + + headers := transportHeaders{ + CallerName: c.localPeerInfo.ServiceName, + } + callOptions.setHeaders(headers) + if opts := currentCallOptions(ctx); opts != nil { + opts.overrideHeaders(headers) + } + + call := new(OutboundCall) + call.mex = mex + call.conn = c + call.callReq = callReq{ + id: requestID, + Headers: headers, + Service: serviceName, + TimeToLive: timeToLive, + } + call.statsReporter = c.statsReporter + call.createStatsTags(c.commonStatsTags, callOptions, methodName) + call.log = c.log.WithFields(LogField{"Out-Call", requestID}) + + // TODO(mmihic): It'd be nice to do this without an fptr + call.messageForFragment = func(initial bool) message { + if initial { + return &call.callReq + } + + return new(callReqContinue) + } + + call.contents = newFragmentingWriter(call.log, call, c.opts.ChecksumType.New()) + + response := new(OutboundCallResponse) + response.startedAt = now + response.timeNow = c.timeNow + response.requestState = callOptions.RequestState + response.mex = mex + response.log = c.log.WithFields(LogField{"Out-Response", requestID}) + response.span = c.startOutboundSpan(ctx, serviceName, methodName, call, now) + response.messageForFragment = func(initial bool) message { + if initial { + return &response.callRes + } + + return new(callResContinue) + } + response.contents = newFragmentingReader(response.log, response) + response.statsReporter = call.statsReporter + response.commonStatsTags = call.commonStatsTags + + call.response = response + + if err := call.writeMethod([]byte(methodName)); err != nil { + return nil, err + } + return call, nil +} + +// handleCallRes handles an incoming call req message, forwarding the +// frame to the response channel waiting for it +func (c *Connection) handleCallRes(frame *Frame) bool { + if err := c.outbound.forwardPeerFrame(frame); err != nil { + return true + } + return false +} + +// handleCallResContinue handles an incoming call res continue message, +// forwarding the frame to the response channel waiting for it +func (c *Connection) handleCallResContinue(frame *Frame) bool { + if err := c.outbound.forwardPeerFrame(frame); err != nil { + return true + } + return false +} + +// An OutboundCall is an active call to a remote peer. A client makes a call +// by calling BeginCall on the Channel, writing argument content via +// ArgWriter2() ArgWriter3(), and then reading reading response data via the +// ArgReader2() and ArgReader3() methods on the Response() object. +type OutboundCall struct { + reqResWriter + + callReq callReq + response *OutboundCallResponse + statsReporter StatsReporter + commonStatsTags map[string]string +} + +// Response provides access to the call's response object, which can be used to +// read response arguments +func (call *OutboundCall) Response() *OutboundCallResponse { + return call.response +} + +// createStatsTags creates the common stats tags, if they are not already created. +func (call *OutboundCall) createStatsTags(connectionTags map[string]string, callOptions *CallOptions, method string) { + call.commonStatsTags = map[string]string{ + "target-service": call.callReq.Service, + } + for k, v := range connectionTags { + call.commonStatsTags[k] = v + } + if callOptions.Format != HTTP { + call.commonStatsTags["target-endpoint"] = string(method) + } +} + +// writeMethod writes the method (arg1) to the call +func (call *OutboundCall) writeMethod(method []byte) error { + call.statsReporter.IncCounter("outbound.calls.send", call.commonStatsTags, 1) + return NewArgWriter(call.arg1Writer()).Write(method) +} + +// Arg2Writer returns a WriteCloser that can be used to write the second argument. +// The returned writer must be closed once the write is complete. +func (call *OutboundCall) Arg2Writer() (ArgWriter, error) { + return call.arg2Writer() +} + +// Arg3Writer returns a WriteCloser that can be used to write the last argument. +// The returned writer must be closed once the write is complete. +func (call *OutboundCall) Arg3Writer() (ArgWriter, error) { + return call.arg3Writer() +} + +// LocalPeer returns the local peer information for this call. +func (call *OutboundCall) LocalPeer() LocalPeerInfo { + return call.conn.localPeerInfo +} + +// RemotePeer returns the remote peer information for this call. +func (call *OutboundCall) RemotePeer() PeerInfo { + return call.conn.RemotePeerInfo() +} + +func (call *OutboundCall) doneSending() {} + +// An OutboundCallResponse is the response to an outbound call +type OutboundCallResponse struct { + reqResReader + + callRes callRes + + requestState *RequestState + // startedAt is the time at which the outbound call was started. + startedAt time.Time + timeNow func() time.Time + span opentracing.Span + statsReporter StatsReporter + commonStatsTags map[string]string +} + +// ApplicationError returns true if the call resulted in an application level error +// TODO(mmihic): In current implementation, you must have called Arg2Reader before this +// method returns the proper value. We should instead have this block until the first +// fragment is available, if the first fragment hasn't been received. +func (response *OutboundCallResponse) ApplicationError() bool { + // TODO(mmihic): Wait for first fragment + return response.callRes.ResponseCode == responseApplicationError +} + +// Format the format of the request from the ArgScheme transport header. +func (response *OutboundCallResponse) Format() Format { + return Format(response.callRes.Headers[ArgScheme]) +} + +// Arg2Reader returns an ArgReader to read the second argument. +// The ReadCloser must be closed once the argument has been read. +func (response *OutboundCallResponse) Arg2Reader() (ArgReader, error) { + var method []byte + if err := NewArgReader(response.arg1Reader()).Read(&method); err != nil { + return nil, err + } + + return response.arg2Reader() +} + +// Arg3Reader returns an ArgReader to read the last argument. +// The ReadCloser must be closed once the argument has been read. +func (response *OutboundCallResponse) Arg3Reader() (ArgReader, error) { + return response.arg3Reader() +} + +// handleError handles an error coming back from the peer. If the error is a +// protocol level error, the entire connection will be closed. If the error is +// a request specific error, it will be written to the request's response +// channel and converted into a SystemError returned from the next reader or +// access call. +// The return value is whether the frame should be released immediately. +func (c *Connection) handleError(frame *Frame) bool { + errMsg := errorMessage{ + id: frame.Header.ID, + } + rbuf := typed.NewReadBuffer(frame.SizedPayload()) + if err := errMsg.read(rbuf); err != nil { + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + ErrField(err), + ).Warn("Unable to read error frame.") + c.connectionError("parsing error frame", err) + return true + } + + if errMsg.errCode == ErrCodeProtocol { + c.log.WithFields( + LogField{"remotePeer", c.remotePeerInfo}, + LogField{"error", errMsg.message}, + ).Warn("Peer reported protocol error.") + c.connectionError("received protocol error", errMsg.AsSystemError()) + return true + } + + if err := c.outbound.forwardPeerFrame(frame); err != nil { + c.log.WithFields( + LogField{"frameHeader", frame.Header.String()}, + LogField{"id", errMsg.id}, + LogField{"errorMessage", errMsg.message}, + LogField{"errorCode", errMsg.errCode}, + ErrField(err), + ).Info("Failed to forward error frame.") + return true + } + + // If the frame was forwarded, then the other side is responsible for releasing the frame. + return false +} + +func cloneTags(tags map[string]string) map[string]string { + newTags := make(map[string]string, len(tags)) + for k, v := range tags { + newTags[k] = v + } + return newTags +} + +// doneReading shuts down the message exchange for this call. +// For outgoing calls, the last message is reading the call response. +func (response *OutboundCallResponse) doneReading(unexpected error) { + now := response.timeNow() + + isSuccess := unexpected == nil && !response.ApplicationError() + lastAttempt := isSuccess || !response.requestState.HasRetries(unexpected) + + // TODO how should this work with retries? + if span := response.span; span != nil { + if unexpected != nil { + span.LogEventWithPayload("error", unexpected) + } + if !isSuccess && lastAttempt { + ext.Error.Set(span, true) + } + span.FinishWithOptions(opentracing.FinishOptions{FinishTime: now}) + } + + latency := now.Sub(response.startedAt) + response.statsReporter.RecordTimer("outbound.calls.per-attempt.latency", response.commonStatsTags, latency) + if lastAttempt { + requestLatency := response.requestState.SinceStart(now, latency) + response.statsReporter.RecordTimer("outbound.calls.latency", response.commonStatsTags, requestLatency) + } + if retryCount := response.requestState.RetryCount(); retryCount > 0 { + retryTags := cloneTags(response.commonStatsTags) + retryTags["retry-count"] = fmt.Sprint(retryCount) + response.statsReporter.IncCounter("outbound.calls.retries", retryTags, 1) + } + + if unexpected != nil { + // TODO(prashant): Report the error code type as per metrics doc and enable. + // response.statsReporter.IncCounter("outbound.calls.system-errors", response.commonStatsTags, 1) + } else if response.ApplicationError() { + // TODO(prashant): Figure out how to add "type" to tags, which TChannel does not know about. + response.statsReporter.IncCounter("outbound.calls.per-attempt.app-errors", response.commonStatsTags, 1) + if lastAttempt { + response.statsReporter.IncCounter("outbound.calls.app-errors", response.commonStatsTags, 1) + } + } else { + response.statsReporter.IncCounter("outbound.calls.success", response.commonStatsTags, 1) + } + + response.mex.shutdown() +} + +func validateCall(ctx context.Context, serviceName, methodName string, callOpts *CallOptions) error { + if serviceName == "" { + return ErrNoServiceName + } + + if len(methodName) > maxMethodSize { + return ErrMethodTooLarge + } + + if _, ok := ctx.Deadline(); !ok { + return ErrTimeoutRequired + } + + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer.go b/vendor/src/github.com/uber/tchannel-go/peer.go new file mode 100644 index 00000000..7fec857f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer.go @@ -0,0 +1,614 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "container/heap" + "errors" + "strings" + "sync" + "time" + + "github.com/uber/tchannel-go/trand" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +var ( + // ErrInvalidConnectionState indicates that the connection is not in a valid state. + // This may be due to a race between selecting the connection and it closing, so + // it is a network failure that can be retried. + ErrInvalidConnectionState = NewSystemError(ErrCodeNetwork, "connection is in an invalid state") + + // ErrNoPeers indicates that there are no peers. + ErrNoPeers = errors.New("no peers available") + + // ErrPeerNotFound indicates that the specified peer was not found. + ErrPeerNotFound = errors.New("peer not found") + + // ErrNoNewPeers indicates that no previously unselected peer is available. + ErrNoNewPeers = errors.New("no new peer available") + + peerRng = trand.NewSeeded() +) + +// Connectable is the interface used by peers to create connections. +type Connectable interface { + // Connect tries to connect to the given hostPort. + Connect(ctx context.Context, hostPort string) (*Connection, error) + // Logger returns the logger to use. + Logger() Logger +} + +// PeerList maintains a list of Peers. +type PeerList struct { + sync.RWMutex + + parent *RootPeerList + peersByHostPort map[string]*peerScore + peerHeap *peerHeap + scoreCalculator ScoreCalculator + lastSelected uint64 +} + +func newPeerList(root *RootPeerList) *PeerList { + return &PeerList{ + parent: root, + peersByHostPort: make(map[string]*peerScore), + scoreCalculator: newPreferIncomingCalculator(), + peerHeap: newPeerHeap(), + } +} + +// SetStrategy sets customized peer selection strategy. +func (l *PeerList) SetStrategy(sc ScoreCalculator) { + l.Lock() + defer l.Unlock() + + l.scoreCalculator = sc + for _, ps := range l.peersByHostPort { + newScore := l.scoreCalculator.GetScore(ps.Peer) + l.updatePeer(ps, newScore) + } +} + +// Siblings don't share peer lists (though they take care not to double-connect +// to the same hosts). +func (l *PeerList) newSibling() *PeerList { + sib := newPeerList(l.parent) + return sib +} + +// Add adds a peer to the list if it does not exist, or returns any existing peer. +func (l *PeerList) Add(hostPort string) *Peer { + if ps, ok := l.exists(hostPort); ok { + return ps.Peer + } + l.Lock() + defer l.Unlock() + + if p, ok := l.peersByHostPort[hostPort]; ok { + return p.Peer + } + + p := l.parent.Add(hostPort) + p.addSC() + ps := newPeerScore(p, l.scoreCalculator.GetScore(p)) + + l.peersByHostPort[hostPort] = ps + l.peerHeap.addPeer(ps) + return p +} + +// GetNew returns a new, previously unselected peer from the peer list, or nil, +// if no new unselected peer can be found. +func (l *PeerList) GetNew(prevSelected map[string]struct{}) (*Peer, error) { + l.Lock() + defer l.Unlock() + if l.peerHeap.Len() == 0 { + return nil, ErrNoPeers + } + + // Select a peer, avoiding previously selected peers. If all peers have been previously + // selected, then it's OK to repick them. + peer := l.choosePeer(prevSelected, true /* avoidHost */) + if peer == nil { + peer = l.choosePeer(prevSelected, false /* avoidHost */) + } + if peer == nil { + return nil, ErrNoNewPeers + } + return peer, nil +} + +// Get returns a peer from the peer list, or nil if none can be found, +// will avoid previously selected peers if possible. +func (l *PeerList) Get(prevSelected map[string]struct{}) (*Peer, error) { + peer, err := l.GetNew(prevSelected) + if err == ErrNoNewPeers { + l.Lock() + peer = l.choosePeer(nil, false /* avoidHost */) + l.Unlock() + } else if err != nil { + return nil, err + } + if peer == nil { + return nil, ErrNoPeers + } + return peer, nil +} + +// Remove removes a peer from the peer list. It returns an error if the peer cannot be found. +// Remove does not affect connections to the peer in any way. +func (l *PeerList) Remove(hostPort string) error { + l.Lock() + defer l.Unlock() + + p, ok := l.peersByHostPort[hostPort] + if !ok { + return ErrPeerNotFound + } + + p.delSC() + delete(l.peersByHostPort, hostPort) + l.peerHeap.removePeer(p) + + return nil +} +func (l *PeerList) choosePeer(prevSelected map[string]struct{}, avoidHost bool) *Peer { + var psPopList []*peerScore + var ps *peerScore + + canChoosePeer := func(hostPort string) bool { + if _, ok := prevSelected[hostPort]; ok { + return false + } + if avoidHost { + if _, ok := prevSelected[getHost(hostPort)]; ok { + return false + } + } + return true + } + + size := l.peerHeap.Len() + for i := 0; i < size; i++ { + popped := l.peerHeap.popPeer() + + if canChoosePeer(popped.HostPort()) { + ps = popped + break + } + psPopList = append(psPopList, popped) + } + + for _, p := range psPopList { + heap.Push(l.peerHeap, p) + } + + if ps == nil { + return nil + } + + l.peerHeap.pushPeer(ps) + ps.chosenCount.Inc() + return ps.Peer +} + +// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist. +func (l *PeerList) GetOrAdd(hostPort string) *Peer { + if ps, ok := l.exists(hostPort); ok { + return ps.Peer + } + return l.Add(hostPort) +} + +// Copy returns a copy of the PeerList as a map from hostPort to peer. +func (l *PeerList) Copy() map[string]*Peer { + l.RLock() + defer l.RUnlock() + + listCopy := make(map[string]*Peer) + for k, v := range l.peersByHostPort { + listCopy[k] = v.Peer + } + return listCopy +} + +// Len returns the length of the PeerList. +func (l *PeerList) Len() int { + l.RLock() + defer l.RUnlock() + return l.peerHeap.Len() +} + +// exists checks if a hostport exists in the peer list. +func (l *PeerList) exists(hostPort string) (*peerScore, bool) { + l.RLock() + ps, ok := l.peersByHostPort[hostPort] + l.RUnlock() + + return ps, ok +} + +// getPeerScore is called to find the peer and its score from a host port key. +// Note that at least a Read lock must be held to call this function. +func (l *PeerList) getPeerScore(hostPort string) (*peerScore, uint64, bool) { + ps, ok := l.peersByHostPort[hostPort] + if !ok { + return nil, 0, false + } + return ps, ps.score, ok +} + +// onPeerChange is called when there is a change that may cause the peer's score to change. +// The new score is calculated, and the peer heap is updated with the new score if the score changes. +func (l *PeerList) onPeerChange(p *Peer) { + l.RLock() + ps, psScore, ok := l.getPeerScore(p.hostPort) + sc := l.scoreCalculator + l.RUnlock() + if !ok { + return + } + + newScore := sc.GetScore(ps.Peer) + if newScore == psScore { + return + } + + l.Lock() + l.updatePeer(ps, newScore) + l.Unlock() +} + +// updatePeer is called to update the score of the peer given the existing score. +// Note that a Write lock must be held to call this function. +func (l *PeerList) updatePeer(ps *peerScore, newScore uint64) { + if ps.score == newScore { + return + } + + ps.score = newScore + l.peerHeap.updatePeer(ps) +} + +// peerScore represents a peer and scoring for the peer heap. +// It is not safe for concurrent access, it should only be used through the PeerList. +type peerScore struct { + *Peer + + // score according to the current peer list's ScoreCalculator. + score uint64 + // index of the peerScore in the peerHeap. Used to interact with container/heap. + index int + // order is the tiebreaker for when score is equal. It is set when a peer + // is pushed to the heap based on peerHeap.order with jitter. + order uint64 +} + +func newPeerScore(p *Peer, score uint64) *peerScore { + return &peerScore{ + Peer: p, + score: score, + index: -1, + } +} + +// Peer represents a single autobahn service or client with a unique host:port. +type Peer struct { + sync.RWMutex + + channel Connectable + hostPort string + onStatusChanged func(*Peer) + onClosedConnRemoved func(*Peer) + + // scCount is the number of subchannels that this peer is added to. + scCount uint32 + + // connections are mutable, and are protected by the mutex. + newConnLock sync.Mutex + inboundConnections []*Connection + outboundConnections []*Connection + chosenCount atomic.Uint64 + + // onUpdate is a test-only hook. + onUpdate func(*Peer) +} + +func newPeer(channel Connectable, hostPort string, onStatusChanged func(*Peer), onClosedConnRemoved func(*Peer)) *Peer { + if hostPort == "" { + panic("Cannot create peer with blank hostPort") + } + if onStatusChanged == nil { + onStatusChanged = noopOnStatusChanged + } + return &Peer{ + channel: channel, + hostPort: hostPort, + onStatusChanged: onStatusChanged, + onClosedConnRemoved: onClosedConnRemoved, + } +} + +// HostPort returns the host:port used to connect to this peer. +func (p *Peer) HostPort() string { + return p.hostPort +} + +// getConn treats inbound and outbound connections as a single virtual list +// that can be indexed. The peer must be read-locked. +func (p *Peer) getConn(i int) *Connection { + inboundLen := len(p.inboundConnections) + if i < inboundLen { + return p.inboundConnections[i] + } + + return p.outboundConnections[i-inboundLen] +} + +func (p *Peer) getActiveConnLocked() (*Connection, bool) { + allConns := len(p.inboundConnections) + len(p.outboundConnections) + if allConns == 0 { + return nil, false + } + + // We cycle through the connection list, starting at a random point + // to avoid always choosing the same connection. + startOffset := peerRng.Intn(allConns) + for i := 0; i < allConns; i++ { + connIndex := (i + startOffset) % allConns + if conn := p.getConn(connIndex); conn.IsActive() { + return conn, true + } + } + + return nil, false +} + +// getActiveConn will randomly select an active connection. +// TODO(prashant): Should we clear inactive connections? +// TODO(prashant): Do we want some sort of scoring for connections? +func (p *Peer) getActiveConn() (*Connection, bool) { + p.RLock() + conn, ok := p.getActiveConnLocked() + p.RUnlock() + + return conn, ok +} + +// GetConnection returns an active connection to this peer. If no active connections +// are found, it will create a new outbound connection and return it. +func (p *Peer) GetConnection(ctx context.Context) (*Connection, error) { + if activeConn, ok := p.getActiveConn(); ok { + return activeConn, nil + } + + // Lock here to restrict new connection creation attempts to one goroutine + p.newConnLock.Lock() + defer p.newConnLock.Unlock() + + // Check active connections again in case someone else got ahead of us. + if activeConn, ok := p.getActiveConn(); ok { + return activeConn, nil + } + + // No active connections, make a new outgoing connection. + return p.Connect(ctx) +} + +// getConnectionRelay gets a connection, and uses the given timeout to lazily +// create a context if a new connection is required. +func (p *Peer) getConnectionRelay(timeout time.Duration) (*Connection, error) { + if conn, ok := p.getActiveConn(); ok { + return conn, nil + } + + // Lock here to restrict new connection creation attempts to one goroutine + p.newConnLock.Lock() + defer p.newConnLock.Unlock() + + // Check active connections again in case someone else got ahead of us. + if activeConn, ok := p.getActiveConn(); ok { + return activeConn, nil + } + + // When the relay creates outbound connections, we don't want those services + // to ever connect back to us and send us traffic. We hide the host:port + // so that service instances on remote machines don't try to connect back + // and don't try to send Hyperbahn traffic on this connection. + ctx, cancel := NewContextBuilder(timeout).HideListeningOnOutbound().Build() + defer cancel() + + return p.Connect(ctx) +} + +// addSC adds a reference to a peer from a subchannel (e.g. peer list). +func (p *Peer) addSC() { + p.Lock() + p.scCount++ + p.Unlock() +} + +// delSC removes a reference to a peer from a subchannel (e.g. peer list). +func (p *Peer) delSC() { + p.Lock() + p.scCount-- + p.Unlock() +} + +// canRemove returns whether this peer can be safely removed from the root peer list. +func (p *Peer) canRemove() bool { + p.RLock() + count := len(p.inboundConnections) + len(p.outboundConnections) + int(p.scCount) + p.RUnlock() + return count == 0 +} + +// addConnection adds an active connection to the peer's connection list. +// If a connection is not active, returns ErrInvalidConnectionState. +func (p *Peer) addConnection(c *Connection, direction connectionDirection) error { + conns := p.connectionsFor(direction) + + if c.readState() != connectionActive { + return ErrInvalidConnectionState + } + + p.Lock() + *conns = append(*conns, c) + p.Unlock() + + // Inform third parties that a peer gained a connection. + p.onStatusChanged(p) + + return nil +} + +func (p *Peer) connectionsFor(direction connectionDirection) *[]*Connection { + if direction == inbound { + return &p.inboundConnections + } + return &p.outboundConnections +} + +// removeConnection will check remove the connection if it exists on connsPtr +// and returns whether it removed the connection. +func (p *Peer) removeConnection(connsPtr *[]*Connection, changed *Connection) bool { + conns := *connsPtr + for i, c := range conns { + if c == changed { + // Remove the connection by moving the last item forward, and slicing the list. + last := len(conns) - 1 + conns[i], conns[last] = conns[last], nil + *connsPtr = conns[:last] + return true + } + } + + return false +} + +// connectionStateChanged is called when one of the peers' connections states changes. +// All non-active connections are removed from the peer. The connection will +// still be tracked by the channel until it's completely closed. +func (p *Peer) connectionCloseStateChange(changed *Connection) { + if changed.IsActive() { + return + } + + p.Lock() + found := p.removeConnection(&p.inboundConnections, changed) + if !found { + found = p.removeConnection(&p.outboundConnections, changed) + } + p.Unlock() + + if found { + p.onClosedConnRemoved(p) + // Inform third parties that a peer lost a connection. + p.onStatusChanged(p) + } +} + +// Connect adds a new outbound connection to the peer. +func (p *Peer) Connect(ctx context.Context) (*Connection, error) { + return p.channel.Connect(ctx, p.hostPort) +} + +// BeginCall starts a new call to this specific peer, returning an OutboundCall that can +// be used to write the arguments of the call. +func (p *Peer) BeginCall(ctx context.Context, serviceName, methodName string, callOptions *CallOptions) (*OutboundCall, error) { + if callOptions == nil { + callOptions = defaultCallOptions + } + callOptions.RequestState.AddSelectedPeer(p.HostPort()) + + if err := validateCall(ctx, serviceName, methodName, callOptions); err != nil { + return nil, err + } + + conn, err := p.GetConnection(ctx) + if err != nil { + return nil, err + } + + call, err := conn.beginCall(ctx, serviceName, methodName, callOptions) + if err != nil { + return nil, err + } + + return call, err +} + +// NumConnections returns the number of inbound and outbound connections for this peer. +func (p *Peer) NumConnections() (inbound int, outbound int) { + p.RLock() + inbound = len(p.inboundConnections) + outbound = len(p.outboundConnections) + p.RUnlock() + return inbound, outbound +} + +// NumPendingOutbound returns the number of pending outbound calls. +func (p *Peer) NumPendingOutbound() int { + count := 0 + p.RLock() + for _, c := range p.outboundConnections { + count += c.outbound.count() + } + + for _, c := range p.inboundConnections { + count += c.outbound.count() + } + p.RUnlock() + return count +} + +func (p *Peer) runWithConnections(f func(*Connection)) { + p.RLock() + for _, c := range p.inboundConnections { + f(c) + } + + for _, c := range p.outboundConnections { + f(c) + } + p.RUnlock() +} + +func (p *Peer) callOnUpdateComplete() { + p.RLock() + f := p.onUpdate + p.RUnlock() + + if f != nil { + f(p) + } +} + +func noopOnStatusChanged(*Peer) {} + +// isEphemeralHostPort returns if hostPort is the default ephemeral hostPort. +func isEphemeralHostPort(hostPort string) bool { + return hostPort == "" || hostPort == ephemeralHostPort || strings.HasSuffix(hostPort, ":0") +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer_bench_test.go b/vendor/src/github.com/uber/tchannel-go/peer_bench_test.go new file mode 100644 index 00000000..92ea8fba --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_bench_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/require" +) + +func benchmarkGetConnection(b *testing.B, numIncoming, numOutgoing int) { + ctx, cancel := NewContext(10 * time.Second) + defer cancel() + + s1 := testutils.NewServer(b, nil) + s2 := testutils.NewServer(b, nil) + defer s1.Close() + defer s2.Close() + + for i := 0; i < numOutgoing; i++ { + _, err := s1.Connect(ctx, s2.PeerInfo().HostPort) + require.NoError(b, err, "Connect from s1 -> s2 failed") + } + for i := 0; i < numIncoming; i++ { + _, err := s2.Connect(ctx, s1.PeerInfo().HostPort) + require.NoError(b, err, "Connect from s2 -> s1 failed") + } + + peer := s1.Peers().GetOrAdd(s2.PeerInfo().HostPort) + b.ResetTimer() + for i := 0; i < b.N; i++ { + peer.GetConnection(ctx) + } +} + +func BenchmarkGetConnection0In1Out(b *testing.B) { benchmarkGetConnection(b, 0, 1) } +func BenchmarkGetConnection1In0Out(b *testing.B) { benchmarkGetConnection(b, 1, 0) } +func BenchmarkGetConnection5In5Out(b *testing.B) { benchmarkGetConnection(b, 5, 5) } diff --git a/vendor/src/github.com/uber/tchannel-go/peer_heap.go b/vendor/src/github.com/uber/tchannel-go/peer_heap.go new file mode 100644 index 00000000..e59ac376 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_heap.go @@ -0,0 +1,122 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "container/heap" + "math/rand" + + "github.com/uber/tchannel-go/trand" +) + +// peerHeap maintains a min-heap of peers based on the peers' score. All method +// calls must be serialized externally. +type peerHeap struct { + peerScores []*peerScore + rng *rand.Rand + order uint64 +} + +func newPeerHeap() *peerHeap { + return &peerHeap{rng: trand.NewSeeded()} +} + +func (ph peerHeap) Len() int { return len(ph.peerScores) } + +func (ph *peerHeap) Less(i, j int) bool { + if ph.peerScores[i].score == ph.peerScores[j].score { + return ph.peerScores[i].order < ph.peerScores[j].order + } + return ph.peerScores[i].score < ph.peerScores[j].score +} + +func (ph peerHeap) Swap(i, j int) { + ph.peerScores[i], ph.peerScores[j] = ph.peerScores[j], ph.peerScores[i] + ph.peerScores[i].index = i + ph.peerScores[j].index = j +} + +// Push implements heap Push interface +func (ph *peerHeap) Push(x interface{}) { + n := len(ph.peerScores) + item := x.(*peerScore) + item.index = n + ph.peerScores = append(ph.peerScores, item) +} + +// Pop implements heap Pop interface +func (ph *peerHeap) Pop() interface{} { + old := *ph + n := len(old.peerScores) + item := old.peerScores[n-1] + item.index = -1 // for safety + ph.peerScores = old.peerScores[:n-1] + return item +} + +// updatePeer updates the score for the given peer. +func (ph *peerHeap) updatePeer(peerScore *peerScore) { + heap.Fix(ph, peerScore.index) +} + +// removePeer remove peer at specific index. +func (ph *peerHeap) removePeer(peerScore *peerScore) { + heap.Remove(ph, peerScore.index) +} + +// popPeer pops the top peer of the heap. +func (ph *peerHeap) popPeer() *peerScore { + return heap.Pop(ph).(*peerScore) +} + +// pushPeer pushes the new peer into the heap. +func (ph *peerHeap) pushPeer(peerScore *peerScore) { + ph.order++ + newOrder := ph.order + // randRange will affect the deviation of peer's chosenCount + randRange := ph.Len()/2 + 1 + peerScore.order = newOrder + uint64(ph.rng.Intn(randRange)) + heap.Push(ph, peerScore) +} + +func (ph *peerHeap) swapOrder(i, j int) { + if i == j { + return + } + + ph.peerScores[i].order, ph.peerScores[j].order = ph.peerScores[j].order, ph.peerScores[i].order + heap.Fix(ph, i) + heap.Fix(ph, j) +} + +// AddPeer adds a peer to the peer heap. +func (ph *peerHeap) addPeer(peerScore *peerScore) { + ph.pushPeer(peerScore) + + // Pick a random element, and swap the order with that peerScore. + r := ph.rng.Intn(ph.Len()) + ph.swapOrder(peerScore.index, r) +} + +// Exposed for testing purposes. +func (ph *peerHeap) peek() *peerScore { + return ph.peerScores[0] +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer_heap_test.go b/vendor/src/github.com/uber/tchannel-go/peer_heap_test.go new file mode 100644 index 00000000..5a5990b1 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_heap_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "math" + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestPeerHeap(t *testing.T) { + const numPeers = 10 + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + peerHeap := newPeerHeap() + + peerScores := make([]*peerScore, numPeers) + minScore := uint64(math.MaxInt64) + for i := 0; i < numPeers; i++ { + ps := newPeerScore(&Peer{}, uint64(r.Intn(numPeers*5))) + peerScores[i] = ps + if ps.score < minScore { + minScore = ps.score + } + } + + for i := 0; i < numPeers; i++ { + peerHeap.pushPeer(peerScores[i]) + } + + assert.Equal(t, numPeers, peerHeap.Len(), "Incorrect peer heap numPeers") + assert.Equal(t, minScore, peerHeap.peek().score, "peerHeap top peer is not minimum") + + lastScore := peerHeap.popPeer().score + for i := 1; i < numPeers; i++ { + assert.Equal(t, numPeers-i, peerHeap.Len(), "Incorrect peer heap numPeers") + score := peerHeap.popPeer().score + assert.True(t, score >= lastScore, "The order of the heap is invalid") + lastScore = score + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer_internal_test.go b/vendor/src/github.com/uber/tchannel-go/peer_internal_test.go new file mode 100644 index 00000000..bad6d53c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_internal_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsEphemeralHostPort(t *testing.T) { + tests := []struct { + hostPort string + want bool + }{ + {"", true}, + {ephemeralHostPort, true}, + {"127.0.0.1:0", true}, + {"10.1.1.1:0", true}, + {"127.0.0.1:1", false}, + {"10.1.1.1:1", false}, + } + + for _, tt := range tests { + got := isEphemeralHostPort(tt.hostPort) + assert.Equal(t, tt.want, got, "Unexpected result for %q", tt.hostPort) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer_strategies.go b/vendor/src/github.com/uber/tchannel-go/peer_strategies.go new file mode 100644 index 00000000..fd12e169 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_strategies.go @@ -0,0 +1,89 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "math" + +// ScoreCalculator defines the interface to calculate the score. +type ScoreCalculator interface { + GetScore(p *Peer) uint64 +} + +// ScoreCalculatorFunc is an adapter that allows functions to be used as ScoreCalculator +type ScoreCalculatorFunc func(p *Peer) uint64 + +// GetScore calls the underlying function. +func (f ScoreCalculatorFunc) GetScore(p *Peer) uint64 { + return f(p) +} + +type zeroCalculator struct{} + +func (zeroCalculator) GetScore(p *Peer) uint64 { + return 0 +} + +func newZeroCalculator() zeroCalculator { + return zeroCalculator{} +} + +type leastPendingCalculator struct{} + +func (leastPendingCalculator) GetScore(p *Peer) uint64 { + inbound, outbound := p.NumConnections() + if inbound+outbound == 0 { + return math.MaxUint64 + } + + return uint64(p.NumPendingOutbound()) +} + +// newLeastPendingCalculator returns a strategy prefers any connected peer. +// Within connected peers, least pending calls is used. Peers with less pending outbound calls +// get a smaller score. +func newLeastPendingCalculator() leastPendingCalculator { + return leastPendingCalculator{} +} + +type preferIncomingCalculator struct{} + +func (preferIncomingCalculator) GetScore(p *Peer) uint64 { + inbound, outbound := p.NumConnections() + if inbound+outbound == 0 { + return math.MaxUint64 + } + + numPendingOutbound := uint64(p.NumPendingOutbound()) + if inbound == 0 { + return math.MaxInt32 + numPendingOutbound + } + + return numPendingOutbound +} + +// newPreferIncomingCalculator returns a strategy that prefers peers with incoming connections. +// The scoring tiers are: +// Peers with incoming connections, peers with any connections, unconnected peers. +// Within each tier, least pending calls is used. Peers with less pending outbound calls +// get a smaller score. +func newPreferIncomingCalculator() preferIncomingCalculator { + return preferIncomingCalculator{} +} diff --git a/vendor/src/github.com/uber/tchannel-go/peer_test.go b/vendor/src/github.com/uber/tchannel-go/peer_test.go new file mode 100644 index 00000000..b465c868 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peer_test.go @@ -0,0 +1,1201 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "fmt" + "sort" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/benchmark" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" +) + +func fakePeer(t *testing.T, ch *Channel, hostPort string) *Peer { + ch.Peers().Add(hostPort) + + peer, err := ch.Peers().Get(nil) + require.NoError(t, err, "Unexpected error getting peer from heap.") + require.Equal(t, hostPort, peer.HostPort(), "Got unexpected peer.") + + in, out := peer.NumConnections() + require.Equal(t, 0, in, "Expected new peer to have no incoming connections.") + require.Equal(t, 0, out, "Expected new peer to have no outgoing connections.") + + return peer +} + +func assertNumConnections(t *testing.T, peer *Peer, in, out int) { + actualIn, actualOut := peer.NumConnections() + assert.Equal(t, actualIn, in, "Expected %v incoming connection.", in) + assert.Equal(t, actualOut, out, "Expected %v outgoing connection.", out) +} + +func TestGetPeerNoPeer(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + peer, err := ch.Peers().Get(nil) + assert.Equal(t, ErrNoPeers, err, "Empty peer list should return error") + assert.Nil(t, peer, "should not return peer") +} + +func TestGetPeerSinglePeer(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + ch.Peers().Add("1.1.1.1:1234") + + peer, err := ch.Peers().Get(nil) + assert.NoError(t, err, "peer list should return contained element") + assert.Equal(t, "1.1.1.1:1234", peer.HostPort(), "returned peer mismatch") +} + +func TestPeerUpdatesLen(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + assert.Zero(t, ch.Peers().Len()) + for i := 1; i < 5; i++ { + ch.Peers().Add(fmt.Sprintf("1.1.1.1:%d", i)) + assert.Equal(t, ch.Peers().Len(), i) + } + for i := 4; i > 0; i-- { + assert.Equal(t, ch.Peers().Len(), i) + ch.Peers().Remove(fmt.Sprintf("1.1.1.1:%d", i)) + } + assert.Zero(t, ch.Peers().Len()) +} + +func TestGetPeerAvoidPrevSelected(t *testing.T) { + const ( + peer1 = "1.1.1.1:1" + peer2 = "2.2.2.2:2" + peer3 = "3.3.3.3:3" + peer4 = "3.3.3.3:4" + ) + + ch := testutils.NewClient(t, nil) + defer ch.Close() + a, m := testutils.StrArray, testutils.StrMap + tests := []struct { + msg string + peers []string + prevSelected []string + expected map[string]struct{} + }{ + { + msg: "no prevSelected", + peers: a(peer1), + expected: m(peer1), + }, + { + msg: "ignore single hostPort in prevSelected", + peers: a(peer1, peer2), + prevSelected: a(peer1), + expected: m(peer2), + }, + { + msg: "ignore multiple hostPorts in prevSelected", + peers: a(peer1, peer2, peer3), + prevSelected: a(peer1, peer2), + expected: m(peer3), + }, + { + msg: "only peer is in prevSelected", + peers: a(peer1), + prevSelected: a(peer1), + expected: m(peer1), + }, + { + msg: "all peers are in prevSelected", + peers: a(peer1, peer2, peer3), + prevSelected: a(peer1, peer2, peer3), + expected: m(peer1, peer2, peer3), + }, + { + msg: "prevSelected host should be ignored", + peers: a(peer1, peer3, peer4), + prevSelected: a(peer3), + expected: m(peer1), + }, + { + msg: "prevSelected only has single host", + peers: a(peer3, peer4), + prevSelected: a(peer3), + expected: m(peer4), + }, + } + + for i, tt := range tests { + peers := ch.GetSubChannel(fmt.Sprintf("test%d", i), Isolated).Peers() + for _, p := range tt.peers { + peers.Add(p) + } + + rs := &RequestState{} + for _, selected := range tt.prevSelected { + rs.AddSelectedPeer(selected) + } + + gotPeer, err := peers.Get(rs.PrevSelectedPeers()) + if err != nil { + t.Errorf("Got unexpected error selecting peer: %v", err) + continue + } + + newPeer, err := peers.GetNew(rs.PrevSelectedPeers()) + if len(tt.peers) == len(tt.prevSelected) { + if newPeer != nil || err != ErrNoNewPeers { + t.Errorf("%s: newPeer should not have been found %v: %v\n", tt.msg, newPeer, err) + } + } else { + if gotPeer != newPeer || err != nil { + t.Errorf("%s: expected equal peers, got %v new %v: %v\n", + tt.msg, gotPeer, newPeer, err) + } + } + + got := gotPeer.HostPort() + if _, ok := tt.expected[got]; !ok { + t.Errorf("%s: got unexpected peer, expected one of %v got %v\n Peers = %v PrevSelected = %v", + tt.msg, tt.expected, got, tt.peers, tt.prevSelected) + } + } +} + +func TestPeerRemoveClosedConnection(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + client := testutils.NewClient(t, nil) + defer client.Close() + + p := client.Peers().Add(hostPort) + + c1, err := p.Connect(ctx) + require.NoError(t, err, "Failed to connect") + require.NoError(t, err, c1.Ping(ctx)) + + c2, err := p.Connect(ctx) + require.NoError(t, err, "Failed to connect") + require.NoError(t, err, c2.Ping(ctx)) + + require.NoError(t, c1.Close(), "Failed to close first connection") + _, outConns := p.NumConnections() + assert.Equal(t, 1, outConns, "Expected 1 remaining outgoing connection") + + c, err := p.GetConnection(ctx) + require.NoError(t, err, "GetConnection failed") + assert.Equal(t, c2, c, "Expected second active connection") + }) +} + +func TestPeerConnectCancelled(t *testing.T) { + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + ctx, cancel := NewContext(100 * time.Millisecond) + cancel() + + _, err := ch.Connect(ctx, "10.255.255.1:1") + require.Error(t, err, "Connect should fail") + assert.EqualError(t, err, ErrRequestCancelled.Error(), "Unexpected error") + }) +} + +func TestPeerGetConnectionWithNoActiveConnections(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + client := testutils.NewClient(t, nil) + defer client.Close() + + var ( + wg sync.WaitGroup + lock sync.Mutex + conn *Connection + concurrency = 10 + p = client.Peers().Add(hostPort) + ) + + for i := 0; i < concurrency; i++ { + wg.Add(1) + go func() { + defer wg.Done() + c, err := p.GetConnection(ctx) + require.NoError(t, err, "GetConnection failed") + + lock.Lock() + defer lock.Unlock() + + if conn == nil { + conn = c + } else { + assert.Equal(t, conn, c, "Expected the same active connection") + } + + }() + } + + wg.Wait() + + _, outbound := p.NumConnections() + assert.Equal(t, 1, outbound, "Expected 1 active outbound connetion") + }) +} + +func TestInboundEphemeralPeerRemoved(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + // No relay, since we look for the exact host:port in peer lists. + opts := testutils.NewOpts().NoRelay() + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + client := testutils.NewClient(t, nil) + assert.NoError(t, client.Ping(ctx, hostPort), "Ping to server failed") + + // Server should have a host:port in the root peers for the client. + var clientHP string + peers := ch.RootPeers().Copy() + for k := range peers { + clientHP = k + } + + waitTillInboundEmpty(t, ch, clientHP, func() { + client.Close() + }) + assert.Equal(t, ChannelClosed, client.State(), "Client should be closed") + + _, ok := ch.RootPeers().Get(clientHP) + assert.False(t, ok, "server's root peers should remove peer for client on close") + }) +} + +func TestOutboundEphemeralPeerRemoved(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + outbound := testutils.NewServer(t, testutils.NewOpts().SetServiceName("asd ")) + assert.NoError(t, ch.Ping(ctx, outbound.PeerInfo().HostPort), "Ping to outbound failed") + outboundHP := outbound.PeerInfo().HostPort + + // Server should have a peer for hostPort that should be gone. + waitTillNConnections(t, ch, outboundHP, 0, 0, func() { + outbound.Close() + }) + assert.Equal(t, ChannelClosed, outbound.State(), "Outbound should be closed") + + _, ok := ch.RootPeers().Get(outboundHP) + assert.False(t, ok, "server's root peers should remove outbound peer") + }) +} + +func TestOutboundPeerNotAdded(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + WithVerifiedServer(t, nil, func(server *Channel, hostPort string) { + server.Register(raw.Wrap(newTestHandler(t)), "echo") + + ch := testutils.NewClient(t, nil) + defer ch.Close() + + ch.Ping(ctx, hostPort) + raw.Call(ctx, ch, hostPort, server.PeerInfo().ServiceName, "echo", nil, nil) + + peer, err := ch.Peers().Get(nil) + assert.Equal(t, ErrNoPeers, err, "Ping should not add peers") + assert.Nil(t, peer, "Expected no peer to be returned") + }) +} + +func TestRemovePeerNotFound(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + + peers := ch.Peers() + peers.Add("1.1.1.1:1") + assert.Error(t, peers.Remove("not-found"), "Remove should fa") + assert.NoError(t, peers.Remove("1.1.1.1:1"), "Remove shouldn't fail for existing peer") +} + +func TestPeerRemovedFromRootPeers(t *testing.T) { + tests := []struct { + addHostPort bool + removeHostPort bool + expectFound bool + }{ + { + addHostPort: true, + expectFound: true, + }, + { + addHostPort: true, + removeHostPort: true, + expectFound: false, + }, + { + addHostPort: false, + expectFound: false, + }, + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + + for _, tt := range tests { + opts := testutils.NewOpts().NoRelay() + WithVerifiedServer(t, opts, func(server *Channel, hostPort string) { + ch := testutils.NewServer(t, nil) + clientHP := ch.PeerInfo().HostPort + + if tt.addHostPort { + server.Peers().Add(clientHP) + } + + assert.NoError(t, ch.Ping(ctx, hostPort), "Ping failed") + + if tt.removeHostPort { + require.NoError(t, server.Peers().Remove(clientHP), "Failed to remove peer") + } + + waitTillInboundEmpty(t, server, clientHP, func() { + ch.Close() + }) + + rootPeers := server.RootPeers() + _, found := rootPeers.Get(clientHP) + assert.Equal(t, tt.expectFound, found, "Peer found mismatch, addHostPort: %v", tt.addHostPort) + }) + } +} + +func TestPeerSelectionConnClosed(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + WithVerifiedServer(t, nil, func(server *Channel, hostPort string) { + client := testutils.NewServer(t, nil) + defer client.Close() + + // Ping will create an outbound connection from client -> server. + require.NoError(t, testutils.Ping(client, server), "Ping failed") + + waitTillInboundEmpty(t, server, client.PeerInfo().HostPort, func() { + peer, ok := client.RootPeers().Get(server.PeerInfo().HostPort) + require.True(t, ok, "Client has no peer for %v", server.PeerInfo()) + + conn, err := peer.GetConnection(ctx) + require.NoError(t, err, "Failed to get a connection") + conn.Close() + }) + + // Make sure the closed connection is not used. + for i := 0; i < 10; i++ { + require.NoError(t, testutils.Ping(client, server), "Ping failed") + } + }) +} + +func TestPeerSelectionPreferIncoming(t *testing.T) { + tests := []struct { + numIncoming, numOutgoing, numUnconnected int + isolated bool + expectedIncoming int + expectedOutgoing int + expectedUnconnected int + }{ + { + numIncoming: 5, + numOutgoing: 5, + numUnconnected: 5, + expectedIncoming: 5, + }, + { + numOutgoing: 5, + numUnconnected: 5, + expectedOutgoing: 5, + }, + { + numUnconnected: 5, + expectedUnconnected: 5, + }, + { + numIncoming: 5, + numOutgoing: 5, + numUnconnected: 5, + isolated: true, + expectedIncoming: 5, + expectedOutgoing: 5, + }, + { + numOutgoing: 5, + numUnconnected: 5, + isolated: true, + expectedOutgoing: 5, + }, + { + numIncoming: 5, + numUnconnected: 5, + isolated: true, + expectedIncoming: 5, + }, + { + numUnconnected: 5, + isolated: true, + expectedUnconnected: 5, + }, + } + + for _, tt := range tests { + // We need to directly connect from the server to the client and verify + // the exact peers. + opts := testutils.NewOpts().NoRelay() + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + selectedIncoming := make(map[string]int) + selectedOutgoing := make(map[string]int) + selectedUnconnected := make(map[string]int) + + peers := ch.Peers() + if tt.isolated { + peers = ch.GetSubChannel("isolated", Isolated).Peers() + } + + // 5 peers that make incoming connections to ch. + for i := 0; i < tt.numIncoming; i++ { + incoming, _, incomingHP := NewServer(t, &testutils.ChannelOpts{ServiceName: fmt.Sprintf("incoming%d", i)}) + defer incoming.Close() + assert.NoError(t, incoming.Ping(ctx, ch.PeerInfo().HostPort), "Ping failed") + peers.Add(incomingHP) + selectedIncoming[incomingHP] = 0 + } + + // 5 random peers that don't have any connections. + for i := 0; i < tt.numUnconnected; i++ { + hp := fmt.Sprintf("1.1.1.1:1%d", i) + peers.Add(hp) + selectedUnconnected[hp] = 0 + } + + // 5 random peers that we have outgoing connections to. + for i := 0; i < tt.numOutgoing; i++ { + outgoing, _, outgoingHP := NewServer(t, &testutils.ChannelOpts{ServiceName: fmt.Sprintf("outgoing%d", i)}) + defer outgoing.Close() + assert.NoError(t, ch.Ping(ctx, outgoingHP), "Ping failed") + peers.Add(outgoingHP) + selectedOutgoing[outgoingHP] = 0 + } + + var mu sync.Mutex + checkMap := func(m map[string]int, peer string) bool { + mu.Lock() + defer mu.Unlock() + + if _, ok := m[peer]; !ok { + return false + } + + m[peer]++ + return true + } + + numSelectedPeers := func(m map[string]int) int { + count := 0 + for _, v := range m { + if v > 0 { + count++ + } + } + return count + } + + peerCheck := func() { + for i := 0; i < 100; i++ { + peer, err := peers.Get(nil) + if assert.NoError(t, err, "Peers.Get failed") { + peerHP := peer.HostPort() + inMap := checkMap(selectedIncoming, peerHP) || + checkMap(selectedOutgoing, peerHP) || + checkMap(selectedUnconnected, peerHP) + assert.True(t, inMap, "Couldn't find peer %v in any of our maps", peerHP) + } + } + } + + // Now select peers in parallel + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + peerCheck() + }() + } + wg.Wait() + + assert.Equal(t, tt.expectedIncoming, numSelectedPeers(selectedIncoming), + "Selected incoming mismatch: %v", selectedIncoming) + assert.Equal(t, tt.expectedOutgoing, numSelectedPeers(selectedOutgoing), + "Selected outgoing mismatch: %v", selectedOutgoing) + assert.Equal(t, tt.expectedUnconnected, numSelectedPeers(selectedUnconnected), + "Selected unconnected mismatch: %v", selectedUnconnected) + }) + } +} + +type peerTest struct { + t testing.TB + channels []*Channel +} + +// NewService will return a new server channel and the host port. +func (pt *peerTest) NewService(t testing.TB, svcName, processName string) (*Channel, string) { + opts := testutils.NewOpts().SetServiceName(svcName).SetProcessName(processName) + ch := testutils.NewServer(t, opts) + pt.channels = append(pt.channels, ch) + return ch, ch.PeerInfo().HostPort +} + +// CleanUp will clean up all channels started as part of the peer test. +func (pt *peerTest) CleanUp() { + for _, ch := range pt.channels { + ch.Close() + } +} + +func TestPeerSelection(t *testing.T) { + pt := &peerTest{t: t} + defer pt.CleanUp() + WithVerifiedServer(t, &testutils.ChannelOpts{ServiceName: "S1"}, func(ch *Channel, hostPort string) { + doPing := func(ch *Channel) { + ctx, cancel := NewContext(time.Second) + defer cancel() + assert.NoError(t, ch.Ping(ctx, hostPort), "Ping failed") + } + + strategy, count := createScoreStrategy(0, 1) + s2, _ := pt.NewService(t, "S2", "S2") + defer s2.Close() + + s2.GetSubChannel("S1").Peers().SetStrategy(strategy) + s2.GetSubChannel("S1").Peers().Add(hostPort) + doPing(s2) + assert.EqualValues(t, 4, count.Load(), + "Expect 4 exchange updates: peer add, new conn, ping, pong") + }) +} + +func getAllPeers(t *testing.T, pl *PeerList) []string { + prevSelected := make(map[string]struct{}) + var got []string + + for { + peer, err := pl.Get(prevSelected) + require.NoError(t, err, "Peer.Get failed") + + hp := peer.HostPort() + if _, ok := prevSelected[hp]; ok { + break + } + + prevSelected[hp] = struct{}{} + got = append(got, hp) + } + + return got +} + +func reverse(s []string) { + for i := 0; i < len(s)/2; i++ { + j := len(s) - i - 1 + s[i], s[j] = s[j], s[i] + } +} + +func TestIsolatedPeerHeap(t *testing.T) { + const numPeers = 10 + ch := testutils.NewClient(t, nil) + defer ch.Close() + + ps1 := createSubChannelWNewStrategy(ch, "S1", numPeers, 1) + ps2 := createSubChannelWNewStrategy(ch, "S2", numPeers, -1, Isolated) + + hostports := make([]string, numPeers) + for i := 0; i < numPeers; i++ { + hostports[i] = fmt.Sprintf("127.0.0.1:%d", i) + ps1.Add(hostports[i]) + ps2.Add(hostports[i]) + } + + ps1Expected := append([]string(nil), hostports...) + assert.Equal(t, ps1Expected, getAllPeers(t, ps1), "Unexpected peer order") + + ps2Expected := append([]string(nil), hostports...) + reverse(ps2Expected) + assert.Equal(t, ps2Expected, getAllPeers(t, ps2), "Unexpected peer order") +} + +func TestPeerSelectionRanking(t *testing.T) { + const numPeers = 10 + const numIterations = 1000 + + // Selected is a map from rank -> [peer, count] + // It tracks how often a peer gets selected at a specific rank. + selected := make([]map[string]int, numPeers) + for i := 0; i < numPeers; i++ { + selected[i] = make(map[string]int) + } + + for i := 0; i < numIterations; i++ { + ch := testutils.NewClient(t, nil) + defer ch.Close() + ch.SetRandomSeed(int64(i * 100)) + + for i := 0; i < numPeers; i++ { + hp := fmt.Sprintf("127.0.0.1:60%v", i) + ch.Peers().Add(hp) + } + + for i := 0; i < numPeers; i++ { + peer, err := ch.Peers().Get(nil) + require.NoError(t, err, "Peers.Get failed") + selected[i][peer.HostPort()]++ + } + } + + for _, m := range selected { + testDistribution(t, m, 50, 150) + } +} + +func createScoreStrategy(initial, delta int64) (calc ScoreCalculator, retCount *atomic.Uint64) { + var ( + count atomic.Uint64 + score atomic.Uint64 + ) + + return ScoreCalculatorFunc(func(p *Peer) uint64 { + count.Add(1) + return score.Add(uint64(delta)) + }), &count +} + +func createSubChannelWNewStrategy(ch *Channel, name string, initial, delta int64, opts ...SubChannelOption) *PeerList { + strategy, _ := createScoreStrategy(initial, delta) + sc := ch.GetSubChannel(name, opts...) + ps := sc.Peers() + ps.SetStrategy(strategy) + return ps +} + +func testDistribution(t testing.TB, counts map[string]int, min, max float64) { + for k, v := range counts { + if float64(v) < min || float64(v) > max { + t.Errorf("Key %v has value %v which is out of range %v-%v", k, v, min, max) + } + } +} + +// waitTillNConnetions will run f which should end up causing the peer with hostPort in ch +// to have the specified number of inbound and outbound connections. +// If the number of connections does not match after a second, the test is failed. +func waitTillNConnections(t *testing.T, ch *Channel, hostPort string, inbound, outbound int, f func()) { + peer, ok := ch.RootPeers().Get(hostPort) + if !ok { + return + } + + var ( + i = -1 + o = -1 + ) + + inboundEmpty := make(chan struct{}) + var onUpdateOnce sync.Once + onUpdate := func(p *Peer) { + if i, o = p.NumConnections(); (i == inbound || inbound == -1) && + (o == outbound || outbound == -1) { + onUpdateOnce.Do(func() { + close(inboundEmpty) + }) + } + } + peer.SetOnUpdate(onUpdate) + + f() + + select { + case <-inboundEmpty: + return + case <-time.After(time.Second): + t.Errorf("Timed out waiting for peer %v to have (in: %v, out: %v) connections, got (in: %v, out: %v)", + hostPort, inbound, outbound, i, o) + } +} + +// waitTillInboundEmpty will run f which should end up causing the peer with hostPort in ch +// to have 0 inbound connections. It will fail the test after a second. +func waitTillInboundEmpty(t *testing.T, ch *Channel, hostPort string, f func()) { + waitTillNConnections(t, ch, hostPort, 0, -1, f) +} + +type peerSelectionTest struct { + peerTest + + // numPeers is the number of peers added to the client channel. + numPeers int + // numAffinity is the number of affinity nodes. + numAffinity int + // numAffinityWithNoCall is the number of affinity nodes which doesn't send call req to client. + numAffinityWithNoCall int + // numConcurrent is the number of concurrent goroutine to make outbound calls. + numConcurrent int + // hasInboundCall is the bool flag to tell whether to have inbound calls from affinity nodes + hasInboundCall bool + + servers []*Channel + affinity []*Channel + affinityWithNoCall []*Channel + client *Channel +} + +func (pt *peerSelectionTest) setup(t testing.TB) { + pt.setupServers(t) + pt.setupClient(t) + pt.setupAffinity(t) +} + +// setupServers will create numPeer servers, and register handlers on them. +func (pt *peerSelectionTest) setupServers(t testing.TB) { + pt.servers = make([]*Channel, pt.numPeers) + + // Set up numPeers servers. + for i := 0; i < pt.numPeers; i++ { + pt.servers[i], _ = pt.NewService(t, "server", fmt.Sprintf("server-%v", i)) + pt.servers[i].Register(raw.Wrap(newTestHandler(pt.t)), "echo") + } +} + +func (pt *peerSelectionTest) setupAffinity(t testing.TB) { + pt.affinity = make([]*Channel, pt.numAffinity) + for i := range pt.affinity { + pt.affinity[i] = pt.servers[i] + } + + pt.affinityWithNoCall = make([]*Channel, pt.numAffinityWithNoCall) + for i := range pt.affinityWithNoCall { + pt.affinityWithNoCall[i] = pt.servers[i+pt.numAffinity] + } + + var wg sync.WaitGroup + wg.Add(pt.numAffinity) + // Connect from the affinity nodes to the service. + hostport := pt.client.PeerInfo().HostPort + serviceName := pt.client.PeerInfo().ServiceName + for _, affinity := range pt.affinity { + go func(affinity *Channel) { + affinity.Peers().Add(hostport) + pt.makeCall(affinity.GetSubChannel(serviceName)) + wg.Done() + }(affinity) + } + wg.Wait() + + wg.Add(pt.numAffinityWithNoCall) + for _, p := range pt.affinityWithNoCall { + go func(p *Channel) { + // use ping to build connection without sending call req. + pt.sendPing(p, hostport) + wg.Done() + }(p) + } + wg.Wait() +} + +func (pt *peerSelectionTest) setupClient(t testing.TB) { + pt.client, _ = pt.NewService(t, "client", "client") + pt.client.Register(raw.Wrap(newTestHandler(pt.t)), "echo") + for _, server := range pt.servers { + pt.client.Peers().Add(server.PeerInfo().HostPort) + } +} + +func (pt *peerSelectionTest) makeCall(sc *SubChannel) { + ctx, cancel := NewContext(time.Second) + defer cancel() + _, _, _, err := raw.CallSC(ctx, sc, "echo", nil, nil) + assert.NoError(pt.t, err, "raw.Call failed") +} + +func (pt *peerSelectionTest) sendPing(ch *Channel, hostport string) { + ctx, cancel := NewContext(time.Second) + defer cancel() + err := ch.Ping(ctx, hostport) + assert.NoError(pt.t, err, "ping failed") +} + +func (pt *peerSelectionTest) runStressSimple(b *testing.B) { + var wg sync.WaitGroup + wg.Add(pt.numConcurrent) + + // server outbound request + sc := pt.client.GetSubChannel("server") + for i := 0; i < pt.numConcurrent; i++ { + go func(sc *SubChannel) { + defer wg.Done() + for j := 0; j < b.N; j++ { + pt.makeCall(sc) + } + }(sc) + } + + wg.Wait() +} + +func (pt *peerSelectionTest) runStress() { + numClock := pt.numConcurrent + pt.numAffinity + clocks := make([]chan struct{}, numClock) + for i := 0; i < numClock; i++ { + clocks[i] = make(chan struct{}) + } + + var wg sync.WaitGroup + wg.Add(numClock) + + // helper that will make a request every n ticks. + reqEveryNTicks := func(n int, sc *SubChannel, clock <-chan struct{}) { + defer wg.Done() + for { + for i := 0; i < n; i++ { + _, ok := <-clock + if !ok { + return + } + } + pt.makeCall(sc) + } + } + + // server outbound request + sc := pt.client.GetSubChannel("server") + for i := 0; i < pt.numConcurrent; i++ { + go reqEveryNTicks(1, sc, clocks[i]) + } + // affinity incoming requests + if pt.hasInboundCall { + serviceName := pt.client.PeerInfo().ServiceName + for i, affinity := range pt.affinity { + go reqEveryNTicks(1, affinity.GetSubChannel(serviceName), clocks[i+pt.numConcurrent]) + } + } + + tickAllClocks := func() { + for i := 0; i < numClock; i++ { + clocks[i] <- struct{}{} + } + } + + const tickNum = 10000 + for i := 0; i < tickNum; i++ { + if i%(tickNum/10) == 0 { + fmt.Printf("Stress test progress: %v\n", 100*i/tickNum) + } + tickAllClocks() + } + + for i := 0; i < numClock; i++ { + close(clocks[i]) + } + wg.Wait() +} + +// Run these commands before run the benchmark. +// sudo sysctl w kern.maxfiles=50000 +// ulimit n 50000 +func BenchmarkSimplePeerHeapPerf(b *testing.B) { + pt := &peerSelectionTest{ + peerTest: peerTest{t: b}, + numPeers: 1000, + numConcurrent: 100, + } + defer pt.CleanUp() + pt.setup(b) + b.ResetTimer() + pt.runStressSimple(b) +} + +func TestPeerHeapPerf(t *testing.T) { + CheckStress(t) + + tests := []struct { + numserver int + affinityRatio float64 + numConcurrent int + hasInboundCall bool + }{ + { + numserver: 1000, + affinityRatio: 0.1, + numConcurrent: 5, + hasInboundCall: true, + }, + { + numserver: 1000, + affinityRatio: 0.1, + numConcurrent: 1, + hasInboundCall: true, + }, + { + numserver: 100, + affinityRatio: 0.1, + numConcurrent: 1, + hasInboundCall: true, + }, + } + + for _, tt := range tests { + peerHeapStress(t, tt.numserver, tt.affinityRatio, tt.numConcurrent, tt.hasInboundCall) + } +} + +func peerHeapStress(t testing.TB, numserver int, affinityRatio float64, numConcurrent int, hasInboundCall bool) { + pt := &peerSelectionTest{ + peerTest: peerTest{t: t}, + numPeers: numserver, + numConcurrent: numConcurrent, + hasInboundCall: hasInboundCall, + numAffinity: int(float64(numserver) * affinityRatio), + numAffinityWithNoCall: 3, + } + defer pt.CleanUp() + pt.setup(t) + pt.runStress() + validateStressTest(t, pt.client, pt.numAffinity, pt.numAffinityWithNoCall) +} + +func validateStressTest(t testing.TB, server *Channel, numAffinity int, numAffinityWithNoCall int) { + state := server.IntrospectState(&IntrospectionOptions{IncludeEmptyPeers: true}) + + countsByPeer := make(map[string]int) + var counts []int + for _, peer := range state.Peers { + p, ok := state.RootPeers[peer.HostPort] + assert.True(t, ok, "Missing peer.") + if p.ChosenCount != 0 { + countsByPeer[p.HostPort] = int(p.ChosenCount) + counts = append(counts, int(p.ChosenCount)) + } + } + // when number of affinity is zero, all peer suppose to be chosen. + if numAffinity == 0 && numAffinityWithNoCall == 0 { + numAffinity = len(state.Peers) + } + assert.EqualValues(t, len(countsByPeer), numAffinity+numAffinityWithNoCall, "Number of affinities nodes mismatch.") + sort.Ints(counts) + median := counts[len(counts)/2] + testDistribution(t, countsByPeer, float64(median)*0.9, float64(median)*1.1) +} + +func TestPeerSelectionAfterClosed(t *testing.T) { + pt := &peerSelectionTest{ + peerTest: peerTest{t: t}, + numPeers: 5, + numAffinity: 5, + } + defer pt.CleanUp() + pt.setup(t) + + toClose := pt.affinity[pt.numAffinity-1] + closedHP := toClose.PeerInfo().HostPort + toClose.Logger().Debugf("About to Close %v", closedHP) + waitTillInboundEmpty(t, pt.client, closedHP, func() { + toClose.Close() + }) + + for i := 0; i < 10*pt.numAffinity; i++ { + peer, err := pt.client.Peers().Get(nil) + assert.NoError(t, err, "Client failed to select a peer") + assert.NotEqual(pt.t, closedHP, peer.HostPort(), "Closed peer shouldn't be chosen") + } +} + +func TestPeerScoreOnNewConnection(t *testing.T) { + tests := []struct { + message string + connect func(s1, s2 *Channel) *Peer + }{ + { + message: "outbound connection", + connect: func(s1, s2 *Channel) *Peer { + return s1.Peers().GetOrAdd(s2.PeerInfo().HostPort) + }, + }, + { + message: "inbound connection", + connect: func(s1, s2 *Channel) *Peer { + return s2.Peers().GetOrAdd(s1.PeerInfo().HostPort) + }, + }, + } + + getScore := func(pl *PeerList) uint64 { + peers := pl.IntrospectList(nil) + require.Equal(t, 1, len(peers), "Wrong number of peers") + return peers[0].Score + } + + for _, tt := range tests { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + s1 := ts.Server() + s2 := ts.NewServer(nil) + + s1.Peers().Add(s2.PeerInfo().HostPort) + s2.Peers().Add(s1.PeerInfo().HostPort) + + initialScore := getScore(s1.Peers()) + peer := tt.connect(s1, s2) + conn, err := peer.GetConnection(ctx) + require.NoError(t, err, "%v: GetConnection failed", tt.message) + + // When receiving an inbound connection, the outbound connect may return + // before the inbound has updated the score, so we may need to retry. + assert.True(t, testutils.WaitFor(time.Second, func() bool { + connectedScore := getScore(s1.Peers()) + return connectedScore < initialScore + }), "%v: Expected connected peer score %v to be less than initial score %v", + tt.message, getScore(s1.Peers()), initialScore) + + // Ping to ensure the connection has been added to peers on both sides. + require.NoError(t, conn.Ping(ctx), "%v: Ping failed", tt.message) + }) + } +} + +func TestConnectToPeerHostPortMismatch(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Set up a relay which will have a different host:port than the + // real TChannel HostPort. + relay, err := benchmark.NewTCPRawRelay([]string{ts.HostPort()}) + require.NoError(t, err, "Failed to set up TCP relay") + defer relay.Close() + + s2 := ts.NewServer(nil) + for i := 0; i < 10; i++ { + require.NoError(t, s2.Ping(ctx, relay.HostPort()), "Ping failed") + } + + assert.Equal(t, 1, s2.IntrospectNumConnections(), "Unexpected number of connections") + }) +} + +// Test ensures that a closing connection does not count in NumConnections. +// NumConnections should only include connections that be used to make calls. +func TestPeerConnectionsClosing(t *testing.T) { + // Disable the relay since we check the host:port directly. + opts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + unblock := make(chan struct{}) + gotCall := make(chan struct{}) + testutils.RegisterEcho(ts.Server(), func() { + close(gotCall) + <-unblock + }) + + client := ts.NewServer(nil) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + }() + + // Wait for the call to be received before checking connections.. + <-gotCall + peer := ts.Server().Peers().GetOrAdd(client.PeerInfo().HostPort) + in, out := peer.NumConnections() + assert.Equal(t, 1, in+out, "Unexpected number of incoming connections") + + // Now when we try to close the channel, all the connections will change + // state, and should no longer count as active connections. + conn, err := peer.GetConnection(nil) + require.NoError(t, err, "Failed to get connection") + require.True(t, conn.IsActive(), "Connection should be active") + + ts.Server().Close() + require.False(t, conn.IsActive(), "Connection should not be active after Close") + in, out = peer.NumConnections() + assert.Equal(t, 0, in+out, "Inactive connections should not be included in peer LAST") + + close(unblock) + wg.Wait() + }) +} + +func BenchmarkAddPeers(b *testing.B) { + for i := 0; i < b.N; i++ { + ch := testutils.NewClient(b, nil) + for i := 0; i < 1000; i++ { + hp := fmt.Sprintf("127.0.0.1:%v", i) + ch.Peers().Add(hp) + } + } +} + +func TestPeerSelectionStrategyChange(t *testing.T) { + const numPeers = 2 + + ch := testutils.NewClient(t, nil) + defer ch.Close() + + for i := 0; i < numPeers; i++ { + ch.Peers().Add(fmt.Sprintf("127.0.0.1:60%v", i)) + } + + for _, score := range []uint64{1000, 2000} { + ch.Peers().SetStrategy(createConstScoreStrategy(score)) + for _, v := range ch.Peers().IntrospectList(nil) { + assert.Equal(t, v.Score, score) + } + } +} + +func createConstScoreStrategy(score uint64) (calc ScoreCalculator) { + return ScoreCalculatorFunc(func(p *Peer) uint64 { + return score + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/peers/doc.go b/vendor/src/github.com/uber/tchannel-go/peers/doc.go new file mode 100644 index 00000000..082a8833 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peers/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* +Package peers provides helpers for managing TChannel peers. +*/ +package peers diff --git a/vendor/src/github.com/uber/tchannel-go/peers/prefer.go b/vendor/src/github.com/uber/tchannel-go/peers/prefer.go new file mode 100644 index 00000000..e7c2076a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peers/prefer.go @@ -0,0 +1,70 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package peers + +import "github.com/uber/tchannel-go" + +type hrwScoreCalc struct { + clientID uint32 +} + +// NewHRWScorer returns a ScoreCalculator based on Rendezvous or Highest Random Weight +// hashing. +// It is useful for distributing load in peer-to-peer situations where we have +// many clients picking from a set of servers with "sticky" semantics that will +// spread load evenly as servers go down or new servers are added. +// The clientID is used to score the servers, so each client should pass in +// a unique client ID. +func NewHRWScorer(clientID uint32) tchannel.ScoreCalculator { + return &hrwScoreCalc{mod2_31(clientID)} +} + +func (s *hrwScoreCalc) GetScore(p *tchannel.Peer) uint64 { + server := mod2_31(fnv32a(p.HostPort())) + + // These constants are taken from W_rand2 in the Rendezvous paper: + // http://www.eecs.umich.edu/techreports/cse/96/CSE-TR-316-96.pdf + v := 1103515245*((1103515245*s.clientID+12345)^server) + 12345 + return uint64(mod2_31(v)) +} + +func mod2_31(v uint32) uint32 { + return v & ((1 << 31) - 1) +} + +// This is based on the standard library's fnv32a implementation. +// We copy it for a couple of reasons: +// 1. Avoid allocations to create a hash.Hash32 +// 2. Avoid converting the []byte to a string (another allocation) since +// the Hash32 interface only supports writing bytes. +func fnv32a(s string) uint32 { + const ( + initial = 2166136261 + prime = 16777619 + ) + + hash := uint32(initial) + for i := 0; i < len(s); i++ { + hash ^= uint32(s[i]) + hash *= prime + } + return hash +} diff --git a/vendor/src/github.com/uber/tchannel-go/peers/prefer_test.go b/vendor/src/github.com/uber/tchannel-go/peers/prefer_test.go new file mode 100644 index 00000000..8a2ee5ae --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/peers/prefer_test.go @@ -0,0 +1,189 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package peers + +import ( + "fmt" + "hash/fnv" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +func TestHRWScorerGetScore(t *testing.T) { + client := testutils.NewClient(t, nil) + peer := client.Peers().GetOrAdd("1.1.1.1") + + c1 := NewHRWScorer(1) + c2 := NewHRWScorer(2) + + assert.NotEqual(t, c1.GetScore(peer), c2.GetScore(peer)) +} + +func TestHRWScorerDistribution(t *testing.T) { + const ( + numClients = 1000 + numServers = 10 + ) + + ch := testutils.NewClient(t, nil) + servers := make([]*tchannel.Peer, numServers) + for i := range servers { + servers[i] = ch.Peers().GetOrAdd(fmt.Sprintf("192.0.2.%v", i)) + } + + serverSelected := make([]int, numServers) + for i := 0; i < numClients; i++ { + client := NewHRWScorer(uint32(i)) + + highestScore := uint64(0) + highestServer := -1 + for s, server := range servers { + if score := client.GetScore(server); score > highestScore { + highestScore = score + highestServer = s + } + } + serverSelected[highestServer]++ + } + + // We can't get a perfect distribution, but should be within 20%. + const ( + expectCalls = numClients / numServers + delta = expectCalls * 0.2 + ) + for serverIdx, count := range serverSelected { + assert.InDelta(t, expectCalls, count, delta, "Server %v out of range", serverIdx) + } +} + +func countingServer(t *testing.T, opts *testutils.ChannelOpts) (*tchannel.Channel, *atomic.Int32) { + var cnt atomic.Int32 + server := testutils.NewServer(t, opts) + testutils.RegisterEcho(server, func() { cnt.Inc() }) + return server, &cnt +} + +func TestHRWScorerIntegration(t *testing.T) { + // Client pings to the server may cause errors during Close. + sOpts := testutils.NewOpts().SetServiceName("svc").DisableLogVerification() + s1, s1Count := countingServer(t, sOpts) + s2, s2Count := countingServer(t, sOpts) + + client := testutils.NewClient(t, testutils.NewOpts().DisableLogVerification()) + client.Peers().SetStrategy(NewHRWScorer(1)) + client.Peers().Add(s1.PeerInfo().HostPort) + client.Peers().Add(s2.PeerInfo().HostPort) + + // We want to call the raw echo function with TChannel retries. + callEcho := func() error { + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + return client.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error { + _, err := raw.CallV2(ctx, client.GetSubChannel("svc"), raw.CArgs{ + Method: "echo", + CallOptions: &tchannel.CallOptions{ + RequestState: rs, + }, + }) + return err + }) + } + + preferred, err := client.Peers().Get(nil) + require.NoError(t, err, "Failed to get peer") + if preferred.HostPort() == s2.PeerInfo().HostPort { + // To make the test easier, we want "s1" to always be the preferred hostPort. + s1, s1Count, s2, s2Count = s2, s2Count, s1, s1Count + } + + // When we make 10 calls, all of them should go to s1 + for i := 0; i < 10; i++ { + err := callEcho() + require.NoError(t, err, "Failed to call echo initially") + } + assert.EqualValues(t, 10, s1Count.Load(), "All calls should go to s1") + + // Stop s1, and ensure the client notices S1 has failed. + s1.Close() + testutils.WaitFor(time.Second, func() bool { + if !s1.Closed() { + return false + } + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + return client.Ping(ctx, s1.PeerInfo().HostPort) != nil + }) + + // Since s1 is stopped, next call should go to s2. + err = callEcho() + require.NoError(t, err, "Failed to call echo after s1 close") + assert.EqualValues(t, 10, s1Count.Load(), "s1 should not get new calls as it's down") + assert.EqualValues(t, 1, s2Count.Load(), "New call should go to s2") + + // And if s1 comes back, calls should resume to s1. + s1Up := testutils.NewClient(t, sOpts) + testutils.RegisterEcho(s1Up, func() { s1Count.Inc() }) + err = s1Up.ListenAndServe(s1.PeerInfo().HostPort) + require.NoError(t, err, "Failed to bring up a new channel as s1") + + for i := 0; i < 10; i++ { + require.NoError(t, callEcho(), "Failed to call echo after s1 restarted") + } + assert.EqualValues(t, 20, s1Count.Load(), "Once s1 is up, calls should resume to s1") + assert.EqualValues(t, 1, s2Count.Load(), "s2 should not receive calls after s1 restarted") +} + +func stdFnv32a(s string) uint32 { + h := fnv.New32a() + h.Write([]byte(s)) + return h.Sum32() +} + +func TestFnv32a(t *testing.T) { + tests := []string{ + "", + "1.1.1.1", + "some-other-data", + } + + for _, tt := range tests { + assert.Equal(t, stdFnv32a(tt), fnv32a(tt), "Different results for %q", tt) + } +} + +func BenchmarkHrwScoreCalc(b *testing.B) { + client := testutils.NewClient(b, nil) + peer := client.Peers().GetOrAdd("1.1.1.1") + + c := NewHRWScorer(1) + for i := 0; i < b.N; i++ { + c.GetScore(peer) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/pprof/pprof.go b/vendor/src/github.com/uber/tchannel-go/pprof/pprof.go new file mode 100644 index 00000000..d6f968b5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/pprof/pprof.go @@ -0,0 +1,54 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package pprof + +import ( + "net/http" + _ "net/http/pprof" // So pprof endpoints are registered on DefaultServeMux. + + "github.com/uber/tchannel-go" + thttp "github.com/uber/tchannel-go/http" + + "golang.org/x/net/context" +) + +func serveHTTP(req *http.Request, response *tchannel.InboundCallResponse) { + rw, finish := thttp.ResponseWriter(response) + http.DefaultServeMux.ServeHTTP(rw, req) + finish() +} + +// Register registers pprof endpoints on the given registrar under _pprof. +// The _pprof endpoint uses as-http and is a tunnel to the default serve mux. +func Register(registrar tchannel.Registrar) { + handler := func(ctx context.Context, call *tchannel.InboundCall) { + req, err := thttp.ReadRequest(call) + if err != nil { + registrar.Logger().WithFields( + tchannel.LogField{Key: "err", Value: err.Error()}, + ).Warn("Failed to read HTTP request.") + return + } + + serveHTTP(req, call.Response()) + } + registrar.Register(tchannel.HandlerFunc(handler), "_pprof") +} diff --git a/vendor/src/github.com/uber/tchannel-go/pprof/pprof_test.go b/vendor/src/github.com/uber/tchannel-go/pprof/pprof_test.go new file mode 100644 index 00000000..a49ca2d3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/pprof/pprof_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package pprof + +import ( + "io/ioutil" + "net/http" + "testing" + "time" + + "github.com/uber/tchannel-go" + thttp "github.com/uber/tchannel-go/http" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPProfEndpoint(t *testing.T) { + ch := testutils.NewServer(t, nil) + Register(ch) + + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + + req, err := http.NewRequest("GET", "/debug/pprof/block?debug=1", nil) + require.NoError(t, err, "NewRequest failed") + + call, err := ch.BeginCall(ctx, ch.PeerInfo().HostPort, ch.ServiceName(), "_pprof", nil) + require.NoError(t, err, "BeginCall failed") + require.NoError(t, err, thttp.WriteRequest(call, req), "thttp.WriteRequest failed") + + response, err := thttp.ReadResponse(call.Response()) + require.NoError(t, err, "ReadResponse failed") + + assert.Equal(t, http.StatusOK, response.StatusCode) + body, err := ioutil.ReadAll(response.Body) + if assert.NoError(t, err, "Read body failed") { + assert.Contains(t, string(body), "contention", "Response does not contain expected string") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/preinit_connection.go b/vendor/src/github.com/uber/tchannel-go/preinit_connection.go new file mode 100644 index 00000000..a130d736 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/preinit_connection.go @@ -0,0 +1,250 @@ +// Copyright (c) 2017 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "net" + "strconv" + "time" + + "golang.org/x/net/context" +) + +func (ch *Channel) outboundHandshake(ctx context.Context, c net.Conn, outboundHP string, events connectionEvents) (_ *Connection, err error) { + defer setInitDeadline(ctx, c)() + defer func() { + err = ch.initError(c, outbound, 1, err) + }() + + msg := &initReq{initMessage: ch.getInitMessage(ctx, 1)} + if err := ch.writeMessage(c, msg); err != nil { + return nil, err + } + + res := &initRes{} + id, err := ch.readMessage(c, res) + if err != nil { + return nil, err + } + + if id != msg.id { + return nil, NewSystemError(ErrCodeProtocol, "received initRes with invalid ID, wanted %v, got %v", msg.id, id) + } + + if res.Version != CurrentProtocolVersion { + return nil, unsupportedProtocolVersion(res.Version) + } + + remotePeer, remotePeerAddress, err := parseRemotePeer(res.initParams, c.RemoteAddr()) + if err != nil { + return nil, NewWrappedSystemError(ErrCodeProtocol, err) + } + + return ch.newConnection(c, 1 /* initialID */, outboundHP, remotePeer, remotePeerAddress, events), nil +} + +func (ch *Channel) inboundHandshake(ctx context.Context, c net.Conn, events connectionEvents) (_ *Connection, err error) { + id := uint32(math.MaxUint32) + + defer setInitDeadline(ctx, c)() + defer func() { + err = ch.initError(c, inbound, id, err) + }() + + req := &initReq{} + id, err = ch.readMessage(c, req) + if err != nil { + return nil, err + } + + if req.Version < CurrentProtocolVersion { + return nil, unsupportedProtocolVersion(req.Version) + } + + remotePeer, remotePeerAddress, err := parseRemotePeer(req.initParams, c.RemoteAddr()) + if err != nil { + return nil, NewWrappedSystemError(ErrCodeProtocol, err) + } + + res := &initRes{initMessage: ch.getInitMessage(ctx, id)} + if err := ch.writeMessage(c, res); err != nil { + return nil, err + } + + return ch.newConnection(c, 0 /* initialID */, "" /* outboundHP */, remotePeer, remotePeerAddress, events), nil +} + +func (ch *Channel) getInitParams() initParams { + localPeer := ch.PeerInfo() + return initParams{ + InitParamHostPort: localPeer.HostPort, + InitParamProcessName: localPeer.ProcessName, + InitParamTChannelLanguage: localPeer.Version.Language, + InitParamTChannelLanguageVersion: localPeer.Version.LanguageVersion, + InitParamTChannelVersion: localPeer.Version.TChannelVersion, + } +} + +func (ch *Channel) getInitMessage(ctx context.Context, id uint32) initMessage { + msg := initMessage{ + id: id, + Version: CurrentProtocolVersion, + initParams: ch.getInitParams(), + } + if p := getTChannelParams(ctx); p != nil && p.hideListeningOnOutbound { + msg.initParams[InitParamHostPort] = ephemeralHostPort + } + + return msg +} + +func (ch *Channel) initError(c net.Conn, connDir connectionDirection, id uint32, err error) error { + if err == nil { + return nil + } + + ch.log.WithFields(LogFields{ + {"connectionDirection", connDir}, + {"localAddr", c.LocalAddr()}, + {"remoteAddr", c.RemoteAddr()}, + ErrField(err), + }...).Error("Failed during connection handshake.") + + if ne, ok := err.(net.Error); ok && ne.Timeout() { + err = ErrTimeout + } + if err == io.EOF { + err = NewWrappedSystemError(ErrCodeNetwork, io.EOF) + } + ch.writeMessage(c, &errorMessage{ + id: id, + errCode: GetSystemErrorCode(err), + message: err.Error(), + }) + c.Close() + return err +} + +func (ch *Channel) writeMessage(c net.Conn, msg message) error { + frame := ch.connectionOptions.FramePool.Get() + defer ch.connectionOptions.FramePool.Release(frame) + + if err := frame.write(msg); err != nil { + return err + } + return frame.WriteOut(c) +} + +func (ch *Channel) readMessage(c net.Conn, msg message) (uint32, error) { + frame := ch.connectionOptions.FramePool.Get() + defer ch.connectionOptions.FramePool.Release(frame) + + if err := frame.ReadIn(c); err != nil { + return 0, err + } + + if frame.Header.messageType != msg.messageType() { + if frame.Header.messageType == messageTypeError { + return frame.Header.ID, readError(frame) + } + return frame.Header.ID, NewSystemError(ErrCodeProtocol, "expected message type %v, got %v", msg.messageType(), frame.Header.messageType) + } + + return frame.Header.ID, frame.read(msg) +} + +func parseRemotePeer(p initParams, remoteAddr net.Addr) (PeerInfo, peerAddressComponents, error) { + var ( + remotePeer PeerInfo + remotePeerAddress peerAddressComponents + ok bool + ) + + if remotePeer.HostPort, ok = p[InitParamHostPort]; !ok { + return remotePeer, remotePeerAddress, fmt.Errorf("header %v is required", InitParamHostPort) + } + if remotePeer.ProcessName, ok = p[InitParamProcessName]; !ok { + return remotePeer, remotePeerAddress, fmt.Errorf("header %v is required", InitParamProcessName) + } + + // If the remote host:port is ephemeral, use the socket address as the + // host:port and set IsEphemeral to true. + if isEphemeralHostPort(remotePeer.HostPort) { + remotePeer.HostPort = remoteAddr.String() + remotePeer.IsEphemeral = true + } + + remotePeer.Version.Language = p[InitParamTChannelLanguage] + remotePeer.Version.LanguageVersion = p[InitParamTChannelLanguageVersion] + remotePeer.Version.TChannelVersion = p[InitParamTChannelVersion] + + address := remotePeer.HostPort + if sHost, sPort, err := net.SplitHostPort(address); err == nil { + address = sHost + if p, err := strconv.ParseUint(sPort, 10, 16); err == nil { + remotePeerAddress.port = uint16(p) + } + } + if address == "localhost" { + remotePeerAddress.ipv4 = 127<<24 | 1 + } else if ip := net.ParseIP(address); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + remotePeerAddress.ipv4 = binary.BigEndian.Uint32(ip4) + } else { + remotePeerAddress.ipv6 = address + } + } else { + remotePeerAddress.hostname = address + } + + return remotePeer, remotePeerAddress, nil +} + +func setInitDeadline(ctx context.Context, c net.Conn) func() { + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(5 * time.Second) + } + + c.SetDeadline(deadline) + return func() { + c.SetDeadline(time.Time{}) + } +} + +func readError(frame *Frame) error { + errMsg := &errorMessage{ + id: frame.Header.ID, + } + if err := frame.read(errMsg); err != nil { + return err + } + + return errMsg.AsSystemError() +} + +func unsupportedProtocolVersion(got uint16) error { + return NewSystemError(ErrCodeProtocol, "unsupported protocol version %d from peer, expected %v", got, CurrentProtocolVersion) +} diff --git a/vendor/src/github.com/uber/tchannel-go/raw/call.go b/vendor/src/github.com/uber/tchannel-go/raw/call.go new file mode 100644 index 00000000..4debbc76 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/raw/call.go @@ -0,0 +1,129 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package raw + +import ( + "errors" + + "golang.org/x/net/context" + + "github.com/uber/tchannel-go" +) + +// ErrAppError is returned if the application sets an error response. +var ErrAppError = errors.New("application error") + +// ReadArgsV2 reads arg2 and arg3 from a reader. +func ReadArgsV2(r tchannel.ArgReadable) ([]byte, []byte, error) { + var arg2, arg3 []byte + + if err := tchannel.NewArgReader(r.Arg2Reader()).Read(&arg2); err != nil { + return nil, nil, err + } + + if err := tchannel.NewArgReader(r.Arg3Reader()).Read(&arg3); err != nil { + return nil, nil, err + } + + return arg2, arg3, nil +} + +// WriteArgs writes the given arguments to the call, and returns the response args. +func WriteArgs(call *tchannel.OutboundCall, arg2, arg3 []byte) ([]byte, []byte, *tchannel.OutboundCallResponse, error) { + if err := tchannel.NewArgWriter(call.Arg2Writer()).Write(arg2); err != nil { + return nil, nil, nil, err + } + + if err := tchannel.NewArgWriter(call.Arg3Writer()).Write(arg3); err != nil { + return nil, nil, nil, err + } + + resp := call.Response() + var respArg2 []byte + if err := tchannel.NewArgReader(resp.Arg2Reader()).Read(&respArg2); err != nil { + return nil, nil, nil, err + } + + var respArg3 []byte + if err := tchannel.NewArgReader(resp.Arg3Reader()).Read(&respArg3); err != nil { + return nil, nil, nil, err + } + + return respArg2, respArg3, resp, nil +} + +// Call makes a call to the given hostPort with the given arguments and returns the response args. +func Call(ctx context.Context, ch *tchannel.Channel, hostPort string, serviceName, method string, + arg2, arg3 []byte) ([]byte, []byte, *tchannel.OutboundCallResponse, error) { + + call, err := ch.BeginCall(ctx, hostPort, serviceName, method, nil) + if err != nil { + return nil, nil, nil, err + } + + return WriteArgs(call, arg2, arg3) +} + +// CallSC makes a call using the given subcahnnel +func CallSC(ctx context.Context, sc *tchannel.SubChannel, method string, arg2, arg3 []byte) ( + []byte, []byte, *tchannel.OutboundCallResponse, error) { + + call, err := sc.BeginCall(ctx, method, nil) + if err != nil { + return nil, nil, nil, err + } + + return WriteArgs(call, arg2, arg3) +} + +// CArgs are the call arguments passed to CallV2. +type CArgs struct { + Method string + Arg2 []byte + Arg3 []byte + CallOptions *tchannel.CallOptions +} + +// CRes is the result of making a call. +type CRes struct { + Arg2 []byte + Arg3 []byte + AppError bool +} + +// CallV2 makes a call and does not attempt any retries. +func CallV2(ctx context.Context, sc *tchannel.SubChannel, cArgs CArgs) (*CRes, error) { + call, err := sc.BeginCall(ctx, cArgs.Method, cArgs.CallOptions) + if err != nil { + return nil, err + } + + arg2, arg3, res, err := WriteArgs(call, cArgs.Arg2, cArgs.Arg3) + if err != nil { + return nil, err + } + + return &CRes{ + Arg2: arg2, + Arg3: arg3, + AppError: res.ApplicationError(), + }, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/raw/handler.go b/vendor/src/github.com/uber/tchannel-go/raw/handler.go new file mode 100644 index 00000000..04aad861 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/raw/handler.go @@ -0,0 +1,106 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package raw + +import ( + "golang.org/x/net/context" + + "github.com/uber/tchannel-go" +) + +// Handler is the interface for a raw handler. +type Handler interface { + // Handle is called on incoming calls, and contains all the arguments. + // If an error is returned, it will set ApplicationError Arg3 will be the error string. + Handle(ctx context.Context, args *Args) (*Res, error) + OnError(ctx context.Context, err error) +} + +// Args parses the arguments from an incoming call req. +type Args struct { + Caller string + Format tchannel.Format + Method string + Arg2 []byte + Arg3 []byte +} + +// Res represents the response to an incoming call req. +type Res struct { + SystemErr error + // IsErr is used to set an application error on the underlying call res. + IsErr bool + Arg2 []byte + Arg3 []byte +} + +// ReadArgs reads the *Args from the given call. +func ReadArgs(call *tchannel.InboundCall) (*Args, error) { + var args Args + args.Caller = call.CallerName() + args.Format = call.Format() + args.Method = string(call.Method()) + if err := tchannel.NewArgReader(call.Arg2Reader()).Read(&args.Arg2); err != nil { + return nil, err + } + if err := tchannel.NewArgReader(call.Arg3Reader()).Read(&args.Arg3); err != nil { + return nil, err + } + return &args, nil +} + +// WriteResponse writes the given Res to the InboundCallResponse. +func WriteResponse(response *tchannel.InboundCallResponse, resp *Res) error { + if resp.SystemErr != nil { + return response.SendSystemError(resp.SystemErr) + } + if resp.IsErr { + if err := response.SetApplicationError(); err != nil { + return err + } + } + if err := tchannel.NewArgWriter(response.Arg2Writer()).Write(resp.Arg2); err != nil { + return err + } + return tchannel.NewArgWriter(response.Arg3Writer()).Write(resp.Arg3) +} + +// Wrap wraps a Handler as a tchannel.Handler that can be passed to tchannel.Register. +func Wrap(handler Handler) tchannel.Handler { + return tchannel.HandlerFunc(func(ctx context.Context, call *tchannel.InboundCall) { + args, err := ReadArgs(call) + if err != nil { + handler.OnError(ctx, err) + return + } + + resp, err := handler.Handle(ctx, args) + response := call.Response() + if err != nil { + resp = &Res{ + SystemErr: err, + } + } + if err := WriteResponse(response, resp); err != nil { + handler.OnError(ctx, err) + } + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay.go b/vendor/src/github.com/uber/tchannel-go/relay.go new file mode 100644 index 00000000..a09bd182 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay.go @@ -0,0 +1,596 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "errors" + "fmt" + "math" + "sync" + "time" + + "github.com/uber/tchannel-go/relay" + + "github.com/uber-go/atomic" +) + +const ( + // _maxRelayTombs is the maximum number of tombs we'll accumulate in a + // single relayItems. + _maxRelayTombs = 3e4 + // _relayTombTTL is the length of time we'll keep a tomb before GC'ing it. + _relayTombTTL = 3 * time.Second + // _defaultRelayMaxTimeout is the default max TTL for relayed calls. + _defaultRelayMaxTimeout = 2 * time.Minute +) + +var ( + errRelayMethodFragmented = NewSystemError(ErrCodeBadRequest, "relay handler cannot receive fragmented calls") + errFrameNotSent = NewSystemError(ErrCodeNetwork, "frame was not sent to remote side") + errBadRelayHost = NewSystemError(ErrCodeDeclined, "bad relay host implementation") + errUnknownID = errors.New("non-callReq for inactive ID") +) + +type relayItem struct { + *time.Timer + + remapID uint32 + tomb bool + local bool + call RelayCall + destination *Relayer + span Span +} + +type relayItems struct { + sync.RWMutex + + logger Logger + tombs uint64 + items map[uint32]relayItem +} + +func newRelayItems(logger Logger) *relayItems { + return &relayItems{ + items: make(map[uint32]relayItem), + logger: logger, + } +} + +// Count returns the number of non-tombstone items in the relay. +func (r *relayItems) Count() int { + r.RLock() + n := len(r.items) - int(r.tombs) + r.RUnlock() + return n +} + +// Get checks for a relay item by ID, returning the item and a bool indicating +// whether the item was found. +func (r *relayItems) Get(id uint32) (relayItem, bool) { + r.RLock() + item, ok := r.items[id] + r.RUnlock() + + return item, ok +} + +// Add adds a relay item. +func (r *relayItems) Add(id uint32, item relayItem) { + r.Lock() + r.items[id] = item + r.Unlock() +} + +// Delete removes a relayItem completely (without leaving a tombstone). It +// returns the deleted item, along with a bool indicating whether we completed a +// relayed call. +func (r *relayItems) Delete(id uint32) (relayItem, bool) { + r.Lock() + item, ok := r.items[id] + if !ok { + r.Unlock() + r.logger.WithFields(LogField{"id", id}).Warn("Attempted to delete non-existent relay item.") + return item, false + } + delete(r.items, id) + if item.tomb { + r.tombs-- + } + r.Unlock() + + item.Stop() + return item, !item.tomb +} + +// Entomb sets the tomb bit on a relayItem and schedules a garbage collection. It +// returns the entombed item, along with a bool indicating whether we completed +// a relayed call. +func (r *relayItems) Entomb(id uint32, deleteAfter time.Duration) (relayItem, bool) { + r.Lock() + if r.tombs > _maxRelayTombs { + r.Unlock() + r.logger.WithFields(LogField{"id", id}).Warn("Too many tombstones, deleting relay item immediately.") + return r.Delete(id) + } + item, ok := r.items[id] + if !ok { + r.Unlock() + r.logger.WithFields(LogField{"id", id}).Warn("Can't find relay item to entomb.") + return item, false + } + if item.tomb { + r.Unlock() + r.logger.WithFields(LogField{"id", id}).Warn("Re-entombing a tombstone.") + return item, false + } + r.tombs++ + item.tomb = true + r.items[id] = item + r.Unlock() + + // TODO: We should be clearing these out in batches, rather than creating + // individual timers for each item. + time.AfterFunc(deleteAfter, func() { r.Delete(id) }) + return item, true +} + +type frameType int + +const ( + requestFrame frameType = 0 + responseFrame frameType = 1 +) + +// A Relayer forwards frames. +type Relayer struct { + relayHost RelayHost + maxTimeout time.Duration + + // localHandlers is the set of service names that are handled by the local + // channel. + localHandler map[string]struct{} + + // outbound is the remapping for requests that originated on this + // connection, and are outbound towards some other connection. + // It stores remappings for all request frames read on this connection. + outbound *relayItems + + // inbound is the remapping for requests that originated on some other + // connection which was directed to this connection. + // It stores remappings for all response frames read on this connection. + inbound *relayItems + + peers *RootPeerList + conn *Connection + logger Logger + pending atomic.Uint32 +} + +// NewRelayer constructs a Relayer. +func NewRelayer(ch *Channel, conn *Connection) *Relayer { + return &Relayer{ + relayHost: ch.RelayHost(), + maxTimeout: ch.relayMaxTimeout, + localHandler: ch.relayLocal, + outbound: newRelayItems(conn.log.WithFields(LogField{"relayItems", "outbound"})), + inbound: newRelayItems(conn.log.WithFields(LogField{"relayItems", "inbound"})), + peers: ch.RootPeers(), + conn: conn, + logger: conn.log, + } +} + +// Relay is called for each frame that is read on the connection. +func (r *Relayer) Relay(f *Frame) error { + if f.messageType() != messageTypeCallReq { + err := r.handleNonCallReq(f) + if err == errUnknownID { + // This ID may be owned by an outgoing call, so check the outbound + // message exchange, and if it succeeds, then the frame has been + // handled successfully. + if err := r.conn.outbound.forwardPeerFrame(f); err == nil { + return nil + } + } + return err + } + return r.handleCallReq(newLazyCallReq(f)) +} + +// Receive receives frames intended for this connection. +// It returns whether the frame was sent and a reason for failure if it failed. +func (r *Relayer) Receive(f *Frame, fType frameType) (sent bool, failureReason string) { + id := f.Header.ID + + // If we receive a response frame, we expect to find that ID in our outbound. + // If we receive a request frame, we expect to find that ID in our inbound. + items := r.receiverItems(fType) + + item, ok := items.Get(id) + if !ok { + r.logger.WithFields( + LogField{"id", id}, + ).Warn("Received a frame without a RelayItem.") + return false, "relay-not-found" + } + + // call res frames don't include the OK bit, so we can't wait until the last + // frame of a relayed RPC to determine if the call succeeded. + if fType == responseFrame { + // If we've gotten a response frame, we're the originating relayer and + // should handle stats. + if succeeded, failMsg := determinesCallSuccess(f); succeeded { + item.call.Succeeded() + } else if len(failMsg) > 0 { + item.call.Failed(failMsg) + } + } + + // When we write the frame to sendCh, we lose ownership of the frame, and it + // may be released to the frame pool at any point. + finished := finishesCall(f) + + select { + case r.conn.sendCh <- f: + default: + // Buffer is full, so drop this frame and cancel the call. + r.logger.WithFields( + LogField{"id", id}, + ).Warn("Dropping call due to slow connection to destination.") + + items := r.receiverItems(fType) + r.failRelayItem(items, id, "relay-dest-conn-slow") + return false, "relay-dest-conn-slow" + } + + if finished { + items := r.receiverItems(fType) + r.finishRelayItem(items, id) + } + + return true, "" +} + +func (r *Relayer) canHandleNewCall() (bool, connectionState) { + var ( + canHandle bool + curState connectionState + ) + + r.conn.withStateRLock(func() error { + curState = r.conn.state + canHandle = curState == connectionActive + if canHandle { + r.pending.Inc() + } + return nil + }) + return canHandle, curState +} + +func (r *Relayer) getDestination(f lazyCallReq, call RelayCall) (*Connection, bool, error) { + if _, ok := r.outbound.Get(f.Header.ID); ok { + r.logger.WithFields( + LogField{"id", f.Header.ID}, + LogField{"source", string(f.Caller())}, + LogField{"dest", string(f.Service())}, + LogField{"method", string(f.Method())}, + ).Warn("Received duplicate callReq.") + call.Failed(ErrCodeProtocol.relayMetricsKey()) + // TODO: this is a protocol error, kill the connection. + return nil, false, errors.New("callReq with already active ID") + } + + // Get the destination + peer, ok := call.Destination() + if !ok { + call.Failed("relay-bad-relay-host") + r.conn.SendSystemError(f.Header.ID, f.Span(), errBadRelayHost) + return nil, false, errBadRelayHost + } + + // TODO: Should connections use the call timeout? Or a separate timeout? + remoteConn, err := peer.getConnectionRelay(f.TTL()) + if err != nil { + r.logger.WithFields( + ErrField(err), + LogField{"source", string(f.Caller())}, + LogField{"dest", string(f.Service())}, + LogField{"method", string(f.Method())}, + LogField{"selectedPeer", peer}, + ).Warn("Failed to connect to relay host.") + call.Failed("relay-connection-failed") + r.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeNetwork, err)) + return nil, false, nil + } + + return remoteConn, true, nil +} + +func (r *Relayer) handleCallReq(f lazyCallReq) error { + if handled := r.handleLocalCallReq(f); handled { + return nil + } + + call, err := r.relayHost.Start(f, r.conn) + if err != nil { + // If we have a RateLimitDropError we record the statistic, but + // we *don't* send an error frame back to the client. + if _, silentlyDrop := err.(relay.RateLimitDropError); silentlyDrop { + if call != nil { + call.Failed("relay-dropped") + call.End() + } + return nil + } + if _, ok := err.(SystemError); !ok { + err = NewSystemError(ErrCodeDeclined, err.Error()) + } + if call != nil { + call.Failed(GetSystemErrorCode(err).relayMetricsKey()) + call.End() + } + r.conn.SendSystemError(f.Header.ID, f.Span(), err) + + // If the RelayHost returns a protocol error, close the connection. + if GetSystemErrorCode(err) == ErrCodeProtocol { + return r.conn.close(LogField{"reason", "RelayHost returned protocol error"}) + } + return nil + } + + if canHandle, state := r.canHandleNewCall(); !canHandle { + call.Failed("relay-conn-inactive") + call.End() + err := errConnNotActive{"incoming", state} + r.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeDeclined, err)) + return err + } + + // Get a remote connection and check whether it can handle this call. + remoteConn, ok, err := r.getDestination(f, call) + if err == nil && ok { + if canHandle, state := remoteConn.relay.canHandleNewCall(); !canHandle { + err = NewWrappedSystemError(ErrCodeNetwork, errConnNotActive{"selected remote", state}) + call.Failed("relay-remote-inactive") + r.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeDeclined, err)) + } + } + if err != nil || !ok { + // Failed to get a remote connection, or the connection is not in the right + // state to handle this call. Since we already incremented pending on + // the current relay, we need to decrement it. + r.decrementPending() + call.End() + return err + } + + origID := f.Header.ID + destinationID := remoteConn.NextMessageID() + ttl := f.TTL() + if ttl > r.maxTimeout { + ttl = r.maxTimeout + f.SetTTL(r.maxTimeout) + } + span := f.Span() + // The remote side of the relay doesn't need to track stats. + remoteConn.relay.addRelayItem(false /* isOriginator */, destinationID, f.Header.ID, r, ttl, span, nil) + relayToDest := r.addRelayItem(true /* isOriginator */, f.Header.ID, destinationID, remoteConn.relay, ttl, span, call) + + f.Header.ID = destinationID + sent, failure := relayToDest.destination.Receive(f.Frame, requestFrame) + if !sent { + r.failRelayItem(r.outbound, origID, failure) + return nil + } + + return nil +} + +// Handle all frames except messageTypeCallReq. +func (r *Relayer) handleNonCallReq(f *Frame) error { + frameType := frameTypeFor(f) + + // If we read a request frame, we need to use the outbound map to decide + // the destination. Otherwise, we use the inbound map. + items := r.outbound + if frameType == responseFrame { + items = r.inbound + } + + item, ok := items.Get(f.Header.ID) + if !ok { + return errUnknownID + } + if item.tomb { + // Call timed out, ignore this frame. (We've already handled stats.) + // TODO: metrics for late-arriving frames. + return nil + } + originalID := f.Header.ID + f.Header.ID = item.remapID + + // Once we call Receive on the frame, we lose ownership of the frame. + finished := finishesCall(f) + + sent, failure := item.destination.Receive(f, frameType) + if !sent { + r.failRelayItem(items, originalID, failure) + return nil + } + + if finished { + r.finishRelayItem(items, originalID) + } + return nil +} + +// addRelayItem adds a relay item to either outbound or inbound. +func (r *Relayer) addRelayItem(isOriginator bool, id, remapID uint32, destination *Relayer, ttl time.Duration, span Span, call RelayCall) relayItem { + item := relayItem{ + call: call, + remapID: remapID, + destination: destination, + span: span, + } + + items := r.inbound + if isOriginator { + items = r.outbound + } + item.Timer = time.AfterFunc(ttl, func() { r.timeoutRelayItem(isOriginator, items, id) }) + items.Add(id, item) + return item +} + +func (r *Relayer) timeoutRelayItem(isOriginator bool, items *relayItems, id uint32) { + item, ok := items.Entomb(id, _relayTombTTL) + if !ok { + return + } + if isOriginator { + r.conn.SendSystemError(id, item.span, ErrTimeout) + item.call.Failed("timeout") + item.call.End() + } + + r.decrementPending() +} + +func (r *Relayer) failRelayItem(items *relayItems, id uint32, failure string) { + // Entomb it so that we don't get unknown exchange errors on further frames + // for this call. + item, ok := items.Entomb(id, _relayTombTTL) + if !ok { + return + } + if item.call != nil { + r.conn.SendSystemError(id, item.span, errFrameNotSent) + item.call.Failed(failure) + item.call.End() + } + + r.decrementPending() +} + +func (r *Relayer) finishRelayItem(items *relayItems, id uint32) { + item, ok := items.Delete(id) + if !ok { + return + } + if item.call != nil { + item.call.End() + } + r.decrementPending() +} + +func (r *Relayer) decrementPending() { + r.pending.Dec() + r.conn.checkExchanges() +} + +func (r *Relayer) canClose() bool { + if r == nil { + return true + } + return r.countPending() == 0 +} + +func (r *Relayer) countPending() uint32 { + return r.pending.Load() +} + +func (r *Relayer) receiverItems(fType frameType) *relayItems { + if fType == requestFrame { + return r.inbound + } + return r.outbound +} + +func (r *Relayer) handleLocalCallReq(cr lazyCallReq) bool { + // Check whether this is a service we want to handle locally. + if _, ok := r.localHandler[string(cr.Service())]; !ok { + return false + } + + f := cr.Frame + + // We can only handle non-fragmented calls in the relay channel. + // This is a simplification to avoid back references from a mex to a + // relayItem so that the relayItem is cleared when the call completes. + if cr.HasMoreFragments() { + r.logger.WithFields( + LogField{"id", cr.Header.ID}, + LogField{"source", string(cr.Caller())}, + LogField{"dest", string(cr.Service())}, + LogField{"method", string(cr.Method())}, + ).Error("Received fragmented callReq intended for local relay channel, can only handle unfragmented calls.") + r.conn.SendSystemError(f.Header.ID, cr.Span(), errRelayMethodFragmented) + return true + } + + if release := r.conn.handleFrameNoRelay(f); release { + r.conn.opts.FramePool.Release(f) + } + return true +} + +func frameTypeFor(f *Frame) frameType { + switch t := f.Header.messageType; t { + case messageTypeCallRes, messageTypeCallResContinue, messageTypeError, messageTypePingRes: + return responseFrame + case messageTypeCallReq, messageTypeCallReqContinue, messageTypePingReq: + return requestFrame + default: + panic(fmt.Sprintf("unsupported frame type: %v", t)) + } +} + +func determinesCallSuccess(f *Frame) (succeeded bool, failMsg string) { + switch f.messageType() { + case messageTypeError: + msg := newLazyError(f).Code().MetricsKey() + return false, msg + case messageTypeCallRes: + if newLazyCallRes(f).OK() { + return true, "" + } + return false, "application-error" + default: + return false, "" + } +} + +func validateRelayMaxTimeout(d time.Duration, logger Logger) time.Duration { + maxMillis := d / time.Millisecond + if maxMillis > 0 && maxMillis <= math.MaxUint32 { + return d + } + if d == 0 { + return _defaultRelayMaxTimeout + } + logger.WithFields( + LogField{"configuredMaxTimeout", d}, + LogField{"defaultMaxTimeout", _defaultRelayMaxTimeout}, + ).Warn("Configured RelayMaxTimeout is invalid, using default instead.") + return _defaultRelayMaxTimeout +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay/relay.go b/vendor/src/github.com/uber/tchannel-go/relay/relay.go new file mode 100644 index 00000000..56e4e16e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay/relay.go @@ -0,0 +1,52 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package relay contains relaying interfaces for external use. +// +// These interfaces are currently unstable, and aren't covered by the API +// backwards-compatibility guarantee. +package relay + +// CallFrame is an interface that abstracts access to the call req frame. +type CallFrame interface { + // Caller is the name of the originating service. + Caller() []byte + // Service is the name of the destination service. + Service() []byte + // Method is the name of the method being called. + Method() []byte + // RoutingDelegate is the name of the routing delegate, if any. + RoutingDelegate() []byte + // RoutingKey may refer to an alternate traffic group instead of the + // traffic group identified by the service name. + RoutingKey() []byte +} + +// RateLimitDropError is the error that should be returned from +// RelayHosts.Get if the request should be dropped silently. +// This is bit of a hack, because rate limiting of this nature isn't part of +// the actual TChannel protocol. +// The relayer will record that it has dropped the packet, but *won't* notify +// the client. +type RateLimitDropError struct{} + +func (e RateLimitDropError) Error() string { + return "frame dropped silently due to rate limiting" +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay/relaytest/func_host.go b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/func_host.go new file mode 100644 index 00000000..ea98e533 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/func_host.go @@ -0,0 +1,47 @@ +package relaytest + +import ( + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/relay" +) + +type hostFunc struct { + ch *tchannel.Channel + stats *MockStats + fn func(relay.CallFrame, *tchannel.Connection) (string, error) +} + +type hostFuncPeer struct { + *MockCallStats + + peer *tchannel.Peer +} + +// HostFunc wraps a given function to implement tchannel.RelayHost. +func HostFunc(fn func(relay.CallFrame, *tchannel.Connection) (string, error)) tchannel.RelayHost { + return &hostFunc{nil, NewMockStats(), fn} +} + +func (hf *hostFunc) SetChannel(ch *tchannel.Channel) { + hf.ch = ch +} + +func (hf *hostFunc) Start(cf relay.CallFrame, conn *tchannel.Connection) (tchannel.RelayCall, error) { + var peer *tchannel.Peer + + peerHP, err := hf.fn(cf, conn) + if peerHP != "" { + peer = hf.ch.GetSubChannel(string(cf.Service())).Peers().GetOrAdd(peerHP) + } + + // We still track stats if we failed to get a peer, so return the peer. + return &hostFuncPeer{hf.stats.Begin(cf), peer}, err +} + +func (hf *hostFunc) Stats() *MockStats { + return hf.stats +} + +func (p *hostFuncPeer) Destination() (*tchannel.Peer, bool) { + return p.peer, p.peer != nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay/relaytest/mock_stats.go b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/mock_stats.go new file mode 100644 index 00000000..a710f368 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/mock_stats.go @@ -0,0 +1,167 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package relaytest + +import ( + "fmt" + "sort" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/tchannel-go/relay" +) + +// MockCallStats is a testing spy for the CallStats interface. +type MockCallStats struct { + // Store ints and slices instead of bools and strings so that we can assert + // the actual sequence of calls (in case we expect to call both Succeeded + // and Failed). The real implementation will have the first writer win. + succeeded int + failedMsgs []string + ended int + wg *sync.WaitGroup +} + +// Succeeded marks the RPC as succeeded. +func (m *MockCallStats) Succeeded() { + m.succeeded++ +} + +// Failed marks the RPC as failed for the provided reason. +func (m *MockCallStats) Failed(reason string) { + m.failedMsgs = append(m.failedMsgs, reason) +} + +// End halts timer and metric collection for the RPC. +func (m *MockCallStats) End() { + m.ended++ + m.wg.Done() +} + +// FluentMockCallStats wraps the MockCallStats in a fluent API that's convenient for tests. +type FluentMockCallStats struct { + *MockCallStats +} + +// Succeeded marks the RPC as succeeded. +func (f *FluentMockCallStats) Succeeded() *FluentMockCallStats { + f.MockCallStats.Succeeded() + return f +} + +// Failed marks the RPC as failed. +func (f *FluentMockCallStats) Failed(reason string) *FluentMockCallStats { + f.MockCallStats.Failed(reason) + return f +} + +// MockStats is a testing spy for the Stats interface. +type MockStats struct { + mu sync.Mutex + wg sync.WaitGroup + stats map[string][]*MockCallStats +} + +// NewMockStats constructs a MockStats. +func NewMockStats() *MockStats { + return &MockStats{ + stats: make(map[string][]*MockCallStats), + } +} + +// Begin starts collecting metrics for an RPC. +func (m *MockStats) Begin(f relay.CallFrame) *MockCallStats { + return m.Add(string(f.Caller()), string(f.Service()), string(f.Method())).MockCallStats +} + +// Add explicitly adds a new call along an edge of the call graph. +func (m *MockStats) Add(caller, callee, procedure string) *FluentMockCallStats { + m.wg.Add(1) + cs := &MockCallStats{wg: &m.wg} + key := m.tripleToKey(caller, callee, procedure) + m.mu.Lock() + m.stats[key] = append(m.stats[key], cs) + m.mu.Unlock() + return &FluentMockCallStats{cs} +} + +// AssertEqual asserts that two MockStats describe the same call graph. +func (m *MockStats) AssertEqual(t testing.TB, expected *MockStats) { + // Wait for any outstandanding CallStats to end. + m.wg.Wait() + + m.mu.Lock() + defer m.mu.Unlock() + + expected.mu.Lock() + defer expected.mu.Unlock() + + if assert.Equal(t, getEdges(expected.stats), getEdges(m.stats), "Found calls along unexpected edges.") { + for edge := range expected.stats { + m.assertEdgeEqual(t, expected, edge) + } + } +} + +func (m *MockStats) assertEdgeEqual(t testing.TB, expected *MockStats, edge string) { + expectedCalls := expected.stats[edge] + actualCalls := m.stats[edge] + if assert.Equal(t, len(expectedCalls), len(actualCalls), "Unexpected number of calls along %s edge.", edge) { + for i := range expectedCalls { + m.assertCallEqual(t, expectedCalls[i], actualCalls[i]) + } + } +} + +func (m *MockStats) assertCallEqual(t testing.TB, expected *MockCallStats, actual *MockCallStats) { + // Revisit these assertions if we ever need to assert zero or many calls to + // End. + require.Equal(t, 1, expected.ended, "Expected call must assert exactly one call to End.") + require.False( + t, + expected.succeeded <= 0 && len(expected.failedMsgs) == 0, + "Expectation must indicate whether RPC should succeed or fail.", + ) + + assert.Equal(t, expected.succeeded, actual.succeeded, "Unexpected number of successes.") + assert.Equal(t, expected.failedMsgs, actual.failedMsgs, "Unexpected reasons for RPC failure.") + assert.Equal(t, expected.ended, actual.ended, "Unexpected number of calls to End.") + + if t.Failed() { + // The default testify output is often insufficient. + t.Logf("\nExpected relayed stats were:\n\t%+v\nActual relayed stats were:\n\t%+v\n", expected, actual) + } +} + +func (m *MockStats) tripleToKey(caller, callee, procedure string) string { + return fmt.Sprintf("%s->%s::%s", caller, callee, procedure) +} + +func getEdges(m map[string][]*MockCallStats) []string { + edges := make([]string, 0, len(m)) + for k := range m { + edges = append(edges, k) + } + sort.Strings(edges) + return edges +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay/relaytest/stub_host.go b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/stub_host.go new file mode 100644 index 00000000..1d1e0f1d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay/relaytest/stub_host.go @@ -0,0 +1,79 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package relaytest + +import ( + "errors" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/relay" +) + +var errNoChannel = errors.New("no channel set to get peers from") + +// Ensure that the StubRelayHost implements tchannel.RelayHost. +var _ tchannel.RelayHost = (*StubRelayHost)(nil) + +// StubRelayHost is a stub RelayHost for tests that backs peer selection to an +// underlying channel using isolated subchannels and the default peer selection. +type StubRelayHost struct { + ch *tchannel.Channel + stats *MockStats +} + +type stubCall struct { + *MockCallStats + + peer *tchannel.Peer +} + +// NewStubRelayHost creates a new stub RelayHost for tests. +func NewStubRelayHost() *StubRelayHost { + return &StubRelayHost{nil, NewMockStats()} +} + +// SetChannel is called by the channel after creation so we can +// get a reference to the channels' peers. +func (rh *StubRelayHost) SetChannel(ch *tchannel.Channel) { + rh.ch = ch +} + +// Start starts a new RelayCall for the given call on a specific connection. +func (rh *StubRelayHost) Start(cf relay.CallFrame, _ *tchannel.Connection) (tchannel.RelayCall, error) { + // Get a peer from the subchannel. + peer, err := rh.ch.GetSubChannel(string(cf.Service())).Peers().Get(nil) + return &stubCall{rh.stats.Begin(cf), peer}, err +} + +// Add adds a service instance with the specified host:port. +func (rh *StubRelayHost) Add(service, hostPort string) { + rh.ch.GetSubChannel(service, tchannel.Isolated).Peers().GetOrAdd(hostPort) +} + +// Stats returns the *MockStats tracked for this channel. +func (rh *StubRelayHost) Stats() *MockStats { + return rh.stats +} + +// Destination returns the selected peer for this call. +func (c *stubCall) Destination() (*tchannel.Peer, bool) { + return c.peer, c.peer != nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_api.go b/vendor/src/github.com/uber/tchannel-go/relay_api.go new file mode 100644 index 00000000..a9bb34bc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_api.go @@ -0,0 +1,54 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "github.com/uber/tchannel-go/relay" + +// RelayHost is the interface used to create RelayCalls when the relay +// receives an incoming call. +type RelayHost interface { + // SetChannels is called on creation of the channel. It's used to set a + // channel reference which can be used to get references to *Peer. + SetChannel(ch *Channel) + + // Start starts a new RelayCall given the call frame and connection. + // It may return a call and an error, in which case the caller will + // call Failed/End on the RelayCall. + Start(relay.CallFrame, *Connection) (RelayCall, error) +} + +// RelayCall abstracts away peer selection, stats, and any other business +// logic from the underlying relay implementation. A RelayCall may not +// have a destination if there was an error during peer selection +// (which should be returned from start). +type RelayCall interface { + // Destination returns the selected peer (if there was no error from Start). + Destination() (peer *Peer, ok bool) + + // The call succeeded (possibly after retrying). + Succeeded() + + // The call failed. + Failed(reason string) + + // End stats collection for this RPC. Will be called exactly once. + End() +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_benchmark_test.go b/vendor/src/github.com/uber/tchannel-go/relay_benchmark_test.go new file mode 100644 index 00000000..f254e80e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_benchmark_test.go @@ -0,0 +1,225 @@ +package tchannel_test + +import ( + "fmt" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/benchmark" + "github.com/uber/tchannel-go/testutils" + + "github.com/bmizerany/perks/quantile" + "github.com/stretchr/testify/require" +) + +type benchmarkParams struct { + servers, clients int + requestSize int +} + +type workerControl struct { + start sync.WaitGroup + unblockStart chan struct{} + done sync.WaitGroup +} + +func init() { + benchmark.BenchmarkDir = "./benchmark/" +} + +func newWorkerControl(numWorkers int) *workerControl { + wc := &workerControl{ + unblockStart: make(chan struct{}), + } + wc.start.Add(numWorkers) + wc.done.Add(numWorkers) + return wc +} + +func (c *workerControl) WaitForStart(f func()) { + c.start.Wait() + f() + close(c.unblockStart) +} + +func (c *workerControl) WaitForEnd() { + c.done.Wait() +} + +func (c *workerControl) WorkerStart() { + c.start.Done() + <-c.unblockStart +} + +func (c *workerControl) WorkerDone() { + c.done.Done() +} + +func defaultParams() benchmarkParams { + return benchmarkParams{ + servers: 2, + clients: 2, + requestSize: 1024, + } +} + +func closeAndVerify(b *testing.B, ch *Channel) { + ch.Close() + isChanClosed := func() bool { + return ch.State() == ChannelClosed + } + if !testutils.WaitFor(time.Second, isChanClosed) { + b.Errorf("Timed out waiting for channel to close, state: %v", ch.State()) + } +} + +func benchmarkRelay(b *testing.B, p benchmarkParams) { + b.SetBytes(int64(p.requestSize)) + b.ReportAllocs() + + services := make(map[string][]string) + + servers := make([]benchmark.Server, p.servers) + for i := range servers { + servers[i] = benchmark.NewServer( + benchmark.WithServiceName("svc"), + benchmark.WithRequestSize(p.requestSize), + benchmark.WithExternalProcess(), + ) + defer servers[i].Close() + services["svc"] = append(services["svc]"], servers[i].HostPort()) + } + + relay, err := benchmark.NewRealRelay(services) + require.NoError(b, err, "Failed to create relay") + defer relay.Close() + + clients := make([]benchmark.Client, p.clients) + for i := range clients { + clients[i] = benchmark.NewClient([]string{relay.HostPort()}, + benchmark.WithServiceName("svc"), + benchmark.WithRequestSize(p.requestSize), + benchmark.WithExternalProcess(), + benchmark.WithTimeout(10*time.Second), + ) + defer clients[i].Close() + require.NoError(b, clients[i].Warmup(), "Warmup failed") + } + + quantileVals := []float64{0.50, 0.95, 0.99, 1.0} + quantiles := make([]*quantile.Stream, p.clients) + for i := range quantiles { + quantiles[i] = quantile.NewTargeted(quantileVals...) + } + + wc := newWorkerControl(p.clients) + dec := testutils.Decrementor(b.N) + + for i, c := range clients { + go func(i int, c benchmark.Client) { + // Do a warm up call. + c.RawCall(1) + + wc.WorkerStart() + defer wc.WorkerDone() + + for { + tokens := dec.Multiple(200) + if tokens == 0 { + break + } + + durations, err := c.RawCall(tokens) + if err != nil { + b.Fatalf("Call failed: %v", err) + } + + for _, d := range durations { + quantiles[i].Insert(float64(d)) + } + } + }(i, c) + } + + var started time.Time + wc.WaitForStart(func() { + b.ResetTimer() + started = time.Now() + }) + wc.WaitForEnd() + duration := time.Since(started) + + fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds()) + + // Merge all the quantiles into 1 + for _, q := range quantiles[1:] { + quantiles[0].Merge(q.Samples()) + } + + for _, q := range quantileVals { + fmt.Printf(" %0.4f = %v\n", q, time.Duration(quantiles[0].Query(q))) + } + fmt.Println() +} + +func BenchmarkRelayNoLatencies(b *testing.B) { + server := benchmark.NewServer( + benchmark.WithServiceName("svc"), + benchmark.WithExternalProcess(), + benchmark.WithNoLibrary(), + ) + defer server.Close() + + hostMapping := map[string][]string{"svc": {server.HostPort()}} + relay, err := benchmark.NewRealRelay(hostMapping) + require.NoError(b, err, "NewRealRelay failed") + defer relay.Close() + + client := benchmark.NewClient([]string{relay.HostPort()}, + benchmark.WithServiceName("svc"), + benchmark.WithExternalProcess(), + benchmark.WithNoLibrary(), + benchmark.WithNumClients(10), + benchmark.WithNoChecking(), + benchmark.WithNoDurations(), + benchmark.WithTimeout(10*time.Second), + ) + defer client.Close() + require.NoError(b, client.Warmup(), "client.Warmup failed") + + b.ResetTimer() + started := time.Now() + for _, calls := range testutils.Batch(b.N, 10000) { + if _, err := client.RawCall(calls); err != nil { + b.Fatalf("Calls failed: %v", err) + } + } + + duration := time.Since(started) + fmt.Printf("\nb.N: %v Duration: %v RPS = %0.0f\n", b.N, duration, float64(b.N)/duration.Seconds()) +} + +func BenchmarkRelay2Servers5Clients1k(b *testing.B) { + p := defaultParams() + p.clients = 5 + p.servers = 2 + benchmarkRelay(b, p) +} + +func BenchmarkRelay4Servers20Clients1k(b *testing.B) { + p := defaultParams() + p.clients = 20 + p.servers = 4 + benchmarkRelay(b, p) +} + +func BenchmarkRelay2Servers5Clients4k(b *testing.B) { + p := defaultParams() + p.requestSize = 4 * 1024 + p.clients = 5 + p.servers = 2 + benchmarkRelay(b, p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_internal_test.go b/vendor/src/github.com/uber/tchannel-go/relay_internal_test.go new file mode 100644 index 00000000..1b093be0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_internal_test.go @@ -0,0 +1,48 @@ +package tchannel + +import ( + "testing" + + "github.com/uber/tchannel-go/typed" + + "github.com/stretchr/testify/assert" +) + +func TestFinishesCallResponses(t *testing.T) { + tests := []struct { + msgType messageType + flags byte + finishesCall bool + }{ + {messageTypeCallRes, 0x00, true}, + {messageTypeCallRes, 0x01, false}, + {messageTypeCallRes, 0x02, true}, + {messageTypeCallRes, 0x03, false}, + {messageTypeCallRes, 0x04, true}, + {messageTypeCallResContinue, 0x00, true}, + {messageTypeCallResContinue, 0x01, false}, + {messageTypeCallResContinue, 0x02, true}, + {messageTypeCallResContinue, 0x03, false}, + {messageTypeCallResContinue, 0x04, true}, + // By definition, callreq should never terminate an RPC. + {messageTypeCallReq, 0x00, false}, + {messageTypeCallReq, 0x01, false}, + {messageTypeCallReq, 0x02, false}, + {messageTypeCallReq, 0x03, false}, + {messageTypeCallReq, 0x04, false}, + } + for _, tt := range tests { + f := NewFrame(100) + fh := FrameHeader{ + size: uint16(0xFF34), + messageType: tt.msgType, + ID: 0xDEADBEEF, + } + f.Header = fh + fh.write(typed.NewWriteBuffer(f.headerBuffer)) + + payload := typed.NewWriteBuffer(f.Payload) + payload.WriteSingleByte(tt.flags) + assert.Equal(t, tt.finishesCall, finishesCall(f), "Wrong isLast for flags %v and message type %v", tt.flags, tt.msgType) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_messages.go b/vendor/src/github.com/uber/tchannel-go/relay_messages.go new file mode 100644 index 00000000..e990cb55 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_messages.go @@ -0,0 +1,198 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "bytes" + "encoding/binary" + "fmt" + "time" +) + +var ( + _callerNameKeyBytes = []byte(CallerName) + _routingDelegateKeyBytes = []byte(RoutingDelegate) + _routingKeyKeyBytes = []byte(RoutingKey) +) + +const ( + // Common to many frame types. + _flagsIndex = 0 + + // For call req. + _ttlIndex = 1 + _ttlLen = 4 + _spanIndex = _ttlIndex + _ttlLen + _spanLength = 25 + _serviceLenIndex = _spanIndex + _spanLength + _serviceNameIndex = _serviceLenIndex + 1 + + // For call res and call res continue. + _resCodeOK = 0x00 + _resCodeIndex = 1 + + // For error. + _errCodeIndex = 0 +) + +type lazyError struct { + *Frame +} + +func newLazyError(f *Frame) lazyError { + if msgType := f.Header.messageType; msgType != messageTypeError { + panic(fmt.Errorf("newLazyError called for wrong messageType: %v", msgType)) + } + return lazyError{f} +} + +func (e lazyError) Code() SystemErrCode { + return SystemErrCode(e.Payload[_errCodeIndex]) +} + +type lazyCallRes struct { + *Frame +} + +func newLazyCallRes(f *Frame) lazyCallRes { + if msgType := f.Header.messageType; msgType != messageTypeCallRes { + panic(fmt.Errorf("newLazyCallRes called for wrong messageType: %v", msgType)) + } + return lazyCallRes{f} +} + +func (cr lazyCallRes) OK() bool { + return cr.Payload[_resCodeIndex] == _resCodeOK +} + +// TODO: Use []byte instead of string for caller/method to avoid allocations. +type lazyCallReq struct { + *Frame + + caller, method, delegate, key []byte +} + +// TODO: Consider pooling lazyCallReq and using pointers to the struct. + +func newLazyCallReq(f *Frame) lazyCallReq { + if msgType := f.Header.messageType; msgType != messageTypeCallReq { + panic(fmt.Errorf("newLazyCallReq called for wrong messageType: %v", msgType)) + } + + cr := lazyCallReq{Frame: f} + + serviceLen := f.Payload[_serviceLenIndex] + // nh:1 (hk~1 hv~1){nh} + headerStart := _serviceLenIndex + 1 /* length byte */ + serviceLen + numHeaders := int(f.Payload[headerStart]) + cur := int(headerStart) + 1 + for i := 0; i < numHeaders; i++ { + keyLen := int(f.Payload[cur]) + cur++ + key := f.Payload[cur : cur+keyLen] + cur += keyLen + + valLen := int(f.Payload[cur]) + cur++ + val := f.Payload[cur : cur+valLen] + cur += valLen + + if bytes.Equal(key, _callerNameKeyBytes) { + cr.caller = val + } else if bytes.Equal(key, _routingDelegateKeyBytes) { + cr.delegate = val + } else if bytes.Equal(key, _routingKeyKeyBytes) { + cr.key = val + } + } + + // csumtype:1 (csum:4){0,1} arg1~2 arg2~2 arg3~2 + checkSumType := ChecksumType(f.Payload[cur]) + cur += 1 /* checksum */ + checkSumType.ChecksumSize() + + // arg1~2 + arg1Len := int(binary.BigEndian.Uint16(f.Payload[cur : cur+2])) + cur += 2 + cr.method = f.Payload[cur : cur+arg1Len] + return cr +} + +// Caller returns the name of the originator of this callReq. +func (f lazyCallReq) Caller() []byte { + return f.caller +} + +// Service returns the name of the destination service for this callReq. +func (f lazyCallReq) Service() []byte { + l := f.Payload[_serviceLenIndex] + return f.Payload[_serviceNameIndex : _serviceNameIndex+l] +} + +// Method returns the name of the method being called. +func (f lazyCallReq) Method() []byte { + return f.method +} + +// RoutingDelegate returns the routing delegate for this call req, if any. +func (f lazyCallReq) RoutingDelegate() []byte { + return f.delegate +} + +// RoutingKey returns the routing delegate for this call req, if any. +func (f lazyCallReq) RoutingKey() []byte { + return f.key +} + +// TTL returns the time to live for this callReq. +func (f lazyCallReq) TTL() time.Duration { + ttl := binary.BigEndian.Uint32(f.Payload[_ttlIndex : _ttlIndex+_ttlLen]) + return time.Duration(ttl) * time.Millisecond +} + +// SetTTL overwrites the frame's TTL. +func (f lazyCallReq) SetTTL(d time.Duration) { + ttl := uint32(d / time.Millisecond) + binary.BigEndian.PutUint32(f.Payload[_ttlIndex:_ttlIndex+_ttlLen], ttl) +} + +// Span returns the Span +func (f lazyCallReq) Span() Span { + return callReqSpan(f.Frame) +} + +// HasMoreFragments returns whether the callReq has more fragments. +func (f lazyCallReq) HasMoreFragments() bool { + return f.Payload[_flagsIndex]&hasMoreFragmentsFlag != 0 +} + +// finishesCall checks whether this frame is the last one we should expect for +// this RPC req-res. +func finishesCall(f *Frame) bool { + switch f.messageType() { + case messageTypeError: + return true + case messageTypeCallRes, messageTypeCallResContinue: + flags := f.Payload[_flagsIndex] + return flags&hasMoreFragmentsFlag == 0 + default: + return false + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_messages_benchmark_test.go b/vendor/src/github.com/uber/tchannel-go/relay_messages_benchmark_test.go new file mode 100644 index 00000000..df2ab61e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_messages_benchmark_test.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "io/ioutil" + "testing" +) + +func BenchmarkCallReqFrame(b *testing.B) { + cr := reqHasAll.req() + f := cr.Frame + + var service, caller, method []byte + + b.ResetTimer() + for i := 0; i < b.N; i++ { + cr := newLazyCallReq(f) + + // Multiple calls due to peer selection, stats, etc. + for i := 0; i < 3; i++ { + service = cr.Service() + caller = cr.Caller() + method = cr.Method() + } + } + b.StopTimer() + + fmt.Fprint(ioutil.Discard, service, caller, method) +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_messages_test.go b/vendor/src/github.com/uber/tchannel-go/relay_messages_test.go new file mode 100644 index 00000000..64c52f7c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_messages_test.go @@ -0,0 +1,307 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "testing" + "time" + + "github.com/uber/tchannel-go/typed" + + "github.com/stretchr/testify/assert" +) + +type testCallReq int + +const ( + reqHasHeaders testCallReq = (1 << iota) + reqHasCaller + reqHasDelegate + reqHasRoutingKey + reqHasChecksum + reqTotalCombinations + reqHasAll testCallReq = reqTotalCombinations - 1 +) + +func (cr testCallReq) req() lazyCallReq { + // TODO: Constructing a frame is ugly because the initial flags byte is + // written in reqResWriter instead of callReq. We should instead handle that + // in callReq, which will allow our tests to be sane. + f := NewFrame(200) + fh := fakeHeader() + f.Header = fh + fh.write(typed.NewWriteBuffer(f.headerBuffer)) + + payload := typed.NewWriteBuffer(f.Payload) + payload.WriteSingleByte(0) // flags + payload.WriteUint32(42) // TTL + payload.WriteBytes(make([]byte, 25)) // tracing + payload.WriteLen8String("bankmoji") // service + + headers := make(map[string]string) + if cr&reqHasHeaders != 0 { + addRandomHeaders(headers) + } + if cr&reqHasCaller != 0 { + headers["cn"] = "fake-caller" + } + if cr&reqHasDelegate != 0 { + headers["rd"] = "fake-delegate" + } + if cr&reqHasRoutingKey != 0 { + headers["rk"] = "fake-routingkey" + } + writeHeaders(payload, headers) + + if cr&reqHasChecksum == 0 { + payload.WriteSingleByte(byte(ChecksumTypeNone)) // checksum type + // no checksum contents for None + } else { + payload.WriteSingleByte(byte(ChecksumTypeCrc32C)) // checksum type + payload.WriteUint32(0) // checksum contents + } + payload.WriteLen16String("moneys") // method + return newLazyCallReq(f) +} + +func withLazyCallReqCombinations(f func(cr testCallReq)) { + for cr := testCallReq(0); cr < reqTotalCombinations; cr++ { + f(cr) + } +} + +type testCallRes int + +const ( + resIsContinued testCallRes = (1 << iota) + resIsOK + resHasHeaders + resHasChecksum + resTotalCombinations +) + +func (cr testCallRes) res() lazyCallRes { + f := NewFrame(100) + fh := FrameHeader{ + size: uint16(0xFF34), + messageType: messageTypeCallRes, + ID: 0xDEADBEEF, + } + f.Header = fh + fh.write(typed.NewWriteBuffer(f.headerBuffer)) + + payload := typed.NewWriteBuffer(f.Payload) + + if cr&resIsContinued == 0 { + payload.WriteSingleByte(0) // flags + } else { + payload.WriteSingleByte(hasMoreFragmentsFlag) // flags + } + + if cr&resIsOK == 0 { + payload.WriteSingleByte(1) // code not ok + } else { + payload.WriteSingleByte(0) // code ok + } + + headers := make(map[string]string) + if cr&resHasHeaders != 0 { + addRandomHeaders(headers) + } + writeHeaders(payload, headers) + + if cr&resHasChecksum == 0 { + payload.WriteSingleByte(byte(ChecksumTypeNone)) // checksum type + // No contents for ChecksumTypeNone. + } else { + payload.WriteSingleByte(byte(ChecksumTypeCrc32C)) // checksum type + payload.WriteUint32(0) // checksum contents + } + payload.WriteUint16(0) // no arg1 for call res + return newLazyCallRes(f) +} + +func withLazyCallResCombinations(f func(cr testCallRes)) { + for cr := testCallRes(0); cr < resTotalCombinations; cr++ { + f(cr) + } +} + +func (ec SystemErrCode) fakeErrFrame() lazyError { + f := NewFrame(100) + fh := FrameHeader{ + size: uint16(0xFF34), + messageType: messageTypeError, + ID: invalidMessageID, + } + f.Header = fh + fh.write(typed.NewWriteBuffer(f.headerBuffer)) + + payload := typed.NewWriteBuffer(f.Payload) + payload.WriteSingleByte(byte(ec)) + payload.WriteBytes(make([]byte, 25)) // tracing + + msg := ec.String() + payload.WriteUint16(uint16(len(msg))) + payload.WriteBytes([]byte(msg)) + return newLazyError(f) +} + +func withLazyErrorCombinations(f func(ec SystemErrCode)) { + codes := []SystemErrCode{ + ErrCodeInvalid, + ErrCodeTimeout, + ErrCodeCancelled, + ErrCodeBusy, + ErrCodeDeclined, + ErrCodeUnexpected, + ErrCodeBadRequest, + ErrCodeNetwork, + ErrCodeProtocol, + } + for _, ec := range codes { + f(ec) + } +} + +func addRandomHeaders(headers map[string]string) { + headers["k1"] = "v1" + headers["k222222"] = "" + headers["k3"] = "thisisalonglongkey" +} + +func writeHeaders(w *typed.WriteBuffer, headers map[string]string) { + w.WriteSingleByte(byte(len(headers))) // number of headers + for k, v := range headers { + w.WriteLen8String(k) + w.WriteLen8String(v) + } +} + +func assertWrappingPanics(t testing.TB, f *Frame, wrap func(f *Frame)) { + assert.Panics(t, func() { + wrap(f) + }, "Should panic when wrapping an unexpected frame type.") +} + +func TestLazyCallReqRejectsOtherFrames(t *testing.T) { + assertWrappingPanics( + t, + resIsContinued.res().Frame, + func(f *Frame) { newLazyCallReq(f) }, + ) +} + +func TestLazyCallReqService(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + assert.Equal(t, "bankmoji", string(cr.Service()), "Service name mismatch") + }) +} + +func TestLazyCallReqCaller(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + if crt&reqHasCaller == 0 { + assert.Equal(t, []byte(nil), cr.Caller(), "Unexpected caller name.") + } else { + assert.Equal(t, "fake-caller", string(cr.Caller()), "Caller name mismatch") + } + }) +} + +func TestLazyCallReqRoutingDelegate(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + if crt&reqHasDelegate == 0 { + assert.Equal(t, []byte(nil), cr.RoutingDelegate(), "Unexpected routing delegate.") + } else { + assert.Equal(t, "fake-delegate", string(cr.RoutingDelegate()), "Routing delegate mismatch.") + } + }) +} + +func TestLazyCallReqRoutingKey(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + if crt&reqHasRoutingKey == 0 { + assert.Equal(t, []byte(nil), cr.RoutingKey(), "Unexpected routing key.") + } else { + assert.Equal(t, "fake-routingkey", string(cr.RoutingKey()), "Routing key mismatch.") + } + }) +} + +func TestLazyCallReqMethod(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + assert.Equal(t, "moneys", string(cr.Method()), "Method name mismatch") + }) +} + +func TestLazyCallReqTTL(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + assert.Equal(t, 42*time.Millisecond, cr.TTL(), "Failed to parse TTL from frame.") + }) +} + +func TestLazyCallReqSetTTL(t *testing.T) { + withLazyCallReqCombinations(func(crt testCallReq) { + cr := crt.req() + cr.SetTTL(time.Second) + assert.Equal(t, time.Second, cr.TTL(), "Failed to write TTL to frame.") + }) +} + +func TestLazyCallResRejectsOtherFrames(t *testing.T) { + assertWrappingPanics( + t, + reqHasHeaders.req().Frame, + func(f *Frame) { newLazyCallRes(f) }, + ) +} + +func TestLazyCallResOK(t *testing.T) { + withLazyCallResCombinations(func(crt testCallRes) { + cr := crt.res() + if crt&resIsOK == 0 { + assert.False(t, cr.OK(), "Expected call res to have a non-ok code.") + } else { + assert.True(t, cr.OK(), "Expected call res to have code ok.") + } + }) +} + +func TestLazyErrorRejectsOtherFrames(t *testing.T) { + assertWrappingPanics( + t, + reqHasHeaders.req().Frame, + func(f *Frame) { newLazyError(f) }, + ) +} + +func TestLazyErrorCodes(t *testing.T) { + withLazyErrorCombinations(func(ec SystemErrCode) { + f := ec.fakeErrFrame() + assert.Equal(t, ec, f.Code(), "Mismatch between error code and lazy frame's Code() method.") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/relay_test.go b/vendor/src/github.com/uber/tchannel-go/relay_test.go new file mode 100644 index 00000000..e17ad693 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/relay_test.go @@ -0,0 +1,793 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "errors" + "io" + "runtime" + "strings" + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/benchmark" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/relay" + "github.com/uber/tchannel-go/relay/relaytest" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/testreader" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +type relayTest struct { + testutils.TestServer +} + +func serviceNameOpts(s string) *testutils.ChannelOpts { + return testutils.NewOpts().SetServiceName(s) +} + +func withRelayedEcho(t testing.TB, f func(relay, server, client *Channel, ts *testutils.TestServer)) { + opts := serviceNameOpts("test").SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + testutils.RegisterEcho(ts.Server(), nil) + client := ts.NewClient(serviceNameOpts("client")) + client.Peers().Add(ts.HostPort()) + f(ts.Relay(), ts.Server(), client, ts) + }) +} + +func TestRelay(t *testing.T) { + withRelayedEcho(t, func(_, _, client *Channel, ts *testutils.TestServer) { + tests := []struct { + header string + body string + }{ + {"fake-header", "fake-body"}, // fits in one frame + {"fake-header", strings.Repeat("fake-body", 10000)}, // requires continuation + } + sc := client.GetSubChannel("test") + for _, tt := range tests { + ctx, cancel := NewContext(time.Second) + defer cancel() + + arg2, arg3, _, err := raw.CallSC(ctx, sc, "echo", []byte(tt.header), []byte(tt.body)) + require.NoError(t, err, "Relayed call failed.") + assert.Equal(t, tt.header, string(arg2), "Header was mangled during relay.") + assert.Equal(t, tt.body, string(arg3), "Body was mangled during relay.") + } + + calls := relaytest.NewMockStats() + for range tests { + calls.Add("client", "test", "echo").Succeeded().End() + } + ts.AssertRelayStats(calls) + }) +} + +func TestRelayHandlesClosedPeers(t *testing.T) { + opts := serviceNameOpts("test").SetRelayOnly(). + // Disable logs as we are closing connections that can error in a lot of places. + DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(300 * time.Millisecond) + defer cancel() + + testutils.RegisterEcho(ts.Server(), nil) + client := ts.NewClient(serviceNameOpts("client")) + client.Peers().Add(ts.HostPort()) + + sc := client.GetSubChannel("test") + _, _, _, err := raw.CallSC(ctx, sc, "echo", []byte("fake-header"), []byte("fake-body")) + require.NoError(t, err, "Relayed call failed.") + + ts.Server().Close() + require.NotPanics(t, func() { + raw.CallSC(ctx, sc, "echo", []byte("fake-header"), []byte("fake-body")) + }) + }) +} + +func TestRelayConnectionCloseDrainsRelayItems(t *testing.T) { + opts := serviceNameOpts("s1").SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + s1 := ts.Server() + s2 := ts.NewServer(serviceNameOpts("s2")) + + s2HP := s2.PeerInfo().HostPort + testutils.RegisterEcho(s1, func() { + // When s1 gets called, it calls Close on the connection from the relay to s2. + conn, err := ts.Relay().Peers().GetOrAdd(s2HP).GetConnection(ctx) + require.NoError(t, err, "Unexpected failure getting connection between s1 and relay") + conn.Close() + }) + + testutils.AssertEcho(t, s2, ts.HostPort(), "s1") + + calls := relaytest.NewMockStats() + calls.Add("s2", "s1", "echo").Succeeded().End() + ts.AssertRelayStats(calls) + }) +} + +func TestRelayIDClash(t *testing.T) { + opts := serviceNameOpts("s1").SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s1 := ts.Server() + s2 := ts.NewServer(serviceNameOpts("s2")) + + unblock := make(chan struct{}) + testutils.RegisterEcho(s1, func() { + <-unblock + }) + testutils.RegisterEcho(s2, nil) + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + testutils.AssertEcho(t, s2, ts.HostPort(), s1.ServiceName()) + }() + } + + for i := 0; i < 5; i++ { + testutils.AssertEcho(t, s1, ts.HostPort(), s2.ServiceName()) + } + + close(unblock) + wg.Wait() + }) +} + +func TestRelayErrorsOnGetPeer(t *testing.T) { + busyErr := NewSystemError(ErrCodeBusy, "busy") + tests := []struct { + desc string + returnPeer string + returnErr error + statsKey string + wantErr error + }{ + { + desc: "No peer and no error", + returnPeer: "", + returnErr: nil, + statsKey: "relay-bad-relay-host", + wantErr: NewSystemError(ErrCodeDeclined, `bad relay host implementation`), + }, + { + desc: "System error getting peer", + returnErr: busyErr, + statsKey: "relay-busy", + wantErr: busyErr, + }, + { + desc: "Unknown error getting peer", + returnErr: errors.New("unknown"), + statsKey: "relay-declined", + wantErr: NewSystemError(ErrCodeDeclined, "unknown"), + }, + } + + for _, tt := range tests { + f := func(relay.CallFrame, *Connection) (string, error) { + return tt.returnPeer, tt.returnErr + } + + opts := testutils.NewOpts(). + SetRelayHost(relaytest.HostFunc(f)). + SetRelayOnly(). + DisableLogVerification() // some of the test cases cause warnings. + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + client := ts.NewClient(nil) + err := testutils.CallEcho(client, ts.HostPort(), "svc", nil) + if !assert.Error(t, err, "Call to unknown service should fail") { + return + } + + assert.Equal(t, tt.wantErr, err, "%v: unexpected error", tt.desc) + + calls := relaytest.NewMockStats() + calls.Add(client.PeerInfo().ServiceName, "svc", "echo"). + Failed(tt.statsKey).End() + ts.AssertRelayStats(calls) + }) + } +} + +func TestErrorFrameEndsRelay(t *testing.T) { + // TestServer validates that there are no relay items left after the given func. + opts := serviceNameOpts("svc").SetRelayOnly().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + client := ts.NewClient(nil) + + err := testutils.CallEcho(client, ts.HostPort(), "svc", nil) + if !assert.Error(t, err, "Expected error due to unknown method") { + return + } + + se, ok := err.(SystemError) + if !assert.True(t, ok, "err should be a SystemError, got %T", err) { + return + } + + assert.Equal(t, ErrCodeBadRequest, se.Code(), "Expected BadRequest error") + + calls := relaytest.NewMockStats() + calls.Add(client.PeerInfo().ServiceName, "svc", "echo").Failed("bad-request").End() + ts.AssertRelayStats(calls) + }) +} + +// Trigger a race between receiving a new call and a connection closing +// by closing the relay while a lot of background calls are being made. +func TestRaceCloseWithNewCall(t *testing.T) { + opts := serviceNameOpts("s1").SetRelayOnly().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s1 := ts.Server() + s2 := ts.NewServer(serviceNameOpts("s2").DisableLogVerification()) + testutils.RegisterEcho(s1, nil) + + // signal to start closing the relay. + var ( + closeRelay sync.WaitGroup + stopCalling atomic.Int32 + callers sync.WaitGroup + ) + + for i := 0; i < 5; i++ { + callers.Add(1) + closeRelay.Add(1) + + go func() { + defer callers.Done() + + calls := 0 + for stopCalling.Load() == 0 { + testutils.CallEcho(s2, ts.HostPort(), "s1", nil) + calls++ + if calls == 5 { + closeRelay.Done() + } + } + }() + } + + closeRelay.Wait() + + // Close the relay, wait for it to close. + ts.Relay().Close() + closed := testutils.WaitFor(time.Second, func() bool { + return ts.Relay().State() == ChannelClosed + }) + assert.True(t, closed, "Relay did not close within timeout") + + // Now stop all calls, and wait for the calling goroutine to end. + stopCalling.Inc() + callers.Wait() + }) +} + +func TestTimeoutCallsThenClose(t *testing.T) { + // Test needs at least 2 CPUs to trigger race conditions. + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + + opts := serviceNameOpts("s1").SetRelayOnly().DisableLogVerification() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s1 := ts.Server() + s2 := ts.NewServer(serviceNameOpts("s2").DisableLogVerification()) + + unblockEcho := make(chan struct{}) + testutils.RegisterEcho(s1, func() { + <-unblockEcho + }) + + ctx, cancel := NewContext(testutils.Timeout(30 * time.Millisecond)) + defer cancel() + + var callers sync.WaitGroup + for i := 0; i < 100; i++ { + callers.Add(1) + go func() { + defer callers.Done() + raw.Call(ctx, s2, ts.HostPort(), "s1", "echo", nil, nil) + }() + } + + close(unblockEcho) + + // Wait for all the callers to end + callers.Wait() + }) +} + +func TestLargeTimeoutsAreClamped(t *testing.T) { + const ( + clampTTL = time.Millisecond + longTTL = time.Minute + ) + + opts := serviceNameOpts("echo-service"). + SetRelayOnly(). + SetRelayMaxTimeout(clampTTL). + DisableLogVerification() // handler returns after deadline + + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + srv := ts.Server() + client := ts.NewClient(nil) + + unblock := make(chan struct{}) + defer close(unblock) // let server shut down cleanly + testutils.RegisterFunc(srv, "echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + now := time.Now() + deadline, ok := ctx.Deadline() + assert.True(t, ok, "Expected deadline to be set in handler.") + assert.True(t, deadline.Sub(now) <= clampTTL, "Expected relay to clamp TTL sent to backend.") + <-unblock + return &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil + }) + + done := make(chan struct{}) + go func() { + ctx, cancel := NewContext(longTTL) + defer cancel() + _, _, _, err := raw.Call(ctx, client, ts.HostPort(), "echo-service", "echo", nil, nil) + require.Error(t, err) + code := GetSystemErrorCode(err) + assert.Equal(t, ErrCodeTimeout, code) + close(done) + }() + + select { + case <-time.After(testutils.Timeout(10 * clampTTL)): + t.Fatal("Failed to clamp timeout.") + case <-done: + } + }) +} + +// TestRelayConcurrentCalls makes many concurrent calls and ensures that +// we don't try to reuse any frames once they've been released. +func TestRelayConcurrentCalls(t *testing.T) { + pool := NewProtectMemFramePool() + opts := testutils.NewOpts().SetRelayOnly().SetFramePool(pool) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + server := benchmark.NewServer( + benchmark.WithNoLibrary(), + benchmark.WithServiceName("s1"), + ) + defer server.Close() + ts.RelayHost().Add("s1", server.HostPort()) + + client := benchmark.NewClient([]string{ts.HostPort()}, + benchmark.WithNoDurations(), + // TODO(prashant): Enable once we have control over concurrency with NoLibrary. + // benchmark.WithNoLibrary(), + benchmark.WithNumClients(20), + benchmark.WithServiceName("s1"), + benchmark.WithTimeout(time.Minute), + ) + defer client.Close() + require.NoError(t, client.Warmup(), "Client warmup failed") + + _, err := client.RawCall(1000) + assert.NoError(t, err, "RawCalls failed") + }) +} + +// Ensure that any connections created in the relay path send the ephemeral +// host:port. +func TestRelayOutgoingConnectionsEphemeral(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s2 := ts.NewServer(serviceNameOpts("s2")) + testutils.RegisterFunc(s2, "echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + assert.True(t, CurrentCall(ctx).RemotePeer().IsEphemeral, + "Connections created for the relay should send ephemeral host:port header") + + return &raw.Res{ + Arg2: args.Arg2, + Arg3: args.Arg3, + }, nil + }) + + require.NoError(t, testutils.CallEcho(ts.Server(), ts.HostPort(), "s2", nil), "CallEcho failed") + }) +} + +func TestRelayHandleLocalCall(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly(). + SetRelayLocal("relay", "tchannel", "test"). + // We make a call to "test" for an unknown method. + AddLogFilter("Couldn't find handler.", 1) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s2 := ts.NewServer(serviceNameOpts("s2")) + testutils.RegisterEcho(s2, nil) + + client := ts.NewClient(nil) + testutils.AssertEcho(t, client, ts.HostPort(), "s2") + + testutils.RegisterEcho(ts.Relay(), nil) + testutils.AssertEcho(t, client, ts.HostPort(), "relay") + + // Sould get a bad request for "test" since the channel does not handle it. + err := testutils.CallEcho(client, ts.HostPort(), "test", nil) + assert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), "Expected BadRequest for test") + + // But an unknown service causes declined + err = testutils.CallEcho(client, ts.HostPort(), "unknown", nil) + assert.Equal(t, ErrCodeDeclined, GetSystemErrorCode(err), "Expected Declined for unknown") + + calls := relaytest.NewMockStats() + calls.Add(client.ServiceName(), "s2", "echo").Succeeded().End() + calls.Add(client.ServiceName(), "unknown", "echo").Failed("relay-declined").End() + ts.AssertRelayStats(calls) + }) +} + +func TestRelayHandleLargeLocalCall(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly(). + SetRelayLocal("relay"). + AddLogFilter("Received fragmented callReq", 1). + // Expect 4 callReqContinues for 256 kb payload that we cannot relay. + AddLogFilter("Failed to relay frame.", 4) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + client := ts.NewClient(nil) + testutils.RegisterEcho(ts.Relay(), nil) + + // This large call should fail with a bad request. + err := testutils.CallEcho(client, ts.HostPort(), "relay", &raw.Args{ + Arg2: testutils.RandBytes(128 * 1024), + Arg3: testutils.RandBytes(128 * 1024), + }) + if assert.Equal(t, ErrCodeBadRequest, GetSystemErrorCode(err), "Expected BadRequest for large call to relay") { + assert.Contains(t, err.Error(), "cannot receive fragmented calls") + } + + // We may get an error before the call is finished flushing. + // Do a ping to ensure everything has been flushed. + ctx, cancel := NewContext(time.Second) + defer cancel() + require.NoError(t, client.Ping(ctx, ts.HostPort()), "Ping failed") + }) +} + +func TestRelayMakeOutgoingCall(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + svr1 := ts.Relay() + svr2 := ts.NewServer(testutils.NewOpts().SetServiceName("svc2")) + testutils.RegisterEcho(svr2, nil) + + sizes := []int{128, 1024, 128 * 1024} + for _, size := range sizes { + err := testutils.CallEcho(svr1, ts.HostPort(), "svc2", &raw.Args{ + Arg2: testutils.RandBytes(size), + Arg3: testutils.RandBytes(size), + }) + assert.NoError(t, err, "Echo with size %v failed", size) + } + }) +} + +func TestRelayConnection(t *testing.T) { + var errTest = errors.New("test") + var wantHostPort string + getHost := func(call relay.CallFrame, conn *Connection) (string, error) { + assert.Equal(t, wantHostPort, conn.RemotePeerInfo().HostPort, "Unexpected RemoteHostPort") + return "", errTest + } + + opts := testutils.NewOpts(). + SetRelayOnly(). + SetRelayHost(relaytest.HostFunc(getHost)) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + // Create a client that is listening so we can set the expected host:port. + clientOpts := testutils.NewOpts().SetProcessName("nodejs-hyperbahn") + client := ts.NewServer(clientOpts) + wantHostPort = client.PeerInfo().HostPort + + err := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil) + require.Error(t, err, "Expected CallEcho to fail") + assert.Contains(t, err.Error(), errTest.Error(), "Unexpected error") + + // Verify that the relay has not closed any connections. + assert.Equal(t, 1, ts.Relay().IntrospectNumConnections(), "Relay should maintain client connection") + }) +} + +func TestRelayConnectionClosed(t *testing.T) { + protocolErr := NewSystemError(ErrCodeProtocol, "invalid service name") + getHost := func(call relay.CallFrame, conn *Connection) (string, error) { + return "", protocolErr + } + + opts := testutils.NewOpts(). + SetRelayOnly(). + SetRelayHost(relaytest.HostFunc(getHost)) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + // The client receives a protocol error which causes the following logs. + opts := testutils.NewOpts(). + AddLogFilter("Peer reported protocol error", 1). + AddLogFilter("Connection error", 1) + client := ts.NewClient(opts) + + err := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil) + assert.Equal(t, protocolErr, err, "Unexpected error on call") + + closedAll := testutils.WaitFor(time.Second, func() bool { + return ts.Relay().IntrospectNumConnections() == 0 + }) + assert.True(t, closedAll, "Relay should close client connection") + }) +} + +func TestRelayUsesRootPeers(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + testutils.RegisterEcho(ts.Server(), nil) + client := testutils.NewClient(t, nil) + err := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil) + assert.NoError(t, err, "Echo failed") + assert.Len(t, ts.Relay().Peers().Copy(), 0, "Peers should not be modified by relay") + }) +} + +// Ensure that if the relay recieves a call on a connection that is not active, +// it declines the call, and increments a relay-conn-inactive stat. +func TestRelayRejectsDuringClose(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly(). + AddLogFilter("Failed to relay frame.", 1, "error", "incoming connection is not active: connectionStartClose") + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + gotCall := make(chan struct{}) + block := make(chan struct{}) + + testutils.RegisterEcho(ts.Server(), func() { + close(gotCall) + <-block + }) + + client := ts.NewClient(nil) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + }() + + <-gotCall + // Close the relay so that it stops accepting more calls. + ts.Relay().Close() + err := testutils.CallEcho(client, ts.HostPort(), ts.ServiceName(), nil) + require.Error(t, err, "Expect call to fail after relay is shutdown") + assert.Contains(t, err.Error(), "incoming connection is not active") + close(block) + wg.Wait() + + // We have a successful call that ran in the goroutine + // and a failed call that we just checked the error on. + calls := relaytest.NewMockStats() + calls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), "echo"). + Succeeded().End() + calls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), "echo"). + // No peer is set since we rejected the call before selecting one. + Failed("relay-conn-inactive").End() + ts.AssertRelayStats(calls) + }) +} + +func TestRelayRateLimitDrop(t *testing.T) { + getHost := func(call relay.CallFrame, _ *Connection) (string, error) { + return "", relay.RateLimitDropError{} + } + + opts := testutils.NewOpts(). + SetRelayOnly(). + SetRelayHost(relaytest.HostFunc(getHost)) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + var gotCall bool + testutils.RegisterEcho(ts.Server(), func() { + gotCall = true + }) + + client := ts.NewClient(nil) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + // We want to use a low timeout here since the test waits for this + // call to timeout. + ctx, cancel := NewContext(testutils.Timeout(100 * time.Millisecond)) + defer cancel() + _, _, _, err := raw.Call(ctx, client, ts.HostPort(), ts.ServiceName(), "echo", nil, nil) + require.Equal(t, ErrTimeout, err, "Expected CallEcho to fail") + defer wg.Done() + }() + + wg.Wait() + assert.False(t, gotCall, "Server should not receive a call") + + calls := relaytest.NewMockStats() + calls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), "echo"). + Failed("relay-dropped").End() + ts.AssertRelayStats(calls) + }) +} + +// Test that a stalled connection to a single server does not block all calls +// from that server, and we have stats to capture that this is happening. +func TestRelayStalledConnection(t *testing.T) { + opts := testutils.NewOpts(). + DisableLogVerification(). // we expect warnings due to removed relay item. + SetSendBufferSize(10). // We want to hit the buffer size earlier. + SetServiceName("s1"). + SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + s2 := ts.NewServer(testutils.NewOpts().SetServiceName("s2")) + testutils.RegisterEcho(s2, nil) + + stall := make(chan struct{}) + stallComplete := make(chan struct{}) + stallHandler := func(ctx context.Context, call *InboundCall) { + <-stall + raw.ReadArgs(call) + close(stallComplete) + } + ts.Register(HandlerFunc(stallHandler), "echo") + + ctx, cancel := NewContext(testutils.Timeout(300 * time.Millisecond)) + defer cancel() + + client := ts.NewClient(nil) + call, err := client.BeginCall(ctx, ts.HostPort(), ts.ServiceName(), "echo", nil) + require.NoError(t, err, "BeginCall failed") + writer, err := call.Arg2Writer() + require.NoError(t, err, "Arg2Writer failed") + go io.Copy(writer, testreader.Looper([]byte("test"))) + + // Try to read the response which might get an error. + readDone := make(chan struct{}) + go func() { + defer close(readDone) + + _, err := call.Response().Arg2Reader() + if assert.Error(t, err, "Expected error while reading") { + assert.Contains(t, err.Error(), "frame was not sent to remote side") + } + }() + + // Wait for the reader to error out. + select { + case <-time.After(testutils.Timeout(10 * time.Second)): + t.Fatalf("Test timed out waiting for reader to fail") + case <-readDone: + cancel() + close(stall) + } + + // We should be able to make calls to s2 even if s1 is stalled. + testutils.AssertEcho(t, client, ts.HostPort(), "s2") + + // The server channel will not close until the stall handler receives + // an error. Since we don't propagate cancels, the handler will keep + // trying to read arguments till the timeout. + select { + case <-stallComplete: + case <-time.After(testutils.Timeout(300 * time.Millisecond)): + t.Fatalf("Stall handler did not complete") + } + + calls := relaytest.NewMockStats() + calls.Add(client.PeerInfo().ServiceName, ts.ServiceName(), "echo"). + Failed("relay-dest-conn-slow").End() + calls.Add(client.PeerInfo().ServiceName, "s2", "echo"). + Succeeded().End() + ts.AssertRelayStats(calls) + }) +} + +func TestRelayThroughSeparateRelay(t *testing.T) { + opts := testutils.NewOpts(). + SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + serverHP := ts.Server().PeerInfo().HostPort + dummyFactory := func(relay.CallFrame, *Connection) (string, error) { + panic("should not get invoked") + } + relay2Opts := testutils.NewOpts().SetRelayHost(relaytest.HostFunc(dummyFactory)) + relay2 := ts.NewServer(relay2Opts) + + // Override where the peers come from. + ts.RelayHost().SetChannel(relay2) + relay2.GetSubChannel(ts.ServiceName(), Isolated).Peers().Add(serverHP) + + testutils.RegisterEcho(ts.Server(), nil) + client := ts.NewClient(nil) + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + + numConns := func(p PeerRuntimeState) int { + return len(p.InboundConnections) + len(p.OutboundConnections) + } + + // Verify that there are no connections from ts.Relay() to the server. + introspected := ts.Relay().IntrospectState(nil) + assert.Zero(t, numConns(introspected.RootPeers[serverHP]), "Expected no connections from relay to server") + + introspected = relay2.IntrospectState(nil) + assert.Equal(t, 1, numConns(introspected.RootPeers[serverHP]), "Expected 1 connection from relay2 to server") + }) +} + +func TestRelayConcurrentNewConnectionAttempts(t *testing.T) { + opts := testutils.NewOpts().SetRelayOnly() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + // Create a server that is slow to accept connections by using + // a frame relay to slow down the initial message. + slowServer := testutils.NewServer(t, serviceNameOpts("slow-server")) + defer slowServer.Close() + testutils.RegisterEcho(slowServer, nil) + + var delayed atomic.Bool + relayFunc := func(outgoing bool, f *Frame) *Frame { + if !delayed.Load() { + time.Sleep(testutils.Timeout(50 * time.Millisecond)) + delayed.Store(true) + } + return f + } + + slowHP, close := testutils.FrameRelay(t, slowServer.PeerInfo().HostPort, relayFunc) + defer close() + ts.RelayHost().Add("slow-server", slowHP) + + // Make concurrent calls to trigger concurrent getConnectionRelay calls. + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(1) + // Create client and get dest host:port in the main goroutine to avoid races. + client := ts.NewClient(nil) + relayHostPort := ts.HostPort() + go func() { + defer wg.Done() + testutils.AssertEcho(t, client, relayHostPort, "slow-server") + }() + } + wg.Wait() + + // Verify that the slow server only received a single connection. + inboundConns := 0 + for _, state := range slowServer.IntrospectState(nil).RootPeers { + inboundConns += len(state.InboundConnections) + } + assert.Equal(t, 1, inboundConns, "Expected a single inbound connection to the server") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/reqres.go b/vendor/src/github.com/uber/tchannel-go/reqres.go new file mode 100644 index 00000000..de014c47 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/reqres.go @@ -0,0 +1,296 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + + "github.com/uber/tchannel-go/typed" +) + +type errReqResWriterStateMismatch struct { + state reqResWriterState + expectedState reqResWriterState +} + +func (e errReqResWriterStateMismatch) Error() string { + return fmt.Sprintf("attempting write outside of expected state, in %v expected %v", + e.state, e.expectedState) +} + +type errReqResReaderStateMismatch struct { + state reqResReaderState + expectedState reqResReaderState +} + +func (e errReqResReaderStateMismatch) Error() string { + return fmt.Sprintf("attempting read outside of expected state, in %v expected %v", + e.state, e.expectedState) +} + +// reqResWriterState defines the state of a request/response writer +type reqResWriterState int + +const ( + reqResWriterPreArg1 reqResWriterState = iota + reqResWriterPreArg2 + reqResWriterPreArg3 + reqResWriterComplete +) + +//go:generate stringer -type=reqResWriterState + +// messageForFragment determines which message should be used for the given +// fragment +type messageForFragment func(initial bool) message + +// A reqResWriter writes out requests/responses. Exactly which it does is +// determined by its messageForFragment function which returns the appropriate +// message to use when building an initial or follow-on fragment. +type reqResWriter struct { + conn *Connection + contents *fragmentingWriter + mex *messageExchange + state reqResWriterState + messageForFragment messageForFragment + log Logger + err error +} + +//go:generate stringer -type=reqResReaderState + +func (w *reqResWriter) argWriter(last bool, inState reqResWriterState, outState reqResWriterState) (ArgWriter, error) { + if w.err != nil { + return nil, w.err + } + + if w.state != inState { + return nil, w.failed(errReqResWriterStateMismatch{state: w.state, expectedState: inState}) + } + + argWriter, err := w.contents.ArgWriter(last) + if err != nil { + return nil, w.failed(err) + } + + w.state = outState + return argWriter, nil +} + +func (w *reqResWriter) arg1Writer() (ArgWriter, error) { + return w.argWriter(false /* last */, reqResWriterPreArg1, reqResWriterPreArg2) +} + +func (w *reqResWriter) arg2Writer() (ArgWriter, error) { + return w.argWriter(false /* last */, reqResWriterPreArg2, reqResWriterPreArg3) +} + +func (w *reqResWriter) arg3Writer() (ArgWriter, error) { + return w.argWriter(true /* last */, reqResWriterPreArg3, reqResWriterComplete) +} + +// newFragment creates a new fragment for marshaling into +func (w *reqResWriter) newFragment(initial bool, checksum Checksum) (*writableFragment, error) { + if err := w.mex.checkError(); err != nil { + return nil, w.failed(err) + } + + message := w.messageForFragment(initial) + + // Create the frame + frame := w.conn.opts.FramePool.Get() + frame.Header.ID = w.mex.msgID + frame.Header.messageType = message.messageType() + + // Write the message into the fragment, reserving flags and checksum bytes + wbuf := typed.NewWriteBuffer(frame.Payload[:]) + fragment := new(writableFragment) + fragment.frame = frame + fragment.flagsRef = wbuf.DeferByte() + if err := message.write(wbuf); err != nil { + return nil, err + } + wbuf.WriteSingleByte(byte(checksum.TypeCode())) + fragment.checksumRef = wbuf.DeferBytes(checksum.Size()) + fragment.checksum = checksum + fragment.contents = wbuf + return fragment, wbuf.Err() +} + +// flushFragment sends a fragment to the peer over the connection +func (w *reqResWriter) flushFragment(fragment *writableFragment) error { + if w.err != nil { + return w.err + } + + frame := fragment.frame.(*Frame) + frame.Header.SetPayloadSize(uint16(fragment.contents.BytesWritten())) + + if err := w.mex.checkError(); err != nil { + return w.failed(err) + } + select { + case <-w.mex.ctx.Done(): + return w.failed(GetContextError(w.mex.ctx.Err())) + case <-w.mex.errCh.c: + return w.failed(w.mex.errCh.err) + case w.conn.sendCh <- frame: + return nil + } +} + +// failed marks the writer as having failed +func (w *reqResWriter) failed(err error) error { + w.log.Debugf("writer failed: %v existing err: %v", err, w.err) + if w.err != nil { + return w.err + } + + w.mex.shutdown() + w.err = err + return w.err +} + +// reqResReaderState defines the state of a request/response reader +type reqResReaderState int + +const ( + reqResReaderPreArg1 reqResReaderState = iota + reqResReaderPreArg2 + reqResReaderPreArg3 + reqResReaderComplete +) + +// A reqResReader is capable of reading arguments from a request or response object. +type reqResReader struct { + contents *fragmentingReader + mex *messageExchange + state reqResReaderState + messageForFragment messageForFragment + initialFragment *readableFragment + previousFragment *readableFragment + log Logger + err error +} + +// arg1Reader returns an ArgReader to read arg1. +func (r *reqResReader) arg1Reader() (ArgReader, error) { + return r.argReader(false /* last */, reqResReaderPreArg1, reqResReaderPreArg2) +} + +// arg2Reader returns an ArgReader to read arg2. +func (r *reqResReader) arg2Reader() (ArgReader, error) { + return r.argReader(false /* last */, reqResReaderPreArg2, reqResReaderPreArg3) +} + +// arg3Reader returns an ArgReader to read arg3. +func (r *reqResReader) arg3Reader() (ArgReader, error) { + return r.argReader(true /* last */, reqResReaderPreArg3, reqResReaderComplete) +} + +// argReader returns an ArgReader that can be used to read an argument. The +// ReadCloser must be closed once the argument has been read. +func (r *reqResReader) argReader(last bool, inState reqResReaderState, outState reqResReaderState) (ArgReader, error) { + if r.state != inState { + return nil, r.failed(errReqResReaderStateMismatch{state: r.state, expectedState: inState}) + } + + argReader, err := r.contents.ArgReader(last) + if err != nil { + return nil, r.failed(err) + } + + r.state = outState + return argReader, nil +} + +// recvNextFragment receives the next fragment from the underlying message exchange. +func (r *reqResReader) recvNextFragment(initial bool) (*readableFragment, error) { + if r.initialFragment != nil { + fragment := r.initialFragment + r.initialFragment = nil + r.previousFragment = fragment + return fragment, nil + } + + // Wait for the appropriate message from the peer + message := r.messageForFragment(initial) + frame, err := r.mex.recvPeerFrameOfType(message.messageType()) + if err != nil { + if err, ok := err.(errorMessage); ok { + // If we received a serialized error from the other side, then we should go through + // the normal doneReading path so stats get updated with this error. + r.err = err.AsSystemError() + return nil, err + } + + return nil, r.failed(err) + } + + // Parse the message and setup the fragment + fragment, err := parseInboundFragment(r.mex.framePool, frame, message) + if err != nil { + return nil, r.failed(err) + } + + r.previousFragment = fragment + return fragment, nil +} + +// releasePreviousFrament releases the last fragment returned by the reader if +// it's still around. This operation is idempotent. +func (r *reqResReader) releasePreviousFragment() { + fragment := r.previousFragment + r.previousFragment = nil + if fragment != nil { + fragment.done() + } +} + +// failed indicates the reader failed +func (r *reqResReader) failed(err error) error { + r.log.Debugf("reader failed: %v existing err: %v", err, r.err) + if r.err != nil { + return r.err + } + + r.mex.shutdown() + r.err = err + return r.err +} + +// parseInboundFragment parses an incoming fragment based on the given message +func parseInboundFragment(framePool FramePool, frame *Frame, message message) (*readableFragment, error) { + rbuf := typed.NewReadBuffer(frame.SizedPayload()) + fragment := new(readableFragment) + fragment.flags = rbuf.ReadSingleByte() + if err := message.read(rbuf); err != nil { + return nil, err + } + + fragment.checksumType = ChecksumType(rbuf.ReadSingleByte()) + fragment.checksum = rbuf.ReadBytes(fragment.checksumType.ChecksumSize()) + fragment.contents = rbuf + fragment.onDone = func() { + framePool.Release(frame) + } + return fragment, rbuf.Err() +} diff --git a/vendor/src/github.com/uber/tchannel-go/reqresreaderstate_string.go b/vendor/src/github.com/uber/tchannel-go/reqresreaderstate_string.go new file mode 100644 index 00000000..8669ffbc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/reqresreaderstate_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=reqResReaderState; DO NOT EDIT + +package tchannel + +import "fmt" + +const _reqResReaderState_name = "reqResReaderPreArg1reqResReaderPreArg2reqResReaderPreArg3reqResReaderComplete" + +var _reqResReaderState_index = [...]uint8{0, 19, 38, 57, 77} + +func (i reqResReaderState) String() string { + if i < 0 || i+1 >= reqResReaderState(len(_reqResReaderState_index)) { + return fmt.Sprintf("reqResReaderState(%d)", i) + } + return _reqResReaderState_name[_reqResReaderState_index[i]:_reqResReaderState_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/reqreswriterstate_string.go b/vendor/src/github.com/uber/tchannel-go/reqreswriterstate_string.go new file mode 100644 index 00000000..6ac637d4 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/reqreswriterstate_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=reqResWriterState; DO NOT EDIT + +package tchannel + +import "fmt" + +const _reqResWriterState_name = "reqResWriterPreArg1reqResWriterPreArg2reqResWriterPreArg3reqResWriterComplete" + +var _reqResWriterState_index = [...]uint8{0, 19, 38, 57, 77} + +func (i reqResWriterState) String() string { + if i < 0 || i+1 >= reqResWriterState(len(_reqResWriterState_index)) { + return fmt.Sprintf("reqResWriterState(%d)", i) + } + return _reqResWriterState_name[_reqResWriterState_index[i]:_reqResWriterState_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/retry.go b/vendor/src/github.com/uber/tchannel-go/retry.go new file mode 100644 index 00000000..5559e5ad --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/retry.go @@ -0,0 +1,269 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "net" + "sync" + "time" + + "golang.org/x/net/context" +) + +// RetryOn represents the types of errors to retry on. +type RetryOn int + +//go:generate stringer -type=RetryOn + +const ( + // RetryDefault is currently the same as RetryConnectionError. + RetryDefault RetryOn = iota + + // RetryConnectionError retries on busy frames, declined frames, and connection errors. + RetryConnectionError + + // RetryNever never retries any errors. + RetryNever + + // RetryNonIdempotent will retry errors that occur before a request has been picked up. + // E.g. busy frames and declined frames. + // This should be used when making calls to non-idempotent endpoints. + RetryNonIdempotent + + // RetryUnexpected will retry busy frames, declined frames, and unenxpected frames. + RetryUnexpected + + // RetryIdempotent will retry all errors that can be retried. This should be used + // for idempotent endpoints. + RetryIdempotent +) + +// RequestState is a global request state that persists across retries. +type RequestState struct { + // Start is the time at which the request was initiated by the caller of RunWithRetry. + Start time.Time + // SelectedPeers is a set of host:ports that have been selected previously. + SelectedPeers map[string]struct{} + // Attempt is 1 for the first attempt, and so on. + Attempt int + retryOpts *RetryOptions +} + +// RetriableFunc is the type of function that can be passed to RunWithRetry. +type RetriableFunc func(context.Context, *RequestState) error + +func isNetError(err error) bool { + // TODO(prashantv): Should TChannel internally these to ErrCodeNetwork before returning + // them to the user? + _, ok := err.(net.Error) + return ok +} + +func getErrCode(err error) SystemErrCode { + code := GetSystemErrorCode(err) + if isNetError(err) { + code = ErrCodeNetwork + } + return code +} + +// CanRetry returns whether an error can be retried for the given retry option. +func (r RetryOn) CanRetry(err error) bool { + if r == RetryNever { + return false + } + if r == RetryDefault { + r = RetryConnectionError + } + + code := getErrCode(err) + + if code == ErrCodeBusy || code == ErrCodeDeclined { + return true + } + // Never retry bad requests, since it will probably cause another bad request. + if code == ErrCodeBadRequest { + return false + } + + switch r { + case RetryConnectionError: + return code == ErrCodeNetwork + case RetryUnexpected: + return code == ErrCodeUnexpected + case RetryIdempotent: + return true + } + + return false +} + +// RetryOptions are the retry options used to configure RunWithRetry. +type RetryOptions struct { + // MaxAttempts is the maximum number of calls and retries that will be made. + // If this is 0, the default number of attempts (5) is used. + MaxAttempts int + + // RetryOn is the types of errors to retry on. + RetryOn RetryOn + + // TimeoutPerAttempt is the per-retry timeout to use. + // If this is zero, then the original timeout is used. + TimeoutPerAttempt time.Duration +} + +var defaultRetryOptions = &RetryOptions{ + MaxAttempts: 5, +} + +var requestStatePool = sync.Pool{ + New: func() interface{} { return &RequestState{} }, +} + +func getRetryOptions(ctx context.Context) *RetryOptions { + params := getTChannelParams(ctx) + if params == nil { + return defaultRetryOptions + } + + opts := params.retryOptions + if opts == nil { + return defaultRetryOptions + } + + if opts.MaxAttempts == 0 { + opts.MaxAttempts = defaultRetryOptions.MaxAttempts + } + return opts +} + +// HasRetries will return true if there are more retries left. +func (rs *RequestState) HasRetries(err error) bool { + if rs == nil { + return false + } + rOpts := rs.retryOpts + return rs.Attempt < rOpts.MaxAttempts && rOpts.RetryOn.CanRetry(err) +} + +// SinceStart returns the time since the start of the request. If there is no request state, +// then the fallback is returned. +func (rs *RequestState) SinceStart(now time.Time, fallback time.Duration) time.Duration { + if rs == nil { + return fallback + } + return now.Sub(rs.Start) +} + +// PrevSelectedPeers returns the previously selected peers for this request. +func (rs *RequestState) PrevSelectedPeers() map[string]struct{} { + if rs == nil { + return nil + } + return rs.SelectedPeers +} + +// AddSelectedPeer adds a given peer to the set of selected peers. +func (rs *RequestState) AddSelectedPeer(hostPort string) { + if rs == nil { + return + } + + host := getHost(hostPort) + if rs.SelectedPeers == nil { + rs.SelectedPeers = map[string]struct{}{ + hostPort: {}, + host: {}, + } + } else { + rs.SelectedPeers[hostPort] = struct{}{} + rs.SelectedPeers[host] = struct{}{} + } +} + +// RetryCount returns the retry attempt this is. Essentially, Attempt - 1. +func (rs *RequestState) RetryCount() int { + if rs == nil { + return 0 + } + return rs.Attempt - 1 +} + +// RunWithRetry will take a function that makes the TChannel call, and will +// rerun it as specifed in the RetryOptions in the Context. +func (ch *Channel) RunWithRetry(runCtx context.Context, f RetriableFunc) error { + var err error + + opts := getRetryOptions(runCtx) + rs := ch.getRequestState(opts) + defer requestStatePool.Put(rs) + + for i := 0; i < opts.MaxAttempts; i++ { + rs.Attempt++ + + if opts.TimeoutPerAttempt == 0 { + err = f(runCtx, rs) + } else { + attemptCtx, cancel := context.WithTimeout(runCtx, opts.TimeoutPerAttempt) + err = f(attemptCtx, rs) + cancel() + } + + if err == nil { + return nil + } + if !opts.RetryOn.CanRetry(err) { + if ch.log.Enabled(LogLevelInfo) { + ch.log.WithFields(ErrField(err)).Info("Failed after non-retriable error.") + } + return err + } + + ch.log.WithFields( + ErrField(err), + LogField{"attempt", rs.Attempt}, + LogField{"maxAttempts", opts.MaxAttempts}, + ).Info("Retrying request after retryable error.") + } + + // Too many retries, return the last error + return err +} + +func (ch *Channel) getRequestState(retryOpts *RetryOptions) *RequestState { + rs := requestStatePool.Get().(*RequestState) + *rs = RequestState{ + Start: ch.timeNow(), + retryOpts: retryOpts, + } + return rs +} + +// getHost returns the host part of a host:port. If no ':' is found, it returns the +// original string. Note: This hand-rolled loop is faster than using strings.IndexByte. +func getHost(hostPort string) string { + for i := 0; i < len(hostPort); i++ { + if hostPort[i] == ':' { + return hostPort[:i] + } + } + return hostPort +} diff --git a/vendor/src/github.com/uber/tchannel-go/retry_request_test.go b/vendor/src/github.com/uber/tchannel-go/retry_request_test.go new file mode 100644 index 00000000..d62c6d7c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/retry_request_test.go @@ -0,0 +1,83 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestRequestStateRetry(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(raw.Wrap(newTestHandler(t)), "echo") + + closedHostPorts := make([]string, 4) + for i := range closedHostPorts { + hostPort, close := testutils.GetAcceptCloseHostPort(t) + defer close() + closedHostPorts[i] = hostPort + } + + // Since we close connections remotely, there will be some warnings that we can ignore. + opts := testutils.NewOpts().DisableLogVerification() + client := ts.NewClient(opts) + defer client.Close() + counter := 0 + + sc := client.GetSubChannel(ts.Server().ServiceName()) + err := client.RunWithRetry(ctx, func(ctx context.Context, rs *RequestState) error { + defer func() { counter++ }() + + expectedPeers := counter + if expectedPeers > 0 { + // An entry is also added for each host. + expectedPeers++ + } + + assert.Equal(t, expectedPeers, len(rs.SelectedPeers), "SelectedPeers should not be reused") + + if counter < 4 { + client.Peers().Add(closedHostPorts[counter]) + } else { + client.Peers().Add(ts.HostPort()) + } + + _, err := raw.CallV2(ctx, sc, raw.CArgs{ + Method: "echo", + CallOptions: &CallOptions{RequestState: rs}, + }) + return err + }) + assert.NoError(t, err, "RunWithRetry should succeed") + assert.Equal(t, 5, counter, "RunWithRetry should retry 5 times") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/retry_test.go b/vendor/src/github.com/uber/tchannel-go/retry_test.go new file mode 100644 index 00000000..1b58dbb2 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/retry_test.go @@ -0,0 +1,270 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "net" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func createFuncToRetry(t *testing.T, errors ...error) (RetriableFunc, *int) { + i := 0 + return func(_ context.Context, rs *RequestState) error { + defer func() { i++ }() + if i >= len(errors) { + t.Fatalf("Retry function has no error to return for this call") + } + assert.Equal(t, i+1, rs.Attempt, "Attempt count mismatch") + + err := errors[i] + return err + }, &i +} + +type testErrors struct { + Busy error + Declined error + Timeout error + Network error + Connection error + BadRequest error + Unexpected error + Cancelled error + + all []error +} + +func getTestErrors() testErrors { + errs := testErrors{ + Busy: ErrServerBusy, + Declined: ErrChannelClosed, + Timeout: ErrTimeout, + Network: NewSystemError(ErrCodeNetwork, "fake network error"), + Connection: net.UnknownNetworkError("fake connection error"), + BadRequest: ErrTimeoutRequired, + Unexpected: NewSystemError(ErrCodeUnexpected, "fake unexpected error"), + Cancelled: NewSystemError(ErrCodeCancelled, "fake cancelled error"), + } + errs.all = []error{errs.Busy, errs.Declined, errs.Timeout, errs.Network, errs.Connection, + errs.BadRequest, errs.Unexpected, errs.Cancelled} + return errs +} + +func TestCanRetry(t *testing.T) { + e := getTestErrors() + tests := []struct { + RetryOn RetryOn + RetryOK []error + }{ + {RetryNever, nil}, + {RetryDefault, []error{e.Busy, e.Declined, e.Network, e.Connection}}, + {RetryConnectionError, []error{e.Busy, e.Declined, e.Network, e.Connection}}, + {RetryNonIdempotent, []error{e.Busy, e.Declined}}, + {RetryUnexpected, []error{e.Busy, e.Declined, e.Unexpected}}, + {RetryIdempotent, []error{e.Busy, e.Declined, e.Timeout, e.Network, e.Connection, e.Unexpected, e.Cancelled}}, + } + + for _, tt := range tests { + retryOK := make(map[error]bool) + for _, err := range tt.RetryOK { + retryOK[err] = true + } + + for _, err := range e.all { + expectOK := retryOK[err] + assert.Equal(t, expectOK, tt.RetryOn.CanRetry(err), + "%v.CanRetry(%v) expected %v", tt.RetryOn, err, expectOK) + } + } +} + +func TestNoRetry(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + + e := getTestErrors() + retryOpts := &RetryOptions{RetryOn: RetryNever} + for _, fErr := range e.all { + ctx, cancel := NewContextBuilder(time.Second).SetRetryOptions(retryOpts).Build() + defer cancel() + + f, counter := createFuncToRetry(t, fErr) + err := ch.RunWithRetry(ctx, f) + assert.Equal(t, fErr, err) + assert.Equal(t, 1, *counter, "f should not be retried when retried are disabled") + } +} + +func TestRetryTillMaxAttempts(t *testing.T) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + + setErr := ErrServerBusy + runTest := func(maxAttempts, numErrors, expectCounter int, expectErr error) { + retryOpts := &RetryOptions{MaxAttempts: maxAttempts} + ctx, cancel := NewContextBuilder(time.Second).SetRetryOptions(retryOpts).Build() + defer cancel() + + var errors []error + for i := 0; i < numErrors; i++ { + errors = append(errors, setErr) + } + errors = append(errors, nil) + + f, counter := createFuncToRetry(t, errors...) + err := ch.RunWithRetry(ctx, f) + assert.Equal(t, expectErr, err, + "unexpected result for maxAttempts = %v numErrors = %v", maxAttempts, numErrors) + assert.Equal(t, expectCounter, *counter, + "expected f to be retried %v times with maxAttempts = %v numErrors = %v", + expectCounter, maxAttempts, numErrors) + } + + for numAttempts := 1; numAttempts < 5; numAttempts++ { + for numErrors := 0; numErrors < numAttempts+3; numErrors++ { + var expectErr error + if numErrors >= numAttempts { + expectErr = setErr + } + + expectCount := numErrors + 1 + if expectCount > numAttempts { + expectCount = numAttempts + } + + runTest(numAttempts, numErrors, expectCount, expectErr) + } + } +} + +func TestRetrySubContextNoTimeoutPerAttempt(t *testing.T) { + e := getTestErrors() + ctx, cancel := NewContext(time.Second) + defer cancel() + + ch := testutils.NewClient(t, nil) + defer ch.Close() + + counter := 0 + ch.RunWithRetry(ctx, func(sctx context.Context, _ *RequestState) error { + counter++ + assert.Equal(t, ctx, sctx, "Sub-context should be the same") + return e.Busy + }) + assert.Equal(t, 5, counter, "RunWithRetry did not run f enough times") +} + +func TestRetrySubContextTimeoutPerAttempt(t *testing.T) { + e := getTestErrors() + ctx, cancel := NewContextBuilder(time.Second). + SetTimeoutPerAttempt(time.Millisecond).Build() + defer cancel() + + ch := testutils.NewClient(t, nil) + defer ch.Close() + + var lastDeadline time.Time + + counter := 0 + ch.RunWithRetry(ctx, func(sctx context.Context, _ *RequestState) error { + counter++ + + assert.NotEqual(t, ctx, sctx, "Sub-context should be different") + deadline, _ := sctx.Deadline() + assert.True(t, deadline.After(lastDeadline), "Deadline is invalid") + lastDeadline = deadline + + overallDeadline, _ := ctx.Deadline() + assert.True(t, overallDeadline.After(deadline), "Deadline is invalid") + + return e.Busy + }) + assert.Equal(t, 5, counter, "RunWithRetry did not run f enough times") +} + +func TestRetryNetConnect(t *testing.T) { + e := getTestErrors() + ch := testutils.NewClient(t, nil) + defer ch.Close() + + ctx, cancel := NewContext(time.Second) + defer cancel() + + closedAddr := testutils.GetClosedHostPort(t) + listenC, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err, "Listen failed") + defer listenC.Close() + + counter := 0 + f := func(ctx context.Context, rs *RequestState) error { + counter++ + if !rs.HasRetries(e.Connection) { + c, err := net.Dial("tcp", listenC.Addr().String()) + if err == nil { + c.Close() + } + return err + } + + _, err := net.Dial("tcp", closedAddr) + return err + } + + assert.NoError(t, ch.RunWithRetry(ctx, f), "RunWithRetry should succeed") + assert.Equal(t, 5, counter, "RunWithRetry should have run f 5 times") +} + +func TestRequestStateSince(t *testing.T) { + baseTime := time.Date(2015, 1, 2, 3, 4, 5, 6, time.UTC) + tests := []struct { + requestState *RequestState + now time.Time + fallback time.Duration + expected time.Duration + }{ + { + requestState: nil, + fallback: 3 * time.Millisecond, + expected: 3 * time.Millisecond, + }, + { + requestState: &RequestState{Start: baseTime}, + now: baseTime.Add(7 * time.Millisecond), + fallback: 5 * time.Millisecond, + expected: 7 * time.Millisecond, + }, + } + + for _, tt := range tests { + got := tt.requestState.SinceStart(tt.now, tt.fallback) + assert.Equal(t, tt.expected, got, "%+v.SinceStart(%v, %v) expected %v got %v", + tt.requestState, tt.now, tt.fallback, tt.expected, got) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/retryon_string.go b/vendor/src/github.com/uber/tchannel-go/retryon_string.go new file mode 100644 index 00000000..56d27128 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/retryon_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=RetryOn; DO NOT EDIT + +package tchannel + +import "fmt" + +const _RetryOn_name = "RetryDefaultRetryConnectionErrorRetryNeverRetryNonIdempotentRetryUnexpectedRetryIdempotent" + +var _RetryOn_index = [...]uint8{0, 12, 32, 42, 60, 75, 90} + +func (i RetryOn) String() string { + if i < 0 || i+1 >= RetryOn(len(_RetryOn_index)) { + return fmt.Sprintf("RetryOn(%d)", i) + } + return _RetryOn_name[_RetryOn_index[i]:_RetryOn_index[i+1]] +} diff --git a/vendor/src/github.com/uber/tchannel-go/root_peer_list.go b/vendor/src/github.com/uber/tchannel-go/root_peer_list.go new file mode 100644 index 00000000..127160fd --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/root_peer_list.go @@ -0,0 +1,122 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import "sync" + +// RootPeerList is the root peer list which is only used to connect to +// peers and share peers between subchannels. +type RootPeerList struct { + sync.RWMutex + + channel Connectable + onPeerStatusChanged func(*Peer) + peersByHostPort map[string]*Peer +} + +func newRootPeerList(ch Connectable, onPeerStatusChanged func(*Peer)) *RootPeerList { + return &RootPeerList{ + channel: ch, + onPeerStatusChanged: onPeerStatusChanged, + peersByHostPort: make(map[string]*Peer), + } +} + +// newChild returns a new isolated peer list that shares the underlying peers +// with the root peer list. +func (l *RootPeerList) newChild() *PeerList { + return newPeerList(l) +} + +// Add adds a peer to the root peer list if it does not exist, or return +// an existing peer if it exists. +func (l *RootPeerList) Add(hostPort string) *Peer { + l.RLock() + + if p, ok := l.peersByHostPort[hostPort]; ok { + l.RUnlock() + return p + } + + l.RUnlock() + l.Lock() + defer l.Unlock() + + if p, ok := l.peersByHostPort[hostPort]; ok { + return p + } + + var p *Peer + // To avoid duplicate connections, only the root list should create new + // peers. All other lists should keep refs to the root list's peers. + p = newPeer(l.channel, hostPort, l.onPeerStatusChanged, l.onClosedConnRemoved) + l.peersByHostPort[hostPort] = p + return p +} + +// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist. +func (l *RootPeerList) GetOrAdd(hostPort string) *Peer { + peer, ok := l.Get(hostPort) + if ok { + return peer + } + + return l.Add(hostPort) +} + +// Get returns a peer for the given hostPort if it exists. +func (l *RootPeerList) Get(hostPort string) (*Peer, bool) { + l.RLock() + p, ok := l.peersByHostPort[hostPort] + l.RUnlock() + return p, ok +} + +func (l *RootPeerList) onClosedConnRemoved(peer *Peer) { + hostPort := peer.HostPort() + p, ok := l.Get(hostPort) + if !ok { + // It's possible that multiple connections were closed and removed at the same time, + // so multiple goroutines might be removing the peer from the root peer list. + return + } + + if p.canRemove() { + l.Lock() + delete(l.peersByHostPort, hostPort) + l.Unlock() + l.channel.Logger().WithFields( + LogField{"remoteHostPort", hostPort}, + ).Debug("Removed peer from root peer list.") + } +} + +// Copy returns a map of the peer list. This method should only be used for testing. +func (l *RootPeerList) Copy() map[string]*Peer { + l.RLock() + defer l.RUnlock() + + listCopy := make(map[string]*Peer) + for k, v := range l.peersByHostPort { + listCopy[k] = v + } + return listCopy +} diff --git a/vendor/src/github.com/uber/tchannel-go/scripts/changelog_halp.sh b/vendor/src/github.com/uber/tchannel-go/scripts/changelog_halp.sh new file mode 100644 index 00000000..d8b430cf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/scripts/changelog_halp.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -e + +LAST=$(grep '^# [0-9]' CHANGELOG.md | head -n1 | cut -d' ' -f2) + +{ + echo Changelog + echo ========= + echo + + echo '# ' + echo + echo "Merged pull requests since ${LAST}:" + echo + + # -e '/Merge pull request/d' \ + + git log --first-parent "v${LAST}.." | grep -A4 '^Date:' | sed \ + -e '/^ *$/d' \ + -e '/^Date:/d' \ + -e '/^commit /d' \ + -e '/^--/d' \ + -e '/Merge pull request/d' \ + -e '/^ / s/^ /* /' + + # TODO: augmenting with PR links should be straight forward, and useful to + # the reviewer + # -e '/Merge pull request/ { s~^.*#\([0-9][0-9]*\).*$~https://github.com/uber/tchannel-go/pull/\1~; h; d; }' \ + # -e '/^ / { s/^ /* /; G; }' + + echo + echo '# -- NEW ABOVE, OLD BELOW --' + echo + + tail -n+4 CHANGELOG.md +} > CHANGELOG.md.new +mv -vf CHANGELOG.md.new CHANGELOG.md diff --git a/vendor/src/github.com/uber/tchannel-go/scripts/install-thrift.sh b/vendor/src/github.com/uber/tchannel-go/scripts/install-thrift.sh new file mode 100644 index 00000000..a29d753f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/scripts/install-thrift.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -euo pipefail + +if [ -z "${1}" ]; then + echo "usage: ${0} installDirPath" >&2 + exit 1 +fi + +BIN_FILE="thrift-1" +TAR_FILE="${BIN_FILE}-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m).tar.gz" +TAR_LOCATION="https://github.com/uber/tchannel-go/releases/download/thrift-v1.0.0-dev/${TAR_FILE}" + +mkdir -p "${1}" +cd "${1}" +wget "${TAR_LOCATION}" +tar xzf "${TAR_FILE}" +rm -f "${TAR_FILE}" +mv "${BIN_FILE}" "thrift" diff --git a/vendor/src/github.com/uber/tchannel-go/stats.go b/vendor/src/github.com/uber/tchannel-go/stats.go new file mode 100644 index 00000000..b1373c64 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats.go @@ -0,0 +1,61 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "log" + "time" +) + +// StatsReporter is the the interface used to report stats. +type StatsReporter interface { + IncCounter(name string, tags map[string]string, value int64) + UpdateGauge(name string, tags map[string]string, value int64) + RecordTimer(name string, tags map[string]string, d time.Duration) +} + +// NullStatsReporter is a stats reporter that discards the statistics. +var NullStatsReporter StatsReporter = nullStatsReporter{} + +type nullStatsReporter struct{} + +func (nullStatsReporter) IncCounter(name string, tags map[string]string, value int64) {} +func (nullStatsReporter) UpdateGauge(name string, tags map[string]string, value int64) {} +func (nullStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) {} + +// SimpleStatsReporter is a stats reporter that reports stats to the log. +var SimpleStatsReporter StatsReporter = simpleStatsReporter{} + +type simpleStatsReporter struct { + commonTags map[string]string +} + +func (simpleStatsReporter) IncCounter(name string, tags map[string]string, value int64) { + log.Printf("Stats: IncCounter(%v, %v) +%v", name, tags, value) +} + +func (simpleStatsReporter) UpdateGauge(name string, tags map[string]string, value int64) { + log.Printf("Stats: UpdateGauge(%v, %v) = %v", name, tags, value) +} + +func (simpleStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) { + log.Printf("Stats: RecordTimer(%v, %v) = %v", name, tags, d) +} diff --git a/vendor/src/github.com/uber/tchannel-go/stats/metrickey.go b/vendor/src/github.com/uber/tchannel-go/stats/metrickey.go new file mode 100644 index 00000000..0331f498 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats/metrickey.go @@ -0,0 +1,87 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package stats + +import ( + "bytes" + "strings" + "sync" +) + +// DefaultMetricPrefix is the default mapping for metrics to statsd keys. +// It uses a "tchannel" prefix for all stats. +func DefaultMetricPrefix(name string, tags map[string]string) string { + return MetricWithPrefix("tchannel.", name, tags) +} + +var bufPool = sync.Pool{ + New: func() interface{} { return &bytes.Buffer{} }, +} + +// MetricWithPrefix is the default mapping for metrics to statsd keys. +func MetricWithPrefix(prefix, name string, tags map[string]string) string { + buf := bufPool.Get().(*bytes.Buffer) + buf.Reset() + + if prefix != "" { + buf.WriteString(prefix) + } + buf.WriteString(name) + + addKeys := make([]string, 0, 5) + switch { + case strings.HasPrefix(name, "outbound"): + addKeys = append(addKeys, "service", "target-service", "target-endpoint") + if strings.HasPrefix(name, "outbound.calls.retries") { + addKeys = append(addKeys, "retry-count") + } + case strings.HasPrefix(name, "inbound"): + addKeys = append(addKeys, "calling-service", "service", "endpoint") + } + + for _, k := range addKeys { + buf.WriteByte('.') + v, ok := tags[k] + if ok { + writeClean(buf, v) + } else { + buf.WriteString("no-") + buf.WriteString(k) + } + } + + m := buf.String() + bufPool.Put(buf) + return m +} + +// writeClean writes v, after replacing special characters [{}/\\:\s.] with '-' +func writeClean(buf *bytes.Buffer, v string) { + for i := 0; i < len(v); i++ { + c := v[i] + switch c { + case '{', '}', '/', '\\', ':', '.', ' ', '\t', '\r', '\n': + buf.WriteByte('-') + default: + buf.WriteByte(c) + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/stats/metrickey_test.go b/vendor/src/github.com/uber/tchannel-go/stats/metrickey_test.go new file mode 100644 index 00000000..67142ff2 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats/metrickey_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package stats + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDefaultMetricPrefix(t *testing.T) { + outboundTags := map[string]string{ + "service": "callerS", + "target-service": "targetS", + "target-endpoint": "targetE", + "retry-count": "retryN", + } + inboundTags := map[string]string{ + "service": "targetS", + "endpoint": "targetE", + "calling-service": "callerS", + } + + tests := []struct { + name string + tags map[string]string + expected string + }{ + { + name: "outbound.calls.sent", + tags: outboundTags, + expected: "tchannel.outbound.calls.sent.callerS.targetS.targetE", + }, + { + name: "outbound.calls.retries", + tags: outboundTags, + expected: "tchannel.outbound.calls.retries.callerS.targetS.targetE.retryN", + }, + { + name: "inbound.calls.recvd", + tags: inboundTags, + expected: "tchannel.inbound.calls.recvd.callerS.targetS.targetE", + }, + { + name: "inbound.calls.recvd", + tags: nil, + expected: "tchannel.inbound.calls.recvd.no-calling-service.no-service.no-endpoint", + }, + } + + for _, tt := range tests { + assert.Equal(t, tt.expected, DefaultMetricPrefix(tt.name, tt.tags), + "DefaultMetricPrefix(%q, %v) failed", tt.name, tt.tags) + } +} + +func TestClean(t *testing.T) { + tests := []struct { + key string + expected string + }{ + {"metric", "metric"}, + {"met:ric", "met-ric"}, + {"met{}ric", "met--ric"}, + {"\\metric", "-metric"}, + {"/metric", "-metric"}, + {" met.ric ", "--met-ric--"}, + } + + for _, tt := range tests { + buf := &bytes.Buffer{} + writeClean(buf, tt.key) + assert.Equal(t, tt.expected, buf.String(), "clean(%q) failed", tt.key) + } +} + +func BenchmarkMetricPrefix(b *testing.B) { + outboundTags := map[string]string{ + "service": "callerS", + "target-service": "targetS", + "target-endpoint": "targetE", + "retry-count": "retryN", + } + + for i := 0; i < b.N; i++ { + MetricWithPrefix("", "outbound.calls.retries", outboundTags) + DefaultMetricPrefix("outbound.calls.retries", outboundTags) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/stats/statsdreporter.go b/vendor/src/github.com/uber/tchannel-go/stats/statsdreporter.go new file mode 100644 index 00000000..b833fc86 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats/statsdreporter.go @@ -0,0 +1,65 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package stats + +import ( + "time" + + "github.com/cactus/go-statsd-client/statsd" + "github.com/uber/tchannel-go" +) + +const samplingRate = 1.0 + +// MetricKey is called to generate the statsd key for a given metric and tags. +var MetricKey = DefaultMetricPrefix + +type statsdReporter struct { + client statsd.Statter +} + +// NewStatsdReporter returns a StatsReporter that reports to statsd on the given addr. +func NewStatsdReporter(addr, prefix string) (tchannel.StatsReporter, error) { + client, err := statsd.NewBufferedClient(addr, prefix, time.Second, 0) + if err != nil { + return nil, err + } + + return NewStatsdReporterClient(client), nil +} + +// NewStatsdReporterClient returns a StatsReporter that reports stats to the given client. +func NewStatsdReporterClient(client statsd.Statter) tchannel.StatsReporter { + return &statsdReporter{client} +} + +func (r *statsdReporter) IncCounter(name string, tags map[string]string, value int64) { + // TODO(prashant): Deal with errors in the client. + r.client.Inc(MetricKey(name, tags), value, samplingRate) +} + +func (r *statsdReporter) UpdateGauge(name string, tags map[string]string, value int64) { + r.client.Gauge(MetricKey(name, tags), value, samplingRate) +} + +func (r *statsdReporter) RecordTimer(name string, tags map[string]string, d time.Duration) { + r.client.TimingDuration(MetricKey(name, tags), d, samplingRate) +} diff --git a/vendor/src/github.com/uber/tchannel-go/stats_test.go b/vendor/src/github.com/uber/tchannel-go/stats_test.go new file mode 100644 index 00000000..d15ac168 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats_test.go @@ -0,0 +1,228 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "fmt" + "os" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func tagsForOutboundCall(serverCh *Channel, clientCh *Channel, method string) map[string]string { + host, _ := os.Hostname() + return map[string]string{ + "app": clientCh.PeerInfo().ProcessName, + "host": host, + "service": clientCh.PeerInfo().ServiceName, + "target-service": serverCh.PeerInfo().ServiceName, + "target-endpoint": method, + } +} + +func tagsForInboundCall(serverCh *Channel, clientCh *Channel, method string) map[string]string { + host, _ := os.Hostname() + return map[string]string{ + "app": serverCh.PeerInfo().ProcessName, + "host": host, + "service": serverCh.PeerInfo().ServiceName, + "calling-service": clientCh.PeerInfo().ServiceName, + "endpoint": method, + } +} + +func TestStatsCalls(t *testing.T) { + defer testutils.SetTimeout(t, 2*time.Second)() + + tests := []struct { + method string + wantErr bool + }{ + { + method: "echo", + }, + { + method: "app-error", + wantErr: true, + }, + } + + for _, tt := range tests { + initialTime := time.Date(2015, 2, 1, 10, 10, 0, 0, time.UTC) + clientNow, clientNowFn := testutils.NowStub(initialTime) + serverNow, serverNowFn := testutils.NowStub(initialTime) + clientNowFn(100 * time.Millisecond) + serverNowFn(50 * time.Millisecond) + + clientStats := newRecordingStatsReporter() + serverStats := newRecordingStatsReporter() + serverOpts := testutils.NewOpts(). + SetStatsReporter(serverStats). + SetTimeNow(serverNow) + WithVerifiedServer(t, serverOpts, func(serverCh *Channel, hostPort string) { + handler := raw.Wrap(newTestHandler(t)) + serverCh.Register(handler, "echo") + serverCh.Register(handler, "app-error") + + ch := testutils.NewClient(t, testutils.NewOpts(). + SetStatsReporter(clientStats). + SetTimeNow(clientNow)) + defer ch.Close() + + ctx, cancel := NewContext(time.Second * 5) + defer cancel() + + _, _, resp, err := raw.Call(ctx, ch, hostPort, testutils.DefaultServerName, tt.method, nil, nil) + require.NoError(t, err, "Call(%v) should fail", tt.method) + assert.Equal(t, tt.wantErr, resp.ApplicationError(), "Call(%v) check application error") + + outboundTags := tagsForOutboundCall(serverCh, ch, tt.method) + inboundTags := tagsForInboundCall(serverCh, ch, tt.method) + + clientStats.Expected.IncCounter("outbound.calls.send", outboundTags, 1) + clientStats.Expected.RecordTimer("outbound.calls.per-attempt.latency", outboundTags, 100*time.Millisecond) + clientStats.Expected.RecordTimer("outbound.calls.latency", outboundTags, 100*time.Millisecond) + serverStats.Expected.IncCounter("inbound.calls.recvd", inboundTags, 1) + serverStats.Expected.RecordTimer("inbound.calls.latency", inboundTags, 50*time.Millisecond) + + if tt.wantErr { + clientStats.Expected.IncCounter("outbound.calls.per-attempt.app-errors", outboundTags, 1) + clientStats.Expected.IncCounter("outbound.calls.app-errors", outboundTags, 1) + serverStats.Expected.IncCounter("inbound.calls.app-errors", inboundTags, 1) + } else { + clientStats.Expected.IncCounter("outbound.calls.success", outboundTags, 1) + serverStats.Expected.IncCounter("inbound.calls.success", inboundTags, 1) + } + }) + + clientStats.Validate(t) + serverStats.Validate(t) + } +} + +func TestStatsWithRetries(t *testing.T) { + defer testutils.SetTimeout(t, 2*time.Second)() + a := testutils.DurationArray + + initialTime := time.Date(2015, 2, 1, 10, 10, 0, 0, time.UTC) + nowStub, nowFn := testutils.NowStub(initialTime) + + clientStats := newRecordingStatsReporter() + ch := testutils.NewClient(t, testutils.NewOpts(). + SetStatsReporter(clientStats). + SetTimeNow(nowStub)) + defer ch.Close() + + nowFn(10 * time.Millisecond) + ctx, cancel := NewContext(time.Second) + defer cancel() + + // TODO why do we need this?? + opts := testutils.NewOpts().NoRelay() + WithVerifiedServer(t, opts, func(serverCh *Channel, hostPort string) { + respErr := make(chan error, 1) + testutils.RegisterFunc(serverCh, "req", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, <-respErr + }) + ch.Peers().Add(serverCh.PeerInfo().HostPort) + + // timeNow is called at: + // RunWithRetry start, per-attempt start, per-attempt end. + // Each attempt takes 2 * step. + tests := []struct { + expectErr error + numFailures int + numAttempts int + overallLatency time.Duration + perAttemptLatencies []time.Duration + }{ + { + numFailures: 0, + numAttempts: 1, + perAttemptLatencies: a(10 * time.Millisecond), + overallLatency: 20 * time.Millisecond, + }, + { + numFailures: 1, + numAttempts: 2, + perAttemptLatencies: a(10*time.Millisecond, 10*time.Millisecond), + overallLatency: 40 * time.Millisecond, + }, + { + numFailures: 4, + numAttempts: 5, + perAttemptLatencies: a(10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond), + overallLatency: 100 * time.Millisecond, + }, + { + numFailures: 5, + numAttempts: 5, + expectErr: ErrServerBusy, + perAttemptLatencies: a(10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond, 10*time.Millisecond), + overallLatency: 100 * time.Millisecond, + }, + } + + for _, tt := range tests { + clientStats.Reset() + err := ch.RunWithRetry(ctx, func(ctx context.Context, rs *RequestState) error { + if rs.Attempt > tt.numFailures { + respErr <- nil + } else { + respErr <- ErrServerBusy + } + + sc := ch.GetSubChannel(serverCh.ServiceName()) + _, err := raw.CallV2(ctx, sc, raw.CArgs{ + Method: "req", + CallOptions: &CallOptions{RequestState: rs}, + }) + return err + }) + assert.Equal(t, tt.expectErr, err, "RunWithRetry unexpected error") + + outboundTags := tagsForOutboundCall(serverCh, ch, "req") + if tt.expectErr == nil { + clientStats.Expected.IncCounter("outbound.calls.success", outboundTags, 1) + } + clientStats.Expected.IncCounter("outbound.calls.send", outboundTags, int64(tt.numAttempts)) + for i, latency := range tt.perAttemptLatencies { + clientStats.Expected.RecordTimer("outbound.calls.per-attempt.latency", outboundTags, latency) + if i > 0 { + tags := tagsForOutboundCall(serverCh, ch, "req") + tags["retry-count"] = fmt.Sprint(i) + clientStats.Expected.IncCounter("outbound.calls.retries", tags, 1) + } + } + clientStats.Expected.RecordTimer("outbound.calls.latency", outboundTags, tt.overallLatency) + clientStats.Validate(t) + } + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/stats_utils_test.go b/vendor/src/github.com/uber/tchannel-go/stats_utils_test.go new file mode 100644 index 00000000..312eeb42 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stats_utils_test.go @@ -0,0 +1,148 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +// This file contains test setup logic, and is named with a _test.go suffix to +// ensure it's only compiled with tests. + +import ( + "fmt" + "reflect" + "sort" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +type statsValue struct { + // count is the counter value if this metric is a counter. + count int64 + + // timers is the list of timer values if this metrics is a timer. + timers []time.Duration +} + +type recordingStatsReporter struct { + sync.Mutex + + // Values is a map from the metricName -> map[tagMapAsString]*statsValue + Values map[string]map[string]*statsValue + + // Expected stores expected counter values. + Expected *recordingStatsReporter +} + +func newRecordingStatsReporter() *recordingStatsReporter { + return &recordingStatsReporter{ + Values: make(map[string]map[string]*statsValue), + Expected: &recordingStatsReporter{ + Values: make(map[string]map[string]*statsValue), + }, + } +} + +// keysMap returns the keys of the given map as a sorted list of strings. +// If the map is not of the type map[string]* then the function will panic. +func keysMap(m interface{}) []string { + var keys []string + mapKeys := reflect.ValueOf(m).MapKeys() + for _, v := range mapKeys { + keys = append(keys, v.Interface().(string)) + } + sort.Strings(keys) + return keys +} + +// tagsToString converts a map of tags to a string that can be used as a map key. +func tagsToString(tags map[string]string) string { + var vals []string + for _, k := range keysMap(tags) { + vals = append(vals, fmt.Sprintf("%v = %v", k, tags[k])) + } + return strings.Join(vals, ", ") +} + +func (r *recordingStatsReporter) getStat(name string, tags map[string]string) *statsValue { + r.Lock() + defer r.Unlock() + + tagMap, ok := r.Values[name] + if !ok { + tagMap = make(map[string]*statsValue) + r.Values[name] = tagMap + } + + tagStr := tagsToString(tags) + statVal, ok := tagMap[tagStr] + if !ok { + statVal = &statsValue{} + tagMap[tagStr] = statVal + } + + return statVal +} + +func (r *recordingStatsReporter) IncCounter(name string, tags map[string]string, value int64) { + statVal := r.getStat(name, tags) + statVal.count += value +} + +func (r *recordingStatsReporter) RecordTimer(name string, tags map[string]string, d time.Duration) { + statVal := r.getStat(name, tags) + statVal.timers = append(statVal.timers, d) +} + +func (r *recordingStatsReporter) Reset() { + newReporter := newRecordingStatsReporter() + r.Values = newReporter.Values + r.Expected = newReporter.Expected +} + +func (r *recordingStatsReporter) Validate(t *testing.T) { + r.Lock() + defer r.Unlock() + + assert.Equal(t, keysMap(r.Expected.Values), keysMap(r.Values), + "Metric keys are different") + for counterKey, counter := range r.Values { + expectedCounter, ok := r.Expected.Values[counterKey] + if !ok { + continue + } + + assert.Equal(t, keysMap(expectedCounter), keysMap(counter), + "Metric %v has different reported tags", counterKey) + for tags, stat := range counter { + expectedStat, ok := expectedCounter[tags] + if !ok { + continue + } + + assert.Equal(t, expectedStat, stat, + "Metric %v with tags %v has mismatched value", counterKey, tags) + } + } +} + +func (r *recordingStatsReporter) UpdateGauge(name string, tags map[string]string, value int64) {} diff --git a/vendor/src/github.com/uber/tchannel-go/stream_test.go b/vendor/src/github.com/uber/tchannel-go/stream_test.go new file mode 100644 index 00000000..2ca89fd2 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stream_test.go @@ -0,0 +1,340 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "strings" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +const ( + streamRequestError = byte(255) + streamRequestClose = byte(254) +) + +func makeRepeatedBytes(n byte) []byte { + data := make([]byte, int(n)) + for i := byte(0); i < n; i++ { + data[i] = n + } + return data +} + +func writeFlushBytes(w ArgWriter, bs []byte) error { + if _, err := w.Write(bs); err != nil { + return err + } + return w.Flush() +} + +type streamHelper struct { + t *testing.T +} + +// startCall starts a call to echoStream and returns the arg3 reader and writer. +func (h streamHelper) startCall(ctx context.Context, ch *Channel, hostPort, serviceName string) (ArgWriter, ArgReader) { + call, err := ch.BeginCall(ctx, hostPort, serviceName, "echoStream", nil) + require.NoError(h.t, err, "BeginCall to echoStream failed") + + // Write empty headers + require.NoError(h.t, NewArgWriter(call.Arg2Writer()).Write(nil), "Write empty headers failed") + + // Flush arg3 to force the call to start without any arg3. + writer, err := call.Arg3Writer() + require.NoError(h.t, err, "Arg3Writer failed") + require.NoError(h.t, writer.Flush(), "Arg3Writer flush failed") + + // Read empty Headers + response := call.Response() + var arg2 []byte + require.NoError(h.t, NewArgReader(response.Arg2Reader()).Read(&arg2), "Read headers failed") + require.False(h.t, response.ApplicationError(), "echoStream failed due to application error") + + reader, err := response.Arg3Reader() + require.NoError(h.t, err, "Arg3Reader failed") + + return writer, reader +} + +// streamPartialHandler returns a streaming handler that has the following contract: +// read a byte, write N bytes where N = the byte that was read. +// The results are be written as soon as the byte is read. +func streamPartialHandler(t *testing.T, reportErrors bool) HandlerFunc { + return func(ctx context.Context, call *InboundCall) { + response := call.Response() + onError := func(err error) { + if reportErrors { + t.Errorf("Handler error: %v", err) + } + response.SendSystemError(fmt.Errorf("failed to read arg2")) + } + + var arg2 []byte + if err := NewArgReader(call.Arg2Reader()).Read(&arg2); err != nil { + onError(fmt.Errorf("failed to read arg2")) + return + } + + if err := NewArgWriter(response.Arg2Writer()).Write(nil); err != nil { + onError(fmt.Errorf("")) + return + } + + argReader, err := call.Arg3Reader() + if err != nil { + onError(fmt.Errorf("failed to read arg3")) + return + } + + argWriter, err := response.Arg3Writer() + if err != nil { + onError(fmt.Errorf("arg3 writer failed")) + return + } + + // Flush arg3 which will force a frame with just arg2 to be sent. + // The test reads arg2 before arg3 has been sent. + if err := argWriter.Flush(); err != nil { + onError(fmt.Errorf("arg3 flush failed")) + return + } + + arg3 := make([]byte, 1) + for { + n, err := argReader.Read(arg3) + if err == io.EOF { + break + } + if n == 0 && err == nil { + err = fmt.Errorf("read 0 bytes") + } + if err != nil { + onError(fmt.Errorf("arg3 Read failed: %v", err)) + return + } + + // Magic number to cause a failure + if arg3[0] == streamRequestError { + // Make sure that the reader is closed. + if err := argReader.Close(); err != nil { + onError(fmt.Errorf("request error failed to close argReader: %v", err)) + return + } + + response.SendSystemError(errors.New("intentional failure")) + return + } + if arg3[0] == streamRequestClose { + if err := argWriter.Close(); err != nil { + onError(err) + } + return + } + + // Write the number of bytes as specified by arg3[0] + if _, err := argWriter.Write(makeRepeatedBytes(arg3[0])); err != nil { + onError(fmt.Errorf("argWriter Write failed: %v", err)) + return + } + if err := argWriter.Flush(); err != nil { + onError(fmt.Errorf("argWriter flush failed: %v", err)) + return + } + } + + if err := argReader.Close(); err != nil { + onError(fmt.Errorf("argReader Close failed: %v", err)) + return + } + + if err := argWriter.Close(); err != nil { + onError(fmt.Errorf("arg3writer Close failed: %v", err)) + return + } + } +} + +func testStreamArg(t *testing.T, f func(argWriter ArgWriter, argReader ArgReader)) { + defer testutils.SetTimeout(t, 2*time.Second)() + ctx, cancel := NewContext(time.Second) + defer cancel() + + helper := streamHelper{t} + WithVerifiedServer(t, nil, func(ch *Channel, hostPort string) { + ch.Register(streamPartialHandler(t, true /* report errors */), "echoStream") + + argWriter, argReader := helper.startCall(ctx, ch, hostPort, ch.ServiceName()) + verifyBytes := func(n byte) { + require.NoError(t, writeFlushBytes(argWriter, []byte{n}), "arg3 write failed") + + arg3 := make([]byte, int(n)) + _, err := io.ReadFull(argReader, arg3) + require.NoError(t, err, "arg3 read failed") + + assert.Equal(t, makeRepeatedBytes(n), arg3, "arg3 result mismatch") + } + + verifyBytes(0) + verifyBytes(5) + verifyBytes(100) + verifyBytes(1) + + f(argWriter, argReader) + }) +} + +func TestStreamPartialArg(t *testing.T) { + testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) { + require.NoError(t, argWriter.Close(), "arg3 close failed") + + // Once closed, we expect the reader to return EOF + n, err := io.Copy(ioutil.Discard, argReader) + assert.Equal(t, int64(0), n, "arg2 reader expected to EOF after arg3 writer is closed") + assert.NoError(t, err, "Copy should not fail") + assert.NoError(t, argReader.Close(), "close arg reader failed") + }) +} + +func TestStreamSendError(t *testing.T) { + testStreamArg(t, func(argWriter ArgWriter, argReader ArgReader) { + // Send the magic number to request an error. + _, err := argWriter.Write([]byte{streamRequestError}) + require.NoError(t, err, "arg3 write failed") + require.NoError(t, argWriter.Close(), "arg3 close failed") + + // Now we expect an error on our next read. + _, err = ioutil.ReadAll(argReader) + assert.Error(t, err, "ReadAll should fail") + assert.True(t, strings.Contains(err.Error(), "intentional failure"), "err %v unexpected", err) + }) +} + +func TestStreamCancelled(t *testing.T) { + // Since the cancel message is unimplemented, the relay does not know that the + // call was cancelled, andwill block closing till the timeout. + opts := testutils.NewOpts().NoRelay() + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + ts.Register(streamPartialHandler(t, false /* report errors */), "echoStream") + + ctx, cancel := NewContext(testutils.Timeout(time.Second)) + defer cancel() + + helper := streamHelper{t} + ch := ts.NewClient(nil) + cancelContext := make(chan struct{}) + + arg3Writer, arg3Reader := helper.startCall(ctx, ch, ts.HostPort(), ts.ServiceName()) + go func() { + for i := 0; i < 10; i++ { + assert.NoError(t, writeFlushBytes(arg3Writer, []byte{1}), "Write failed") + } + + // Our reads and writes should fail now. + <-cancelContext + cancel() + + _, err := arg3Writer.Write([]byte{1}) + // The write will succeed since it's buffered. + assert.NoError(t, err, "Write after fail should be buffered") + assert.Error(t, arg3Writer.Flush(), "writer.Flush should fail after cancel") + assert.Error(t, arg3Writer.Close(), "writer.Close should fail after cancel") + }() + + for i := 0; i < 10; i++ { + arg3 := make([]byte, 1) + n, err := arg3Reader.Read(arg3) + assert.Equal(t, 1, n, "Read did not correct number of bytes") + assert.NoError(t, err, "Read failed") + } + + close(cancelContext) + + n, err := io.Copy(ioutil.Discard, arg3Reader) + assert.EqualValues(t, 0, n, "Read should not read any bytes after cancel") + assert.Error(t, err, "Read should fail after cancel") + assert.Error(t, arg3Reader.Close(), "reader.Close should fail after cancel") + }) +} + +func TestResponseClosedBeforeRequest(t *testing.T) { + testutils.WithTestServer(t, nil, func(ts *testutils.TestServer) { + ts.Register(streamPartialHandler(t, false /* report errors */), "echoStream") + + ctx, cancel := NewContext(testutils.Timeout(time.Second)) + defer cancel() + + helper := streamHelper{t} + ch := ts.NewClient(nil) + responseClosed := make(chan struct{}) + writerDone := make(chan struct{}) + + arg3Writer, arg3Reader := helper.startCall(ctx, ch, ts.HostPort(), ts.Server().ServiceName()) + go func() { + defer close(writerDone) + + for i := 0; i < 10; i++ { + assert.NoError(t, writeFlushBytes(arg3Writer, []byte{1}), "Write failed") + } + + // Ignore the error of writeFlushBytes here since once we flush, the + // remote side could receive and close the response before we've created + // a new fragment (see fragmentingWriter.Flush). This could result + // in the Flush returning a "mex is already shutdown" error. + writeFlushBytes(arg3Writer, []byte{streamRequestClose}) + + // Wait until our reader gets the EOF. + <-responseClosed + + // Now our writes should fail, since the stream is shutdown + err := writeFlushBytes(arg3Writer, []byte{1}) + if assert.Error(t, err, "Req write should fail since response stream ended") { + assert.Contains(t, err.Error(), "mex has been shutdown") + } + }() + + for i := 0; i < 10; i++ { + arg3 := make([]byte, 1) + n, err := arg3Reader.Read(arg3) + assert.Equal(t, 1, n, "Read did not correct number of bytes") + assert.NoError(t, err, "Read failed") + } + + eofBuf := make([]byte, 1) + _, err := arg3Reader.Read(eofBuf) + assert.Equal(t, io.EOF, err, "Response should EOF after request close") + assert.NoError(t, arg3Reader.Close(), "Close should succeed") + close(responseClosed) + <-writerDone + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/stress_flag_test.go b/vendor/src/github.com/uber/tchannel-go/stress_flag_test.go new file mode 100644 index 00000000..4a0be5e8 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/stress_flag_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "flag" + "testing" +) + +// This file contains functions for tests to access internal tchannel state. +// Since it has a _test.go suffix, it is only compiled with tests in this package. + +var flagStressTest = flag.Bool("stressTest", false, "Run stress tests (very slow)") + +// CheckStress will skip the test if stress testing is not enabled. +func CheckStress(t *testing.T) { + if !*flagStressTest { + t.Skip("Skipping long-running test as stressTest is not set") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/subchannel.go b/vendor/src/github.com/uber/tchannel-go/subchannel.go new file mode 100644 index 00000000..54b424c1 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/subchannel.go @@ -0,0 +1,220 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "sync" + + "github.com/opentracing/opentracing-go" + "golang.org/x/net/context" +) + +// SubChannelOption are used to set options for subchannels. +type SubChannelOption func(*SubChannel) + +// Isolated is a SubChannelOption that creates an isolated subchannel. +func Isolated(s *SubChannel) { + s.Lock() + s.peers = s.topChannel.peers.newSibling() + s.peers.SetStrategy(newLeastPendingCalculator()) + s.Unlock() +} + +// SubChannel allows calling a specific service on a channel. +// TODO(prashant): Allow creating a subchannel with default call options. +// TODO(prashant): Allow registering handlers on a subchannel. +type SubChannel struct { + sync.RWMutex + serviceName string + topChannel *Channel + defaultCallOptions *CallOptions + peers *PeerList + handler Handler + logger Logger + statsReporter StatsReporter +} + +// Map of subchannel and the corresponding service +type subChannelMap struct { + sync.RWMutex + subchannels map[string]*SubChannel +} + +func newSubChannel(serviceName string, ch *Channel) *SubChannel { + logger := ch.Logger().WithFields(LogField{"subchannel", serviceName}) + return &SubChannel{ + serviceName: serviceName, + peers: ch.peers, + topChannel: ch, + handler: &handlerMap{}, // use handlerMap by default + logger: logger, + statsReporter: ch.StatsReporter(), + } +} + +// ServiceName returns the service name that this subchannel is for. +func (c *SubChannel) ServiceName() string { + return c.serviceName +} + +// BeginCall starts a new call to a remote peer, returning an OutboundCall that can +// be used to write the arguments of the call. +func (c *SubChannel) BeginCall(ctx context.Context, methodName string, callOptions *CallOptions) (*OutboundCall, error) { + if callOptions == nil { + callOptions = defaultCallOptions + } + + peer, err := c.peers.Get(callOptions.RequestState.PrevSelectedPeers()) + if err != nil { + return nil, err + } + + return peer.BeginCall(ctx, c.ServiceName(), methodName, callOptions) +} + +// Peers returns the PeerList for this subchannel. +func (c *SubChannel) Peers() *PeerList { + return c.peers +} + +// Isolated returns whether this subchannel is an isolated subchannel. +func (c *SubChannel) Isolated() bool { + c.RLock() + defer c.RUnlock() + return c.topChannel.Peers() != c.peers +} + +// Register registers a handler on the subchannel for the given method. +// +// This function panics if the Handler for the SubChannel was overwritten with +// SetHandler. +func (c *SubChannel) Register(h Handler, methodName string) { + handlers, ok := c.handler.(*handlerMap) + if !ok { + panic(fmt.Sprintf( + "handler for SubChannel(%v) was changed to disallow method registration", + c.ServiceName(), + )) + } + handlers.register(h, methodName) +} + +// GetHandlers returns all handlers registered on this subchannel by method name. +// +// This function panics if the Handler for the SubChannel was overwritten with +// SetHandler. +func (c *SubChannel) GetHandlers() map[string]Handler { + handlers, ok := c.handler.(*handlerMap) + if !ok { + panic(fmt.Sprintf( + "handler for SubChannel(%v) was changed to disallow method registration", + c.ServiceName(), + )) + } + + handlers.RLock() + handlersMap := make(map[string]Handler, len(handlers.handlers)) + for k, v := range handlers.handlers { + handlersMap[k] = v + } + handlers.RUnlock() + return handlersMap +} + +// SetHandler changes the SubChannel's underlying handler. This may be used to +// set up a catch-all Handler for all requests received by this SubChannel. +// +// Methods registered on this SubChannel using Register() before calling +// SetHandler() will be forgotten. Further calls to Register() on this +// SubChannel after SetHandler() is called will cause panics. +func (c *SubChannel) SetHandler(h Handler) { + c.handler = h +} + +// Logger returns the logger for this subchannel. +func (c *SubChannel) Logger() Logger { + return c.logger +} + +// StatsReporter returns the stats reporter for this subchannel. +func (c *SubChannel) StatsReporter() StatsReporter { + return c.topChannel.StatsReporter() +} + +// StatsTags returns the stats tags for this subchannel. +func (c *SubChannel) StatsTags() map[string]string { + tags := c.topChannel.StatsTags() + tags["subchannel"] = c.serviceName + return tags +} + +// Tracer returns OpenTracing Tracer from the top channel. +func (c *SubChannel) Tracer() opentracing.Tracer { + return c.topChannel.Tracer() +} + +// Register a new subchannel for the given serviceName +func (subChMap *subChannelMap) registerNewSubChannel(serviceName string, ch *Channel) (_ *SubChannel, added bool) { + subChMap.Lock() + defer subChMap.Unlock() + + if subChMap.subchannels == nil { + subChMap.subchannels = make(map[string]*SubChannel) + } + + if sc, ok := subChMap.subchannels[serviceName]; ok { + return sc, false + } + + sc := newSubChannel(serviceName, ch) + subChMap.subchannels[serviceName] = sc + return sc, true +} + +// Get subchannel if, we have one +func (subChMap *subChannelMap) get(serviceName string) (*SubChannel, bool) { + subChMap.RLock() + sc, ok := subChMap.subchannels[serviceName] + subChMap.RUnlock() + return sc, ok +} + +// GetOrAdd a subchannel for the given serviceName on the map +func (subChMap *subChannelMap) getOrAdd(serviceName string, ch *Channel) (_ *SubChannel, added bool) { + if sc, ok := subChMap.get(serviceName); ok { + return sc, false + } + + return subChMap.registerNewSubChannel(serviceName, ch) +} + +func (subChMap *subChannelMap) updatePeer(p *Peer) { + subChMap.RLock() + for _, subCh := range subChMap.subchannels { + if subCh.Isolated() { + subCh.RLock() + subCh.Peers().onPeerChange(p) + subCh.RUnlock() + } + } + subChMap.RUnlock() +} diff --git a/vendor/src/github.com/uber/tchannel-go/subchannel_test.go b/vendor/src/github.com/uber/tchannel-go/subchannel_test.go new file mode 100644 index 00000000..d96726db --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/subchannel_test.go @@ -0,0 +1,270 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +type chanSet struct { + main Registrar + sub Registrar + isolated Registrar +} + +func withNewSet(t *testing.T, f func(*testing.T, chanSet)) { + ch := testutils.NewClient(t, nil) + defer ch.Close() + f(t, chanSet{ + main: ch, + sub: ch.GetSubChannel("hyperbahn"), + isolated: ch.GetSubChannel("ringpop", Isolated), + }) +} + +// Assert that two Registrars have references to the same Peer. +func assertHaveSameRef(t *testing.T, r1, r2 Registrar) { + p1, err := r1.Peers().Get(nil) + assert.NoError(t, err, "First registrar has no peers.") + + p2, err := r2.Peers().Get(nil) + assert.NoError(t, err, "Second registrar has no peers.") + + assert.True(t, p1 == p2, "Registrars have references to different peers.") +} + +func assertNoPeer(t *testing.T, r Registrar) { + _, err := r.Peers().Get(nil) + assert.Equal(t, err, ErrNoPeers) +} + +func TestMainAddVisibility(t *testing.T) { + withNewSet(t, func(t *testing.T, set chanSet) { + // Adding a peer to the main channel should be reflected in the + // subchannel, but not the isolated subchannel. + set.main.Peers().Add("127.0.0.1:3000") + assertHaveSameRef(t, set.main, set.sub) + assertNoPeer(t, set.isolated) + }) +} + +func TestSubchannelAddVisibility(t *testing.T) { + withNewSet(t, func(t *testing.T, set chanSet) { + // Adding a peer to a non-isolated subchannel should be reflected in + // the main channel but not in isolated siblings. + set.sub.Peers().Add("127.0.0.1:3000") + assertHaveSameRef(t, set.main, set.sub) + assertNoPeer(t, set.isolated) + }) +} + +func TestIsolatedAddVisibility(t *testing.T) { + withNewSet(t, func(t *testing.T, set chanSet) { + // Adding a peer to an isolated subchannel shouldn't change the main + // channel or sibling channels. + set.isolated.Peers().Add("127.0.0.1:3000") + + _, err := set.isolated.Peers().Get(nil) + assert.NoError(t, err) + + assertNoPeer(t, set.main) + assertNoPeer(t, set.sub) + }) +} + +func TestAddReusesPeers(t *testing.T) { + withNewSet(t, func(t *testing.T, set chanSet) { + // Adding to both a channel and an isolated subchannel shouldn't create + // two separate peers. + set.main.Peers().Add("127.0.0.1:3000") + set.isolated.Peers().Add("127.0.0.1:3000") + + assertHaveSameRef(t, set.main, set.sub) + assertHaveSameRef(t, set.main, set.isolated) + }) +} + +func TestSetHandler(t *testing.T) { + // Generate a Handler that expects only the given methods to be called. + genHandler := func(methods ...string) Handler { + allowedMethods := make(map[string]struct{}, len(methods)) + for _, m := range methods { + allowedMethods[m] = struct{}{} + } + + return HandlerFunc(func(ctx context.Context, call *InboundCall) { + method := call.MethodString() + assert.Contains(t, allowedMethods, method, "unexpected call to %q", method) + err := raw.WriteResponse(call.Response(), &raw.Res{Arg3: []byte(method)}) + require.NoError(t, err) + }) + } + + ch := testutils.NewServer(t, testutils.NewOpts(). + AddLogFilter("Couldn't find handler", 1, "serviceName", "svc2", "method", "bar")) + defer ch.Close() + + // Catch-all handler for the main channel that accepts foo, bar, and baz, + // and a single registered handler for a different subchannel. + ch.GetSubChannel("svc1").SetHandler(genHandler("foo", "bar", "baz")) + ch.GetSubChannel("svc2").Register(genHandler("foo"), "foo") + + client := testutils.NewClient(t, nil) + client.Peers().Add(ch.PeerInfo().HostPort) + defer client.Close() + + tests := []struct { + Service string + Method string + ShouldFail bool + }{ + {"svc1", "foo", false}, + {"svc1", "bar", false}, + {"svc1", "baz", false}, + + {"svc2", "foo", false}, + {"svc2", "bar", true}, + } + + for _, tt := range tests { + c := client.GetSubChannel(tt.Service) + ctx, _ := NewContext(time.Second) + _, data, _, err := raw.CallSC(ctx, c, tt.Method, nil, []byte("irrelevant")) + + if tt.ShouldFail { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.Method, string(data)) + } + } + + st := ch.IntrospectState(nil) + assert.Equal(t, "overriden", st.SubChannels["svc1"].Handler.Type.String()) + assert.Nil(t, st.SubChannels["svc1"].Handler.Methods) + + assert.Equal(t, "methods", st.SubChannels["svc2"].Handler.Type.String()) + assert.Equal(t, []string{"foo"}, st.SubChannels["svc2"].Handler.Methods) +} + +func TestGetHandlers(t *testing.T) { + ch := testutils.NewServer(t, nil) + defer ch.Close() + + var handler1 HandlerFunc = func(_ context.Context, _ *InboundCall) { + panic("unexpected call") + } + var handler2 HandlerFunc = func(_ context.Context, _ *InboundCall) { + panic("unexpected call") + } + + ch.Register(handler1, "method1") + ch.Register(handler2, "method2") + ch.GetSubChannel("foo").Register(handler2, "method1") + + tests := []struct { + serviceName string + wantMethods []string + }{ + { + serviceName: ch.ServiceName(), + // Default service name comes with extra introspection methods. + wantMethods: []string{"_gometa_introspect", "_gometa_runtime", "method1", "method2"}, + }, + { + serviceName: "foo", + wantMethods: []string{"method1"}, + }, + } + + for _, tt := range tests { + handlers := ch.GetSubChannel(tt.serviceName).GetHandlers() + if !assert.Equal(t, len(tt.wantMethods), len(handlers), + "Unexpected number of methods found, expected %v, got %v", tt.wantMethods, handlers) { + continue + } + + for _, method := range tt.wantMethods { + _, ok := handlers[method] + assert.True(t, ok, "Expected to find method %v in handlers: %v", method, handlers) + } + } +} + +func TestCannotRegisterOrGetAfterSetHandler(t *testing.T) { + ch := testutils.NewServer(t, nil) + defer ch.Close() + + var someHandler HandlerFunc = func(ctx context.Context, call *InboundCall) { + panic("unexpected call") + } + var anotherHandler HandlerFunc = func(ctx context.Context, call *InboundCall) { + panic("unexpected call") + } + + ch.GetSubChannel("foo").SetHandler(someHandler) + + // Registering against the original service should not panic but + // registering against the "foo" service should panic. + assert.NotPanics(t, func() { ch.Register(anotherHandler, "bar") }) + assert.NotPanics(t, func() { ch.GetSubChannel("svc").GetHandlers() }) + assert.Panics(t, func() { ch.GetSubChannel("foo").Register(anotherHandler, "bar") }) + assert.Panics(t, func() { ch.GetSubChannel("foo").GetHandlers() }) +} + +func TestGetSubchannelOptionsOnNew(t *testing.T) { + ch := testutils.NewServer(t, nil) + defer ch.Close() + + peers := ch.GetSubChannel("s", Isolated).Peers() + want := peers.Add("1.1.1.1:1") + + peers2 := ch.GetSubChannel("s", Isolated).Peers() + assert.Equal(t, peers, peers2, "Get isolated subchannel should not clear existing peers") + peer, err := peers2.Get(nil) + require.NoError(t, err, "Should get peer") + assert.Equal(t, want, peer, "Unexpected peer") +} + +func TestHandlerWithoutSubChannel(t *testing.T) { + opts := testutils.NewOpts().NoRelay() + opts.Handler = raw.Wrap(newTestHandler(t)) + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + client := ts.NewClient(nil) + testutils.AssertEcho(t, client, ts.HostPort(), ts.ServiceName()) + testutils.AssertEcho(t, client, ts.HostPort(), "larry") + testutils.AssertEcho(t, client, ts.HostPort(), "curly") + testutils.AssertEcho(t, client, ts.HostPort(), "moe") + + assert.Panics(t, func() { + ts.Server().Register(raw.Wrap(newTestHandler(t)), "nyuck") + }) + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/systemerrcode_string.go b/vendor/src/github.com/uber/tchannel-go/systemerrcode_string.go new file mode 100644 index 00000000..be54362f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/systemerrcode_string.go @@ -0,0 +1,26 @@ +// generated by stringer -type=SystemErrCode; DO NOT EDIT + +package tchannel + +import "fmt" + +const ( + _SystemErrCode_name_0 = "ErrCodeInvalidErrCodeTimeoutErrCodeCancelledErrCodeBusyErrCodeDeclinedErrCodeUnexpectedErrCodeBadRequestErrCodeNetwork" + _SystemErrCode_name_1 = "ErrCodeProtocol" +) + +var ( + _SystemErrCode_index_0 = [...]uint8{0, 14, 28, 44, 55, 70, 87, 104, 118} + _SystemErrCode_index_1 = [...]uint8{0, 15} +) + +func (i SystemErrCode) String() string { + switch { + case 0 <= i && i <= 7: + return _SystemErrCode_name_0[_SystemErrCode_index_0[i]:_SystemErrCode_index_0[i+1]] + case i == 255: + return _SystemErrCode_name_1 + default: + return fmt.Sprintf("SystemErrCode(%d)", i) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/tchannel_test.go b/vendor/src/github.com/uber/tchannel-go/tchannel_test.go new file mode 100644 index 00000000..9df13a03 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tchannel_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "flag" + "fmt" + "os" + "testing" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils/goroutines" +) + +func checkAllChannels() error { + ch, err := NewChannel("test-end", nil) + if err != nil { + return err + } + + var foundChannels bool + allChannels := ch.IntrospectOthers(&IntrospectionOptions{}) + for _, cs := range allChannels { + if len(cs) != 0 { + foundChannels = true + } + } + + if !foundChannels { + return nil + } + + return fmt.Errorf("unclosed channels:\n%+v", allChannels) +} + +func TestMain(m *testing.M) { + flag.Parse() + exitCode := m.Run() + + if exitCode == 0 { + // Only do extra checks if the tests were successful. + if err := goroutines.IdentifyLeaks(nil); err != nil { + fmt.Fprintf(os.Stderr, "Found goroutine leaks on successful test run: %v", err) + exitCode = 1 + } + + if err := checkAllChannels(); err != nil { + fmt.Fprintf(os.Stderr, "Found unclosed channels on successful test run: %v", err) + exitCode = 1 + } + } + + os.Exit(exitCode) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/call.go b/vendor/src/github.com/uber/tchannel-go/testutils/call.go new file mode 100644 index 00000000..9a3cec57 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/call.go @@ -0,0 +1,124 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/relay" +) + +// FakeIncomingCall implements IncomingCall interface. +// Note: the F suffix for the fields is to clash with the method name. +type FakeIncomingCall struct { + // CallerNameF is the calling service's name. + CallerNameF string + + // ShardKeyF is the intended destination for this call. + ShardKeyF string + + // RemotePeerF is the calling service's peer info. + RemotePeerF tchannel.PeerInfo + + // LocalPeerF is the local service's peer info. + LocalPeerF tchannel.LocalPeerInfo + + // RoutingKeyF is the routing key. + RoutingKeyF string + + // RoutingDelegateF is the routing delegate. + RoutingDelegateF string +} + +// CallerName returns the caller name as specified in the fake call. +func (f *FakeIncomingCall) CallerName() string { + return f.CallerNameF +} + +// ShardKey returns the shard key as specified in the fake call. +func (f *FakeIncomingCall) ShardKey() string { + return f.ShardKeyF +} + +// RoutingKey returns the routing delegate as specified in the fake call. +func (f *FakeIncomingCall) RoutingKey() string { + return f.RoutingKeyF +} + +// RoutingDelegate returns the routing delegate as specified in the fake call. +func (f *FakeIncomingCall) RoutingDelegate() string { + return f.RoutingDelegateF +} + +// LocalPeer returns the local peer information for this call. +func (f *FakeIncomingCall) LocalPeer() tchannel.LocalPeerInfo { + return f.LocalPeerF +} + +// RemotePeer returns the remote peer information for this call. +func (f *FakeIncomingCall) RemotePeer() tchannel.PeerInfo { + return f.RemotePeerF +} + +// CallOptions returns the incoming call options suitable for proxying a request. +func (f *FakeIncomingCall) CallOptions() *tchannel.CallOptions { + return &tchannel.CallOptions{ + ShardKey: f.ShardKey(), + RoutingKey: f.RoutingKey(), + RoutingDelegate: f.RoutingDelegate(), + } +} + +// NewIncomingCall creates an incoming call for tests. +func NewIncomingCall(callerName string) tchannel.IncomingCall { + return &FakeIncomingCall{CallerNameF: callerName} +} + +// FakeCallFrame is a stub implementation of the CallFrame interface. +type FakeCallFrame struct { + ServiceF, MethodF, CallerF, RoutingKeyF, RoutingDelegateF string +} + +var _ relay.CallFrame = FakeCallFrame{} + +// Service returns the service name field. +func (f FakeCallFrame) Service() []byte { + return []byte(f.ServiceF) +} + +// Method returns the method field. +func (f FakeCallFrame) Method() []byte { + return []byte(f.MethodF) +} + +// Caller returns the caller field. +func (f FakeCallFrame) Caller() []byte { + return []byte(f.CallerF) +} + +// RoutingKey returns the routing delegate field. +func (f FakeCallFrame) RoutingKey() []byte { + return []byte(f.RoutingKeyF) +} + +// RoutingDelegate returns the routing delegate field. +func (f FakeCallFrame) RoutingDelegate() []byte { + return []byte(f.RoutingDelegateF) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/channel.go b/vendor/src/github.com/uber/tchannel-go/testutils/channel.go new file mode 100644 index 00000000..7d3f5ebf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/channel.go @@ -0,0 +1,98 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "fmt" + "net" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +// NewServerChannel creates a TChannel that is listening and returns the channel. +// Passed in options may be mutated (for post-verification of state). +func NewServerChannel(opts *ChannelOpts) (*tchannel.Channel, error) { + opts = opts.Copy() + + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, fmt.Errorf("failed to listen: %v", err) + } + _, port, err := net.SplitHostPort(l.Addr().String()) + if err != nil { + return nil, fmt.Errorf("could not get listening port from %v: %v", l.Addr().String(), err) + } + + serviceName := defaultString(opts.ServiceName, DefaultServerName) + opts.ProcessName = defaultString(opts.ProcessName, serviceName+"-"+port) + updateOptsLogger(opts) + ch, err := tchannel.NewChannel(serviceName, &opts.ChannelOptions) + if err != nil { + return nil, fmt.Errorf("NewChannel failed: %v", err) + } + + if err := ch.Serve(l); err != nil { + return nil, fmt.Errorf("Serve failed: %v", err) + } + + return ch, nil +} + +var totalClients atomic.Uint32 + +// NewClientChannel creates a TChannel that is not listening. +// Passed in options may be mutated (for post-verification of state). +func NewClientChannel(opts *ChannelOpts) (*tchannel.Channel, error) { + opts = opts.Copy() + + clientNum := totalClients.Inc() + serviceName := defaultString(opts.ServiceName, DefaultClientName) + opts.ProcessName = defaultString(opts.ProcessName, serviceName+"-"+fmt.Sprint(clientNum)) + updateOptsLogger(opts) + return tchannel.NewChannel(serviceName, &opts.ChannelOptions) +} + +type rawFuncHandler struct { + ch tchannel.Registrar + f func(context.Context, *raw.Args) (*raw.Res, error) +} + +func (h rawFuncHandler) OnError(ctx context.Context, err error) { + h.ch.Logger().WithFields( + tchannel.LogField{Key: "context", Value: ctx}, + tchannel.ErrField(err), + ).Error("simpleHandler OnError.") +} + +func (h rawFuncHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + return h.f(ctx, args) +} + +// RegisterFunc registers a function as a handler for the given method name. +func RegisterFunc(ch tchannel.Registrar, name string, + f func(ctx context.Context, args *raw.Args) (*raw.Res, error)) { + + ch.Register(raw.Wrap(rawFuncHandler{ch, f}), name) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/channel_opts.go b/vendor/src/github.com/uber/tchannel-go/testutils/channel_opts.go new file mode 100644 index 00000000..b40874f9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/channel_opts.go @@ -0,0 +1,233 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "flag" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/tos" + + "github.com/uber-go/atomic" +) + +var connectionLog = flag.Bool("connectionLog", false, "Enables connection logging in tests") + +// Default service names for the test channels. +const ( + DefaultServerName = "testService" + DefaultClientName = "testService-client" +) + +// ChannelOpts contains options to create a test channel using WithServer +type ChannelOpts struct { + tchannel.ChannelOptions + + // ServiceName defaults to DefaultServerName or DefaultClientName. + ServiceName string + + // LogVerification contains options for controlling the log verification. + LogVerification LogVerification + + // DisableRelay disables the relay interposed between clients/servers. + // By default, all tests are run with a relay interposed. + DisableRelay bool + + // OnlyRelay instructs TestServer the test must only be run with a relay. + OnlyRelay bool + + // RunCount is the number of times the test should be run. Zero or + // negative values are treated as a single run. + RunCount int + + // postFns is a list of functions that are run after the test. + // They are run even if the test fails. + postFns []func() +} + +// LogVerification contains options to control the log verification. +type LogVerification struct { + Disabled bool + + Filters []LogFilter +} + +// LogFilter is a single substring match that can be ignored. +type LogFilter struct { + // Filter specifies the substring match to search + // for in the log message to skip raising an error. + Filter string + + // Count is the maximum number of allowed warn+ logs matching + // Filter before errors are raised. + Count uint + + // FieldFilters specifies expected substring matches for fields. + FieldFilters map[string]string +} + +// Copy copies the channel options (so that they can be safely modified). +func (o *ChannelOpts) Copy() *ChannelOpts { + if o == nil { + return NewOpts() + } + copiedOpts := *o + return &copiedOpts +} + +// SetServiceName sets ServiceName. +func (o *ChannelOpts) SetServiceName(svcName string) *ChannelOpts { + o.ServiceName = svcName + return o +} + +// SetProcessName sets the ProcessName in ChannelOptions. +func (o *ChannelOpts) SetProcessName(processName string) *ChannelOpts { + o.ProcessName = processName + return o +} + +// SetStatsReporter sets StatsReporter in ChannelOptions. +func (o *ChannelOpts) SetStatsReporter(statsReporter tchannel.StatsReporter) *ChannelOpts { + o.StatsReporter = statsReporter + return o +} + +// SetFramePool sets FramePool in DefaultConnectionOptions. +func (o *ChannelOpts) SetFramePool(framePool tchannel.FramePool) *ChannelOpts { + o.DefaultConnectionOptions.FramePool = framePool + return o +} + +// SetSendBufferSize sets the SendBufferSize in DefaultConnectionOptions. +func (o *ChannelOpts) SetSendBufferSize(bufSize int) *ChannelOpts { + o.DefaultConnectionOptions.SendBufferSize = bufSize + return o +} + +// SetTosPriority set TosPriority in DefaultConnectionOptions. +func (o *ChannelOpts) SetTosPriority(tosPriority tos.ToS) *ChannelOpts { + o.DefaultConnectionOptions.TosPriority = tosPriority + return o +} + +// SetTimeNow sets TimeNow in ChannelOptions. +func (o *ChannelOpts) SetTimeNow(timeNow func() time.Time) *ChannelOpts { + o.TimeNow = timeNow + return o +} + +// DisableLogVerification disables log verification for this channel. +func (o *ChannelOpts) DisableLogVerification() *ChannelOpts { + o.LogVerification.Disabled = true + return o +} + +// NoRelay disables running the test with a relay interposed. +func (o *ChannelOpts) NoRelay() *ChannelOpts { + o.DisableRelay = true + return o +} + +// SetRelayOnly instructs TestServer to only run with a relay in front of this channel. +func (o *ChannelOpts) SetRelayOnly() *ChannelOpts { + o.OnlyRelay = true + return o +} + +// SetRunCount sets the number of times run the test. +func (o *ChannelOpts) SetRunCount(n int) *ChannelOpts { + o.RunCount = n + return o +} + +// AddLogFilter sets an allowed filter for warning/error logs and sets +// the maximum number of times that log can occur. +func (o *ChannelOpts) AddLogFilter(filter string, maxCount uint, fields ...string) *ChannelOpts { + fieldFilters := make(map[string]string) + for i := 0; i < len(fields); i += 2 { + fieldFilters[fields[i]] = fields[i+1] + } + + o.LogVerification.Filters = append(o.LogVerification.Filters, LogFilter{ + Filter: filter, + Count: maxCount, + FieldFilters: fieldFilters, + }) + return o +} + +func (o *ChannelOpts) addPostFn(f func()) { + o.postFns = append(o.postFns, f) +} + +// SetRelayHost sets the channel's RelayHost, which enables relaying. +func (o *ChannelOpts) SetRelayHost(rh tchannel.RelayHost) *ChannelOpts { + o.ChannelOptions.RelayHost = rh + return o +} + +// SetRelayLocal sets the channel's relay local handlers for service names +// that should be handled by the relay channel itself. +func (o *ChannelOpts) SetRelayLocal(relayLocal ...string) *ChannelOpts { + o.ChannelOptions.RelayLocalHandlers = relayLocal + return o +} + +// SetRelayMaxTimeout sets the maximum allowable timeout for relayed calls. +func (o *ChannelOpts) SetRelayMaxTimeout(d time.Duration) *ChannelOpts { + o.ChannelOptions.RelayMaxTimeout = d + return o +} + +// SetOnPeerStatusChanged sets the callback for channel status change +// noficiations. +func (o *ChannelOpts) SetOnPeerStatusChanged(f func(*tchannel.Peer)) *ChannelOpts { + o.ChannelOptions.OnPeerStatusChanged = f + return o +} + +func defaultString(v string, defaultValue string) string { + if v == "" { + return defaultValue + } + return v +} + +// NewOpts returns a new ChannelOpts that can be used in a chained fashion. +func NewOpts() *ChannelOpts { return &ChannelOpts{} } + +// DefaultOpts will return opts if opts is non-nil, NewOpts otherwise. +func DefaultOpts(opts *ChannelOpts) *ChannelOpts { + if opts == nil { + return NewOpts() + } + return opts +} + +// WrapLogger wraps the given logger with extra verification. +func (v *LogVerification) WrapLogger(t testing.TB, l tchannel.Logger) tchannel.Logger { + return errorLogger{l, t, v, &errorLoggerState{ + matchCount: make([]atomic.Uint32, len(v.Filters)), + }} +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/channel_t.go b/vendor/src/github.com/uber/tchannel-go/testutils/channel_t.go new file mode 100644 index 00000000..4486a3b9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/channel_t.go @@ -0,0 +1,88 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "testing" + + "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/require" +) + +func updateOptsLogger(opts *ChannelOpts) { + if opts.Logger == nil && *connectionLog { + opts.Logger = tchannel.SimpleLogger + } +} + +func updateOptsForTest(t testing.TB, opts *ChannelOpts) { + updateOptsLogger(opts) + + // If there's no logger, then register the test logger which will record + // everything to a buffer, and print out the buffer if the test fails. + if opts.Logger == nil { + tl := newTestLogger(t) + opts.Logger = tl + opts.addPostFn(tl.report) + } + + if !opts.LogVerification.Disabled { + opts.Logger = opts.LogVerification.WrapLogger(t, opts.Logger) + } +} + +// WithServer sets up a TChannel that is listening and runs the given function with the channel. +func WithServer(t testing.TB, opts *ChannelOpts, f func(ch *tchannel.Channel, hostPort string)) { + opts = opts.Copy() + updateOptsForTest(t, opts) + ch := NewServer(t, opts) + f(ch, ch.PeerInfo().HostPort) + ch.Close() +} + +// NewServer returns a new TChannel server that listens on :0. +func NewServer(t testing.TB, opts *ChannelOpts) *tchannel.Channel { + return newServer(t, opts.Copy()) +} + +// newServer must be passed non-nil opts that may be mutated to include +// post-verification steps. +func newServer(t testing.TB, opts *ChannelOpts) *tchannel.Channel { + updateOptsForTest(t, opts) + ch, err := NewServerChannel(opts) + require.NoError(t, err, "NewServerChannel failed") + return ch +} + +// NewClient returns a new TChannel that is not listening. +func NewClient(t testing.TB, opts *ChannelOpts) *tchannel.Channel { + return newClient(t, opts.Copy()) +} + +// newClient must be passed non-nil opts that may be mutated to include +// post-verification steps. +func newClient(t testing.TB, opts *ChannelOpts) *tchannel.Channel { + updateOptsForTest(t, opts) + ch, err := NewClientChannel(opts) + require.NoError(t, err, "NewServerChannel failed") + return ch +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/conn.go b/vendor/src/github.com/uber/tchannel-go/testutils/conn.go new file mode 100644 index 00000000..1baa083c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/conn.go @@ -0,0 +1,69 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "net" + "testing" +) + +// GetClosedHostPort will return a host:port that will refuse connections. +func GetClosedHostPort(t testing.TB) string { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("net.Listen failed: %v", err) + return "" + } + + if err := listener.Close(); err != nil { + t.Fatalf("listener.Close failed") + return "" + } + + return listener.Addr().String() +} + +// GetAcceptCloseHostPort returns a host:port that will accept a connection then +// immediately close it. The returned function can be used to stop the listener. +func GetAcceptCloseHostPort(t testing.TB) (string, func()) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("net.Listen failed: %v", err) + return "", nil + } + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + return + } + + conn.Close() + } + }() + + return listener.Addr().String(), func() { + if err := listener.Close(); err != nil { + t.Fatalf("listener.Close failed") + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/counter.go b/vendor/src/github.com/uber/tchannel-go/testutils/counter.go new file mode 100644 index 00000000..76cb758c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/counter.go @@ -0,0 +1,109 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "sync" + + "github.com/uber-go/atomic" +) + +// Decrement is the interface returned by Decrementor. +type Decrement interface { + // Single returns whether any more tokens are remaining. + Single() bool + + // Multiple tries to get n tokens. It returns the actual amount of tokens + // available to use. If this is 0, it means there are no tokens left. + Multiple(n int) int +} + +type decrementor struct { + n atomic.Int64 +} + +func (d *decrementor) Single() bool { + return d.n.Dec() >= 0 +} + +func (d *decrementor) Multiple(n int) int { + decBy := -1 * int64(n) + decremented := d.n.Add(decBy) + if decremented <= decBy { + // Already out of tokens before this decrement. + return 0 + } else if decremented < 0 { + // Not enough tokens, return how many tokens we actually could decrement. + return n + int(decremented) + } + + return n +} + +// Decrementor returns a function that can be called from multiple goroutines and ensures +// it will only return true n times. +func Decrementor(n int) Decrement { + return &decrementor{ + n: *atomic.NewInt64(int64(n)), + } +} + +// Batch returns a slice with n broken into batches of size batchSize. +func Batch(n, batchSize int) []int { + fullBatches := n / batchSize + batches := make([]int, 0, fullBatches+1) + for i := 0; i < fullBatches; i++ { + batches = append(batches, batchSize) + } + if remaining := n % batchSize; remaining > 0 { + batches = append(batches, remaining) + } + return batches +} + +// Buckets splits n over the specified number of buckets. +func Buckets(n int, numBuckets int) []int { + perBucket := n / numBuckets + + buckets := make([]int, numBuckets) + for i := range buckets { + buckets[i] = perBucket + if i == 0 { + buckets[i] += n % numBuckets + } + } + return buckets +} + +// RunN runs the given f n times (and passes the run's index) and waits till they complete. +// It starts n-1 goroutines, and runs one instance in the current goroutine. +func RunN(n int, f func(i int)) { + var wg sync.WaitGroup + for i := 0; i < n-1; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + f(i) + }(i) + } + f(n - 1) + wg.Wait() +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/counter_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/counter_test.go new file mode 100644 index 00000000..b2bd3dc9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/counter_test.go @@ -0,0 +1,106 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testDecrementor(t *testing.T, f func(dec Decrement) int) { + const count = 10000 + const numGoroutines = 100 + + dec := Decrementor(count) + results := make(chan int, numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func() { + results <- f(dec) + }() + } + + var total int + for i := 0; i < numGoroutines; i++ { + total += <-results + } + assert.Equal(t, count, total, "Count mismatch") +} + +func TestDecrementSingle(t *testing.T) { + testDecrementor(t, func(dec Decrement) int { + count := 0 + for dec.Single() { + count++ + } + return count + }) +} + +func TestDecrementMultiple(t *testing.T) { + testDecrementor(t, func(dec Decrement) int { + count := 0 + for { + tokens := dec.Multiple(rand.Intn(100) + 1) + if tokens == 0 { + break + } + count += tokens + } + return count + }) +} + +func TestBatch(t *testing.T) { + tests := []struct { + n int + batch int + want []int + }{ + {40, 10, []int{10, 10, 10, 10}}, + {5, 10, []int{5}}, + {45, 10, []int{10, 10, 10, 10, 5}}, + } + + for _, tt := range tests { + got := Batch(tt.n, tt.batch) + assert.Equal(t, tt.want, got, "Batch(%v, %v) unexpected result", tt.n, tt.batch) + } +} + +func TestBuckets(t *testing.T) { + tests := []struct { + n int + buckets int + want []int + }{ + {2, 3, []int{2, 0, 0}}, + {3, 3, []int{1, 1, 1}}, + {4, 3, []int{2, 1, 1}}, + } + + for _, tt := range tests { + got := Buckets(tt.n, tt.buckets) + assert.Equal(t, tt.want, got, "Buckets(%v, %v) unexpected result", tt.n, tt.buckets) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/data.go b/vendor/src/github.com/uber/tchannel-go/testutils/data.go new file mode 100644 index 00000000..8e339e4b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/data.go @@ -0,0 +1,105 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "encoding/base32" + "encoding/binary" + "math/rand" + "sync" +) + +// This file contains functions for tests to access internal tchannel state. +// Since it has a _test.go suffix, it is only compiled with tests in this package. + +var ( + randCache []byte + randMut sync.RWMutex +) + +func checkCacheSize(n int) { + // Start with a reasonably large cache. + if n < 1024 { + n = 1024 + } + + randMut.RLock() + curSize := len(randCache) + randMut.RUnlock() + + // The cache needs to be at least twice as large as the requested size. + if curSize >= n*2 { + return + } + + resizeCache(n) +} + +func resizeCache(n int) { + randMut.Lock() + defer randMut.Unlock() + + // Double check under the write lock + if len(randCache) >= n*2 { + return + } + + newSize := (n * 2 / 8) * 8 + newCache := make([]byte, newSize) + copied := copy(newCache, randCache) + for i := copied; i < newSize; i += 8 { + n := rand.Int63() + binary.BigEndian.PutUint64(newCache[i:], uint64(n)) + } + randCache = newCache +} + +// RandBytes returns n random byte slice that points to a shared random byte array. +// Since the underlying random array is shared, the returned byte slice must NOT be modified. +func RandBytes(n int) []byte { + const maxSize = 2 * 1024 * 1024 + data := make([]byte, 0, n) + for i := 0; i < n; i += maxSize { + s := n - i + if s > maxSize { + s = maxSize + } + data = append(data, randBytes(s)...) + } + return data +} + +// RandString returns a random alphanumeric string for testing. +func RandString(n int) string { + encoding := base32.StdEncoding + numBytes := encoding.DecodedLen(n) + 5 + return base32.StdEncoding.EncodeToString(RandBytes(numBytes))[:n] +} + +func randBytes(n int) []byte { + checkCacheSize(n) + + randMut.RLock() + startAt := rand.Intn(len(randCache) - n) + bs := randCache[startAt : startAt+n] + randMut.RUnlock() + return bs +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/echo.go b/vendor/src/github.com/uber/tchannel-go/testutils/echo.go new file mode 100644 index 00000000..8fe305cc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/echo.go @@ -0,0 +1,84 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +// CallEcho calls the "echo" endpoint from the given src to target. +func CallEcho(src *tchannel.Channel, targetHostPort, targetService string, args *raw.Args) error { + ctx, cancel := tchannel.NewContext(Timeout(300 * time.Millisecond)) + defer cancel() + + if args == nil { + args = &raw.Args{} + } + + _, _, _, err := raw.Call(ctx, src, targetHostPort, targetService, "echo", args.Arg2, args.Arg3) + return err +} + +// AssertEcho calls the "echo" endpoint with random data, and asserts +// that the returned data matches the arguments "echo" was called with. +func AssertEcho(tb testing.TB, src *tchannel.Channel, targetHostPort, targetService string) { + ctx, cancel := tchannel.NewContext(Timeout(300 * time.Millisecond)) + defer cancel() + + args := &raw.Args{ + Arg2: RandBytes(1000), + Arg3: RandBytes(1000), + } + + arg2, arg3, _, err := raw.Call(ctx, src, targetHostPort, targetService, "echo", args.Arg2, args.Arg3) + if !assert.NoError(tb, err, "Call from %v (%v) to %v (%v) failed", src.ServiceName(), src.PeerInfo().HostPort, targetService, targetHostPort) { + return + } + + assert.Equal(tb, args.Arg2, arg2, "Arg2 mismatch") + assert.Equal(tb, args.Arg3, arg3, "Arg3 mismatch") +} + +// RegisterEcho registers an echo endpoint on the given channel. The optional provided +// function is run before the handler returns. +func RegisterEcho(src tchannel.Registrar, f func()) { + RegisterFunc(src, "echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + if f != nil { + f() + } + return &raw.Res{Arg2: args.Arg2, Arg3: args.Arg3}, nil + }) +} + +// Ping sends a ping from src to target. +func Ping(src, target *tchannel.Channel) error { + ctx, cancel := tchannel.NewContext(Timeout(100 * time.Millisecond)) + defer cancel() + + return src.Ping(ctx, target.PeerInfo().HostPort) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/stacks.go b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/stacks.go new file mode 100644 index 00000000..40754614 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/stacks.go @@ -0,0 +1,147 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package goroutines + +import ( + "bufio" + "bytes" + "fmt" + "io" + "runtime" + "strconv" + "strings" +) + +// Stack represents a single Goroutine's stack. +type Stack struct { + id int + state string + firstFunction string + fullStack *bytes.Buffer +} + +// ID returns the goroutine ID. +func (s Stack) ID() int { + return s.id +} + +// State returns the Goroutine's state. +func (s Stack) State() string { + return s.state +} + +// Full returns the full stack trace for this goroutine. +func (s Stack) Full() []byte { + return s.fullStack.Bytes() +} + +func (s Stack) String() string { + return fmt.Sprintf( + "Goroutine %v in state %v, with %v on top of the stack:\n%s", + s.id, s.state, s.firstFunction, s.Full()) +} + +func getStacks(all bool) []Stack { + var stacks []Stack + + var curStack *Stack + stackReader := bufio.NewReader(bytes.NewReader(getStackBuffer(all))) + for { + line, err := stackReader.ReadString('\n') + if err == io.EOF { + break + } + if err != nil { + panic("stack reader failed") + } + + // If we see the goroutine header, start a new stack. + isFirstLine := false + if strings.HasPrefix(line, "goroutine ") { + // flush any previous stack + if curStack != nil { + stacks = append(stacks, *curStack) + } + id, goState := parseGoStackHeader(line) + curStack = &Stack{ + id: id, + state: goState, + fullStack: &bytes.Buffer{}, + } + isFirstLine = true + } + curStack.fullStack.WriteString(line) + if !isFirstLine && curStack.firstFunction == "" { + curStack.firstFunction = parseFirstFunc(line) + } + } + + if curStack != nil { + stacks = append(stacks, *curStack) + } + return stacks +} + +// GetAll returns the stacks for all running goroutines. +func GetAll() []Stack { + return getStacks(true) +} + +// GetCurrentStack returns the stack for the current goroutine. +func GetCurrentStack() Stack { + return getStacks(false)[0] +} + +func getStackBuffer(all bool) []byte { + for i := 4096; ; i *= 2 { + buf := make([]byte, i) + if n := runtime.Stack(buf, all); n < i { + return buf + } + } +} + +func parseFirstFunc(line string) string { + line = strings.TrimSpace(line) + if idx := strings.LastIndex(line, "("); idx > 0 { + return line[:idx] + } + return line +} + +// parseGoStackHeader parses a stack header that looks like: +// goroutine 643 [runnable]:\n +// And returns the goroutine ID, and the state. +func parseGoStackHeader(line string) (goroutineID int, state string) { + line = strings.TrimSuffix(line, ":\n") + parts := strings.SplitN(line, " ", 3) + if len(parts) != 3 { + panic(fmt.Sprintf("unexpected stack header format: %v", line)) + } + + id, err := strconv.Atoi(parts[1]) + if err != nil { + panic(fmt.Sprintf("failed to parse goroutine ID: %v", parts[1])) + } + + state = strings.TrimSuffix(strings.TrimPrefix(parts[2], "["), "]") + return id, state +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify.go b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify.go new file mode 100644 index 00000000..eeb24520 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify.go @@ -0,0 +1,88 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package goroutines + +import ( + "fmt" + "runtime" + "strings" + "testing" + "time" +) + +// filterStacks will filter any stacks excluded by the given VerifyOpts. +func filterStacks(stacks []Stack, skipID int, opts *VerifyOpts) []Stack { + filtered := stacks[:0] + for _, stack := range stacks { + if stack.ID() == skipID || isTestStack(stack) { + continue + } + if opts.ShouldSkip(stack) { + continue + } + filtered = append(filtered, stack) + } + return filtered +} + +func isTestStack(s Stack) bool { + switch funcName := s.firstFunction; funcName { + case "testing.RunTests", "testing.(*T).Run": + return strings.HasPrefix(s.State(), "chan receive") + case "runtime.goexit": + return strings.HasPrefix(s.State(), "syscall") + default: + return false + } +} + +// IdentifyLeaks looks for extra goroutines, and returns a descriptive error if +// it finds any. +func IdentifyLeaks(opts *VerifyOpts) error { + cur := GetCurrentStack().id + + const maxAttempts = 50 + var stacks []Stack + for i := 0; i < maxAttempts; i++ { + stacks = GetAll() + stacks = filterStacks(stacks, cur, opts) + + if len(stacks) == 0 { + return nil + } + + if i > maxAttempts/2 { + time.Sleep(time.Duration(i) * time.Millisecond) + } else { + runtime.Gosched() + } + } + + return fmt.Errorf("found unexpected goroutines:\n%s", stacks) +} + +// VerifyNoLeaks calls IdentifyLeaks and fails the test if it finds any leaked +// goroutines. +func VerifyNoLeaks(t testing.TB, opts *VerifyOpts) { + if err := IdentifyLeaks(opts); err != nil { + t.Error(err.Error()) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify_opts.go b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify_opts.go new file mode 100644 index 00000000..3f667976 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/goroutines/verify_opts.go @@ -0,0 +1,44 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package goroutines + +import "bytes" + +// VerifyOpts contains +type VerifyOpts struct { + // Excludes is a list of strings that will exclude a stack from being considered a leak. + Excludes []string +} + +// ShouldSkip returns whether the given stack should be skipped when doing verification. +func (opts *VerifyOpts) ShouldSkip(s Stack) bool { + if opts == nil || len(opts.Excludes) == 0 { + return false + } + + for _, exclude := range opts.Excludes { + if bytes.Contains(s.Full(), []byte(exclude)) { + return true + } + } + + return false +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/lists.go b/vendor/src/github.com/uber/tchannel-go/testutils/lists.go new file mode 100644 index 00000000..fd69e750 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/lists.go @@ -0,0 +1,42 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import "time" + +// StrArray will return an array with the given strings. +func StrArray(ss ...string) []string { + return ss +} + +// StrMap returns a map where the keys are the given strings. +func StrMap(ss ...string) map[string]struct{} { + m := make(map[string]struct{}, len(ss)) + for _, v := range ss { + m[v] = struct{}{} + } + return m +} + +// DurationArray returns an array with the given durations. +func DurationArray(dd ...time.Duration) []time.Duration { + return dd +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/logfilter_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/logfilter_test.go new file mode 100644 index 00000000..c85d78e5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/logfilter_test.go @@ -0,0 +1,123 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go" +) + +func TestLogFilterMatches(t *testing.T) { + msgFilter := LogFilter{ + Filter: "msgFilter", + } + + fieldsFilter := LogFilter{ + Filter: "msgFilter", + FieldFilters: map[string]string{ + "f1": "v1", + "f2": "v2", + }, + } + + // fields takes a varargs list of strings which it reads as: + // key, value, key, value... + fields := func(vals ...string) []tchannel.LogField { + fs := make([]tchannel.LogField, len(vals)/2) + for i := 0; i < len(vals); i += 2 { + fs[i/2] = tchannel.LogField{ + Key: vals[i], + Value: vals[i+1], + } + } + return fs + } + + tests := []struct { + Filter LogFilter + Message string + Fields []tchannel.LogField + Match bool + }{ + { + Filter: msgFilter, + Message: "random message", + Match: false, + }, + { + Filter: msgFilter, + Message: "msgFilter", + Match: true, + }, + { + // Case matters. + Filter: msgFilter, + Message: "msgfilter", + Match: false, + }, + { + Filter: msgFilter, + Message: "abc msgFilterdef", + Match: true, + }, + { + Filter: fieldsFilter, + Message: "random message", + Fields: fields("f1", "v1", "f2", "v2"), + Match: false, + }, + { + Filter: fieldsFilter, + Message: "msgFilter", + Fields: fields("f1", "v1", "f2", "v2"), + Match: true, + }, + { + // Field mismatch should not match. + Filter: fieldsFilter, + Message: "msgFilter", + Fields: fields("f1", "v0", "f2", "v2"), + Match: false, + }, + { + // Missing field should not match. + Filter: fieldsFilter, + Message: "msgFilter", + Fields: fields("f2", "v2"), + Match: false, + }, + { + // Extra fields are OK. + Filter: fieldsFilter, + Message: "msgFilter", + Fields: fields("f1", "v0", "f2", "v2", "f3", "v3"), + Match: false, + }, + } + + for _, tt := range tests { + got := tt.Filter.Matches(tt.Message, tt.Fields) + assert.Equal(t, tt.Match, got, "Filter %+v .Matches(%v, %v) mismatch", + tt.Filter, tt.Message, tt.Fields) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/logger.go b/vendor/src/github.com/uber/tchannel-go/testutils/logger.go new file mode 100644 index 00000000..969b4fc0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/logger.go @@ -0,0 +1,207 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "bytes" + "fmt" + "strings" + "sync" + "testing" + "time" + + "github.com/uber/tchannel-go" + + "github.com/uber-go/atomic" +) + +// writer is shared between multiple loggers, and serializes acccesses to +// the underlying buffer. +type writer struct { + sync.Mutex + buf *bytes.Buffer +} + +// testLogger is a logger that writes all output to a buffer, and can report +// the logs if the test has failed. +type testLogger struct { + t testing.TB + fields tchannel.LogFields + w *writer +} + +type errorLoggerState struct { + matchCount []atomic.Uint32 +} + +type errorLogger struct { + tchannel.Logger + t testing.TB + v *LogVerification + s *errorLoggerState +} + +func newWriter() *writer { + return &writer{buf: &bytes.Buffer{}} +} + +func (w *writer) withLock(f func(*bytes.Buffer)) { + w.Lock() + f(w.buf) + w.Unlock() +} + +// Matches returns true if the message and fields match the filter. +func (f LogFilter) Matches(msg string, fields tchannel.LogFields) bool { + // First check the message and ensure it contains Filter + if !strings.Contains(msg, f.Filter) { + return false + } + + // if there are no field filters, then the message match is enough. + if len(f.FieldFilters) == 0 { + return true + } + + fieldsMap := make(map[string]interface{}) + for _, field := range fields { + fieldsMap[field.Key] = field.Value + } + + for k, filter := range f.FieldFilters { + value, ok := fieldsMap[k] + if !ok { + return false + } + + if !strings.Contains(fmt.Sprint(value), filter) { + return false + } + } + + return true +} +func newTestLogger(t testing.TB) testLogger { + return testLogger{t, nil, newWriter()} +} + +func (l testLogger) Enabled(level tchannel.LogLevel) bool { + return true +} + +func (l testLogger) log(prefix string, msg string) { + logLine := fmt.Sprintf("%s [%v] %v %v\n", time.Now().Format("15:04:05.000000"), prefix, msg, l.Fields()) + l.w.withLock(func(w *bytes.Buffer) { + w.WriteString(logLine) + }) +} + +func (l testLogger) Fatal(msg string) { + l.log("F", msg) +} + +func (l testLogger) Error(msg string) { + l.log("E", msg) +} + +func (l testLogger) Warn(msg string) { + l.log("W", msg) +} + +func (l testLogger) Info(msg string) { + l.log("I", msg) +} + +func (l testLogger) Infof(msg string, args ...interface{}) { + l.log("I", fmt.Sprintf(msg, args...)) +} + +func (l testLogger) Debug(msg string) { + l.log("D", msg) +} + +func (l testLogger) Debugf(msg string, args ...interface{}) { + l.log("D", fmt.Sprintf(msg, args...)) +} + +func (l testLogger) Fields() tchannel.LogFields { + return l.fields +} + +func (l testLogger) WithFields(fields ...tchannel.LogField) tchannel.Logger { + existing := len(l.Fields()) + newFields := make(tchannel.LogFields, existing+len(fields)) + copy(newFields, l.Fields()) + copy(newFields[existing:], fields) + return testLogger{l.t, newFields, l.w} +} + +func (l testLogger) report() { + if l.t.Failed() { + l.w.withLock(func(w *bytes.Buffer) { + l.t.Logf("Debug logs:\n%s", w.String()) + }) + } +} + +// checkFilters returns whether the message can be ignored by the filters. +func (l errorLogger) checkFilters(msg string) bool { + match := -1 + for i, filter := range l.v.Filters { + if filter.Matches(msg, l.Fields()) { + match = i + } + } + + if match == -1 { + return false + } + + matchCount := l.s.matchCount[match].Inc() + return uint(matchCount) <= l.v.Filters[match].Count +} + +func (l errorLogger) checkErr(prefix, msg string) { + if l.checkFilters(msg) { + return + } + + l.t.Errorf("Unexpected log: %v: %s %v", prefix, msg, l.Logger.Fields()) +} + +func (l errorLogger) Fatal(msg string) { + l.checkErr("[Fatal]", msg) + l.Logger.Fatal(msg) +} + +func (l errorLogger) Error(msg string) { + l.checkErr("[Error]", msg) + l.Logger.Error(msg) +} + +func (l errorLogger) Warn(msg string) { + l.checkErr("[Warn]", msg) + l.Logger.Warn(msg) +} + +func (l errorLogger) WithFields(fields ...tchannel.LogField) tchannel.Logger { + return errorLogger{l.Logger.WithFields(fields...), l.t, l.v, l.s} +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn.go b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn.go new file mode 100644 index 00000000..6c1e4bda --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn.go @@ -0,0 +1,161 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mockhyperbahn + +import ( + "errors" + "fmt" + "sync" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/hyperbahn" + hthrift "github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn" + "github.com/uber/tchannel-go/json" + "github.com/uber/tchannel-go/relay/relaytest" + "github.com/uber/tchannel-go/thrift" +) + +// Mock is up a mock Hyperbahn server for tests. +type Mock struct { + sync.RWMutex + + ch *tchannel.Channel + respCh chan int + advertised []string + discoverResults map[string][]string +} + +// New returns a mock Hyperbahn server that can be used for testing. +func New() (*Mock, error) { + stubHost := relaytest.NewStubRelayHost() + ch, err := tchannel.NewChannel("hyperbahn", &tchannel.ChannelOptions{ + RelayHost: stubHost, + RelayLocalHandlers: []string{"hyperbahn"}, + }) + if err != nil { + return nil, err + } + mh := &Mock{ + ch: ch, + respCh: make(chan int), + discoverResults: make(map[string][]string), + } + if err := json.Register(ch, json.Handlers{"ad": mh.adHandler}, nil); err != nil { + return nil, err + } + + thriftServer := thrift.NewServer(ch) + thriftServer.Register(hthrift.NewTChanHyperbahnServer(mh)) + + return mh, ch.ListenAndServe("127.0.0.1:0") +} + +// SetDiscoverResult sets the given hostPorts as results for the Discover call. +func (h *Mock) SetDiscoverResult(serviceName string, hostPorts []string) { + h.Lock() + defer h.Unlock() + + h.discoverResults[serviceName] = hostPorts +} + +// Discover returns the IPs for a discovery query if some were set using SetDiscoverResult. +// Otherwise, it returns an error. +func (h *Mock) Discover(ctx thrift.Context, query *hthrift.DiscoveryQuery) (*hthrift.DiscoveryResult_, error) { + h.RLock() + defer h.RUnlock() + + hostPorts, ok := h.discoverResults[query.ServiceName] + if !ok { + return nil, fmt.Errorf("no discovery results set for %v", query.ServiceName) + } + + peers, err := toServicePeers(hostPorts) + if err != nil { + return nil, fmt.Errorf("invalid discover result set: %v", err) + } + + return &hthrift.DiscoveryResult_{ + Peers: peers, + }, nil +} + +// Configuration returns a hyperbahn.Configuration object used to configure a +// hyperbahn.Client to talk to this mock server. +func (h *Mock) Configuration() hyperbahn.Configuration { + return hyperbahn.Configuration{ + InitialNodes: []string{h.ch.PeerInfo().HostPort}, + } +} + +// Channel returns the underlying tchannel that implements relaying. +func (h *Mock) Channel() *tchannel.Channel { + return h.ch +} + +func (h *Mock) adHandler(ctx json.Context, req *hyperbahn.AdRequest) (*hyperbahn.AdResponse, error) { + callerHostPort := tchannel.CurrentCall(ctx).RemotePeer().HostPort + h.Lock() + for _, s := range req.Services { + h.advertised = append(h.advertised, s.Name) + sc := h.ch.GetSubChannel(s.Name, tchannel.Isolated) + sc.Peers().Add(callerHostPort) + } + h.Unlock() + + select { + case n := <-h.respCh: + if n == 0 { + return nil, errors.New("error") + } + return &hyperbahn.AdResponse{ConnectionCount: n}, nil + default: + // Return a default response + return &hyperbahn.AdResponse{ConnectionCount: 3}, nil + } +} + +// GetAdvertised returns the list of services registered. +func (h *Mock) GetAdvertised() []string { + h.RLock() + defer h.RUnlock() + + return h.advertised +} + +// Close stops the mock Hyperbahn server. +func (h *Mock) Close() { + h.ch.Close() +} + +// QueueError queues an error to be returned on the next advertise call. +func (h *Mock) QueueError() { + h.respCh <- 0 +} + +// QueueResponse queues a response from Hyperbahn. +// numConnections must be greater than 0. +func (h *Mock) QueueResponse(numConnections int) { + if numConnections <= 0 { + panic("QueueResponse must have numConnections > 0") + } + + h.respCh <- numConnections +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn_test.go new file mode 100644 index 00000000..973e1e25 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/hyperbahn_test.go @@ -0,0 +1,171 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mockhyperbahn_test + +import ( + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/hyperbahn" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/mockhyperbahn" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" +) + +var config = struct { + hyperbahnConfig hyperbahn.Configuration +}{} + +// setupServer is the application code we are attempting to test. +func setupServer() (*hyperbahn.Client, error) { + ch, err := tchannel.NewChannel("myservice", nil) + if err != nil { + return nil, err + } + + if err := ch.ListenAndServe("127.0.0.1:0"); err != nil { + return nil, err + } + + client, err := hyperbahn.NewClient(ch, config.hyperbahnConfig, nil) + if err != nil { + return nil, err + } + + return client, client.Advertise() +} + +func newAdvertisedEchoServer(t *testing.T, name string, mockHB *mockhyperbahn.Mock, f func()) *tchannel.Channel { + server := testutils.NewServer(t, &testutils.ChannelOpts{ + ServiceName: name, + }) + testutils.RegisterEcho(server, f) + + hbClient, err := hyperbahn.NewClient(server, mockHB.Configuration(), nil) + require.NoError(t, err, "Failed to set up Hyperbahn client") + require.NoError(t, hbClient.Advertise(), "Advertise failed") + + return server +} + +func TestMockHyperbahn(t *testing.T) { + mh, err := mockhyperbahn.New() + require.NoError(t, err, "mock hyperbahn failed") + defer mh.Close() + + config.hyperbahnConfig = mh.Configuration() + _, err = setupServer() + require.NoError(t, err, "setupServer failed") + assert.Equal(t, []string{"myservice"}, mh.GetAdvertised()) +} + +func TestMockDiscovery(t *testing.T) { + mh, err := mockhyperbahn.New() + require.NoError(t, err, "mock hyperbahn failed") + defer mh.Close() + + peers := []string{ + "1.3.5.7:1456", + "255.255.255.255:25", + } + mh.SetDiscoverResult("discover-svc", peers) + + config.hyperbahnConfig = mh.Configuration() + client, err := setupServer() + require.NoError(t, err, "setupServer failed") + + gotPeers, err := client.Discover("discover-svc") + require.NoError(t, err, "Discover failed") + assert.Equal(t, peers, gotPeers, "Discover returned invalid peers") +} + +func TestMockForwards(t *testing.T) { + mockHB, err := mockhyperbahn.New() + require.NoError(t, err, "Failed to set up mock hyperbahm") + + called := false + server := newAdvertisedEchoServer(t, "svr", mockHB, func() { + called = true + }) + defer server.Close() + client := newAdvertisedEchoServer(t, "client", mockHB, nil) + defer client.Close() + + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + + _, _, _, err = raw.CallSC(ctx, client.GetSubChannel("svr"), "echo", nil, nil) + require.NoError(t, err, "Call failed") + require.True(t, called, "Advertised server was not called") +} + +func TestMockIgnoresDown(t *testing.T) { + mockHB, err := mockhyperbahn.New() + require.NoError(t, err, "Failed to set up mock hyperbahm") + + var ( + moe1Called atomic.Bool + moe2Called atomic.Bool + ) + + moe1 := newAdvertisedEchoServer(t, "moe", mockHB, func() { moe1Called.Store(true) }) + defer moe1.Close() + moe2 := newAdvertisedEchoServer(t, "moe", mockHB, func() { moe2Called.Store(true) }) + defer moe2.Close() + client := newAdvertisedEchoServer(t, "client", mockHB, nil) + + ctx, cancel := tchannel.NewContext(time.Second) + defer cancel() + + for i := 0; i < 20; i++ { + _, _, _, err = raw.CallSC(ctx, client.GetSubChannel("moe"), "echo", nil, nil) + assert.NoError(t, err, "Call failed") + } + + require.True(t, moe1Called.Load(), "moe1 not called") + require.True(t, moe2Called.Load(), "moe2 not called") + + // If moe2 is brought down, all calls should now be sent to moe1. + moe2.Close() + + // Wait for the mock HB to have 0 connections to moe + ok := testutils.WaitFor(time.Second, func() bool { + in, out := mockHB.Channel().Peers().GetOrAdd(moe2.PeerInfo().HostPort).NumConnections() + return in+out == 0 + }) + require.True(t, ok, "Failed waiting for mock HB to have 0 connections") + + // Make sure that all calls succeed (they should all go to moe2) + moe1Called.Store(false) + moe2Called.Store(false) + for i := 0; i < 20; i++ { + _, _, _, err = raw.CallSC(ctx, client.GetSubChannel("moe"), "echo", nil, nil) + assert.NoError(t, err, "Call failed") + } + + require.True(t, moe1Called.Load(), "moe1 not called") + require.False(t, moe2Called.Load(), "moe2 should not be called after Close") +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/utils.go b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/utils.go new file mode 100644 index 00000000..41480389 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/mockhyperbahn/utils.go @@ -0,0 +1,73 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mockhyperbahn + +import ( + "fmt" + "net" + "strconv" + + hthrift "github.com/uber/tchannel-go/hyperbahn/gen-go/hyperbahn" +) + +func toServicePeer(hostPort string) (*hthrift.ServicePeer, error) { + host, port, err := net.SplitHostPort(hostPort) + if err != nil { + return nil, fmt.Errorf("invalid hostPort %v: %v", hostPort, err) + } + + ip := net.ParseIP(host) + if ip == nil { + return nil, fmt.Errorf("host %v is not an ip", host) + } + ip = ip.To4() + + if len(ip) != net.IPv4len { + return nil, fmt.Errorf("ip %v is not a v4 ip, expected length to be %v, got %v", + host, net.IPv4len, len(ip)) + } + + portInt, err := strconv.Atoi(port) + if err != nil { + return nil, fmt.Errorf("invalid port %v: %v", port, err) + } + + // We have 4 bytes for the IP, use that as an int. + ipInt := int32(uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])) + return &hthrift.ServicePeer{ + IP: &hthrift.IpAddress{Ipv4: &ipInt}, + Port: int32(portInt), + }, nil +} + +func toServicePeers(hostPorts []string) ([]*hthrift.ServicePeer, error) { + var peers []*hthrift.ServicePeer + for _, hostPort := range hostPorts { + peer, err := toServicePeer(hostPort) + if err != nil { + return nil, err + } + + peers = append(peers, peer) + } + + return peers, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/now.go b/vendor/src/github.com/uber/tchannel-go/testutils/now.go new file mode 100644 index 00000000..384e0226 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/now.go @@ -0,0 +1,46 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "sync" + "time" +) + +// NowStub returns a stub time.Now function that allows the return values to +// to be controller by the caller. It returns two functions: +// stub: The stub time.Now function. +// increment: Used to control the increment amount between calls. +func NowStub(initial time.Time) (stub func() time.Time, increment func(time.Duration)) { + var mut sync.Mutex + cur := initial + var addAmt time.Duration + stub = func() time.Time { + mut.Lock() + defer mut.Unlock() + cur = cur.Add(addAmt) + return cur + } + increment = func(d time.Duration) { + addAmt = d + } + return stub, increment +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/random_bench_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/random_bench_test.go new file mode 100644 index 00000000..9167ca03 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/random_bench_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "bytes" + "io" + "io/ioutil" + "testing" +) + +func benchmarkRandom(b *testing.B, numBytes int) { + var bs []byte + for i := 0; i < b.N; i++ { + randCache = nil + bs = RandBytes(numBytes) + } + io.Copy(ioutil.Discard, bytes.NewReader(bs)) +} + +func BenchmarkRandom256(b *testing.B) { + benchmarkRandom(b, 256) +} + +func BenchmarkRandom1024(b *testing.B) { + benchmarkRandom(b, 1024) +} + +func BenchmarkRandom4096(b *testing.B) { + benchmarkRandom(b, 4096) +} + +func BenchmarkRandom16384(b *testing.B) { + benchmarkRandom(b, 16384) +} + +func BenchmarkRandom32768(b *testing.B) { + benchmarkRandom(b, 32768) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/relay.go b/vendor/src/github.com/uber/tchannel-go/testutils/relay.go new file mode 100644 index 00000000..94d19266 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/relay.go @@ -0,0 +1,144 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "io" + "net" + "sync" + "testing" + + "github.com/uber/tchannel-go" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" +) + +type frameRelay struct { + sync.Mutex // protects conns + + t *testing.T + destination string + relayFunc func(outgoing bool, f *tchannel.Frame) *tchannel.Frame + closed atomic.Uint32 + conns []net.Conn + wg sync.WaitGroup +} + +func (r *frameRelay) listen() (listenHostPort string, cancel func()) { + conn, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(r.t, err, "net.Listen failed") + + go func() { + for { + c, err := conn.Accept() + if err != nil { + if r.closed.Load() == 0 { + r.t.Errorf("Accept failed: %v", err) + } + return + } + + r.Lock() + r.conns = append(r.conns, c) + r.Unlock() + + r.relayConn(c) + } + }() + + return conn.Addr().String(), func() { + r.closed.Inc() + conn.Close() + r.Lock() + for _, c := range r.conns { + c.Close() + } + r.Unlock() + // Wait for all the outbound connections we created to close. + r.wg.Wait() + } +} + +func (r *frameRelay) relayConn(c net.Conn) { + outC, err := net.Dial("tcp", r.destination) + if !assert.NoError(r.t, err, "relay connection failed") { + return + } + r.Lock() + defer r.Unlock() + + if r.closed.Load() > 0 { + outC.Close() + return + } + + r.conns = append(r.conns, outC) + + r.wg.Add(2) + go r.relayBetween(true /* outgoing */, c, outC) + go r.relayBetween(false /* outgoing */, outC, c) +} + +func (r *frameRelay) relayBetween(outgoing bool, c net.Conn, outC net.Conn) { + defer r.wg.Done() + + frame := tchannel.NewFrame(tchannel.MaxFramePayloadSize) + for { + err := frame.ReadIn(c) + if err == io.EOF { + // Connection gracefully closed. + return + } + if err != nil && r.closed.Load() > 0 { + // Once the relay is shutdown, we expect connection errors. + return + } + if !assert.NoError(r.t, err, "read frame failed") { + return + } + + outFrame := r.relayFunc(outgoing, frame) + if outFrame == nil { + continue + } + + err = outFrame.WriteOut(outC) + if err != nil && r.closed.Load() > 0 { + // Once the relay is shutdown, we expect connection errors. + return + } + if !assert.NoError(r.t, err, "write frame failed") { + return + } + } +} + +// FrameRelay sets up a relay that can modify frames using relayFunc. +func FrameRelay(t *testing.T, destination string, relayFunc func(outgoing bool, f *tchannel.Frame) *tchannel.Frame) (listenHostPort string, cancel func()) { + relay := &frameRelay{ + t: t, + destination: destination, + relayFunc: relayFunc, + } + return relay.listen() +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/sleep.go b/vendor/src/github.com/uber/tchannel-go/testutils/sleep.go new file mode 100644 index 00000000..459d9bb6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/sleep.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import "time" + +// SleepStub stubs a function variable that points to time.Sleep. It returns +// two channels to control the sleep stub, and a function to close the channels. +// Once the stub is closed, any further sleeps will cause panics. +// The two channels returned are: +// <-chan time.Duration which will contain arguments that the stub was called with. +// chan<- struct{} that should be written to when you want the Sleep to return. +func SleepStub(funcVar *func(time.Duration)) ( + argCh <-chan time.Duration, unblockCh chan<- struct{}, closeFn func()) { + + args := make(chan time.Duration) + block := make(chan struct{}) + *funcVar = func(t time.Duration) { + args <- t + <-block + } + closeSleepChans := func() { + close(args) + close(block) + } + return args, block, closeSleepChans +} + +// ResetSleepStub resets a Sleep stub. +func ResetSleepStub(funcVar *func(time.Duration)) { + *funcVar = time.Sleep +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/test_server.go b/vendor/src/github.com/uber/tchannel-go/testutils/test_server.go new file mode 100644 index 00000000..e67fe72f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/test_server.go @@ -0,0 +1,448 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "encoding/json" + "fmt" + "runtime" + "strings" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/relay/relaytest" + "github.com/uber/tchannel-go/testutils/goroutines" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" + "golang.org/x/net/context" +) + +// Has a previous test already leaked a goroutine? +var _leakedGoroutine = atomic.NewInt32(0) + +// A TestServer encapsulates a TChannel server, a client factory, and functions +// to ensure that we're not leaking resources. +type TestServer struct { + testing.TB + + // relayIdx is the index of the relay channel, if any, in the channels slice. + relayIdx int + + // relayHost is the relayer's StubRelayHost (if any). + relayHost *relaytest.StubRelayHost + + // relayStats is the backing stats for the relay. + // Note: if a user passes a custom RelayHosts that does not implement + // relayStatter, then this will be nil, and relay stats cannot be verified. + relayStats *relaytest.MockStats + + // channels is the list of channels created for this TestServer. The first + // element is always the initial server. + channels []*tchannel.Channel + + // channelState the initial runtime state for all channels created + // as part of the TestServer (including the server). + channelStates map[*tchannel.Channel]*tchannel.RuntimeState + + introspectOpts *tchannel.IntrospectionOptions + verifyOpts *goroutines.VerifyOpts + postFns []func() +} + +type relayStatter interface { + Stats() *relaytest.MockStats +} + +// NewTestServer constructs a TestServer. +func NewTestServer(t testing.TB, opts *ChannelOpts) *TestServer { + ts := &TestServer{ + TB: t, + channelStates: make(map[*tchannel.Channel]*tchannel.RuntimeState), + introspectOpts: &tchannel.IntrospectionOptions{ + IncludeExchanges: true, + IncludeTombstones: true, + }, + } + + ts.NewServer(opts) + if opts == nil || !opts.DisableRelay { + ts.addRelay(opts) + } + + return ts +} + +// WithTestServer creates a new TestServer, runs the passed function, and then +// verifies that no resources were leaked. +func WithTestServer(t testing.TB, chanOpts *ChannelOpts, f func(*TestServer)) { + chanOpts = chanOpts.Copy() + runCount := chanOpts.RunCount + if runCount < 1 { + runCount = 1 + } + + for i := 0; i < runCount; i++ { + if t.Failed() { + return + } + + // Run without the relay, unless OnlyRelay was set. + if !chanOpts.OnlyRelay { + noRelayOpts := chanOpts.Copy() + noRelayOpts.DisableRelay = true + withServer(t, noRelayOpts, f) + } + + // Run with the relay, unless the user has disabled it. + if !chanOpts.DisableRelay { + withServer(t, chanOpts.Copy(), f) + } + } +} + +// SetVerifyOpts specifies the options we'll use during teardown to verify that +// no goroutines were leaked. +func (ts *TestServer) SetVerifyOpts(opts *goroutines.VerifyOpts) { + ts.verifyOpts = opts +} + +// Server returns the underlying TChannel for the server (i.e., the channel on +// which we're registering handlers). +// +// To support test cases with relays interposed between clients and servers, +// callers should use the Client(), HostPort(), ServiceName(), and Register() +// methods instead of accessing the server channel explicitly. +func (ts *TestServer) Server() *tchannel.Channel { + return ts.channels[0] +} + +// Relay returns the relay channel, if one is present. +func (ts *TestServer) Relay() *tchannel.Channel { + if ts.HasRelay() { + return ts.channels[ts.relayIdx] + } + return nil +} + +// RelayHost returns the stub RelayHost for mapping service names to peers. +func (ts *TestServer) RelayHost() *relaytest.StubRelayHost { + return ts.relayHost +} + +// HostPort returns the host:port for clients to connect to. Note that this may +// not be the same as the host:port of the server channel. +func (ts *TestServer) HostPort() string { + if ts.HasRelay() { + return ts.Relay().PeerInfo().HostPort + } + return ts.Server().PeerInfo().HostPort +} + +// ServiceName returns the service name of the server channel. +func (ts *TestServer) ServiceName() string { + return ts.Server().PeerInfo().ServiceName +} + +// Register registers a handler on the server channel. +func (ts *TestServer) Register(h tchannel.Handler, methodName string) { + ts.Server().Register(h, methodName) +} + +// RegisterFunc registers a function as a handler for the given method name. +// +// TODO: Delete testutils.RegisterFunc in favor of this test server. +func (ts *TestServer) RegisterFunc(name string, f func(context.Context, *raw.Args) (*raw.Res, error)) { + ts.Register(raw.Wrap(rawFuncHandler{ts.Server(), f}), name) +} + +// CloseAndVerify closes all channels verifying each channel as it is closed. +// It then verifies that no goroutines were leaked. +func (ts *TestServer) CloseAndVerify() { + for i := len(ts.channels) - 1; i >= 0; i-- { + ch := ts.channels[i] + ch.Logger().Debugf("TEST: TestServer is closing and verifying channel") + ts.close(ch) + ts.verify(ch) + } +} + +// AssertRelayStats checks that the relayed call graph matches expectations. If +// there's no relay, AssertRelayStats is a no-op. +func (ts *TestServer) AssertRelayStats(expected *relaytest.MockStats) { + if !ts.HasRelay() { + return + } + + if ts.relayStats == nil { + ts.TB.Error("Cannot verify relay stats, passed in RelayStats does not implement relayStatter") + return + } + + ts.relayStats.AssertEqual(ts, expected) +} + +// NewClient returns a client that with log verification. +// TODO: Verify message exchanges and leaks for client channels as well. +func (ts *TestServer) NewClient(opts *ChannelOpts) *tchannel.Channel { + return ts.addChannel(newClient, opts.Copy()) +} + +// NewServer returns a server with log and channel state verification. +func (ts *TestServer) NewServer(opts *ChannelOpts) *tchannel.Channel { + ch := ts.addChannel(newServer, opts.Copy()) + if ts.relayHost != nil { + ts.relayHost.Add(ch.ServiceName(), ch.PeerInfo().HostPort) + } + return ch +} + +// addRelay adds a relay in front of the test server, altering public methods as +// necessary to route traffic through the relay. +func (ts *TestServer) addRelay(parentOpts *ChannelOpts) { + opts := parentOpts.Copy() + + relayHost := opts.ChannelOptions.RelayHost + if relayHost == nil { + ts.relayHost = relaytest.NewStubRelayHost() + relayHost = ts.relayHost + } + + opts.ServiceName = "relay" + opts.ChannelOptions.RelayHost = relayHost + + ts.addChannel(newServer, opts) + if ts.relayHost != nil { + ts.relayHost.Add(ts.Server().ServiceName(), ts.Server().PeerInfo().HostPort) + } + + if statter, ok := relayHost.(relayStatter); ok { + ts.relayStats = statter.Stats() + } + + ts.relayIdx = len(ts.channels) - 1 +} + +// HasRelay indicates whether this TestServer has a relay interposed between the +// server and clients. +func (ts *TestServer) HasRelay() bool { + return ts.relayIdx > 0 +} + +func (ts *TestServer) addChannel(createChannel func(t testing.TB, opts *ChannelOpts) *tchannel.Channel, opts *ChannelOpts) *tchannel.Channel { + ch := createChannel(ts, opts) + ts.postFns = append(ts.postFns, opts.postFns...) + ts.channels = append(ts.channels, ch) + ts.channelStates[ch] = comparableState(ch, ts.introspectOpts) + return ch +} + +// close closes all channels in most-recently-created order. +// it waits for the channels to close. +func (ts *TestServer) close(ch *tchannel.Channel) { + ch.Close() + ts.waitForChannelClose(ch) +} + +func (ts *TestServer) verify(ch *tchannel.Channel) { + // For the main server channel, we want to ensure there's no goroutine leaks + // which will wait for all runnable goroutines. We cannot verify goroutines + // for all channels, as it would detect goroutines in the outer channels. + if ch == ts.channels[0] { + ts.verifyNoGoroutinesLeaked() + } + + ts.verifyRelaysEmpty(ch) + ts.verifyExchangesCleared(ch) +} + +func (ts *TestServer) post() { + if !ts.Failed() { + for _, ch := range ts.channels { + ts.verifyNoStateLeak(ch) + } + } + for _, fn := range ts.postFns { + fn() + } +} + +func (ts *TestServer) waitForChannelClose(ch *tchannel.Channel) { + if ts.Failed() { + return + } + started := time.Now() + + var state tchannel.ChannelState + for i := 0; i < 60; i++ { + if state = ch.State(); state == tchannel.ChannelClosed { + return + } + + runtime.Gosched() + if i < 5 { + continue + } + + sleepFor := time.Duration(i) * 100 * time.Microsecond + time.Sleep(Timeout(sleepFor)) + } + + // Channel is not closing, fail the test. + sinceStart := time.Since(started) + ts.Errorf("Channel %p did not close after %v, last state: %v", ch, sinceStart, state) + + // The introspected state might help debug why the channel isn't closing. + introspected := ch.IntrospectState(&tchannel.IntrospectionOptions{IncludeExchanges: true, IncludeTombstones: true}) + ts.Logf("Introspected state: %s", spew.Sdump(introspected)) +} + +func (ts *TestServer) verifyNoStateLeak(ch *tchannel.Channel) { + initial := ts.channelStates[ch] + final := comparableState(ch, ts.introspectOpts) + assert.Equal(ts.TB, initial, final, "Runtime state has leaks") +} + +func (ts *TestServer) verifyExchangesCleared(ch *tchannel.Channel) { + if ts.Failed() { + return + } + // Ensure that all the message exchanges are empty. + serverState := ch.IntrospectState(ts.introspectOpts) + if exchangesLeft := describeLeakedExchanges(serverState); exchangesLeft != "" { + ts.Errorf("Found uncleared message exchanges on server:\n%v", exchangesLeft) + } +} + +func (ts *TestServer) verifyRelaysEmpty(ch *tchannel.Channel) { + if ts.Failed() { + return + } + var foundErrors bool + state := ch.IntrospectState(ts.introspectOpts) + for _, peerState := range state.RootPeers { + var connStates []tchannel.ConnectionRuntimeState + connStates = append(connStates, peerState.InboundConnections...) + connStates = append(connStates, peerState.OutboundConnections...) + for _, connState := range connStates { + n := connState.Relayer.Count + if assert.Equal(ts, 0, n, "Found %v left-over items in relayer for %v.", n, connState.LocalHostPort) { + continue + } + foundErrors = true + } + } + + if !foundErrors { + return + } + + marshalled, err := json.MarshalIndent(state, "", " ") + require.NoError(ts, err, "Failed to marshal relayer state") + // Print out all the exchanges we found. + ts.Logf("Relayer state:\n%s", marshalled) +} + +func (ts *TestServer) verifyNoGoroutinesLeaked() { + if _leakedGoroutine.Load() == 1 { + ts.Log("Skipping check for leaked goroutines because of a previous leak.") + return + } + err := goroutines.IdentifyLeaks(ts.verifyOpts) + if err == nil { + // No leaks, nothing to do. + return + } + if isFirstLeak := _leakedGoroutine.CAS(0, 1); !isFirstLeak { + ts.Log("Skipping check for leaked goroutines because of a previous leak.") + return + } + if ts.Failed() { + // If we've already failed this test, don't pollute the test output with + // more failures. + return + } + ts.Error(err.Error()) +} + +func comparableState(ch *tchannel.Channel, opts *tchannel.IntrospectionOptions) *tchannel.RuntimeState { + s := ch.IntrospectState(opts) + s.SubChannels = nil + s.Peers = nil + return s +} + +func describeLeakedExchanges(rs *tchannel.RuntimeState) string { + var connections []*tchannel.ConnectionRuntimeState + for _, peer := range rs.RootPeers { + for _, conn := range peer.InboundConnections { + connections = append(connections, &conn) + } + for _, conn := range peer.OutboundConnections { + connections = append(connections, &conn) + } + } + return describeLeakedExchangesConns(connections) +} + +func describeLeakedExchangesConns(connections []*tchannel.ConnectionRuntimeState) string { + var exchanges []string + for _, c := range connections { + if exch := describeLeakedExchangesSingleConn(c); exch != "" { + exchanges = append(exchanges, exch) + } + } + return strings.Join(exchanges, "\n") +} + +func describeLeakedExchangesSingleConn(cs *tchannel.ConnectionRuntimeState) string { + var exchanges []string + checkExchange := func(e tchannel.ExchangeSetRuntimeState) { + if e.Count > 0 { + exchanges = append(exchanges, fmt.Sprintf(" %v leftover %v exchanges", e.Name, e.Count)) + for _, v := range e.Exchanges { + exchanges = append(exchanges, fmt.Sprintf(" exchanges: %+v", v)) + } + } + } + checkExchange(cs.InboundExchange) + checkExchange(cs.OutboundExchange) + if len(exchanges) == 0 { + return "" + } + + return fmt.Sprintf("Connection %d has leftover exchanges:\n\t%v", cs.ID, strings.Join(exchanges, "\n\t")) +} + +func withServer(t testing.TB, chanOpts *ChannelOpts, f func(*TestServer)) { + ts := NewTestServer(t, chanOpts) + // Note: We use defer, as we want the postFns to run even if the test + // goroutine exits (e.g. user calls t.Fatalf). + defer ts.post() + + f(ts) + ts.Server().Logger().Debugf("TEST: Test function complete") + ts.CloseAndVerify() +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk.go b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk.go new file mode 100644 index 00000000..4efc0ac3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testreader + +import ( + "errors" + "io" +) + +// ErrUser is returned by ChunkReader when the user requests an error. +var ErrUser = errors.New("error set by user") + +// ChunkReader returns a reader that returns chunks written to the control channel. +// The caller should write byte chunks to return to the channel, or write nil if they +// want the Reader to return an error. The control channel should be closed to signal EOF. +func ChunkReader() (chan<- []byte, io.Reader) { + reader := &errorReader{ + c: make(chan []byte, 100), + } + return reader.c, reader +} + +type errorReader struct { + c chan []byte + remaining []byte +} + +func (r *errorReader) Read(bs []byte) (int, error) { + for len(r.remaining) == 0 { + var ok bool + r.remaining, ok = <-r.c + if !ok { + return 0, io.EOF + } + if r.remaining == nil { + return 0, ErrUser + } + if len(r.remaining) == 0 { + return 0, nil + } + } + + n := copy(bs, r.remaining) + r.remaining = r.remaining[n:] + return n, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk_test.go new file mode 100644 index 00000000..952c9ed5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/chunk_test.go @@ -0,0 +1,74 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testreader + +import ( + "io" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestChunkReader0ByteRead(t *testing.T) { + writer, reader := ChunkReader() + + writer <- []byte{} + writer <- []byte{'a'} + close(writer) + + buf := make([]byte, 1) + n, err := reader.Read(buf) + assert.NoError(t, err, "Read should not fail") + assert.Equal(t, 0, n, "Read should not read any bytes") + + n, err = reader.Read(buf) + assert.NoError(t, err, "Read should not fail") + assert.Equal(t, 1, n, "Read should read one byte") + assert.EqualValues(t, 'a', buf[0], "Read did not read correct byte") + + n, err = reader.Read(buf) + assert.Equal(t, io.EOF, err, "Read should EOF") + assert.Equal(t, 0, n, "Read should not read any bytes") +} + +func TestChunkReader(t *testing.T) { + writer, reader := ChunkReader() + + writer <- []byte{1, 2} + writer <- []byte{3} + writer <- nil + writer <- []byte{4} + writer <- []byte{} + writer <- []byte{5} + writer <- []byte{} + writer <- []byte{6} + writer <- []byte{} + close(writer) + + buf, err := ioutil.ReadAll(reader) + assert.Equal(t, ErrUser, err, "Expected error after initial bytes") + assert.Equal(t, []byte{1, 2, 3}, buf, "Unexpected bytes") + + buf, err = ioutil.ReadAll(reader) + assert.NoError(t, err, "Reader shouldn't fail on second set of bytes") + assert.Equal(t, []byte{4, 5, 6}, buf, "Unexpected bytes") +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop.go b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop.go new file mode 100644 index 00000000..493ba380 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop.go @@ -0,0 +1,43 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testreader + +import "io" + +type loopReader struct { + bs []byte + pos int +} + +func (r loopReader) Read(p []byte) (int, error) { + for i := range p { + p[i] = r.bs[r.pos] + if r.pos++; r.pos == len(r.bs) { + r.pos = 0 + } + } + return len(p), nil +} + +// Looper returns a reader that will return the bytes in bs as if it was a circular buffer. +func Looper(bs []byte) io.Reader { + return &loopReader{bs, 0} +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop_test.go new file mode 100644 index 00000000..98d2ec2f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testreader/loop_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testreader + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLooper(t *testing.T) { + tests := []struct { + bs []byte + expected []byte + }{ + {[]byte{0x1}, []byte{0x1, 0x1, 0x1}}, + {[]byte{0x1, 0x2}, []byte{0x1, 0x2, 0x1}}, + {[]byte{0x1, 0x2}, []byte{0x1, 0x2, 0x1, 0x2, 0x1, 0x2}}, + } + + for _, tt := range tests { + r := Looper(tt.bs) + got := make([]byte, len(tt.expected)) + n, err := r.Read(got) + assert.NoError(t, err, "Read failed") + assert.Equal(t, len(got), n) + assert.Equal(t, tt.expected, got, "Got unexpected bytes") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation.go b/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation.go new file mode 100644 index 00000000..92d4c3c8 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation.go @@ -0,0 +1,313 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testtracing + +import ( + "fmt" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber/jaeger-client-go" + "golang.org/x/net/context" +) + +const ( + // BaggageKey is used for testing baggage propagation + BaggageKey = "luggage" + + // BaggageValue is used for testing baggage propagation + BaggageValue = "suitcase" +) + +// TracingRequest tests tracing capabilities in a given server. +type TracingRequest struct { + // ForwardCount tells the server how many times to forward this request to itself recursively + ForwardCount int +} + +// TracingResponse captures the trace info observed in the server and its downstream calls +type TracingResponse struct { + TraceID uint64 + SpanID uint64 + ParentID uint64 + TracingEnabled bool + Child *TracingResponse + Luggage string +} + +// ObserveSpan extracts an OpenTracing span from the context and populates the response. +func (r *TracingResponse) ObserveSpan(ctx context.Context) *TracingResponse { + if span := opentracing.SpanFromContext(ctx); span != nil { + if mockSpan, ok := span.(*mocktracer.MockSpan); ok { + sc := mockSpan.Context().(mocktracer.MockSpanContext) + r.TraceID = uint64(sc.TraceID) + r.SpanID = uint64(sc.SpanID) + r.ParentID = uint64(mockSpan.ParentID) + r.TracingEnabled = sc.Sampled + } else if span := tchannel.CurrentSpan(ctx); span != nil { + r.TraceID = span.TraceID() + r.SpanID = span.SpanID() + r.ParentID = span.ParentID() + r.TracingEnabled = span.Flags()&1 == 1 + } + r.Luggage = span.BaggageItem(BaggageKey) + } + return r +} + +// TraceHandler is a base class for testing tracing propagation +type TraceHandler struct { + Ch *tchannel.Channel +} + +// HandleCall is used by handlers from different encodings as the main business logic. +// It respects the ForwardCount input parameter to make downstream calls, and returns +// a result containing the observed tracing span and the downstream results. +func (h *TraceHandler) HandleCall( + ctx context.Context, + req *TracingRequest, + downstream TracingCall, +) (*TracingResponse, error) { + var childResp *TracingResponse + if req.ForwardCount > 0 { + downstreamReq := &TracingRequest{ForwardCount: req.ForwardCount - 1} + if resp, err := downstream(ctx, downstreamReq); err == nil { + childResp = resp + } else { + return nil, err + } + } + + resp := &TracingResponse{Child: childResp} + resp.ObserveSpan(ctx) + + return resp, nil +} + +// TracerType is a convenient enum to indicate which type of tracer is being used in the test. +// It is a string because it's printed as part of the test description in the logs. +type TracerType string + +const ( + // Noop is for the default no-op tracer from OpenTracing + Noop TracerType = "NOOP" + // Mock tracer, baggage-capable, non-Zipkin trace IDs + Mock TracerType = "MOCK" + // Jaeger is Uber's tracer, baggage-capable, Zipkin-style trace IDs + Jaeger TracerType = "JAEGER" +) + +// TracingCall is used in a few other structs here +type TracingCall func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) + +// EncodingInfo describes the encoding used with tracing propagation test +type EncodingInfo struct { + Format tchannel.Format + HeadersSupported bool +} + +// PropagationTestSuite is a collection of test cases for a certain encoding +type PropagationTestSuite struct { + Encoding EncodingInfo + Register func(t *testing.T, ch *tchannel.Channel) TracingCall + TestCases map[TracerType][]PropagationTestCase +} + +// PropagationTestCase describes a single propagation test case and expected results +type PropagationTestCase struct { + ForwardCount int + TracingDisabled bool + ExpectedBaggage string + ExpectedSpanCount int +} + +type tracerChoice struct { + tracerType TracerType + tracer opentracing.Tracer + spansRecorded func() int + resetSpans func() + isFake bool + zipkinCompatible bool +} + +// Run executes the test cases in the test suite against 3 different tracer implementations +func (s *PropagationTestSuite) Run(t *testing.T) { + tests := []struct { + name string + run func(t *testing.T) + }{ + {"Noop_Tracer", s.runWithNoopTracer}, + {"Mock_Tracer", s.runWithMockTracer}, + {"Jaeger_Tracer", s.runWithJaegerTracer}, + } + for _, test := range tests { + t.Logf("Running with %s", test.name) + test.run(t) + } +} + +func (s *PropagationTestSuite) runWithNoopTracer(t *testing.T) { + s.runWithTracer(t, tracerChoice{ + tracer: nil, // will cause opentracing.GlobalTracer() to be used + tracerType: Noop, + spansRecorded: func() int { return 0 }, + resetSpans: func() {}, + isFake: true, + }) +} + +func (s *PropagationTestSuite) runWithMockTracer(t *testing.T) { + mockTracer := mocktracer.New() + s.runWithTracer(t, tracerChoice{ + tracerType: Mock, + tracer: mockTracer, + spansRecorded: func() int { + return len(MockTracerSampledSpans(mockTracer)) + }, + resetSpans: func() { + mockTracer.Reset() + }, + }) +} + +func (s *PropagationTestSuite) runWithJaegerTracer(t *testing.T) { + jaegerReporter := jaeger.NewInMemoryReporter() + jaegerTracer, jaegerCloser := jaeger.NewTracer(testutils.DefaultServerName, + jaeger.NewConstSampler(true), + jaegerReporter) + // To enable logging, use composite reporter: + // jaeger.NewCompositeReporter(jaegerReporter, jaeger.NewLoggingReporter(jaeger.StdLogger))) + defer jaegerCloser.Close() + s.runWithTracer(t, tracerChoice{ + tracerType: Jaeger, + tracer: jaegerTracer, + spansRecorded: func() int { + return len(jaegerReporter.GetSpans()) + }, + resetSpans: func() { + jaegerReporter.Reset() + }, + zipkinCompatible: true, + }) +} + +func (s *PropagationTestSuite) runWithTracer(t *testing.T, tracer tracerChoice) { + testCases, ok := s.TestCases[tracer.tracerType] + if !ok { + t.Logf("No test cases for encoding=%s and tracer=%s", s.Encoding.Format, tracer.tracerType) + return + } + opts := &testutils.ChannelOpts{ + ChannelOptions: tchannel.ChannelOptions{Tracer: tracer.tracer}, + DisableRelay: true, + } + ch := testutils.NewServer(t, opts) + defer ch.Close() + ch.Peers().Add(ch.PeerInfo().HostPort) + call := s.Register(t, ch) + for _, tt := range testCases { + s.runTestCase(t, tracer, ch, tt, call) + } +} + +func (s *PropagationTestSuite) runTestCase( + t *testing.T, + tracer tracerChoice, + ch *tchannel.Channel, + test PropagationTestCase, + call TracingCall, +) { + descr := fmt.Sprintf("test %+v with tracer %+v", test, tracer) + ch.Logger().Debugf("Starting tracing test %s", descr) + + tracer.resetSpans() + + span := ch.Tracer().StartSpan("client") + span.SetBaggageItem(BaggageKey, BaggageValue) + ctx := opentracing.ContextWithSpan(context.Background(), span) + + ctxBuilder := tchannel.NewContextBuilder(5 * time.Second).SetParentContext(ctx) + if test.TracingDisabled { + ctxBuilder.DisableTracing() + } + ctx, cancel := ctxBuilder.Build() + defer cancel() + + req := &TracingRequest{ForwardCount: test.ForwardCount} + ch.Logger().Infof("Sending tracing request %+v", req) + response, err := call(ctx, req) + require.NoError(t, err) + ch.Logger().Infof("Received tracing response %+v", response) + + // Spans are finished in inbound.doneSending() or outbound.doneReading(), + // which are called on different go-routines and may execute *after* the + // response has been received by the client. Give them a chance to run. + for i := 0; i < 1000; i++ { + if spanCount := tracer.spansRecorded(); spanCount == test.ExpectedSpanCount { + break + } + time.Sleep(testutils.Timeout(time.Millisecond)) + } + spanCount := tracer.spansRecorded() + ch.Logger().Debugf("end span count: %d", spanCount) + + // finish span after taking count of recorded spans, as we're only interested + // in the count of spans created by RPC calls. + span.Finish() + + root := new(TracingResponse).ObserveSpan(ctx) + + if !tracer.isFake { + assert.Equal(t, uint64(0), root.ParentID) + assert.NotEqual(t, uint64(0), root.TraceID) + } + + assert.Equal(t, test.ExpectedSpanCount, spanCount, "Wrong span count; %s", descr) + + for r, cnt := response, 0; r != nil || cnt <= test.ForwardCount; r, cnt = r.Child, cnt+1 { + require.NotNil(t, r, "Expecting response for forward=%d; %s", cnt, descr) + if !tracer.isFake { + if tracer.zipkinCompatible || s.Encoding.HeadersSupported { + assert.Equal(t, root.TraceID, r.TraceID, "traceID should be the same; %s", descr) + } + assert.Equal(t, test.ExpectedBaggage, r.Luggage, "baggage should propagate; %s", descr) + } + } + ch.Logger().Debugf("Finished tracing test %s", descr) +} + +// MockTracerSampledSpans is a helper function that returns only sampled spans from MockTracer +func MockTracerSampledSpans(tracer *mocktracer.MockTracer) []*mocktracer.MockSpan { + var spans []*mocktracer.MockSpan + for _, span := range tracer.FinishedSpans() { + if span.Context().(mocktracer.MockSpanContext).Sampled { + spans = append(spans, span) + } + } + return spans +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation_test.go new file mode 100644 index 00000000..5158e216 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testtracing/propagation_test.go @@ -0,0 +1,130 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testtracing + +import ( + json_encoding "encoding/json" + "testing" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + + "golang.org/x/net/context" +) + +func requestFromRaw(args *raw.Args) *TracingRequest { + r := new(TracingRequest) + r.ForwardCount = int(args.Arg3[0]) + return r +} + +func requestToRaw(r *TracingRequest) []byte { + return []byte{byte(r.ForwardCount)} +} + +func responseFromRaw(t *testing.T, arg3 []byte) (*TracingResponse, error) { + var r TracingResponse + err := json_encoding.Unmarshal(arg3, &r) + if err != nil { + return nil, err + } + return &r, nil +} + +func responseToRaw(t *testing.T, r *TracingResponse) (*raw.Res, error) { + jsonBytes, err := json_encoding.Marshal(r) + if err != nil { + return nil, err + } + return &raw.Res{Arg3: jsonBytes}, nil +} + +// RawHandler tests tracing over Raw encoding +type RawHandler struct { + TraceHandler + t *testing.T +} + +func (h *RawHandler) Handle(ctx context.Context, args *raw.Args) (*raw.Res, error) { + req := requestFromRaw(args) + res, err := h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + _, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort, + h.Ch.PeerInfo().ServiceName, "rawcall", nil, requestToRaw(req)) + if err != nil { + return nil, err + } + return responseFromRaw(h.t, arg3) + }) + if err != nil { + return nil, err + } + return responseToRaw(h.t, res) +} + +func (h *RawHandler) OnError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } + +func (h *RawHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + _, arg3, _, err := raw.Call(ctx, h.Ch, h.Ch.PeerInfo().HostPort, h.Ch.PeerInfo().ServiceName, + "rawcall", nil, requestToRaw(req)) + if err != nil { + return nil, err + } + return responseFromRaw(h.t, arg3) +} + +func TestRawTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.Raw, HeadersSupported: false}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + handler := &RawHandler{ + TraceHandler: TraceHandler{Ch: ch}, + t: t, + } + ch.Register(raw.Wrap(handler), "rawcall") + return handler.firstCall + }, + // Since Raw encoding does not support headers, there is no baggage propagation + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: "", ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: "", ExpectedSpanCount: 0}, + }, + Mock: { + // Since Raw encoding does not propagate generic traces, the tracingDisable + // only affects the first outbound span (it's not sampled), but the other + // two outbound spans are still sampled and recorded. + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: "", ExpectedSpanCount: 2}, + // Since Raw encoding does not propagate generic traces, we record 3 spans + // for outbound calls, but none for inbound calls. + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: "", ExpectedSpanCount: 3}, + }, + Jaeger: { + // Since Jaeger is Zipkin-compatible, it is able to keep track of tracingDisabled + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: "", ExpectedSpanCount: 0}, + // Since Jaeger is Zipkin-compatible, it is able to decode the trace + // even from the Raw encoding. + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: "", ExpectedSpanCount: 6}, + }, + }, + } + suite.Run(t) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited.go b/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited.go new file mode 100644 index 00000000..eb9677ed --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testwriter + +import ( + "errors" + "io" +) + +// ErrOutOfSpace is returned by Limited reader when it is out of bytes. +var ErrOutOfSpace = errors.New("out of space") + +type writerFunc func([]byte) (int, error) + +func (f writerFunc) Write(p []byte) (n int, err error) { + return f(p) +} + +// Limited returns an io.Writer that will only accept n bytes. +// All further calls will cause an error. +func Limited(n int) io.Writer { + return writerFunc(func(p []byte) (int, error) { + if n < len(p) { + retN := n + n = 0 + return retN, ErrOutOfSpace + } + + n -= len(p) + return len(p), nil + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited_test.go b/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited_test.go new file mode 100644 index 00000000..5553094b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/testwriter/limited_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testwriter + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLimitedWriter(t *testing.T) { + tests := []struct { + limit int + writeBytes []byte + wantErr error + wantBytes int + }{ + { + limit: 1, + writeBytes: []byte{1}, + wantBytes: 1, + }, + { + limit: 1, + writeBytes: []byte{1, 2}, + wantErr: ErrOutOfSpace, + wantBytes: 1, + }, + { + limit: 0, + writeBytes: nil, + wantBytes: 0, + }, + { + limit: 5, + writeBytes: []byte{1, 2, 3, 4, 5, 6}, + wantErr: ErrOutOfSpace, + wantBytes: 5, + }, + } + + for _, tt := range tests { + writer := Limited(tt.limit) + n, err := writer.Write(tt.writeBytes) + if tt.wantErr != nil { + assert.Equal(t, tt.wantErr, err, "Write %v to Limited(%v) should fail", tt.writeBytes, tt.limit) + } else { + assert.NoError(t, err, "Write %v to Limited(%v) should not fail", tt.writeBytes, tt.limit) + } + assert.Equal(t, tt.wantBytes, n, "Unexpected number of bytes written to Limited(%v)", tt.limit) + + n, err = writer.Write([]byte{2}) + assert.Equal(t, ErrOutOfSpace, err, "Write should be out of space") + assert.Equal(t, 0, n, "Write should not write any bytes when it is out of space") + } +} + +func TestLimitedWriter2(t *testing.T) { + writer := Limited(1) + n, err := writer.Write([]byte{1, 2}) + assert.Equal(t, ErrOutOfSpace, err, "Write should fail") + assert.Equal(t, 1, n, "Write should only write one byte") + + n, err = writer.Write([]byte{2}) + assert.Equal(t, ErrOutOfSpace, err, "Write should be out of space") + assert.Equal(t, 0, n, "Write should not write any bytes when it is out of space") +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/timeout.go b/vendor/src/github.com/uber/tchannel-go/testutils/timeout.go new file mode 100644 index 00000000..2f64ddc9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/timeout.go @@ -0,0 +1,82 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "fmt" + "os" + "runtime" + "strconv" + "strings" + "testing" + "time" +) + +var timeoutScaleFactor = 1.0 + +func init() { + if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" { + fv, err := strconv.ParseFloat(v, 64) + if err != nil { + panic(err) + } + timeoutScaleFactor = fv + fmt.Fprintln(os.Stderr, "Scaling timeouts by factor", timeoutScaleFactor) + } +} + +// Timeout returns the timeout multiplied by any set multiplier. +func Timeout(timeout time.Duration) time.Duration { + return time.Duration(timeoutScaleFactor * float64(timeout)) +} + +// getCallerName returns the test name that called this function. +// It traverses the stack to find the function name directly after a testing.* call. +func getCallerName() string { + pc := make([]uintptr, 10) + n := runtime.Callers(2, pc) + for i := n; i > 0; i-- { + fname := runtime.FuncForPC(pc[i-1]).Name() + if strings.HasPrefix(fname, "testing.") { + return runtime.FuncForPC(pc[i-2]).Name() + } + } + return "unknown" +} + +// SetTimeout is used to fail tests after a timeout. It returns a function that should be +// run once the test is complete. The standard way is to use defer, e.g. +// defer SetTimeout(t, time.Second)() +func SetTimeout(t *testing.T, timeout time.Duration) func() { + timeout = Timeout(timeout) + + caller := getCallerName() + + timer := time.AfterFunc(timeout, func() { + t.Logf("Test %s timed out after %v", caller, timeout) + // Unfortunately, tests cannot be failed from new goroutines, so use a panic. + panic(fmt.Errorf("Test %s timed out after %v", caller, timeout)) + }) + + return func() { + timer.Stop() + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/testutils/wait.go b/vendor/src/github.com/uber/tchannel-go/testutils/wait.go new file mode 100644 index 00000000..a50c1f55 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/testutils/wait.go @@ -0,0 +1,66 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package testutils + +import ( + "sync" + "time" +) + +// WaitFor will retry f till it returns true for a maximum of timeout. +// It returns true if f returned true, false if timeout was hit. +func WaitFor(timeout time.Duration, f func() bool) bool { + timeoutEnd := time.Now().Add(Timeout(timeout)) + + const maxSleep = time.Millisecond * 50 + sleepFor := time.Millisecond + for { + if f() { + return true + } + + if time.Now().After(timeoutEnd) { + return false + } + + time.Sleep(sleepFor) + if sleepFor < maxSleep { + sleepFor *= 2 + } + } +} + +// WaitWG waits for the given WaitGroup to be complete with a timeout +// and returns whether the WaitGroup completed within the timeout. +func WaitWG(wg *sync.WaitGroup, timeout time.Duration) bool { + wgC := make(chan struct{}) + + go func() { + wg.Wait() + wgC <- struct{}{} + }() + select { + case <-time.After(timeout): + return false + case <-wgC: + return true + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/client.go b/vendor/src/github.com/uber/tchannel-go/thrift/client.go new file mode 100644 index 00000000..7a45126d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/client.go @@ -0,0 +1,160 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/internal/argreader" + + "github.com/apache/thrift/lib/go/thrift" + "golang.org/x/net/context" +) + +// client implements TChanClient and makes outgoing Thrift calls. +type client struct { + ch *tchannel.Channel + sc *tchannel.SubChannel + serviceName string + opts ClientOptions +} + +// ClientOptions are options to customize the client. +type ClientOptions struct { + // HostPort specifies a specific server to hit. + HostPort string +} + +// NewClient returns a Client that makes calls over the given tchannel to the given Hyperbahn service. +func NewClient(ch *tchannel.Channel, serviceName string, opts *ClientOptions) TChanClient { + client := &client{ + ch: ch, + sc: ch.GetSubChannel(serviceName), + serviceName: serviceName, + } + if opts != nil { + client.opts = *opts + } + return client +} + +func (c *client) startCall(ctx context.Context, method string, callOptions *tchannel.CallOptions) (*tchannel.OutboundCall, error) { + if c.opts.HostPort != "" { + return c.ch.BeginCall(ctx, c.opts.HostPort, c.serviceName, method, callOptions) + } + return c.sc.BeginCall(ctx, method, callOptions) +} + +func writeArgs(call *tchannel.OutboundCall, headers map[string]string, req thrift.TStruct) error { + writer, err := call.Arg2Writer() + if err != nil { + return err + } + headers = tchannel.InjectOutboundSpan(call.Response(), headers) + if err := WriteHeaders(writer, headers); err != nil { + return err + } + if err := writer.Close(); err != nil { + return err + } + + writer, err = call.Arg3Writer() + if err != nil { + return err + } + + if err := WriteStruct(writer, req); err != nil { + return err + } + + return writer.Close() +} + +// readResponse reads the response struct into resp, and returns: +// (response headers, whether there was an application error, unexpected error). +func readResponse(response *tchannel.OutboundCallResponse, resp thrift.TStruct) (map[string]string, bool, error) { + reader, err := response.Arg2Reader() + if err != nil { + return nil, false, err + } + + headers, err := ReadHeaders(reader) + if err != nil { + return nil, false, err + } + + if err := argreader.EnsureEmpty(reader, "reading response headers"); err != nil { + return nil, false, err + } + + if err := reader.Close(); err != nil { + return nil, false, err + } + + success := !response.ApplicationError() + reader, err = response.Arg3Reader() + if err != nil { + return headers, success, err + } + + if err := ReadStruct(reader, resp); err != nil { + return headers, success, err + } + + if err := argreader.EnsureEmpty(reader, "reading response body"); err != nil { + return nil, false, err + } + + return headers, success, reader.Close() +} + +func (c *client) Call(ctx Context, thriftService, methodName string, req, resp thrift.TStruct) (bool, error) { + var ( + headers = ctx.Headers() + + respHeaders map[string]string + isOK bool + ) + + err := c.ch.RunWithRetry(ctx, func(ctx context.Context, rs *tchannel.RequestState) error { + respHeaders, isOK = nil, false + + call, err := c.startCall(ctx, thriftService+"::"+methodName, &tchannel.CallOptions{ + Format: tchannel.Thrift, + RequestState: rs, + }) + if err != nil { + return err + } + + if err := writeArgs(call, headers, req); err != nil { + return err + } + + respHeaders, isOK, err = readResponse(call.Response(), resp) + return err + }) + if err != nil { + return false, err + } + + ctx.SetResponseHeaders(respHeaders) + return isOK, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/context.go b/vendor/src/github.com/uber/tchannel-go/thrift/context.go new file mode 100644 index 00000000..9f04ef18 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/context.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "time" + + "github.com/uber/tchannel-go" + "golang.org/x/net/context" +) + +// Context is a Thrift Context which contains request and response headers. +type Context tchannel.ContextWithHeaders + +// NewContext returns a Context that can be used to make Thrift calls. +func NewContext(timeout time.Duration) (Context, context.CancelFunc) { + ctx, cancel := tchannel.NewContext(timeout) + return Wrap(ctx), cancel +} + +// Wrap returns a Thrift Context that wraps around a Context. +func Wrap(ctx context.Context) Context { + return tchannel.Wrap(ctx) +} + +// WithHeaders returns a Context that can be used to make a call with request headers. +func WithHeaders(ctx context.Context, headers map[string]string) Context { + return tchannel.WrapWithHeaders(ctx, headers) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/context_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/context_test.go new file mode 100644 index 00000000..9becc8e9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/context_test.go @@ -0,0 +1,76 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift_test + +import ( + "errors" + "testing" + "time" + + . "github.com/uber/tchannel-go/thrift" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestWrapContext(t *testing.T) { + tctx, cancel := NewContext(time.Second) + defer cancel() + + headers := map[string]string{"h1": "v1"} + ctx := context.WithValue(WithHeaders(tctx, headers), "1", "2") + wrapped := Wrap(ctx) + assert.NotNil(t, wrapped, "Should not return nil.") + + assert.Equal(t, headers, wrapped.Headers(), "Unexpected headers") + assert.Equal(t, "2", wrapped.Value("1"), "Unexpected value") +} + +func TestContextBuilder(t *testing.T) { + ctx, cancel := tchannel.NewContextBuilder(time.Second).SetShardKey("shard").Build() + defer cancel() + + var called bool + testutils.WithServer(t, nil, func(ch *tchannel.Channel, hostPort string) { + peerInfo := ch.PeerInfo() + + testutils.RegisterFunc(ch, "SecondService::Echo", func(ctx context.Context, args *raw.Args) (*raw.Res, error) { + call := tchannel.CurrentCall(ctx) + assert.Equal(t, peerInfo.ServiceName, call.CallerName(), "unexpected caller name") + assert.Equal(t, "shard", call.ShardKey(), "unexpected shard key") + assert.Equal(t, tchannel.Thrift, args.Format) + called = true + return nil, errors.New("err") + }) + + client := NewClient(ch, ch.PeerInfo().ServiceName, &ClientOptions{ + HostPort: peerInfo.HostPort, + }) + secondClient := gen.NewTChanSecondServiceClient(client) + secondClient.Echo(ctx, "asd") + assert.True(t, called, "test not called") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/doc.go b/vendor/src/github.com/uber/tchannel-go/thrift/doc.go new file mode 100644 index 00000000..2635c46e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/doc.go @@ -0,0 +1,44 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* +Package thrift adds support to use Thrift services over TChannel. + +To start listening to a Thrift service using TChannel, create the channel, +and register the service using: + server := thrift.NewServer(tchan) + server.Register(gen.NewTChan[SERVICE]Server(handler) + + // Any number of services can be registered on the same Thrift server. + server.Register(gen.NewTChan[SERVICE2]Server(handler) + +To use a Thrift client use the generated TChan client: + thriftClient := thrift.NewClient(ch, "hyperbahnService", nil) + client := gen.NewTChan[SERVICE]Client(thriftClient) + + // Any number of service clients can be made using the same Thrift client. + client2 := gen.NewTChan[SERVICE2]Client(thriftClient) + +This client can be used similar to a standard Thrift client, except a Context +is passed with options (such as timeout). + +TODO(prashant): Add and document header support. +*/ +package thrift diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/errors_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/errors_test.go new file mode 100644 index 00000000..4584aaa6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/errors_test.go @@ -0,0 +1,94 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift_test + +import ( + "testing" + "time" + + // Test is in a separate package to avoid circular dependencies. + . "github.com/uber/tchannel-go/thrift" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/raw" + "github.com/uber/tchannel-go/testutils" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + "github.com/uber/tchannel-go/thrift/mocks" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func serializeStruct(t *testing.T, s thrift.TStruct) []byte { + trans := thrift.NewTMemoryBuffer() + p := thrift.NewTBinaryProtocolTransport(trans) + require.NoError(t, s.Write(p), "Struct serialization failed") + return trans.Bytes() +} + +func TestInvalidThriftBytes(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + ch := testutils.NewClient(t, nil) + sCh := testutils.NewServer(t, nil) + defer sCh.Close() + + svr := NewServer(sCh) + svr.Register(gen.NewTChanSecondServiceServer(new(mocks.TChanSecondService))) + + tests := []struct { + name string + arg3 []byte + }{ + { + name: "missing bytes", + arg3: serializeStruct(t, &gen.SecondServiceEchoArgs{Arg: "Hello world"})[:5], + }, + { + name: "wrong struct", + arg3: serializeStruct(t, &gen.Data{B1: true}), + }, + } + + for _, tt := range tests { + sPeer := sCh.PeerInfo() + call, err := ch.BeginCall(ctx, sPeer.HostPort, sPeer.ServiceName, "SecondService::Echo", &tchannel.CallOptions{ + Format: tchannel.Thrift, + }) + require.NoError(t, err, "BeginCall failed") + require.NoError(t, tchannel.NewArgWriter(call.Arg2Writer()).Write([]byte{0, 0}), "Write arg2 failed") + + writer, err := call.Arg3Writer() + require.NoError(t, err, "Arg3Writer failed") + + _, err = writer.Write(tt.arg3) + require.NoError(t, err, "Write arg3 failed") + require.NoError(t, writer.Close(), "Close failed") + + response := call.Response() + _, _, err = raw.ReadArgsV2(response) + assert.Error(t, err, "%v: Expected error", tt.name) + assert.Equal(t, tchannel.ErrCodeBadRequest, tchannel.GetSystemErrorCode(err), + "%v: Expected bad request, got %v", tt.name, err) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/constants.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/constants.go new file mode 100644 index 00000000..40403d9c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package meta + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/meta.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/meta.go new file mode 100644 index 00000000..5e618d33 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/meta.go @@ -0,0 +1,920 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package meta + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type Meta interface { + Health() (r *HealthStatus, err error) + ThriftIDL() (r *ThriftIDLs, err error) + VersionInfo() (r *VersionInfo, err error) +} + +type MetaClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewMetaClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *MetaClient { + return &MetaClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewMetaClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *MetaClient { + return &MetaClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +func (p *MetaClient) Health() (r *HealthStatus, err error) { + if err = p.sendHealth(); err != nil { + return + } + return p.recvHealth() +} + +func (p *MetaClient) sendHealth() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("health", thrift.CALL, p.SeqId); err != nil { + return + } + args := MetaHealthArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *MetaClient) recvHealth() (value *HealthStatus, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "health" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "health failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "health failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "health failed: invalid message type") + return + } + result := MetaHealthResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +func (p *MetaClient) ThriftIDL() (r *ThriftIDLs, err error) { + if err = p.sendThriftIDL(); err != nil { + return + } + return p.recvThriftIDL() +} + +func (p *MetaClient) sendThriftIDL() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("thriftIDL", thrift.CALL, p.SeqId); err != nil { + return + } + args := MetaThriftIDLArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *MetaClient) recvThriftIDL() (value *ThriftIDLs, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "thriftIDL" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "thriftIDL failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "thriftIDL failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error5 error + error5, err = error4.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error5 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "thriftIDL failed: invalid message type") + return + } + result := MetaThriftIDLResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +func (p *MetaClient) VersionInfo() (r *VersionInfo, err error) { + if err = p.sendVersionInfo(); err != nil { + return + } + return p.recvVersionInfo() +} + +func (p *MetaClient) sendVersionInfo() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("versionInfo", thrift.CALL, p.SeqId); err != nil { + return + } + args := MetaVersionInfoArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *MetaClient) recvVersionInfo() (value *VersionInfo, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "versionInfo" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "versionInfo failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "versionInfo failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error6 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error7 error + error7, err = error6.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error7 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "versionInfo failed: invalid message type") + return + } + result := MetaVersionInfoResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type MetaProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Meta +} + +func (p *MetaProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *MetaProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *MetaProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewMetaProcessor(handler Meta) *MetaProcessor { + + self8 := &MetaProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self8.processorMap["health"] = &metaProcessorHealth{handler: handler} + self8.processorMap["thriftIDL"] = &metaProcessorThriftIDL{handler: handler} + self8.processorMap["versionInfo"] = &metaProcessorVersionInfo{handler: handler} + return self8 +} + +func (p *MetaProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x9 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x9.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x9 + +} + +type metaProcessorHealth struct { + handler Meta +} + +func (p *metaProcessorHealth) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := MetaHealthArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("health", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := MetaHealthResult{} + var retval *HealthStatus + var err2 error + if retval, err2 = p.handler.Health(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing health: "+err2.Error()) + oprot.WriteMessageBegin("health", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("health", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type metaProcessorThriftIDL struct { + handler Meta +} + +func (p *metaProcessorThriftIDL) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := MetaThriftIDLArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("thriftIDL", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := MetaThriftIDLResult{} + var retval *ThriftIDLs + var err2 error + if retval, err2 = p.handler.ThriftIDL(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing thriftIDL: "+err2.Error()) + oprot.WriteMessageBegin("thriftIDL", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("thriftIDL", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type metaProcessorVersionInfo struct { + handler Meta +} + +func (p *metaProcessorVersionInfo) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := MetaVersionInfoArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("versionInfo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := MetaVersionInfoResult{} + var retval *VersionInfo + var err2 error + if retval, err2 = p.handler.VersionInfo(); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing versionInfo: "+err2.Error()) + oprot.WriteMessageBegin("versionInfo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("versionInfo", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +type MetaHealthArgs struct { +} + +func NewMetaHealthArgs() *MetaHealthArgs { + return &MetaHealthArgs{} +} + +func (p *MetaHealthArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaHealthArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("health_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaHealthArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaHealthArgs(%+v)", *p) +} + +// Attributes: +// - Success +type MetaHealthResult struct { + Success *HealthStatus `thrift:"success,0" json:"success,omitempty"` +} + +func NewMetaHealthResult() *MetaHealthResult { + return &MetaHealthResult{} +} + +var MetaHealthResult_Success_DEFAULT *HealthStatus + +func (p *MetaHealthResult) GetSuccess() *HealthStatus { + if !p.IsSetSuccess() { + return MetaHealthResult_Success_DEFAULT + } + return p.Success +} +func (p *MetaHealthResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *MetaHealthResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaHealthResult) readField0(iprot thrift.TProtocol) error { + p.Success = &HealthStatus{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *MetaHealthResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("health_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaHealthResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *MetaHealthResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaHealthResult(%+v)", *p) +} + +type MetaThriftIDLArgs struct { +} + +func NewMetaThriftIDLArgs() *MetaThriftIDLArgs { + return &MetaThriftIDLArgs{} +} + +func (p *MetaThriftIDLArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaThriftIDLArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("thriftIDL_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaThriftIDLArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaThriftIDLArgs(%+v)", *p) +} + +// Attributes: +// - Success +type MetaThriftIDLResult struct { + Success *ThriftIDLs `thrift:"success,0" json:"success,omitempty"` +} + +func NewMetaThriftIDLResult() *MetaThriftIDLResult { + return &MetaThriftIDLResult{} +} + +var MetaThriftIDLResult_Success_DEFAULT *ThriftIDLs + +func (p *MetaThriftIDLResult) GetSuccess() *ThriftIDLs { + if !p.IsSetSuccess() { + return MetaThriftIDLResult_Success_DEFAULT + } + return p.Success +} +func (p *MetaThriftIDLResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *MetaThriftIDLResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaThriftIDLResult) readField0(iprot thrift.TProtocol) error { + p.Success = &ThriftIDLs{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *MetaThriftIDLResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("thriftIDL_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaThriftIDLResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *MetaThriftIDLResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaThriftIDLResult(%+v)", *p) +} + +type MetaVersionInfoArgs struct { +} + +func NewMetaVersionInfoArgs() *MetaVersionInfoArgs { + return &MetaVersionInfoArgs{} +} + +func (p *MetaVersionInfoArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaVersionInfoArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("versionInfo_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaVersionInfoArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaVersionInfoArgs(%+v)", *p) +} + +// Attributes: +// - Success +type MetaVersionInfoResult struct { + Success *VersionInfo `thrift:"success,0" json:"success,omitempty"` +} + +func NewMetaVersionInfoResult() *MetaVersionInfoResult { + return &MetaVersionInfoResult{} +} + +var MetaVersionInfoResult_Success_DEFAULT *VersionInfo + +func (p *MetaVersionInfoResult) GetSuccess() *VersionInfo { + if !p.IsSetSuccess() { + return MetaVersionInfoResult_Success_DEFAULT + } + return p.Success +} +func (p *MetaVersionInfoResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *MetaVersionInfoResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.readField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *MetaVersionInfoResult) readField0(iprot thrift.TProtocol) error { + p.Success = &VersionInfo{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *MetaVersionInfoResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("versionInfo_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *MetaVersionInfoResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *MetaVersionInfoResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("MetaVersionInfoResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/ttypes.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/ttypes.go new file mode 100644 index 00000000..50686a8a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/meta/ttypes.go @@ -0,0 +1,510 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package meta + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +type Filename string + +func FilenamePtr(v Filename) *Filename { return &v } + +// Attributes: +// - Ok +// - Message +type HealthStatus struct { + Ok bool `thrift:"ok,1,required" json:"ok"` + Message *string `thrift:"message,2" json:"message,omitempty"` +} + +func NewHealthStatus() *HealthStatus { + return &HealthStatus{} +} + +func (p *HealthStatus) GetOk() bool { + return p.Ok +} + +var HealthStatus_Message_DEFAULT string + +func (p *HealthStatus) GetMessage() string { + if !p.IsSetMessage() { + return HealthStatus_Message_DEFAULT + } + return *p.Message +} +func (p *HealthStatus) IsSetMessage() bool { + return p.Message != nil +} + +func (p *HealthStatus) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetOk = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) + } + return nil +} + +func (p *HealthStatus) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Ok = v + } + return nil +} + +func (p *HealthStatus) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.Message = &v + } + return nil +} + +func (p *HealthStatus) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("HealthStatus"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *HealthStatus) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) + } + if err := oprot.WriteBool(bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) + } + return err +} + +func (p *HealthStatus) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetMessage() { + if err := oprot.WriteFieldBegin("message", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:message: ", p), err) + } + if err := oprot.WriteString(string(*p.Message)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.message (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:message: ", p), err) + } + } + return err +} + +func (p *HealthStatus) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("HealthStatus(%+v)", *p) +} + +// Attributes: +// - Idls +// - EntryPoint +type ThriftIDLs struct { + Idls map[Filename]string `thrift:"idls,1,required" json:"idls"` + EntryPoint Filename `thrift:"entryPoint,2,required" json:"entryPoint"` +} + +func NewThriftIDLs() *ThriftIDLs { + return &ThriftIDLs{} +} + +func (p *ThriftIDLs) GetIdls() map[Filename]string { + return p.Idls +} + +func (p *ThriftIDLs) GetEntryPoint() Filename { + return p.EntryPoint +} +func (p *ThriftIDLs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetIdls bool = false + var issetEntryPoint bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetIdls = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetEntryPoint = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetIdls { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Idls is not set")) + } + if !issetEntryPoint { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field EntryPoint is not set")) + } + return nil +} + +func (p *ThriftIDLs) readField1(iprot thrift.TProtocol) error { + _, _, size, err := iprot.ReadMapBegin() + if err != nil { + return thrift.PrependError("error reading map begin: ", err) + } + tMap := make(map[Filename]string, size) + p.Idls = tMap + for i := 0; i < size; i++ { + var _key0 Filename + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + temp := Filename(v) + _key0 = temp + } + var _val1 string + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + _val1 = v + } + p.Idls[_key0] = _val1 + } + if err := iprot.ReadMapEnd(); err != nil { + return thrift.PrependError("error reading map end: ", err) + } + return nil +} + +func (p *ThriftIDLs) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + temp := Filename(v) + p.EntryPoint = temp + } + return nil +} + +func (p *ThriftIDLs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("ThriftIDLs"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *ThriftIDLs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("idls", thrift.MAP, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:idls: ", p), err) + } + if err := oprot.WriteMapBegin(thrift.STRING, thrift.STRING, len(p.Idls)); err != nil { + return thrift.PrependError("error writing map begin: ", err) + } + for k, v := range p.Idls { + if err := oprot.WriteString(string(k)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) + } + if err := oprot.WriteString(string(v)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) + } + } + if err := oprot.WriteMapEnd(); err != nil { + return thrift.PrependError("error writing map end: ", err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:idls: ", p), err) + } + return err +} + +func (p *ThriftIDLs) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("entryPoint", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:entryPoint: ", p), err) + } + if err := oprot.WriteString(string(p.EntryPoint)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.entryPoint (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:entryPoint: ", p), err) + } + return err +} + +func (p *ThriftIDLs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ThriftIDLs(%+v)", *p) +} + +// Attributes: +// - Language +// - LanguageVersion +// - Version +type VersionInfo struct { + Language string `thrift:"language,1,required" json:"language"` + LanguageVersion string `thrift:"language_version,2,required" json:"language_version"` + Version string `thrift:"version,3,required" json:"version"` +} + +func NewVersionInfo() *VersionInfo { + return &VersionInfo{} +} + +func (p *VersionInfo) GetLanguage() string { + return p.Language +} + +func (p *VersionInfo) GetLanguageVersion() string { + return p.LanguageVersion +} + +func (p *VersionInfo) GetVersion() string { + return p.Version +} +func (p *VersionInfo) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetLanguage bool = false + var issetLanguageVersion bool = false + var issetVersion bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.readField1(iprot); err != nil { + return err + } + issetLanguage = true + case 2: + if err := p.readField2(iprot); err != nil { + return err + } + issetLanguageVersion = true + case 3: + if err := p.readField3(iprot); err != nil { + return err + } + issetVersion = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetLanguage { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Language is not set")) + } + if !issetLanguageVersion { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field LanguageVersion is not set")) + } + if !issetVersion { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Version is not set")) + } + return nil +} + +func (p *VersionInfo) readField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Language = v + } + return nil +} + +func (p *VersionInfo) readField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.LanguageVersion = v + } + return nil +} + +func (p *VersionInfo) readField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.Version = v + } + return nil +} + +func (p *VersionInfo) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("VersionInfo"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *VersionInfo) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("language", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:language: ", p), err) + } + if err := oprot.WriteString(string(p.Language)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.language (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:language: ", p), err) + } + return err +} + +func (p *VersionInfo) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("language_version", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:language_version: ", p), err) + } + if err := oprot.WriteString(string(p.LanguageVersion)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.language_version (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:language_version: ", p), err) + } + return err +} + +func (p *VersionInfo) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("version", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:version: ", p), err) + } + if err := oprot.WriteString(string(p.Version)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.version (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:version: ", p), err) + } + return err +} + +func (p *VersionInfo) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("VersionInfo(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/constants.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/constants.go new file mode 100644 index 00000000..a84ae302 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/constants.go @@ -0,0 +1,18 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package test + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +func init() { +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/secondservice.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/secondservice.go new file mode 100644 index 00000000..b80329c5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/secondservice.go @@ -0,0 +1,411 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package test + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type SecondService interface { + // Parameters: + // - Arg + Echo(arg string) (r string, err error) +} + +type SecondServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSecondServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SecondServiceClient { + return &SecondServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSecondServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SecondServiceClient { + return &SecondServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Arg +func (p *SecondServiceClient) Echo(arg string) (r string, err error) { + if err = p.sendEcho(arg); err != nil { + return + } + return p.recvEcho() +} + +func (p *SecondServiceClient) sendEcho(arg string) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Echo", thrift.CALL, p.SeqId); err != nil { + return + } + args := SecondServiceEchoArgs{ + Arg: arg, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SecondServiceClient) recvEcho() (value string, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Echo" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Echo failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Echo failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error14 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error15 error + error15, err = error14.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error15 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Echo failed: invalid message type") + return + } + result := SecondServiceEchoResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +type SecondServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler SecondService +} + +func (p *SecondServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SecondServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SecondServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSecondServiceProcessor(handler SecondService) *SecondServiceProcessor { + + self16 := &SecondServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self16.processorMap["Echo"] = &secondServiceProcessorEcho{handler: handler} + return self16 +} + +func (p *SecondServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x17 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x17.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x17 + +} + +type secondServiceProcessorEcho struct { + handler SecondService +} + +func (p *secondServiceProcessorEcho) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SecondServiceEchoArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Echo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SecondServiceEchoResult{} + var retval string + var err2 error + if retval, err2 = p.handler.Echo(args.Arg); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Echo: "+err2.Error()) + oprot.WriteMessageBegin("Echo", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = &retval + } + if err2 = oprot.WriteMessageBegin("Echo", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Arg +type SecondServiceEchoArgs struct { + Arg string `thrift:"arg,1" db:"arg" json:"arg"` +} + +func NewSecondServiceEchoArgs() *SecondServiceEchoArgs { + return &SecondServiceEchoArgs{} +} + +func (p *SecondServiceEchoArgs) GetArg() string { + return p.Arg +} +func (p *SecondServiceEchoArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SecondServiceEchoArgs) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Arg = v + } + return nil +} + +func (p *SecondServiceEchoArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Echo_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SecondServiceEchoArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("arg", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:arg: ", p), err) + } + if err := oprot.WriteString(string(p.Arg)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.arg (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:arg: ", p), err) + } + return err +} + +func (p *SecondServiceEchoArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SecondServiceEchoArgs(%+v)", *p) +} + +// Attributes: +// - Success +type SecondServiceEchoResult struct { + Success *string `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewSecondServiceEchoResult() *SecondServiceEchoResult { + return &SecondServiceEchoResult{} +} + +var SecondServiceEchoResult_Success_DEFAULT string + +func (p *SecondServiceEchoResult) GetSuccess() string { + if !p.IsSetSuccess() { + return SecondServiceEchoResult_Success_DEFAULT + } + return *p.Success +} +func (p *SecondServiceEchoResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *SecondServiceEchoResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SecondServiceEchoResult) ReadField0(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 0: ", err) + } else { + p.Success = &v + } + return nil +} + +func (p *SecondServiceEchoResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Echo_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SecondServiceEchoResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRING, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := oprot.WriteString(string(*p.Success)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.success (0) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *SecondServiceEchoResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SecondServiceEchoResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/simpleservice.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/simpleservice.go new file mode 100644 index 00000000..b7da3c36 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/simpleservice.go @@ -0,0 +1,1032 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package test + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +type SimpleService interface { + // Parameters: + // - Arg + Call(arg *Data) (r *Data, err error) + Simple() (err error) + SimpleFuture() (err error) +} + +type SimpleServiceClient struct { + Transport thrift.TTransport + ProtocolFactory thrift.TProtocolFactory + InputProtocol thrift.TProtocol + OutputProtocol thrift.TProtocol + SeqId int32 +} + +func NewSimpleServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *SimpleServiceClient { + return &SimpleServiceClient{Transport: t, + ProtocolFactory: f, + InputProtocol: f.GetProtocol(t), + OutputProtocol: f.GetProtocol(t), + SeqId: 0, + } +} + +func NewSimpleServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *SimpleServiceClient { + return &SimpleServiceClient{Transport: t, + ProtocolFactory: nil, + InputProtocol: iprot, + OutputProtocol: oprot, + SeqId: 0, + } +} + +// Parameters: +// - Arg +func (p *SimpleServiceClient) Call(arg *Data) (r *Data, err error) { + if err = p.sendCall(arg); err != nil { + return + } + return p.recvCall() +} + +func (p *SimpleServiceClient) sendCall(arg *Data) (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Call", thrift.CALL, p.SeqId); err != nil { + return + } + args := SimpleServiceCallArgs{ + Arg: arg, + } + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SimpleServiceClient) recvCall() (value *Data, err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Call" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Call failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Call failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error1 error + error1, err = error0.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error1 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Call failed: invalid message type") + return + } + result := SimpleServiceCallResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + value = result.GetSuccess() + return +} + +func (p *SimpleServiceClient) Simple() (err error) { + if err = p.sendSimple(); err != nil { + return + } + return p.recvSimple() +} + +func (p *SimpleServiceClient) sendSimple() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("Simple", thrift.CALL, p.SeqId); err != nil { + return + } + args := SimpleServiceSimpleArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SimpleServiceClient) recvSimple() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "Simple" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Simple failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Simple failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error3 error + error3, err = error2.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error3 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Simple failed: invalid message type") + return + } + result := SimpleServiceSimpleResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.SimpleErr != nil { + err = result.SimpleErr + return + } + return +} + +func (p *SimpleServiceClient) SimpleFuture() (err error) { + if err = p.sendSimpleFuture(); err != nil { + return + } + return p.recvSimpleFuture() +} + +func (p *SimpleServiceClient) sendSimpleFuture() (err error) { + oprot := p.OutputProtocol + if oprot == nil { + oprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.OutputProtocol = oprot + } + p.SeqId++ + if err = oprot.WriteMessageBegin("SimpleFuture", thrift.CALL, p.SeqId); err != nil { + return + } + args := SimpleServiceSimpleFutureArgs{} + if err = args.Write(oprot); err != nil { + return + } + if err = oprot.WriteMessageEnd(); err != nil { + return + } + return oprot.Flush() +} + +func (p *SimpleServiceClient) recvSimpleFuture() (err error) { + iprot := p.InputProtocol + if iprot == nil { + iprot = p.ProtocolFactory.GetProtocol(p.Transport) + p.InputProtocol = iprot + } + method, mTypeId, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return + } + if method != "SimpleFuture" { + err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "SimpleFuture failed: wrong method name") + return + } + if p.SeqId != seqId { + err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "SimpleFuture failed: out of sequence response") + return + } + if mTypeId == thrift.EXCEPTION { + error4 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") + var error5 error + error5, err = error4.Read(iprot) + if err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + err = error5 + return + } + if mTypeId != thrift.REPLY { + err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "SimpleFuture failed: invalid message type") + return + } + result := SimpleServiceSimpleFutureResult{} + if err = result.Read(iprot); err != nil { + return + } + if err = iprot.ReadMessageEnd(); err != nil { + return + } + if result.SimpleErr != nil { + err = result.SimpleErr + return + } else if result.NewErr_ != nil { + err = result.NewErr_ + return + } + return +} + +type SimpleServiceProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler SimpleService +} + +func (p *SimpleServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *SimpleServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *SimpleServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewSimpleServiceProcessor(handler SimpleService) *SimpleServiceProcessor { + + self6 := &SimpleServiceProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} + self6.processorMap["Call"] = &simpleServiceProcessorCall{handler: handler} + self6.processorMap["Simple"] = &simpleServiceProcessorSimple{handler: handler} + self6.processorMap["SimpleFuture"] = &simpleServiceProcessorSimpleFuture{handler: handler} + return self6 +} + +func (p *SimpleServiceProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err := iprot.ReadMessageBegin() + if err != nil { + return false, err + } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(seqId, iprot, oprot) + } + iprot.Skip(thrift.STRUCT) + iprot.ReadMessageEnd() + x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) + oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) + x7.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, x7 + +} + +type simpleServiceProcessorCall struct { + handler SimpleService +} + +func (p *simpleServiceProcessorCall) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SimpleServiceCallArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Call", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SimpleServiceCallResult{} + var retval *Data + var err2 error + if retval, err2 = p.handler.Call(args.Arg); err2 != nil { + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Call: "+err2.Error()) + oprot.WriteMessageBegin("Call", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } else { + result.Success = retval + } + if err2 = oprot.WriteMessageBegin("Call", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type simpleServiceProcessorSimple struct { + handler SimpleService +} + +func (p *simpleServiceProcessorSimple) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SimpleServiceSimpleArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("Simple", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SimpleServiceSimpleResult{} + var err2 error + if err2 = p.handler.Simple(); err2 != nil { + switch v := err2.(type) { + case *SimpleErr: + result.SimpleErr = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Simple: "+err2.Error()) + oprot.WriteMessageBegin("Simple", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } + if err2 = oprot.WriteMessageBegin("Simple", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +type simpleServiceProcessorSimpleFuture struct { + handler SimpleService +} + +func (p *simpleServiceProcessorSimpleFuture) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := SimpleServiceSimpleFutureArgs{} + if err = args.Read(iprot); err != nil { + iprot.ReadMessageEnd() + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) + oprot.WriteMessageBegin("SimpleFuture", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return false, err + } + + iprot.ReadMessageEnd() + result := SimpleServiceSimpleFutureResult{} + var err2 error + if err2 = p.handler.SimpleFuture(); err2 != nil { + switch v := err2.(type) { + case *SimpleErr: + result.SimpleErr = v + case *NewErr_: + result.NewErr_ = v + default: + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing SimpleFuture: "+err2.Error()) + oprot.WriteMessageBegin("SimpleFuture", thrift.EXCEPTION, seqId) + x.Write(oprot) + oprot.WriteMessageEnd() + oprot.Flush() + return true, err2 + } + } + if err2 = oprot.WriteMessageBegin("SimpleFuture", thrift.REPLY, seqId); err2 != nil { + err = err2 + } + if err2 = result.Write(oprot); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { + err = err2 + } + if err2 = oprot.Flush(); err == nil && err2 != nil { + err = err2 + } + if err != nil { + return + } + return true, err +} + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Arg +type SimpleServiceCallArgs struct { + Arg *Data `thrift:"arg,1" db:"arg" json:"arg"` +} + +func NewSimpleServiceCallArgs() *SimpleServiceCallArgs { + return &SimpleServiceCallArgs{} +} + +var SimpleServiceCallArgs_Arg_DEFAULT *Data + +func (p *SimpleServiceCallArgs) GetArg() *Data { + if !p.IsSetArg() { + return SimpleServiceCallArgs_Arg_DEFAULT + } + return p.Arg +} +func (p *SimpleServiceCallArgs) IsSetArg() bool { + return p.Arg != nil +} + +func (p *SimpleServiceCallArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceCallArgs) ReadField1(iprot thrift.TProtocol) error { + p.Arg = &Data{} + if err := p.Arg.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Arg), err) + } + return nil +} + +func (p *SimpleServiceCallArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Call_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceCallArgs) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("arg", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:arg: ", p), err) + } + if err := p.Arg.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Arg), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:arg: ", p), err) + } + return err +} + +func (p *SimpleServiceCallArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceCallArgs(%+v)", *p) +} + +// Attributes: +// - Success +type SimpleServiceCallResult struct { + Success *Data `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewSimpleServiceCallResult() *SimpleServiceCallResult { + return &SimpleServiceCallResult{} +} + +var SimpleServiceCallResult_Success_DEFAULT *Data + +func (p *SimpleServiceCallResult) GetSuccess() *Data { + if !p.IsSetSuccess() { + return SimpleServiceCallResult_Success_DEFAULT + } + return p.Success +} +func (p *SimpleServiceCallResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *SimpleServiceCallResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 0: + if err := p.ReadField0(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceCallResult) ReadField0(iprot thrift.TProtocol) error { + p.Success = &Data{} + if err := p.Success.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Success), err) + } + return nil +} + +func (p *SimpleServiceCallResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Call_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField0(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceCallResult) writeField0(oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin("success", thrift.STRUCT, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) + } + if err := p.Success.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Success), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) + } + } + return err +} + +func (p *SimpleServiceCallResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceCallResult(%+v)", *p) +} + +type SimpleServiceSimpleArgs struct { +} + +func NewSimpleServiceSimpleArgs() *SimpleServiceSimpleArgs { + return &SimpleServiceSimpleArgs{} +} + +func (p *SimpleServiceSimpleArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceSimpleArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Simple_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceSimpleArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceSimpleArgs(%+v)", *p) +} + +// Attributes: +// - SimpleErr +type SimpleServiceSimpleResult struct { + SimpleErr *SimpleErr `thrift:"simpleErr,1" db:"simpleErr" json:"simpleErr,omitempty"` +} + +func NewSimpleServiceSimpleResult() *SimpleServiceSimpleResult { + return &SimpleServiceSimpleResult{} +} + +var SimpleServiceSimpleResult_SimpleErr_DEFAULT *SimpleErr + +func (p *SimpleServiceSimpleResult) GetSimpleErr() *SimpleErr { + if !p.IsSetSimpleErr() { + return SimpleServiceSimpleResult_SimpleErr_DEFAULT + } + return p.SimpleErr +} +func (p *SimpleServiceSimpleResult) IsSetSimpleErr() bool { + return p.SimpleErr != nil +} + +func (p *SimpleServiceSimpleResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceSimpleResult) ReadField1(iprot thrift.TProtocol) error { + p.SimpleErr = &SimpleErr{} + if err := p.SimpleErr.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.SimpleErr), err) + } + return nil +} + +func (p *SimpleServiceSimpleResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Simple_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceSimpleResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetSimpleErr() { + if err := oprot.WriteFieldBegin("simpleErr", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:simpleErr: ", p), err) + } + if err := p.SimpleErr.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.SimpleErr), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:simpleErr: ", p), err) + } + } + return err +} + +func (p *SimpleServiceSimpleResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceSimpleResult(%+v)", *p) +} + +type SimpleServiceSimpleFutureArgs struct { +} + +func NewSimpleServiceSimpleFutureArgs() *SimpleServiceSimpleFutureArgs { + return &SimpleServiceSimpleFutureArgs{} +} + +func (p *SimpleServiceSimpleFutureArgs) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureArgs) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SimpleFuture_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceSimpleFutureArgs(%+v)", *p) +} + +// Attributes: +// - SimpleErr +// - NewErr_ +type SimpleServiceSimpleFutureResult struct { + SimpleErr *SimpleErr `thrift:"simpleErr,1" db:"simpleErr" json:"simpleErr,omitempty"` + NewErr_ *NewErr_ `thrift:"newErr,2" db:"newErr" json:"newErr,omitempty"` +} + +func NewSimpleServiceSimpleFutureResult() *SimpleServiceSimpleFutureResult { + return &SimpleServiceSimpleFutureResult{} +} + +var SimpleServiceSimpleFutureResult_SimpleErr_DEFAULT *SimpleErr + +func (p *SimpleServiceSimpleFutureResult) GetSimpleErr() *SimpleErr { + if !p.IsSetSimpleErr() { + return SimpleServiceSimpleFutureResult_SimpleErr_DEFAULT + } + return p.SimpleErr +} + +var SimpleServiceSimpleFutureResult_NewErr__DEFAULT *NewErr_ + +func (p *SimpleServiceSimpleFutureResult) GetNewErr_() *NewErr_ { + if !p.IsSetNewErr_() { + return SimpleServiceSimpleFutureResult_NewErr__DEFAULT + } + return p.NewErr_ +} +func (p *SimpleServiceSimpleFutureResult) IsSetSimpleErr() bool { + return p.SimpleErr != nil +} + +func (p *SimpleServiceSimpleFutureResult) IsSetNewErr_() bool { + return p.NewErr_ != nil +} + +func (p *SimpleServiceSimpleFutureResult) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureResult) ReadField1(iprot thrift.TProtocol) error { + p.SimpleErr = &SimpleErr{} + if err := p.SimpleErr.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.SimpleErr), err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureResult) ReadField2(iprot thrift.TProtocol) error { + p.NewErr_ = &NewErr_{} + if err := p.NewErr_.Read(iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.NewErr_), err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureResult) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SimpleFuture_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleServiceSimpleFutureResult) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetSimpleErr() { + if err := oprot.WriteFieldBegin("simpleErr", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:simpleErr: ", p), err) + } + if err := p.SimpleErr.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.SimpleErr), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:simpleErr: ", p), err) + } + } + return err +} + +func (p *SimpleServiceSimpleFutureResult) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetNewErr_() { + if err := oprot.WriteFieldBegin("newErr", thrift.STRUCT, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:newErr: ", p), err) + } + if err := p.NewErr_.Write(oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.NewErr_), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:newErr: ", p), err) + } + } + return err +} + +func (p *SimpleServiceSimpleFutureResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleServiceSimpleFutureResult(%+v)", *p) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/tchan-test.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/tchan-test.go new file mode 100644 index 00000000..fe2a6481 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/tchan-test.go @@ -0,0 +1,296 @@ +// @generated Code generated by thrift-gen. Do not modify. + +// Package test is generated code used to make or handle TChannel calls using Thrift. +package test + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + "github.com/uber/tchannel-go/thrift" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// TChanSecondService is the interface that defines the server handler and client interface. +type TChanSecondService interface { + Echo(ctx thrift.Context, arg string) (string, error) +} + +// TChanSimpleService is the interface that defines the server handler and client interface. +type TChanSimpleService interface { + Call(ctx thrift.Context, arg *Data) (*Data, error) + Simple(ctx thrift.Context) error + SimpleFuture(ctx thrift.Context) error +} + +// Implementation of a client and service handler. + +type tchanSecondServiceClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanSecondServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanSecondServiceClient { + return &tchanSecondServiceClient{ + thriftService, + client, + } +} + +// NewTChanSecondServiceClient creates a client that can be used to make remote calls. +func NewTChanSecondServiceClient(client thrift.TChanClient) TChanSecondService { + return NewTChanSecondServiceInheritedClient("SecondService", client) +} + +func (c *tchanSecondServiceClient) Echo(ctx thrift.Context, arg string) (string, error) { + var resp SecondServiceEchoResult + args := SecondServiceEchoArgs{ + Arg: arg, + } + success, err := c.client.Call(ctx, c.thriftService, "Echo", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for Echo") + } + } + + return resp.GetSuccess(), err +} + +type tchanSecondServiceServer struct { + handler TChanSecondService +} + +// NewTChanSecondServiceServer wraps a handler for TChanSecondService so it can be +// registered with a thrift.Server. +func NewTChanSecondServiceServer(handler TChanSecondService) thrift.TChanServer { + return &tchanSecondServiceServer{ + handler, + } +} + +func (s *tchanSecondServiceServer) Service() string { + return "SecondService" +} + +func (s *tchanSecondServiceServer) Methods() []string { + return []string{ + "Echo", + } +} + +func (s *tchanSecondServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "Echo": + return s.handleEcho(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanSecondServiceServer) handleEcho(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req SecondServiceEchoArgs + var res SecondServiceEchoResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Echo(ctx, req.Arg) + + if err != nil { + return false, nil, err + } else { + res.Success = &r + } + + return err == nil, &res, nil +} + +type tchanSimpleServiceClient struct { + thriftService string + client thrift.TChanClient +} + +func NewTChanSimpleServiceInheritedClient(thriftService string, client thrift.TChanClient) *tchanSimpleServiceClient { + return &tchanSimpleServiceClient{ + thriftService, + client, + } +} + +// NewTChanSimpleServiceClient creates a client that can be used to make remote calls. +func NewTChanSimpleServiceClient(client thrift.TChanClient) TChanSimpleService { + return NewTChanSimpleServiceInheritedClient("SimpleService", client) +} + +func (c *tchanSimpleServiceClient) Call(ctx thrift.Context, arg *Data) (*Data, error) { + var resp SimpleServiceCallResult + args := SimpleServiceCallArgs{ + Arg: arg, + } + success, err := c.client.Call(ctx, c.thriftService, "Call", &args, &resp) + if err == nil && !success { + switch { + default: + err = fmt.Errorf("received no result or unknown exception for Call") + } + } + + return resp.GetSuccess(), err +} + +func (c *tchanSimpleServiceClient) Simple(ctx thrift.Context) error { + var resp SimpleServiceSimpleResult + args := SimpleServiceSimpleArgs{} + success, err := c.client.Call(ctx, c.thriftService, "Simple", &args, &resp) + if err == nil && !success { + switch { + case resp.SimpleErr != nil: + err = resp.SimpleErr + default: + err = fmt.Errorf("received no result or unknown exception for Simple") + } + } + + return err +} + +func (c *tchanSimpleServiceClient) SimpleFuture(ctx thrift.Context) error { + var resp SimpleServiceSimpleFutureResult + args := SimpleServiceSimpleFutureArgs{} + success, err := c.client.Call(ctx, c.thriftService, "SimpleFuture", &args, &resp) + if err == nil && !success { + switch { + case resp.SimpleErr != nil: + err = resp.SimpleErr + case resp.NewErr_ != nil: + err = resp.NewErr_ + default: + err = fmt.Errorf("received no result or unknown exception for SimpleFuture") + } + } + + return err +} + +type tchanSimpleServiceServer struct { + handler TChanSimpleService +} + +// NewTChanSimpleServiceServer wraps a handler for TChanSimpleService so it can be +// registered with a thrift.Server. +func NewTChanSimpleServiceServer(handler TChanSimpleService) thrift.TChanServer { + return &tchanSimpleServiceServer{ + handler, + } +} + +func (s *tchanSimpleServiceServer) Service() string { + return "SimpleService" +} + +func (s *tchanSimpleServiceServer) Methods() []string { + return []string{ + "Call", + "Simple", + "SimpleFuture", + } +} + +func (s *tchanSimpleServiceServer) Handle(ctx thrift.Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "Call": + return s.handleCall(ctx, protocol) + case "Simple": + return s.handleSimple(ctx, protocol) + case "SimpleFuture": + return s.handleSimpleFuture(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanSimpleServiceServer) handleCall(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req SimpleServiceCallArgs + var res SimpleServiceCallResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Call(ctx, req.Arg) + + if err != nil { + return false, nil, err + } else { + res.Success = r + } + + return err == nil, &res, nil +} + +func (s *tchanSimpleServiceServer) handleSimple(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req SimpleServiceSimpleArgs + var res SimpleServiceSimpleResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.Simple(ctx) + + if err != nil { + switch v := err.(type) { + case *SimpleErr: + if v == nil { + return false, nil, fmt.Errorf("Handler for simpleErr returned non-nil error type *SimpleErr but nil value") + } + res.SimpleErr = v + default: + return false, nil, err + } + } else { + } + + return err == nil, &res, nil +} + +func (s *tchanSimpleServiceServer) handleSimpleFuture(ctx thrift.Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req SimpleServiceSimpleFutureArgs + var res SimpleServiceSimpleFutureResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + err := + s.handler.SimpleFuture(ctx) + + if err != nil { + switch v := err.(type) { + case *SimpleErr: + if v == nil { + return false, nil, fmt.Errorf("Handler for simpleErr returned non-nil error type *SimpleErr but nil value") + } + res.SimpleErr = v + case *NewErr_: + if v == nil { + return false, nil, fmt.Errorf("Handler for newErr returned non-nil error type *NewErr_ but nil value") + } + res.NewErr_ = v + default: + return false, nil, err + } + } else { + } + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/ttypes.go b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/ttypes.go new file mode 100644 index 00000000..4b952e9b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/gen-go/test/ttypes.go @@ -0,0 +1,384 @@ +// Autogenerated by Thrift Compiler (1.0.0-dev) +// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + +package test + +import ( + "bytes" + "fmt" + "github.com/apache/thrift/lib/go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = bytes.Equal + +var GoUnusedProtection__ int + +// Attributes: +// - B1 +// - S2 +// - I3 +type Data struct { + B1 bool `thrift:"b1,1,required" db:"b1" json:"b1"` + S2 string `thrift:"s2,2,required" db:"s2" json:"s2"` + I3 int32 `thrift:"i3,3,required" db:"i3" json:"i3"` +} + +func NewData() *Data { + return &Data{} +} + +func (p *Data) GetB1() bool { + return p.B1 +} + +func (p *Data) GetS2() string { + return p.S2 +} + +func (p *Data) GetI3() int32 { + return p.I3 +} +func (p *Data) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetB1 bool = false + var issetS2 bool = false + var issetI3 bool = false + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + issetB1 = true + case 2: + if err := p.ReadField2(iprot); err != nil { + return err + } + issetS2 = true + case 3: + if err := p.ReadField3(iprot); err != nil { + return err + } + issetI3 = true + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetB1 { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field B1 is not set")) + } + if !issetS2 { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field S2 is not set")) + } + if !issetI3 { + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field I3 is not set")) + } + return nil +} + +func (p *Data) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.B1 = v + } + return nil +} + +func (p *Data) ReadField2(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 2: ", err) + } else { + p.S2 = v + } + return nil +} + +func (p *Data) ReadField3(iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(); err != nil { + return thrift.PrependError("error reading field 3: ", err) + } else { + p.I3 = v + } + return nil +} + +func (p *Data) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("Data"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := p.writeField2(oprot); err != nil { + return err + } + if err := p.writeField3(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *Data) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("b1", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:b1: ", p), err) + } + if err := oprot.WriteBool(bool(p.B1)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.b1 (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:b1: ", p), err) + } + return err +} + +func (p *Data) writeField2(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("s2", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:s2: ", p), err) + } + if err := oprot.WriteString(string(p.S2)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.s2 (2) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:s2: ", p), err) + } + return err +} + +func (p *Data) writeField3(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("i3", thrift.I32, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:i3: ", p), err) + } + if err := oprot.WriteI32(int32(p.I3)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.i3 (3) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:i3: ", p), err) + } + return err +} + +func (p *Data) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Data(%+v)", *p) +} + +// Attributes: +// - Message +type SimpleErr struct { + Message string `thrift:"message,1" db:"message" json:"message"` +} + +func NewSimpleErr() *SimpleErr { + return &SimpleErr{} +} + +func (p *SimpleErr) GetMessage() string { + return p.Message +} +func (p *SimpleErr) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *SimpleErr) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Message = v + } + return nil +} + +func (p *SimpleErr) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("SimpleErr"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *SimpleErr) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("message", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:message: ", p), err) + } + if err := oprot.WriteString(string(p.Message)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.message (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:message: ", p), err) + } + return err +} + +func (p *SimpleErr) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SimpleErr(%+v)", *p) +} + +func (p *SimpleErr) Error() string { + return p.String() +} + +// Attributes: +// - Message +type NewErr_ struct { + Message string `thrift:"message,1" db:"message" json:"message"` +} + +func NewNewErr_() *NewErr_ { + return &NewErr_{} +} + +func (p *NewErr_) GetMessage() string { + return p.Message +} +func (p *NewErr_) Read(iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if err := p.ReadField1(iprot); err != nil { + return err + } + default: + if err := iprot.Skip(fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *NewErr_) ReadField1(iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(); err != nil { + return thrift.PrependError("error reading field 1: ", err) + } else { + p.Message = v + } + return nil +} + +func (p *NewErr_) Write(oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin("NewErr"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) + } + if err := p.writeField1(oprot); err != nil { + return err + } + if err := oprot.WriteFieldStop(); err != nil { + return thrift.PrependError("write field stop error: ", err) + } + if err := oprot.WriteStructEnd(); err != nil { + return thrift.PrependError("write struct stop error: ", err) + } + return nil +} + +func (p *NewErr_) writeField1(oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin("message", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:message: ", p), err) + } + if err := oprot.WriteString(string(p.Message)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.message (1) field write error: ", p), err) + } + if err := oprot.WriteFieldEnd(); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:message: ", p), err) + } + return err +} + +func (p *NewErr_) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("NewErr_(%+v)", *p) +} + +func (p *NewErr_) Error() string { + return p.String() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/headers.go b/vendor/src/github.com/uber/tchannel-go/thrift/headers.go new file mode 100644 index 00000000..05becce3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/headers.go @@ -0,0 +1,90 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "fmt" + "io" + + "github.com/uber/tchannel-go/typed" +) + +// WriteHeaders writes the given key-value pairs using the following encoding: +// len~2 (k~4 v~4)~len +func WriteHeaders(w io.Writer, headers map[string]string) error { + // TODO(prashant): Since we are not writing length-prefixed data here, + // we can write out to the buffer, and if it fills up, flush it. + // Right now, we calculate the size of the required buffer and write it out. + + // Calculate the size of the buffer that we need. + size := 2 + for k, v := range headers { + size += 4 /* size of key/value lengths */ + size += len(k) + len(v) + } + + buf := make([]byte, size) + writeBuffer := typed.NewWriteBuffer(buf) + writeBuffer.WriteUint16(uint16(len(headers))) + for k, v := range headers { + writeBuffer.WriteLen16String(k) + writeBuffer.WriteLen16String(v) + } + + if err := writeBuffer.Err(); err != nil { + return err + } + + // Safety check to ensure the bytes written calculation is correct. + if writeBuffer.BytesWritten() != size { + return fmt.Errorf( + "writeHeaders size calculation wrong, expected to write %v bytes, only wrote %v bytes", + size, writeBuffer.BytesWritten()) + } + + _, err := writeBuffer.FlushTo(w) + return err +} + +func readHeaders(reader *typed.Reader) (map[string]string, error) { + numHeaders := reader.ReadUint16() + if numHeaders == 0 { + return nil, reader.Err() + } + + headers := make(map[string]string, numHeaders) + for i := 0; i < int(numHeaders) && reader.Err() == nil; i++ { + k := reader.ReadLen16String() + v := reader.ReadLen16String() + headers[k] = v + } + + return headers, reader.Err() +} + +// ReadHeaders reads key-value pairs encoded using WriteHeaders. +func ReadHeaders(r io.Reader) (map[string]string, error) { + reader := typed.NewReader(r) + m, err := readHeaders(reader) + reader.Release() + + return m, err +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/headers_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/headers_test.go new file mode 100644 index 00000000..968bdcec --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/headers_test.go @@ -0,0 +1,174 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "bytes" + "io/ioutil" + "testing" + "testing/iotest" + + "github.com/stretchr/testify/assert" +) + +var headers = map[string]string{ + "header1": "value1", + "header2": "value2", + "header3": "value1", + "header4": "value2", + "header5": "value1", + "header6": "value2", + "header7": "value1", + "header8": "value2", + "header9": "value1", + "header0": "value2", +} + +var headerTests = []struct { + m map[string]string + encoding []byte + encoding2 []byte +}{ + { + m: nil, + encoding: []byte{0, 0}, + }, + { + m: make(map[string]string), + encoding: []byte{0, 0}, + }, + { + m: map[string]string{ + "k": "v", + }, + encoding: []byte{ + 0, 1, /* number of headers */ + 0, 1, /* length of key */ + 'k', + 0, 1, /* length of value */ + 'v', + }, + }, + { + m: map[string]string{ + "": "", + }, + encoding: []byte{ + 0, 1, /* number of headers */ + 0, 0, + 0, 0, + }, + }, + { + m: map[string]string{ + "k1": "v12", + "k2": "v34", + }, + encoding: []byte{ + 0, 2, /* number of headers */ + 0, 2, /* length of key */ + 'k', '2', + 0, 3, /* length of value */ + 'v', '3', '4', + 0, 2, /* length of key */ + 'k', '1', + 0, 3, /* length of value */ + 'v', '1', '2', + }, + encoding2: []byte{ + 0, 2, /* number of headers */ + 0, 2, /* length of key */ + 'k', '1', + 0, 3, /* length of value */ + 'v', '1', '2', + 0, 2, /* length of key */ + 'k', '2', + 0, 3, /* length of value */ + 'v', '3', '4', + }, + }, +} + +func TestWriteHeadersSuccessful(t *testing.T) { + for _, tt := range headerTests { + buf := &bytes.Buffer{} + err := WriteHeaders(buf, tt.m) + assert.NoError(t, err, "WriteHeaders failed") + + // Writes iterate over the map in an undefined order, so we might get + // encoding or encoding2. If it's not encoding, assert that it's encoding2. + if !bytes.Equal(tt.encoding, buf.Bytes()) { + assert.Equal(t, tt.encoding2, buf.Bytes(), "Unexpected bytes") + } + } +} + +func TestReadHeadersSuccessful(t *testing.T) { + for _, tt := range headerTests { + // when the bytes are {0, 0}, we always return nil. + if tt.m != nil && len(tt.m) == 0 { + continue + } + + reader := iotest.OneByteReader(bytes.NewReader(tt.encoding)) + got, err := ReadHeaders(reader) + assert.NoError(t, err, "ReadHeaders failed") + assert.Equal(t, tt.m, got, "Map mismatch") + + if tt.encoding2 != nil { + reader := iotest.OneByteReader(bytes.NewReader(tt.encoding2)) + got, err := ReadHeaders(reader) + assert.NoError(t, err, "ReadHeaders failed") + assert.Equal(t, tt.m, got, "Map mismatch") + } + } +} + +func TestReadHeadersLeftoverBytes(t *testing.T) { + buf := []byte{0, 0, 1, 2, 3} + r := bytes.NewReader(buf) + headers, err := ReadHeaders(r) + assert.NoError(t, err, "ReadHeaders failed") + assert.Equal(t, map[string]string(nil), headers, "Headers mismatch") + + leftover, err := ioutil.ReadAll(r) + assert.NoError(t, err, "ReadAll failed") + assert.Equal(t, []byte{1, 2, 3}, leftover, "Reader consumed leftover bytes") +} + +func BenchmarkWriteHeaders(b *testing.B) { + for i := 0; i < b.N; i++ { + WriteHeaders(ioutil.Discard, headers) + } +} + +func BenchmarkReadHeaders(b *testing.B) { + buf := &bytes.Buffer{} + assert.NoError(b, WriteHeaders(buf, headers)) + bs := buf.Bytes() + reader := bytes.NewReader(bs) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + reader.Seek(0, 0) + ReadHeaders(reader) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/interfaces.go b/vendor/src/github.com/uber/tchannel-go/thrift/interfaces.go new file mode 100644 index 00000000..a04a7d5f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/interfaces.go @@ -0,0 +1,46 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import athrift "github.com/apache/thrift/lib/go/thrift" + +// This file defines interfaces that are used or exposed by thrift-gen generated code. +// TChanClient is used by the generated code to make outgoing requests. +// TChanServer is exposed by the generated code, and is called on incoming requests. + +// TChanClient abstracts calling a Thrift endpoint, and is used by the generated client code. +type TChanClient interface { + // Call should be passed the method to call and the request/response Thrift structs. + Call(ctx Context, serviceName, methodName string, req, resp athrift.TStruct) (success bool, err error) +} + +// TChanServer abstracts handling of an RPC that is implemented by the generated server code. +type TChanServer interface { + // Handle should read the request from the given reqReader, and return the response struct. + // The arguments returned are success, result struct, unexpected error + Handle(ctx Context, methodName string, protocol athrift.TProtocol) (success bool, resp athrift.TStruct, err error) + + // Service returns the service name. + Service() string + + // Methods returns the method names handled by this server. + Methods() []string +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/meta.go b/vendor/src/github.com/uber/tchannel-go/thrift/meta.go new file mode 100644 index 00000000..40a7b9c4 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/meta.go @@ -0,0 +1,75 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "errors" + "runtime" + "strings" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/thrift/gen-go/meta" +) + +// HealthFunc is the interface for custom health endpoints. +// ok is whether the service health is OK, and message is optional additional information for the health result. +type HealthFunc func(ctx Context) (ok bool, message string) + +// healthHandler implements the default health check enpoint. +type metaHandler struct { + healthFn HealthFunc +} + +// newMetaHandler return a new HealthHandler instance. +func newMetaHandler() *metaHandler { + return &metaHandler{healthFn: defaultHealth} +} + +// Health returns true as default Health endpoint. +func (h *metaHandler) Health(ctx Context) (*meta.HealthStatus, error) { + ok, message := h.healthFn(ctx) + if message == "" { + return &meta.HealthStatus{Ok: ok}, nil + } + return &meta.HealthStatus{Ok: ok, Message: &message}, nil +} + +func (h *metaHandler) ThriftIDL(ctx Context) (*meta.ThriftIDLs, error) { + // TODO(prashant): Add thriftIDL to the generated code. + return nil, errors.New("unimplemented") +} + +func (h *metaHandler) VersionInfo(ctx Context) (*meta.VersionInfo, error) { + return &meta.VersionInfo{ + Language: "go", + LanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + Version: tchannel.VersionInfo, + }, nil +} + +func defaultHealth(ctx Context) (bool, string) { + return true, "" +} + +// SetHandler sets customized handler for health endpoint. +func (h *metaHandler) setHandler(f HealthFunc) { + h.healthFn = f +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/meta.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/meta.thrift new file mode 100644 index 00000000..eaa398bb --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/meta.thrift @@ -0,0 +1,28 @@ +struct HealthStatus { + 1: required bool ok + 2: optional string message +} + +typedef string filename + +struct ThriftIDLs { + // map: filename -> contents + 1: required map idls + // the entry IDL that imports others + 2: required filename entryPoint +} + +struct VersionInfo { + // short string naming the implementation language + 1: required string language + // language-specific version string representing runtime or build chain + 2: required string language_version + // semver version indicating the version of the tchannel library + 3: required string version +} + +service Meta { + HealthStatus health() + ThriftIDLs thriftIDL() + VersionInfo versionInfo() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/meta_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/meta_test.go new file mode 100644 index 00000000..b1e2e2c5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/meta_test.go @@ -0,0 +1,123 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "runtime" + "strings" + "testing" + "time" + + "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/thrift/gen-go/meta" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" +) + +func TestDefaultHealth(t *testing.T) { + withMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) { + ret, err := c.Health(ctx) + if assert.NoError(t, err, "Health endpoint failed") { + assert.True(t, ret.Ok, "Health status mismatch") + assert.Nil(t, ret.Message, "Health message mismatch") + } + }) +} + +func TestThriftIDL(t *testing.T) { + withMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) { + _, err := c.ThriftIDL(ctx) + assert.Error(t, err, "Health endpoint failed") + assert.Contains(t, err.Error(), "unimplemented") + }) +} + +func TestVersionInfo(t *testing.T) { + withMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) { + ret, err := c.VersionInfo(ctx) + if assert.NoError(t, err, "VersionInfo endpoint failed") { + expected := &meta.VersionInfo{ + Language: "go", + LanguageVersion: strings.TrimPrefix(runtime.Version(), "go"), + Version: tchannel.VersionInfo, + } + assert.Equal(t, expected, ret, "Unexpected version info") + } + }) +} + +func customHealthEmpty(ctx Context) (bool, string) { + return false, "" +} + +func TestCustomHealthEmpty(t *testing.T) { + withMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) { + server.RegisterHealthHandler(customHealthEmpty) + ret, err := c.Health(ctx) + if assert.NoError(t, err, "Health endpoint failed") { + assert.False(t, ret.Ok, "Health status mismatch") + assert.Nil(t, ret.Message, "Health message mismatch") + } + }) +} + +func customHealthNoEmpty(ctx Context) (bool, string) { + return false, "from me" +} + +func TestCustomHealthNoEmpty(t *testing.T) { + withMetaSetup(t, func(ctx Context, c tchanMeta, server *Server) { + server.RegisterHealthHandler(customHealthNoEmpty) + ret, err := c.Health(ctx) + if assert.NoError(t, err, "Health endpoint failed") { + assert.False(t, ret.Ok, "Health status mismatch") + assert.Equal(t, ret.Message, thrift.StringPtr("from me"), "Health message mismatch") + } + }) +} + +func withMetaSetup(t *testing.T, f func(ctx Context, c tchanMeta, server *Server)) { + ctx, cancel := NewContext(time.Second * 10) + defer cancel() + + // Start server + tchan, server := setupMetaServer(t) + defer tchan.Close() + + // Get client1 + c := getMetaClient(t, tchan.PeerInfo().HostPort) + f(ctx, c, server) +} + +func setupMetaServer(t *testing.T) (*tchannel.Channel, *Server) { + tchan := testutils.NewServer(t, testutils.NewOpts().SetServiceName("meta")) + server := NewServer(tchan) + return tchan, server +} + +func getMetaClient(t *testing.T, dst string) tchanMeta { + tchan := testutils.NewClient(t, nil) + tchan.Peers().Add(dst) + thriftClient := NewClient(tchan, "meta", nil) + return newTChanMetaClient(thriftClient) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanMeta.go b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanMeta.go new file mode 100644 index 00000000..24e6070c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanMeta.go @@ -0,0 +1,32 @@ +package mocks + +import "github.com/uber/tchannel-go/thrift/gen-go/meta" +import "github.com/stretchr/testify/mock" + +import "github.com/uber/tchannel-go/thrift" + +type TChanMeta struct { + mock.Mock +} + +func (_m *TChanMeta) Health(ctx thrift.Context) (*meta.HealthStatus, error) { + ret := _m.Called(ctx) + + var r0 *meta.HealthStatus + if rf, ok := ret.Get(0).(func(thrift.Context) *meta.HealthStatus); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*meta.HealthStatus) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(thrift.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSecondService.go b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSecondService.go new file mode 100644 index 00000000..34325fcb --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSecondService.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mocks + +import "github.com/stretchr/testify/mock" + +import "github.com/uber/tchannel-go/thrift" + +type TChanSecondService struct { + mock.Mock +} + +func (_m *TChanSecondService) Echo(_ctx thrift.Context, _arg string) (string, error) { + ret := _m.Called(_ctx, _arg) + + var r0 string + if rf, ok := ret.Get(0).(func(thrift.Context, string) string); ok { + r0 = rf(_ctx, _arg) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(thrift.Context, string) error); ok { + r1 = rf(_ctx, _arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSimpleService.go b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSimpleService.go new file mode 100644 index 00000000..c74743cc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/mocks/TChanSimpleService.go @@ -0,0 +1,76 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package mocks + +import "github.com/uber/tchannel-go/thrift/gen-go/test" +import "github.com/stretchr/testify/mock" + +import "github.com/uber/tchannel-go/thrift" + +type TChanSimpleService struct { + mock.Mock +} + +func (_m *TChanSimpleService) Call(_ctx thrift.Context, _arg *test.Data) (*test.Data, error) { + ret := _m.Called(_ctx, _arg) + + var r0 *test.Data + if rf, ok := ret.Get(0).(func(thrift.Context, *test.Data) *test.Data); ok { + r0 = rf(_ctx, _arg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*test.Data) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(thrift.Context, *test.Data) error); ok { + r1 = rf(_ctx, _arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} +func (_m *TChanSimpleService) Simple(_ctx thrift.Context) error { + ret := _m.Called(_ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(thrift.Context) error); ok { + r0 = rf(_ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} +func (_m *TChanSimpleService) SimpleFuture(_ctx thrift.Context) error { + ret := _m.Called(_ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(thrift.Context) error); ok { + r0 = rf(_ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/options.go b/vendor/src/github.com/uber/tchannel-go/thrift/options.go new file mode 100644 index 00000000..050a3282 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/options.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "github.com/apache/thrift/lib/go/thrift" + "golang.org/x/net/context" +) + +// RegisterOption is the interface for options to Register. +type RegisterOption interface { + Apply(h *handler) +} + +// PostResponseCB registers a callback that is run after a response has been +// compeltely processed (e.g. written to the channel). +// This gives the server a chance to clean up resources from the response object +type PostResponseCB func(ctx context.Context, method string, response thrift.TStruct) + +type optPostResponse PostResponseCB + +// OptPostResponse registers a PostResponseCB. +func OptPostResponse(cb PostResponseCB) RegisterOption { + return optPostResponse(cb) +} + +func (o optPostResponse) Apply(h *handler) { + h.postResponseCB = PostResponseCB(o) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/server.go b/vendor/src/github.com/uber/tchannel-go/thrift/server.go new file mode 100644 index 00000000..99f02929 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/server.go @@ -0,0 +1,216 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "log" + "strings" + "sync" + + tchannel "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/internal/argreader" + + "github.com/apache/thrift/lib/go/thrift" + "golang.org/x/net/context" +) + +type handler struct { + server TChanServer + postResponseCB PostResponseCB +} + +// Server handles incoming TChannel calls and forwards them to the matching TChanServer. +type Server struct { + sync.RWMutex + ch tchannel.Registrar + log tchannel.Logger + handlers map[string]handler + metaHandler *metaHandler + ctxFn func(ctx context.Context, method string, headers map[string]string) Context +} + +// NewServer returns a server that can serve thrift services over TChannel. +func NewServer(registrar tchannel.Registrar) *Server { + metaHandler := newMetaHandler() + server := &Server{ + ch: registrar, + log: registrar.Logger(), + handlers: make(map[string]handler), + metaHandler: metaHandler, + ctxFn: defaultContextFn, + } + server.Register(newTChanMetaServer(metaHandler)) + if ch, ok := registrar.(*tchannel.Channel); ok { + // Register the meta endpoints on the "tchannel" service name. + NewServer(ch.GetSubChannel("tchannel")) + } + return server +} + +// Register registers the given TChanServer to be called on any incoming call for its' services. +// TODO(prashant): Replace Register call with this call. +func (s *Server) Register(svr TChanServer, opts ...RegisterOption) { + service := svr.Service() + handler := &handler{server: svr} + for _, opt := range opts { + opt.Apply(handler) + } + + s.Lock() + s.handlers[service] = *handler + s.Unlock() + + for _, m := range svr.Methods() { + s.ch.Register(s, service+"::"+m) + } +} + +// RegisterHealthHandler uses the user-specified function f for the Health endpoint. +func (s *Server) RegisterHealthHandler(f HealthFunc) { + s.metaHandler.setHandler(f) +} + +// SetContextFn sets the function used to convert a context.Context to a thrift.Context. +// Note: This API may change and is only intended to bridge different contexts. +func (s *Server) SetContextFn(f func(ctx context.Context, method string, headers map[string]string) Context) { + s.ctxFn = f +} + +func (s *Server) onError(err error) { + // TODO(prashant): Expose incoming call errors through options for NewServer. + // Timeouts should not be reported as errors. + if tchannel.GetSystemErrorCode(err) == tchannel.ErrCodeTimeout { + s.log.Debugf("thrift Server timeout: %v", err) + } else { + s.log.WithFields(tchannel.ErrField(err)).Error("Thrift server error.") + } +} + +func defaultContextFn(ctx context.Context, method string, headers map[string]string) Context { + return WithHeaders(ctx, headers) +} + +func (s *Server) handle(origCtx context.Context, handler handler, method string, call *tchannel.InboundCall) error { + reader, err := call.Arg2Reader() + if err != nil { + return err + } + headers, err := ReadHeaders(reader) + if err != nil { + return err + } + + if err := argreader.EnsureEmpty(reader, "reading request headers"); err != nil { + return err + } + + if err := reader.Close(); err != nil { + return err + } + + reader, err = call.Arg3Reader() + if err != nil { + return err + } + + tracer := tchannel.TracerFromRegistrar(s.ch) + origCtx = tchannel.ExtractInboundSpan(origCtx, call, headers, tracer) + ctx := s.ctxFn(origCtx, method, headers) + + wp := getProtocolReader(reader) + success, resp, err := handler.server.Handle(ctx, method, wp.protocol) + thriftProtocolPool.Put(wp) + + if handler.postResponseCB != nil { + defer handler.postResponseCB(ctx, method, resp) + } + + if err != nil { + if _, ok := err.(thrift.TProtocolException); ok { + // We failed to parse the Thrift generated code, so convert the error to bad request. + err = tchannel.NewSystemError(tchannel.ErrCodeBadRequest, err.Error()) + } + + reader.Close() + call.Response().SendSystemError(err) + return nil + } + + if err := argreader.EnsureEmpty(reader, "reading request body"); err != nil { + return err + } + if err := reader.Close(); err != nil { + return err + } + + if !success { + call.Response().SetApplicationError() + } + + writer, err := call.Response().Arg2Writer() + if err != nil { + return err + } + + if err := WriteHeaders(writer, ctx.ResponseHeaders()); err != nil { + return err + } + if err := writer.Close(); err != nil { + return err + } + + writer, err = call.Response().Arg3Writer() + wp = getProtocolWriter(writer) + resp.Write(wp.protocol) + thriftProtocolPool.Put(wp) + err = writer.Close() + + return err +} + +func getServiceMethod(method string) (string, string, bool) { + s := string(method) + sep := strings.Index(s, "::") + if sep == -1 { + return "", "", false + } + return s[:sep], s[sep+2:], true +} + +// Handle handles an incoming TChannel call and forwards it to the correct handler. +func (s *Server) Handle(ctx context.Context, call *tchannel.InboundCall) { + op := call.MethodString() + service, method, ok := getServiceMethod(op) + if !ok { + log.Fatalf("Handle got call for %s which does not match the expected call format", op) + } + + s.RLock() + handler, ok := s.handlers[service] + s.RUnlock() + if !ok { + log.Fatalf("Handle got call for service %v which is not registered", service) + } + + if err := s.handle(ctx, handler, method, call); err != nil { + s.onError(err) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/struct.go b/vendor/src/github.com/uber/tchannel-go/thrift/struct.go new file mode 100644 index 00000000..33f80b16 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/struct.go @@ -0,0 +1,43 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "io" + + "github.com/apache/thrift/lib/go/thrift" +) + +// WriteStruct writes the given Thrift struct to a writer. It pools TProtocols. +func WriteStruct(writer io.Writer, s thrift.TStruct) error { + wp := getProtocolWriter(writer) + err := s.Write(wp.protocol) + thriftProtocolPool.Put(wp) + return err +} + +// ReadStruct reads the given Thrift struct. It pools TProtocols. +func ReadStruct(reader io.Reader, s thrift.TStruct) error { + wp := getProtocolReader(reader) + err := s.Read(wp.protocol) + thriftProtocolPool.Put(wp) + return err +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/struct_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/struct_test.go new file mode 100644 index 00000000..81b37cdf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/struct_test.go @@ -0,0 +1,201 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift_test + +import ( + "bytes" + "io/ioutil" + "sync" + "testing" + + . "github.com/uber/tchannel-go/thrift" + + "github.com/uber/tchannel-go/testutils/testreader" + "github.com/uber/tchannel-go/testutils/testwriter" + "github.com/uber/tchannel-go/thrift/gen-go/test" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" +) + +var structTest = struct { + s thrift.TStruct + encoded []byte +}{ + s: &test.Data{ + B1: true, + S2: "S2", + I3: 3, + }, + encoded: []byte{ + 0x2, // bool + 0x0, 0x1, // field 1 + 0x1, // true + 0xb, // string + 0x0, 0x2, // field 2 + 0x0, 0x0, 0x0, 0x2, // length of string "S2" + 'S', '2', // string "S2" + 0x8, // i32 + 0x0, 0x3, // field 3 + 0x0, 0x0, 0x0, 0x3, // i32 3 + 0x0, // end of struct + }, +} + +func TestReadStruct(t *testing.T) { + appendBytes := func(bs []byte, append []byte) []byte { + b := make([]byte, len(bs)+len(append)) + n := copy(b, bs) + copy(b[n:], append) + return b + } + + tests := []struct { + s thrift.TStruct + encoded []byte + wantErr bool + leftover []byte + }{ + { + s: structTest.s, + encoded: structTest.encoded, + }, + { + s: &test.Data{ + B1: true, + S2: "S2", + }, + // Missing field 3. + encoded: structTest.encoded[:len(structTest.encoded)-8], + wantErr: true, + }, + { + s: structTest.s, + encoded: appendBytes(structTest.encoded, []byte{1, 2, 3, 4}), + leftover: []byte{1, 2, 3, 4}, + }, + } + + for _, tt := range tests { + reader := bytes.NewReader(tt.encoded) + var s thrift.TStruct = &test.Data{} + err := ReadStruct(reader, s) + assert.Equal(t, tt.wantErr, err != nil, "Unexpected error: %v", err) + + // Even if there's an error, the struct will be partially filled. + assert.Equal(t, tt.s, s, "Unexpected struct") + + leftover, err := ioutil.ReadAll(reader) + if assert.NoError(t, err, "Read leftover bytes failed") { + // ReadAll always returns a non-nil byte slice. + if tt.leftover == nil { + tt.leftover = make([]byte, 0) + } + assert.Equal(t, tt.leftover, leftover, "Leftover bytes mismatch") + } + } +} + +func TestReadStructErr(t *testing.T) { + writer, reader := testreader.ChunkReader() + writer <- structTest.encoded[:10] + writer <- nil + close(writer) + + s := &test.Data{} + err := ReadStruct(reader, s) + if assert.Error(t, err, "ReadStruct should fail") { + // Apache Thrift just prepends the error message, and doesn't give us access + // to the underlying error, so we can't check the underlying error exactly. + assert.Contains(t, err.Error(), testreader.ErrUser.Error(), "Underlying error missing") + } +} + +func TestWriteStruct(t *testing.T) { + tests := []struct { + s thrift.TStruct + encoded []byte + wantErr bool + }{ + { + s: structTest.s, + encoded: structTest.encoded, + }, + } + + for _, tt := range tests { + buf := &bytes.Buffer{} + err := WriteStruct(buf, tt.s) + assert.Equal(t, tt.wantErr, err != nil, "Unexpected err: %v", err) + if err != nil { + continue + } + + assert.Equal(t, tt.encoded, buf.Bytes(), "Encoded data mismatch") + } +} + +func TestWriteStructErr(t *testing.T) { + writer := testwriter.Limited(10) + err := WriteStruct(writer, structTest.s) + if assert.Error(t, err, "WriteStruct should fail") { + // Apache Thrift just prepends the error message, and doesn't give us access + // to the underlying error, so we can't check the underlying error exactly. + assert.Contains(t, err.Error(), testwriter.ErrOutOfSpace.Error(), "Underlying error missing") + } +} + +func TestParallelReadWrites(t *testing.T) { + var wg sync.WaitGroup + testBG := func(f func(t *testing.T)) { + wg.Add(1) + go func() { + f(t) + wg.Done() + }() + } + for i := 0; i < 50; i++ { + testBG(TestReadStruct) + testBG(TestWriteStruct) + } + wg.Wait() +} + +func BenchmarkWriteStruct(b *testing.B) { + buf := &bytes.Buffer{} + for i := 0; i < b.N; i++ { + buf.Reset() + WriteStruct(buf, structTest.s) + } +} + +func BenchmarkReadStruct(b *testing.B) { + buf := bytes.NewReader(structTest.encoded) + var d test.Data + + buf.Seek(0, 0) + assert.NoError(b, ReadStruct(buf, &d)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Seek(0, 0) + ReadStruct(buf, &d) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/tchan-meta.go b/vendor/src/github.com/uber/tchannel-go/thrift/tchan-meta.go new file mode 100644 index 00000000..20231bfc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/tchan-meta.go @@ -0,0 +1,174 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "fmt" + + athrift "github.com/apache/thrift/lib/go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/meta" +) + +// Interfaces for the service and client for the services defined in the IDL. + +// tchanMeta is the interface that defines the server handler and client interface. +type tchanMeta interface { + Health(ctx Context) (*gen.HealthStatus, error) + ThriftIDL(ctx Context) (*gen.ThriftIDLs, error) + VersionInfo(ctx Context) (*gen.VersionInfo, error) +} + +// Implementation of a client and service handler. + +type tchanMetaClient struct { + thriftService string + client TChanClient +} + +func newTChanMetaClient(client TChanClient) tchanMeta { + return &tchanMetaClient{ + "Meta", + client, + } +} + +func (c *tchanMetaClient) Health(ctx Context) (*gen.HealthStatus, error) { + var resp gen.MetaHealthResult + args := gen.MetaHealthArgs{} + success, err := c.client.Call(ctx, c.thriftService, "health", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +func (c *tchanMetaClient) ThriftIDL(ctx Context) (*gen.ThriftIDLs, error) { + var resp gen.MetaThriftIDLResult + args := gen.MetaThriftIDLArgs{} + success, err := c.client.Call(ctx, c.thriftService, "thriftIDL", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +func (c *tchanMetaClient) VersionInfo(ctx Context) (*gen.VersionInfo, error) { + var resp gen.MetaVersionInfoResult + args := gen.MetaVersionInfoArgs{} + success, err := c.client.Call(ctx, c.thriftService, "versionInfo", &args, &resp) + if err == nil && !success { + } + + return resp.GetSuccess(), err +} + +type tchanMetaServer struct { + handler tchanMeta +} + +func newTChanMetaServer(handler tchanMeta) TChanServer { + return &tchanMetaServer{ + handler, + } +} + +func (s *tchanMetaServer) Service() string { + return "Meta" +} + +func (s *tchanMetaServer) Methods() []string { + return []string{ + "health", + "thriftIDL", + "versionInfo", + } +} + +func (s *tchanMetaServer) Handle(ctx Context, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + case "health": + return s.handleHealth(ctx, protocol) + case "thriftIDL": + return s.handleThriftIDL(ctx, protocol) + case "versionInfo": + return s.handleVersionInfo(ctx, protocol) + + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +func (s *tchanMetaServer) handleHealth(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req gen.MetaHealthArgs + var res gen.MetaHealthResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.Health(ctx) + + if err != nil { + return false, nil, err + } + res.Success = r + + return err == nil, &res, nil +} + +func (s *tchanMetaServer) handleThriftIDL(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req gen.MetaThriftIDLArgs + var res gen.MetaThriftIDLResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.ThriftIDL(ctx) + + if err != nil { + return false, nil, err + } + res.Success = r + + return err == nil, &res, nil +} + +func (s *tchanMetaServer) handleVersionInfo(ctx Context, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req gen.MetaVersionInfoArgs + var res gen.MetaVersionInfoResult + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + r, err := + s.handler.VersionInfo(ctx) + + if err != nil { + return false, nil, err + } + res.Success = r + + return err == nil, &res, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/test.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/test.thrift new file mode 100644 index 00000000..7b7f3d90 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/test.thrift @@ -0,0 +1,23 @@ +struct Data { + 1: required bool b1, + 2: required string s2, + 3: required i32 i3 +} + +exception SimpleErr { + 1: string message +} + +exception NewErr { + 1: string message +} + +service SimpleService { + Data Call(1: Data arg) + void Simple() throws (1: SimpleErr simpleErr) + void SimpleFuture() throws (1: SimpleErr simpleErr, 2: NewErr newErr) +} + +service SecondService { + string Echo(1: string arg) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/compile_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/compile_test.go new file mode 100644 index 00000000..f49ef6dd --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/compile_test.go @@ -0,0 +1,344 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "sync" + "testing" + + "github.com/stretchr/testify/require" + "github.com/uber/tchannel-go/testutils" +) + +// These tests ensure that the code generator generates valid code that can be built +// in combination with Thrift's autogenerated code. + +const _tchannelPackage = "github.com/uber/tchannel-go" + +var ( + _testGoPath string + _testGoPathOnce sync.Once +) + +func TestMain(m *testing.M) { + exitCode := m.Run() + + // If we created a fake GOPATH, we should clean it up on success. + if _testGoPath != "" && exitCode == 0 { + os.RemoveAll(_testGoPath) + } + + os.Exit(exitCode) +} + +func getTChannelDir(goPath string) string { + return filepath.Join(goPath, "src", _tchannelPackage) +} + +func getCurrentTChannelPath(t *testing.T) string { + wd, err := os.Getwd() + require.NoError(t, err, "Failed to get working directory") + + // Walk up "wd" till we find "tchannel-go". + for filepath.Base(wd) != filepath.Base(_tchannelPackage) { + wd = filepath.Dir(wd) + if wd == "" { + t.Fatalf("Failed to find tchannel-go in parents of current directory") + } + } + + return wd +} + +func createGoPath(t *testing.T) { + goPath, err := ioutil.TempDir("", "thrift-gen") + require.NoError(t, err, "TempDir failed") + + // Create $GOPATH/src/github.com/uber/tchannel-go and symlink everything. + // And then create a dummy directory for all the test output. + tchannelDir := getTChannelDir(goPath) + require.NoError(t, os.MkdirAll(tchannelDir, 0755), "MkDirAll failed") + + // Symlink the contents of tchannel-go into the temp directory. + realTChannelDir := getCurrentTChannelPath(t) + realDirContents, err := ioutil.ReadDir(realTChannelDir) + require.NoError(t, err, "Failed to read real tchannel-go dir") + + for _, f := range realDirContents { + realPath := filepath.Join(realTChannelDir, f.Name()) + err := os.Symlink(realPath, filepath.Join(tchannelDir, filepath.Base(f.Name()))) + require.NoError(t, err, "Failed to symlink %v", f.Name()) + } + + _testGoPath = goPath + + // None of the other tests in this package should use GOPATH, so we don't + // restore this. + os.Setenv("GOPATH", goPath) +} + +func getOutputDir(t *testing.T) (dir, pkg string) { + _testGoPathOnce.Do(func() { createGoPath(t) }) + + // Create a random directory inside of the GOPATH in tmp + randStr := testutils.RandString(10) + randDir := filepath.Join(getTChannelDir(_testGoPath), randStr) + // In case it's not empty. + os.RemoveAll(randDir) + + return randDir, filepath.Join(_tchannelPackage, randStr) +} + +func TestAllThrift(t *testing.T) { + files, err := ioutil.ReadDir("test_files") + require.NoError(t, err, "Cannot read test_files directory: %v", err) + + for _, f := range files { + fname := f.Name() + if f.IsDir() || filepath.Ext(fname) != ".thrift" { + continue + } + + if err := runBuildTest(t, filepath.Join("test_files", fname)); err != nil { + t.Errorf("Thrift file %v failed: %v", fname, err) + } + } +} + +func TestIncludeThrift(t *testing.T) { + dirs, err := ioutil.ReadDir("test_files/include_test") + require.NoError(t, err, "Cannot read test_files/include_test directory: %v", err) + + for _, d := range dirs { + dname := d.Name() + if !d.IsDir() { + continue + } + + thriftFile := filepath.Join(dname, path.Base(dname)+".thrift") + if err := runBuildTest(t, filepath.Join("test_files/include_test/", thriftFile)); err != nil { + t.Errorf("Thrift test %v failed: %v", dname, err) + } + } +} + +func TestMultipleFiles(t *testing.T) { + if err := runBuildTest(t, filepath.Join("test_files", "multi_test", "file1.thrift")); err != nil { + t.Errorf("Multiple file test failed: %v", err) + } +} + +func TestExternalTemplate(t *testing.T) { + template1 := `package {{ .Package }} + +{{ range .AST.Services }} +// Service {{ .Name }} has {{ len .Methods }} methods. +{{ range .Methods }} +// func {{ .Name | goPublicName }} ({{ range .Arguments }}{{ .Type | goType }}, {{ end }}) ({{ if .ReturnType }}{{ .ReturnType | goType }}{{ end }}){{ end }} +{{ end }} + ` + templateFile := writeTempFile(t, template1) + defer os.Remove(templateFile) + + expected := `package service_extend + +// Service S1 has 1 methods. + +// func M1 ([]byte, ) ([]byte) + +// Service S2 has 1 methods. + +// func M2 (*S, int32, ) (*S) + +// Service S3 has 1 methods. + +// func M3 () () +` + + opts := processOptions{ + InputFile: "test_files/service_extend.thrift", + TemplateFiles: []string{templateFile}, + } + checks := func(dir string) error { + dir = filepath.Join(dir, "service_extend") + if err := checkDirectoryFiles(dir, 6); err != nil { + return err + } + + // Verify the contents of the extra file. + outFile := filepath.Join(dir, defaultPackageName(templateFile)+"-service_extend.go") + return verifyFileContents(outFile, expected) + } + if err := runTest(t, opts, checks); err != nil { + t.Errorf("Failed to run test: %v", err) + } +} + +func writeTempFile(t *testing.T, contents string) string { + tempFile, err := ioutil.TempFile("", "temp") + require.NoError(t, err, "Failed to create temp file") + tempFile.Close() + require.NoError(t, ioutil.WriteFile(tempFile.Name(), []byte(contents), 0666), + "Write temp file failed") + return tempFile.Name() +} + +func verifyFileContents(filename, expected string) error { + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + bytesStr := string(bytes) + if bytesStr != expected { + return fmt.Errorf("file contents mismatch. got:\n%vexpected:\n%v", bytesStr, expected) + } + + return nil +} + +func copyFile(src, dst string) error { + f, err := os.Open(src) + if err != nil { + return err + } + defer f.Close() + + writeF, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) + if err != nil { + return err + } + defer writeF.Close() + + _, err = io.Copy(writeF, f) + return err +} + +// setupDirectory creates a temporary directory. +func setupDirectory(thriftFile string) (string, error) { + tempDir, err := ioutil.TempDir("", "thrift-gen") + if err != nil { + return "", err + } + + return tempDir, nil +} + +func createAdditionalTestFile(thriftFile, tempDir string) error { + f, err := os.Open(thriftFile) + if err != nil { + return err + } + + var writer io.Writer + rdr := bufio.NewReader(f) + for { + line, err := rdr.ReadString('\n') + if err != nil { + if err == io.EOF { + return nil + } + } + + if strings.HasPrefix(line, "//Go code:") { + fileName := strings.TrimSpace(strings.TrimPrefix(line, "//Go code:")) + outFile := filepath.Join(tempDir, fileName) + f, err := os.OpenFile(outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) + if err != nil { + return err + } + defer f.Close() + writer = f + } else if writer != nil { + if strings.HasPrefix(line, "//") { + writer.Write([]byte(strings.TrimPrefix(line, "//"))) + } else { + return nil + } + } + } +} + +func checkDirectoryFiles(dir string, n int) error { + dirContents, err := ioutil.ReadDir(dir) + if err != nil { + return err + } + + if len(dirContents) < n { + return fmt.Errorf("expected to generate at least %v files, but found: %v", n, len(dirContents)) + } + + return nil +} + +func runBuildTest(t *testing.T, thriftFile string) error { + extraChecks := func(dir string) error { + return checkDirectoryFiles(filepath.Join(dir, defaultPackageName(thriftFile)), 4) + } + + opts := processOptions{InputFile: thriftFile} + return runTest(t, opts, extraChecks) +} + +func runTest(t *testing.T, opts processOptions, extraChecks func(string) error) error { + tempDir, outputPkg := getOutputDir(t) + + // Generate code from the Thrift file. + *packagePrefix = outputPkg + "/" + opts.GenerateThrift = true + opts.OutputDir = tempDir + if err := processFile(opts); err != nil { + return fmt.Errorf("processFile(%s) in %q failed: %v", opts.InputFile, tempDir, err) + } + + // Create any extra Go files as specified in the Thrift file. + if err := createAdditionalTestFile(opts.InputFile, tempDir); err != nil { + return fmt.Errorf("failed creating additional test files for %s in %q: %v", opts.InputFile, tempDir, err) + } + + // Run go build to ensure that the generated code builds. + cmd := exec.Command("go", "build", "-i", "./...") + cmd.Dir = tempDir + // NOTE: we check output, since go build ./... returns 0 status code on failure: + // https://github.com/golang/go/issues/11407 + if output, err := cmd.CombinedOutput(); err != nil || len(output) > 0 { + return fmt.Errorf("build in %q failed.\nError: %v Output:\n%v", tempDir, err, string(output)) + } + + // Run any extra checks the user may want. + if err := extraChecks(tempDir); err != nil { + return err + } + + // Only delete the temp directory on success. + os.RemoveAll(tempDir) + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/extends.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/extends.go new file mode 100644 index 00000000..11fbc1ac --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/extends.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + "sort" + "strings" +) + +// setExtends will set the ExtendsService for all services. +// It is done after all files are parsed, as services may extend those +// found in an included file. +func setExtends(state map[string]parseState) error { + for _, v := range state { + for _, s := range v.services { + if s.Extends == "" { + continue + } + + var searchServices []*Service + var searchFor string + parts := strings.SplitN(s.Extends, ".", 2) + // If it's not imported, then look at the current file's services. + if len(parts) < 2 { + searchServices = v.services + searchFor = s.Extends + } else { + include := v.global.includes[parts[0]] + s.ExtendsPrefix = include.pkg + "." + searchServices = state[include.file].services + searchFor = parts[1] + } + + foundService := sort.Search(len(searchServices), func(i int) bool { + return searchServices[i].Name >= searchFor + }) + if foundService == len(searchServices) { + return fmt.Errorf("failed to find base service %q for %q", s.Extends, s.Name) + } + s.ExtendsService = searchServices[foundService] + } + } + + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/generate.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/generate.go new file mode 100644 index 00000000..3d90e58a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/generate.go @@ -0,0 +1,91 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +var ( + thriftBinary = flag.String("thriftBinary", "thrift", "Command to use for the Apache Thrift binary") + apacheThriftImport = flag.String("thriftImport", "github.com/apache/thrift/lib/go/thrift", "Go package to use for the Thrift import") + packagePrefix = flag.String("packagePrefix", "", "The package prefix (will be used similar to how Apache Thrift uses it)") +) + +func execCmd(name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func execThrift(args ...string) error { + return execCmd(*thriftBinary, args...) +} + +func deleteRemote(dir string) error { + return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() || !strings.HasSuffix(path, "-remote") { + return nil + } + + if err := os.RemoveAll(path); err != nil { + return err + } + // Once the directory is deleted, we can skip the rest of it. + return filepath.SkipDir + }) +} + +func runThrift(inFile string, outDir string) error { + inFile, err := filepath.Abs(inFile) + if err != nil { + return err + } + + // Delete any existing generated code for this Thrift file. + genDir := filepath.Join(outDir, defaultPackageName(inFile)) + if err := execCmd("rm", "-rf", genDir); err != nil { + return fmt.Errorf("failed to delete directory %s: %v", genDir, err) + } + + // Generate the Apache Thrift generated code. + goArgs := fmt.Sprintf("go:thrift_import=%s,package_prefix=%s", *apacheThriftImport, *packagePrefix) + if err := execThrift("-r", "--gen", goArgs, "-out", outDir, inFile); err != nil { + return fmt.Errorf("thrift compile failed: %v", err) + } + + // Delete the -remote folders. + if err := deleteRemote(outDir); err != nil { + return fmt.Errorf("failed to delete -remote folders: %v", err) + } + + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath.go new file mode 100644 index 00000000..53978afe --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + "os" + "path/filepath" +) + +// ResolveWithGoPath will resolve the filename relative to GOPATH and returns +// the first file that exists, or an error otherwise. +func ResolveWithGoPath(filename string) (string, error) { + for _, file := range goPathCandidates(filename) { + if _, err := os.Stat(file); !os.IsNotExist(err) { + return file, nil + } + } + + return "", fmt.Errorf("file not found on GOPATH: %q", filename) +} + +func goPathCandidates(filename string) []string { + candidates := []string{filename} + paths := filepath.SplitList(os.Getenv("GOPATH")) + for _, path := range paths { + resolvedFilename := filepath.Join(path, "src", filename) + candidates = append(candidates, resolvedFilename) + } + + return candidates +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath_test.go new file mode 100644 index 00000000..34a6ef67 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/gopath_test.go @@ -0,0 +1,123 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func getFakeFS(t *testing.T) string { + files := []string{ + "src/pkg1/sub/ringpop.thriftgen", + "src/pkg2/sub/ringpop.thriftgen", + } + + tempDir, err := ioutil.TempDir("", "thriftgen") + require.NoError(t, err, "TempDir failed") + + for _, f := range files { + require.NoError(t, os.MkdirAll(filepath.Join(tempDir, filepath.Dir(f)), 0770), + "Failed to create directory structure for %v", f) + require.NoError(t, ioutil.WriteFile(filepath.Join(tempDir, f), nil, 0660), + "Failed to create dummy file") + } + return tempDir +} + +func TestGoPathCandidates(t *testing.T) { + tests := []struct { + goPath string + filename string + expectedCandidates []string + }{ + { + goPath: "onepath", + filename: "github.com/uber/tchannel-go/tchan.thrift-gen", + expectedCandidates: []string{ + "github.com/uber/tchannel-go/tchan.thrift-gen", + "onepath/src/github.com/uber/tchannel-go/tchan.thrift-gen", + }, + }, + { + goPath: "onepath:secondpath", + filename: "github.com/uber/tchannel-go/tchan.thrift-gen", + expectedCandidates: []string{ + "github.com/uber/tchannel-go/tchan.thrift-gen", + "onepath/src/github.com/uber/tchannel-go/tchan.thrift-gen", + "secondpath/src/github.com/uber/tchannel-go/tchan.thrift-gen", + }, + }, + } + + for _, tt := range tests { + os.Setenv("GOPATH", tt.goPath) + candidates := goPathCandidates(tt.filename) + if !reflect.DeepEqual(candidates, tt.expectedCandidates) { + t.Errorf("GOPATH=%s FileCandidatesWithGopath(%s) = %q, want %q", + tt.goPath, tt.filename, candidates, tt.expectedCandidates) + } + } +} + +func TestResolveWithGoPath(t *testing.T) { + goPath1 := getFakeFS(t) + goPath2 := getFakeFS(t) + os.Setenv("GOPATH", goPath1+string(filepath.ListSeparator)+goPath2) + defer os.RemoveAll(goPath1) + defer os.RemoveAll(goPath2) + + tests := []struct { + filename string + want string + wantErr bool + }{ + { + filename: "pkg1/sub/ringpop.thriftgen", + want: filepath.Join(goPath1, "src/pkg1/sub/ringpop.thriftgen"), + }, + { + filename: "pkg2/sub/ringpop.thriftgen", + want: filepath.Join(goPath1, "src/pkg2/sub/ringpop.thriftgen"), + }, + { + filename: filepath.Join(goPath2, "src/pkg2/sub/ringpop.thriftgen"), + want: filepath.Join(goPath2, "src/pkg2/sub/ringpop.thriftgen"), + }, + { + filename: "pkg3/sub/ringpop.thriftgen", + wantErr: true, + }, + } + + for _, tt := range tests { + file, err := ResolveWithGoPath(tt.filename) + gotErr := err != nil + assert.Equal(t, tt.wantErr, gotErr, "%v expected error: %v got: %v", tt.filename, tt.wantErr, err) + assert.Equal(t, tt.want, file, "%v expected to resolve to %v, got %v", tt.filename, tt.want, file) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/include.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/include.go new file mode 100644 index 00000000..43cdac83 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/include.go @@ -0,0 +1,56 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import "github.com/samuel/go-thrift/parser" + +// Include represents a single include statement in the Thrift file. +type Include struct { + key string + file string + pkg string +} + +// Import returns the go import to use for this package. +func (i *Include) Import() string { + // TODO(prashant): Rename imports so they don't clash with standard imports. + // This is not high priority since Apache thrift clashes already with "bytes" and "fmt". + // which are the same imports we would clash with. + return *packagePrefix + i.Package() +} + +// Package returns the package selector for this package. +func (i *Include) Package() string { + return i.pkg +} + +func createIncludes(parsed *parser.Thrift, all map[string]parseState) map[string]*Include { + includes := make(map[string]*Include) + for k, v := range parsed.Includes { + included := all[v] + includes[k] = &Include{ + key: k, + file: v, + pkg: included.namespace, + } + } + return includes +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/main.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/main.go new file mode 100644 index 00000000..0b3efdf7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/main.go @@ -0,0 +1,236 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// thrift-gen generates code for Thrift services that can be used with the +// uber/tchannel/thrift package. thrift-gen generated code relies on the +// Apache Thrift generated code for serialization/deserialization, and should +// be a part of the generated code's package. +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "text/template" + + "github.com/samuel/go-thrift/parser" +) + +const tchannelThriftImport = "github.com/uber/tchannel-go/thrift" + +var ( + generateThrift = flag.Bool("generateThrift", false, "Whether to generate all Thrift go code") + inputFile = flag.String("inputFile", "", "The .thrift file to generate a client for") + outputDir = flag.String("outputDir", "gen-go", "The output directory to generate go code to.") + skipTChannel = flag.Bool("skipTChannel", false, "Whether to skip the TChannel template") + templateFiles = NewStringSliceFlag("template", "Template file to compile code from") + + nlSpaceNL = regexp.MustCompile(`\n[ \t]+\n`) +) + +// TemplateData is the data passed to the template that generates code. +type TemplateData struct { + Package string + AST *parser.Thrift + Services []*Service + Includes map[string]*Include + Imports imports + + // global should not be directly exported to the template, but functions on + // global can be exposed to templates. + global *State +} + +type imports struct { + Thrift string + TChannel string +} + +func main() { + flag.Parse() + if *inputFile == "" { + log.Fatalf("Please specify an inputFile") + } + + opts := processOptions{ + InputFile: *inputFile, + GenerateThrift: *generateThrift, + OutputDir: *outputDir, + SkipTChannel: *skipTChannel, + TemplateFiles: *templateFiles, + } + if err := processFile(opts); err != nil { + log.Fatal(err) + } +} + +type processOptions struct { + InputFile string + GenerateThrift bool + OutputDir string + SkipTChannel bool + TemplateFiles []string +} + +func processFile(opts processOptions) error { + if err := os.MkdirAll(opts.OutputDir, 0770); err != nil { + return fmt.Errorf("failed to create output directory %q: %v", opts.OutputDir, err) + } + + if opts.GenerateThrift { + if err := runThrift(opts.InputFile, opts.OutputDir); err != nil { + return fmt.Errorf("failed to run thrift for file %q: %v", opts.InputFile, err) + } + } + + allParsed, err := parseFile(opts.InputFile) + if err != nil { + return fmt.Errorf("failed to parse file %q: %v", opts.InputFile, err) + } + + allTemplates, err := parseTemplates(opts.SkipTChannel, opts.TemplateFiles) + if err != nil { + return fmt.Errorf("failed to parse templates: %v", err) + } + + for filename, v := range allParsed { + pkg := getNamespace(filename, v.ast) + + for _, template := range allTemplates { + outputFile := filepath.Join(opts.OutputDir, pkg, template.outputFile(pkg)) + if err := generateCode(outputFile, template, pkg, v); err != nil { + return err + } + } + } + + return nil +} + +type parseState struct { + ast *parser.Thrift + namespace string + global *State + services []*Service +} + +// parseTemplates returns a list of Templates that must be rendered given the template files. +func parseTemplates(skipTChannel bool, templateFiles []string) ([]*Template, error) { + var templates []*Template + + if !skipTChannel { + templates = append(templates, &Template{ + name: "tchan", + template: template.Must(parseTemplate(tchannelTmpl)), + }) + } + + for _, f := range templateFiles { + t, err := parseTemplateFile(f) + if err != nil { + return nil, err + } + + templates = append(templates, t) + } + + return templates, nil +} + +func parseFile(inputFile string) (map[string]parseState, error) { + parser := &parser.Parser{} + parsed, _, err := parser.ParseFile(inputFile) + if err != nil { + return nil, err + } + + allParsed := make(map[string]parseState) + for filename, v := range parsed { + state := newState(v, allParsed) + services, err := wrapServices(v, state) + if err != nil { + return nil, fmt.Errorf("wrap services failed: %v", err) + } + + namespace := getNamespace(filename, v) + allParsed[filename] = parseState{v, namespace, state, services} + } + setIncludes(allParsed) + return allParsed, setExtends(allParsed) +} + +func defaultPackageName(fullPath string) string { + filename := filepath.Base(fullPath) + file := strings.TrimSuffix(filename, filepath.Ext(filename)) + return strings.ToLower(file) +} + +func getNamespace(filename string, v *parser.Thrift) string { + if ns, ok := v.Namespaces["go"]; ok { + return ns + } + + // TODO(prashant): Remove any characters that are not valid in Go package names. + return defaultPackageName(filename) +} + +func generateCode(outputFile string, template *Template, pkg string, state parseState) error { + if outputFile == "" { + return fmt.Errorf("must speciy an output file") + } + if len(state.services) == 0 { + return nil + } + + td := TemplateData{ + Package: pkg, + AST: state.ast, + Includes: state.global.includes, + Services: state.services, + global: state.global, + Imports: imports{ + Thrift: *apacheThriftImport, + TChannel: tchannelThriftImport, + }, + } + return template.execute(outputFile, td) +} + +type stringSliceFlag []string + +func (s *stringSliceFlag) String() string { + return strings.Join(*s, ", ") +} + +func (s *stringSliceFlag) Set(in string) error { + *s = append(*s, in) + return nil +} + +// NewStringSliceFlag creates a new string slice flag. The default value is always nil. +func NewStringSliceFlag(name string, usage string) *[]string { + var ss stringSliceFlag + flag.Var(&ss, name, usage) + return (*[]string)(&ss) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/names.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/names.go new file mode 100644 index 00000000..4394d808 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/names.go @@ -0,0 +1,171 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +// This file implements go name generation for thrift identifiers. +// It has to match the Apache Thrift generated names. + +import "strings" + +// goKeywords taken from https://golang.org/ref/spec#Keywords (and added error). +var goKeywords = map[string]bool{ + "error": true, + "break": true, + "default": true, + "func": true, + "interface": true, + "select": true, + "case": true, + "defer": true, + "go": true, + "map": true, + "struct": true, + "chan": true, + "else": true, + "goto": true, + "package": true, + "switch": true, + "const": true, + "fallthrough": true, + "if": true, + "range": true, + "type": true, + "continue": true, + "for": true, + "import": true, + "return": true, + "var": true, +} + +// This set is taken from https://github.com/golang/lint/blob/master/lint.go#L692 +var commonInitialisms = map[string]bool{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} + +func goName(name string) string { + // Thrift Identifier from IDL: ( Letter | '_' ) ( Letter | Digit | '.' | '_' )* + // Go identifier from spec: letter { letter | unicode_digit } . + // Go letter allows underscore, so the only difference is period. However, periods cannot + // actaully be used - this seems to be a bug in the IDL. + if _, ok := goKeywords[name]; ok { + // The thrift compiler appends _a1 for any clashes with go keywords. + name += "_a1" + } + + name = camelCase(name, false /* publicName */) + return name +} + +// camelCase takes a name with underscores such as my_arg and returns camelCase (e.g. myArg). +// if publicName is true, then it returns UpperCamelCase. +// This method will also fix common initialisms (e.g. ID, API, etc). +func camelCase(name string, publicName bool) string { + parts := strings.Split(name, "_") + startAt := 1 + if publicName { + startAt = 0 + } + for i := startAt; i < len(parts); i++ { + name := parts[i] + if name == "" { + continue + } + + // For all words except the first, if the first letter of the word is + // uppercase, Thrift keeps the underscore. + if i > 0 && strings.ToUpper(name[0:1]) == name[0:1] { + name = "_" + name + } else { + name = strings.ToUpper(name[0:1]) + name[1:] + } + + if isInitialism := commonInitialisms[strings.ToUpper(name)]; isInitialism { + name = strings.ToUpper(name) + } + + parts[i] = name + } + return strings.Join(parts, "") +} + +func avoidThriftClash(name string) string { + if strings.HasSuffix(name, "Result") || + strings.HasSuffix(name, "Args") || + strings.HasPrefix(name, "New") { + return name + "_" + } + return name +} + +// goPublicName returns a go identifier that is exported. +func goPublicName(name string) string { + return camelCase(name, true /* publicName */) +} + +// goPublicFieldName returns the name of the field as used in a struct. +func goPublicFieldName(name string) string { + return avoidThriftClash(goPublicName(name)) +} + +var thriftToGo = map[string]string{ + "bool": "bool", + "byte": "int8", + "i16": "int16", + "i32": "int32", + "i64": "int64", + "double": "float64", + "string": "string", +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/tchannel-template.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/tchannel-template.go new file mode 100644 index 00000000..ac8ff0dc --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/tchannel-template.go @@ -0,0 +1,191 @@ +package main + +var tchannelTmpl = ` +// @generated Code generated by thrift-gen. Do not modify. + +// Package {{ .Package }} is generated code used to make or handle TChannel calls using Thrift. +package {{ .Package }} + +import ( +"fmt" + +athrift "{{ .Imports.Thrift }}" +"{{ .Imports.TChannel }}" + +{{ range .Includes }} + "{{ .Import }}" +{{ end }} +) + + +{{ range .Includes }} + var _ = {{ .Package }}.GoUnusedProtection__ +{{ end }} + + +// Interfaces for the service and client for the services defined in the IDL. + +{{ range .Services }} +// {{ .Interface }} is the interface that defines the server handler and client interface. +type {{ .Interface }} interface { + {{ if .HasExtends }} + {{ .ExtendsServicePrefix }}{{ .ExtendsService.Interface }} + + {{ end }} + {{ range .Methods }} + {{ .Name }}({{ .ArgList }}) {{ .RetType }} + {{ end }} +} +{{ end }} + +// Implementation of a client and service handler. + +{{/* Generate client and service implementations for the above interfaces. */}} +{{ range $svc := .Services }} +type {{ .ClientStruct }} struct { + {{ if .HasExtends }} + {{ .ExtendsServicePrefix }}{{ .ExtendsService.Interface }} + + {{ end }} + thriftService string + client thrift.TChanClient +} + + +func {{ .InheritedClientConstructor }}(thriftService string, client thrift.TChanClient) *{{ .ClientStruct }} { + return &{{ .ClientStruct }}{ + {{ if .HasExtends }} + {{ .ExtendsServicePrefix }}{{ .ExtendsService.InheritedClientConstructor }}(thriftService, client), + {{ end }} + thriftService, + client, + } +} + +// {{ .ClientConstructor }} creates a client that can be used to make remote calls. +func {{ .ClientConstructor }}(client thrift.TChanClient) {{ .Interface }} { + return {{ .InheritedClientConstructor }}("{{ .ThriftName }}", client) +} + +{{ range .Methods }} + func (c *{{ $svc.ClientStruct }}) {{ .Name }}({{ .ArgList }}) {{ .RetType }} { + var resp {{ .ResultType }} + args := {{ .ArgsType }}{ + {{ range .Arguments }} + {{ .ArgStructName }}: {{ .Name }}, + {{ end }} + } + success, err := c.client.Call(ctx, c.thriftService, "{{ .ThriftName }}", &args, &resp) + if err == nil && !success { + switch { + {{ range .Exceptions }} + case resp.{{ .ArgStructName }} != nil: + err = resp.{{ .ArgStructName }} + {{ end }} + default: + err = fmt.Errorf("received no result or unknown exception for {{ .ThriftName }}") + } + } + + {{ if .HasReturn }} + return resp.GetSuccess(), err + {{ else }} + return err + {{ end }} + } +{{ end }} + +type {{ .ServerStruct }} struct { + {{ if .HasExtends }} + thrift.TChanServer + + {{ end }} + handler {{ .Interface }} +} + +// {{ .ServerConstructor }} wraps a handler for {{ .Interface }} so it can be +// registered with a thrift.Server. +func {{ .ServerConstructor }}(handler {{ .Interface }}) thrift.TChanServer { + return &{{ .ServerStruct }}{ + {{ if .HasExtends }} + {{ .ExtendsServicePrefix }}{{ .ExtendsService.ServerConstructor }}(handler), + {{ end }} + handler, + } +} + +func (s *{{ .ServerStruct }}) Service() string { + return "{{ .ThriftName }}" +} + +func (s *{{ .ServerStruct }}) Methods() []string { + return []string{ + {{ range .Methods }} + "{{ .ThriftName }}", + {{ end }} + {{ range .InheritedMethods }} + "{{ . }}", + {{ end }} + } +} + +func (s *{{ .ServerStruct }}) Handle(ctx {{ contextType }}, methodName string, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + switch methodName { + {{ range .Methods }} + case "{{ .ThriftName }}": + return s.{{ .HandleFunc }}(ctx, protocol) + {{ end }} + {{ range .InheritedMethods }} + case "{{ . }}": + return s.TChanServer.Handle(ctx, methodName, protocol) + {{ end }} + default: + return false, nil, fmt.Errorf("method %v not found in service %v", methodName, s.Service()) + } +} + +{{ range .Methods }} + func (s *{{ $svc.ServerStruct }}) {{ .HandleFunc }}(ctx {{ contextType }}, protocol athrift.TProtocol) (bool, athrift.TStruct, error) { + var req {{ .ArgsType }} + var res {{ .ResultType }} + + if err := req.Read(protocol); err != nil { + return false, nil, err + } + + {{ if .HasReturn }} + r, err := + {{ else }} + err := + {{ end }} + s.handler.{{ .Name }}({{ .CallList "req" }}) + + if err != nil { + {{ if .HasExceptions }} + switch v := err.(type) { + {{ range .Exceptions }} + case {{ .ArgType }}: + if v == nil { + return false, nil, fmt.Errorf("Handler for {{ .Name }} returned non-nil error type {{ .ArgType }} but nil value") + } + res.{{ .ArgStructName }} = v + {{ end }} + default: + return false, nil, err + } + {{ else }} + return false, nil, err + {{ end }} + } else { + {{ if .HasReturn }} + res.Success = {{ .WrapResult "r" }} + {{ end }} + } + + return err == nil, &res, nil + } + +{{ end }} + +{{ end }} +` diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/template.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/template.go new file mode 100644 index 00000000..362083c7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/template.go @@ -0,0 +1,111 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// thrift-gen generates code for Thrift services that can be used with the +// uber/tchannel/thrift package. thrift-gen generated code relies on the +// Apache Thrift generated code for serialization/deserialization, and should +// be a part of the generated code's package. +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os/exec" + "text/template" +) + +// Template represents a single thrift-gen template that will be used to generate code. +type Template struct { + name string + template *template.Template +} + +// dummyGoType is a function to be passed to the test/template package as the named +// function 'goType'. This named function is overwritten by an actual implementation +// specific to the thrift file being rendered at the time of rendering. +func dummyGoType() string { + return "" +} + +func parseTemplate(contents string) (*template.Template, error) { + funcs := map[string]interface{}{ + "contextType": contextType, + "goPrivateName": goName, + "goPublicName": goPublicName, + "goType": dummyGoType, + } + return template.New("thrift-gen").Funcs(funcs).Parse(contents) +} + +func parseTemplateFile(file string) (*Template, error) { + file, err := ResolveWithGoPath(file) + if err != nil { + return nil, err + } + + bytes, err := ioutil.ReadFile(file) + if err != nil { + return nil, fmt.Errorf("failed to read file %q: %v", file, err) + } + + t, err := parseTemplate(string(bytes)) + if err != nil { + return nil, fmt.Errorf("failed to parse template in file %q: %v", file, err) + } + + return &Template{defaultPackageName(file), t}, nil +} + +func contextType() string { + return "thrift.Context" +} + +func cleanGeneratedCode(generated []byte) []byte { + generated = nlSpaceNL.ReplaceAll(generated, []byte("\n")) + return generated +} + +// withStateFuncs adds functions to the template that are dependent upon state. +func (t *Template) withStateFuncs(td TemplateData) *template.Template { + return t.template.Funcs(map[string]interface{}{ + "goType": td.global.goType, + }) +} + +func (t *Template) execute(outputFile string, td TemplateData) error { + buf := &bytes.Buffer{} + if err := t.withStateFuncs(td).Execute(buf, td); err != nil { + return fmt.Errorf("failed to execute template: %v", err) + } + + generated := cleanGeneratedCode(buf.Bytes()) + if err := ioutil.WriteFile(outputFile, generated, 0660); err != nil { + return fmt.Errorf("cannot write output file %q: %v", outputFile, err) + } + + // Run gofmt on the file (ignore any errors) + exec.Command("gofmt", "-w", outputFile).Run() + return nil +} + +func (t *Template) outputFile(pkg string) string { + return fmt.Sprintf("%v-%v.go", t.name, pkg) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/binary.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/binary.thrift new file mode 100644 index 00000000..87ea7597 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/binary.thrift @@ -0,0 +1,11 @@ +typedef binary Z + +struct S { + 1: binary s1 + 2: Z s2 +} + +service Test { + binary M1(1: binary arg1) + S M2(1: binary arg1, 2: S arg2) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/byte.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/byte.thrift new file mode 100644 index 00000000..85d140d7 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/byte.thrift @@ -0,0 +1,11 @@ +typedef byte Z + +struct S { + 1: byte s1 + 2: Z s2 +} + +service Test { + byte M1(1: byte arg1) + S M2(1: byte arg1, 2: S arg2) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/gokeywords.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/gokeywords.thrift new file mode 100644 index 00000000..e7e9517e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/gokeywords.thrift @@ -0,0 +1,19 @@ +// Test to make sure that reserved names are handled correctly. + +exception Exception { + 1: required string message +} + +struct Result { + 1: required string error + 2: required i32 func + 3: required i32 chan + 4: required i32 result + 5: required i64 newRole +} + +service func { + string func1() + void func(1: i32 func) + Result chan(1: i32 func, 2: i32 result) throws (1: Exception error) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/a/shared.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/a/shared.thrift new file mode 100644 index 00000000..3fa12338 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/a/shared.thrift @@ -0,0 +1,9 @@ +namespace go a_shared + +include "../b/shared.thrift" + +typedef shared.b_string a_string + +service AShared extends shared.BShared { + bool healthA() +} \ No newline at end of file diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/b/shared.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/b/shared.thrift new file mode 100644 index 00000000..98d8bf16 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/b/shared.thrift @@ -0,0 +1,7 @@ +namespace go b_shared + +typedef string b_string + +service BShared { + bool healthB() +} \ No newline at end of file diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/namespace.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/namespace.thrift new file mode 100644 index 00000000..e89126df --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/namespace/namespace.thrift @@ -0,0 +1,5 @@ +include "a/shared.thrift" + +service Foo extends shared.AShared { + void Foo(1: shared.a_string str) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared.thrift new file mode 100644 index 00000000..9ebcd0d6 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared.thrift @@ -0,0 +1,7 @@ +include "shared2.thrift" + +typedef string a_shared_string + +typedef shared2.a_shared2_string a_shared_string2 + +typedef shared2.a_shared2_mystruct a_shared_mystruct2 diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared2.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared2.thrift new file mode 100644 index 00000000..9ca4828c --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/shared2.thrift @@ -0,0 +1,7 @@ +typedef string a_shared2_string + +struct MyStruct { + 1: string name +} + +typedef MyStruct a_shared2_mystruct diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/simple.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/simple.thrift new file mode 100644 index 00000000..75e93bb9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/simple/simple.thrift @@ -0,0 +1,6 @@ +include "shared.thrift" +include "shared2.thrift" + +service Foo { + void Foo(1: shared.a_shared_string str, 2: shared.a_shared_string2 str2, 3: shared2.MyStruct str3) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/shared.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/shared.thrift new file mode 100644 index 00000000..5af888a9 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/shared.thrift @@ -0,0 +1,10 @@ +typedef string UUID + + +struct Health { + 1: bool ok +} + +service FooBase { + UUID getUUID() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/svc_extend.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/svc_extend.thrift new file mode 100644 index 00000000..c9191f7a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/include_test/svc_extend/svc_extend.thrift @@ -0,0 +1,12 @@ +include "shared.thrift" + +service Foo extends shared.FooBase { + shared.UUID getMyUUID(1: shared.UUID uuid, 2: shared.Health health) + shared.Health health(1: shared.UUID uuid, 2: shared.Health health) +} + +//Go code: svc_extend/test.go +// package svc_extend +// var _ = TChanFoo(nil).GetMyUUID +// var _ = TChanFoo(nil).Health +// var _ = TChanFoo(nil).GetUUID diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file1.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file1.thrift new file mode 100644 index 00000000..b4d0c103 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file1.thrift @@ -0,0 +1,5 @@ +include "file2.thrift" + +service Foo1 { + void M() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file2.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file2.thrift new file mode 100644 index 00000000..f77b01a3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/multi_test/file2.thrift @@ -0,0 +1,4 @@ + +service Foo2 { + void M() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/service_extend.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/service_extend.thrift new file mode 100644 index 00000000..e50e36f5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/service_extend.thrift @@ -0,0 +1,22 @@ +struct S { + 1: binary s1 +} + +service S1 { + binary M1(1: binary bits) +} + +service S2 extends S1 { + S M2(1: optional S s, 2: optional i32 i) +} + +service S3 extends S2 { + void M3() +} + +//Go code: service_extend/test.go +// package service_extend +// var _ = TChanS3(nil).M1 +// var _ = TChanS3(nil).M2 +// var _ = TChanS3(nil).M3 +// var _ int32 = S2M2Args{}.I diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/sets.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/sets.thrift new file mode 100644 index 00000000..84172743 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/sets.thrift @@ -0,0 +1,6 @@ + +service Test { + list getInts(1: list nums) + set getIntSet(1: set nums) + map getIntMap(1: map nums) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/test1.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/test1.thrift new file mode 100644 index 00000000..d3d177ee --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/test1.thrift @@ -0,0 +1,26 @@ + +struct FakeStruct { + 1: i64 id + 2: i64 user_id +} + +service Fake { + // Test initialisms in the method name (as well as name clashes). + void id_get() + void id() + void get_id() + void get_Id() + void get_ID() + + // Test initialisms in parameter names. + void initialisms_in_args1(1: string LoL_http_TEST_Name) + void initialisms_in_args2(1: string user_id) + void initialisms_in_args3(1: string id) + + + // Test casing for method names + void fAkE(1: i32 func, 2: i32 pkg, 3: FakeStruct fakeStruct) + + void MyArgs() + void MyResult() +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/typedefs.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/typedefs.thrift new file mode 100644 index 00000000..7ee64bb1 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/typedefs.thrift @@ -0,0 +1,30 @@ + +typedef i64 X +typedef X Z +typedef X Y +typedef i64 i +typedef i64 func + +struct S { + 1: X x + 2: Y y + 3: Z z +} + +typedef S ST + +enum Operator +{ + ADD = 1, + SUBTRACT = 2 +} + +service Test { + Y M1(1: X arg1, 2: i arg2) + X M2(1: Y arg1) + Z M3(1: X arg1) + S M4(1: S arg1, 2: Operator op) + + // Thrift compiler is broken on this case. + // ST M5(1: ST arg1, 2: S arg2) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/union.thrift b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/union.thrift new file mode 100644 index 00000000..f6ac936a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/test_files/union.thrift @@ -0,0 +1,8 @@ +union Constraint { + 1: i32 intV + 2: string stringV +} + +// thrift-gen generated code assumes there is a service. +service Test {} + diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/typestate.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/typestate.go new file mode 100644 index 00000000..719f730d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/typestate.go @@ -0,0 +1,152 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "strings" + + "github.com/samuel/go-thrift/parser" +) + +// State is global Thrift state for a file with type information. +type State struct { + // typedefs is a map from a typedef name to the underlying type. + typedefs map[string]*parser.Type + + // includes is a map from Thrift base name to the include. + includes map[string]*Include + + // all is used for includes. + all map[string]parseState +} + +// newState parses the type information for a parsed Thrift file and returns the state. +func newState(v *parser.Thrift, all map[string]parseState) *State { + typedefs := make(map[string]*parser.Type) + for k, v := range v.Typedefs { + typedefs[k] = v.Type + } + + // Enums are typedefs to an int64. + i64Type := &parser.Type{Name: "i64"} + for k := range v.Enums { + typedefs[k] = i64Type + } + + return &State{typedefs, nil, all} +} + +func setIncludes(all map[string]parseState) { + for _, v := range all { + v.global.includes = createIncludes(v.ast, all) + } +} + +func (s *State) isBasicType(thriftType string) bool { + _, ok := thriftToGo[thriftType] + return ok +} + +// rootType recurses through typedefs and returns the underlying type. +func (s *State) rootType(thriftType *parser.Type) *parser.Type { + if state, newType, include := s.checkInclude(thriftType); include != nil { + return state.rootType(newType) + } + + if v, ok := s.typedefs[thriftType.Name]; ok { + return s.rootType(v) + } + return thriftType +} + +// checkInclude will check if the type is an included type, and if so, return the +// state and type from the state for that file. +func (s *State) checkInclude(thriftType *parser.Type) (*State, *parser.Type, *Include) { + parts := strings.SplitN(thriftType.Name, ".", 2) + if len(parts) < 2 { + return nil, nil, nil + } + + newType := *thriftType + newType.Name = parts[1] + + include := s.includes[parts[0]] + state := s.all[include.file] + return state.global, &newType, include +} + +// isResultPointer returns whether the result for this method is a pointer. +func (s *State) isResultPointer(thriftType *parser.Type) bool { + _, basicGoType := thriftToGo[s.rootType(thriftType).Name] + return !basicGoType +} + +// goType returns the Go type name for the given thrift type. +func (s *State) goType(thriftType *parser.Type) string { + return s.goTypePrefix("", thriftType) +} + +// goTypePrefix returns the Go type name for the given thrift type with the prefix. +func (s *State) goTypePrefix(prefix string, thriftType *parser.Type) string { + switch thriftType.Name { + case "binary": + return "[]byte" + case "list": + return "[]" + s.goType(thriftType.ValueType) + case "set": + return "map[" + s.goType(thriftType.ValueType) + "]bool" + case "map": + return "map[" + s.goType(thriftType.KeyType) + "]" + s.goType(thriftType.ValueType) + } + + // If the type is imported, then ignore the package. + if state, newType, include := s.checkInclude(thriftType); include != nil { + return state.goTypePrefix(include.Package()+".", newType) + } + + // If the type is a direct Go type, use that. + if goType, ok := thriftToGo[thriftType.Name]; ok { + return goType + } + + goThriftName := goPublicFieldName(thriftType.Name) + goThriftName = prefix + goThriftName + + // Check if the type has a typedef to the direct Go type. + rootType := s.rootType(thriftType) + if _, ok := thriftToGo[rootType.Name]; ok { + return goThriftName + } + if rootType.Name == "list" || + rootType.Name == "set" || + rootType.Name == "map" { + return goThriftName + } + + // If it's a typedef to another struct, then the typedef is defined as a pointer + // so we do not want the pointer type here. + if rootType != thriftType { + return goThriftName + } + + // If it's not a typedef for a basic type, we use a pointer. + return "*" + goThriftName +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/validate.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/validate.go new file mode 100644 index 00000000..a35957a0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/validate.go @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "fmt" + + "github.com/samuel/go-thrift/parser" +) + +// Validate validates that the given spec is supported by thrift-gen. +func Validate(svc *parser.Service) error { + for _, m := range svc.Methods { + if err := validateMethod(svc, m); err != nil { + return err + } + } + return nil +} + +func validateMethod(svc *parser.Service, m *parser.Method) error { + if m.Oneway { + return fmt.Errorf("oneway methods are not supported: %s.%v", svc.Name, m.Name) + } + for _, arg := range m.Arguments { + if arg.Optional { + // Go treats argument structs as "Required" in the generated code interface. + arg.Optional = false + } + } + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/wrap.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/wrap.go new file mode 100644 index 00000000..a40d6dcb --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift-gen/wrap.go @@ -0,0 +1,271 @@ +package main + +import ( + "fmt" + "sort" + "strings" + + "github.com/samuel/go-thrift/parser" +) + +type byServiceName []*Service + +func (l byServiceName) Len() int { return len(l) } +func (l byServiceName) Less(i, j int) bool { return l[i].Service.Name < l[j].Service.Name } +func (l byServiceName) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + +func wrapServices(v *parser.Thrift, state *State) ([]*Service, error) { + var services []*Service + for _, s := range v.Services { + if err := Validate(s); err != nil { + return nil, err + } + + services = append(services, &Service{ + Service: s, + state: state, + }) + } + + // Have a stable ordering for services so code generation is consistent. + sort.Sort(byServiceName(services)) + return services, nil +} + +// Service is a wrapper for parser.Service. +type Service struct { + *parser.Service + state *State + + // ExtendsService and ExtendsPrefix are set in `setExtends`. + ExtendsService *Service + ExtendsPrefix string + + // methods is a cache of all methods. + methods []*Method + // inheritedMethods is a list of inherited method names. + inheritedMethods []string +} + +// ThriftName returns the thrift identifier for this service. +func (s *Service) ThriftName() string { + return s.Service.Name +} + +// Interface returns the name of the interface representing the service. +func (s *Service) Interface() string { + return "TChan" + goPublicName(s.Name) +} + +// ClientStruct returns the name of the unexported struct that satisfies the interface as a client. +func (s *Service) ClientStruct() string { + return "tchan" + goPublicName(s.Name) + "Client" +} + +// ClientConstructor returns the name of the constructor used to create a client. +func (s *Service) ClientConstructor() string { + return "NewTChan" + goPublicName(s.Name) + "Client" +} + +// InheritedClientConstructor returns the name of the constructor used by the generated code +// for inherited services. This allows the parent service to set the service name that should +// be used. +func (s *Service) InheritedClientConstructor() string { + return "NewTChan" + goPublicName(s.Name) + "InheritedClient" +} + +// ServerStruct returns the name of the unexported struct that satisfies TChanServer. +func (s *Service) ServerStruct() string { + return "tchan" + goPublicName(s.Name) + "Server" +} + +// ServerConstructor returns the name of the constructor used to create the TChanServer interface. +func (s *Service) ServerConstructor() string { + return "NewTChan" + goPublicName(s.Name) + "Server" +} + +// HasExtends returns whether this service extends another service. +func (s *Service) HasExtends() bool { + return s.ExtendsService != nil +} + +// ExtendsServicePrefix returns a package selector (if any) for the extended service. +func (s *Service) ExtendsServicePrefix() string { + if dotIndex := strings.Index(s.Extends, "."); dotIndex > 0 { + return s.ExtendsPrefix + } + return "" +} + +type byMethodName []*Method + +func (l byMethodName) Len() int { return len(l) } +func (l byMethodName) Less(i, j int) bool { return l[i].Method.Name < l[j].Method.Name } +func (l byMethodName) Swap(i, j int) { l[i], l[j] = l[j], l[i] } + +// Methods returns the methods on this service, not including methods from inherited services. +func (s *Service) Methods() []*Method { + if s.methods != nil { + return s.methods + } + + for _, m := range s.Service.Methods { + s.methods = append(s.methods, &Method{m, s, s.state}) + } + sort.Sort(byMethodName(s.methods)) + return s.methods +} + +// InheritedMethods returns names for inherited methods on this service. +func (s *Service) InheritedMethods() []string { + if s.inheritedMethods != nil { + return s.inheritedMethods + } + + for svc := s.ExtendsService; svc != nil; svc = svc.ExtendsService { + for m := range svc.Service.Methods { + s.inheritedMethods = append(s.inheritedMethods, m) + } + } + sort.Strings(s.inheritedMethods) + + return s.inheritedMethods +} + +// Method is a wrapper for parser.Method. +type Method struct { + *parser.Method + + service *Service + state *State +} + +// ThriftName returns the thrift identifier for this function. +func (m *Method) ThriftName() string { + return m.Method.Name +} + +// Name returns the go method name. +func (m *Method) Name() string { + return goPublicName(m.Method.Name) +} + +// HandleFunc is the go method name for the handle function which decodes the payload. +func (m *Method) HandleFunc() string { + return "handle" + goPublicName(m.Method.Name) +} + +// Arguments returns the argument declarations for this method. +func (m *Method) Arguments() []*Field { + var args []*Field + for _, f := range m.Method.Arguments { + args = append(args, &Field{f, m.state}) + } + return args +} + +// Exceptions returns the exceptions that this method may return. +func (m *Method) Exceptions() []*Field { + var args []*Field + for _, f := range m.Method.Exceptions { + args = append(args, &Field{f, m.state}) + } + return args +} + +// HasReturn returns false if this method is declared as void in the Thrift file. +func (m *Method) HasReturn() bool { + return m.Method.ReturnType != nil +} + +// HasExceptions returns true if this method has +func (m *Method) HasExceptions() bool { + return len(m.Method.Exceptions) > 0 +} + +func (m *Method) argResPrefix() string { + return goPublicName(m.service.Name) + m.Name() +} + +// ArgsType returns the Go name for the struct used to encode the method's arguments. +func (m *Method) ArgsType() string { + return m.argResPrefix() + "Args" +} + +// ResultType returns the Go name for the struct used to encode the method's result. +func (m *Method) ResultType() string { + return m.argResPrefix() + "Result" +} + +// ArgList returns the argument list for the function. +func (m *Method) ArgList() string { + args := []string{"ctx " + contextType()} + for _, arg := range m.Arguments() { + args = append(args, arg.Declaration()) + } + return strings.Join(args, ", ") +} + +// CallList creates the call to a function satisfying Interface from an Args struct. +func (m *Method) CallList(reqStruct string) string { + args := []string{"ctx"} + for _, arg := range m.Arguments() { + args = append(args, reqStruct+"."+arg.ArgStructName()) + } + return strings.Join(args, ", ") +} + +// RetType returns the go return type of the method. +func (m *Method) RetType() string { + if !m.HasReturn() { + return "error" + } + return fmt.Sprintf("(%v, %v)", m.state.goType(m.Method.ReturnType), "error") +} + +// WrapResult wraps the result variable before being used in the result struct. +func (m *Method) WrapResult(respVar string) string { + if !m.HasReturn() { + panic("cannot wrap a return when there is no return mode") + } + + if m.state.isResultPointer(m.ReturnType) { + return respVar + } + return "&" + respVar +} + +// ReturnWith takes the result name and the error name, and generates the return expression. +func (m *Method) ReturnWith(respName string, errName string) string { + if !m.HasReturn() { + return errName + } + return fmt.Sprintf("%v, %v", respName, errName) +} + +// Field is a wrapper for parser.Field. +type Field struct { + *parser.Field + + state *State +} + +// Declaration returns the declaration for this field. +func (a *Field) Declaration() string { + return fmt.Sprintf("%s %s", a.Name(), a.ArgType()) +} + +// Name returns the field name. +func (a *Field) Name() string { + return goName(a.Field.Name) +} + +// ArgType returns the Go type for the given field. +func (a *Field) ArgType() string { + return a.state.goType(a.Type) +} + +// ArgStructName returns the name of this field in the Args struct generated by thrift. +func (a *Field) ArgStructName() string { + return goPublicFieldName(a.Field.Name) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift_bench_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift_bench_test.go new file mode 100644 index 00000000..ca1f9e73 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift_bench_test.go @@ -0,0 +1,111 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift_test + +import ( + "flag" + "testing" + "time" + + "github.com/uber/tchannel-go/benchmark" + "github.com/uber/tchannel-go/testutils" + + "github.com/stretchr/testify/require" + "github.com/uber-go/atomic" +) + +const callBatch = 100 + +var ( + useHyperbahn = flag.Bool("useHyperbahn", false, "Whether to advertise and route requests through Hyperbahn") + hyperbahnNodes = flag.String("hyperbahn-nodes", "127.0.0.1:21300,127.0.0.1:21301", "Comma-separated list of Hyperbahn nodes") + requestSize = flag.Int("request-size", 4, "Call payload size") + timeout = flag.Duration("call-timeout", time.Second, "Timeout for each call") +) + +func init() { + benchmark.BenchmarkDir = "../benchmark/" +} + +func BenchmarkBothSerial(b *testing.B) { + server := benchmark.NewServer() + client := benchmark.NewClient( + []string{server.HostPort()}, + benchmark.WithTimeout(*timeout), + benchmark.WithRequestSize(*requestSize), + ) + + b.ResetTimer() + + for _, calls := range testutils.Batch(b.N, callBatch) { + if _, err := client.ThriftCall(calls); err != nil { + b.Errorf("Call failed: %v", err) + } + } +} + +func BenchmarkInboundSerial(b *testing.B) { + server := benchmark.NewServer() + client := benchmark.NewClient( + []string{server.HostPort()}, + benchmark.WithTimeout(*timeout), + benchmark.WithExternalProcess(), + benchmark.WithRequestSize(*requestSize), + ) + defer client.Close() + require.NoError(b, client.Warmup(), "Warmup failed") + + b.ResetTimer() + for _, calls := range testutils.Batch(b.N, callBatch) { + if _, err := client.ThriftCall(calls); err != nil { + b.Errorf("Call failed: %v", err) + } + } +} + +func BenchmarkInboundParallel(b *testing.B) { + server := benchmark.NewServer() + + var reqCounter atomic.Int32 + started := time.Now() + + b.RunParallel(func(pb *testing.PB) { + client := benchmark.NewClient( + []string{server.HostPort()}, + benchmark.WithTimeout(*timeout), + benchmark.WithExternalProcess(), + benchmark.WithRequestSize(*requestSize), + ) + defer client.Close() + require.NoError(b, client.Warmup(), "Warmup failed") + + for pb.Next() { + if _, err := client.ThriftCall(100); err != nil { + b.Errorf("Call failed: %v", err) + } + reqCounter.Add(100) + } + }) + + duration := time.Since(started) + reqs := reqCounter.Load() + b.Logf("Requests: %v RPS: %v", reqs, float64(reqs)/duration.Seconds()) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/thrift_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/thrift_test.go new file mode 100644 index 00000000..faa625d5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/thrift_test.go @@ -0,0 +1,534 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift_test + +import ( + "errors" + "fmt" + "strings" + "testing" + "time" + + "golang.org/x/net/context" + + // Test is in a separate package to avoid circular dependencies. + . "github.com/uber/tchannel-go/thrift" + + tchannel "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/testutils" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + "github.com/uber/tchannel-go/thrift/mocks" + + "github.com/apache/thrift/lib/go/thrift" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +// Generate the service mocks using go generate. +//go:generate mockery -dir ./gen-go/test -name TChanSimpleService +//go:generate mockery -dir ./gen-go/test -name TChanSecondService + +type testArgs struct { + server *Server + s1 *mocks.TChanSimpleService + s2 *mocks.TChanSecondService + c1 gen.TChanSimpleService + c2 gen.TChanSecondService + + serverCh *tchannel.Channel + clientCh *tchannel.Channel +} + +func ctxArg() mock.AnythingOfTypeArgument { + return mock.AnythingOfType("tchannel.headerCtx") +} + +func TestThriftArgs(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + arg := &gen.Data{ + B1: true, + S2: "str", + I3: 102, + } + ret := &gen.Data{ + B1: false, + S2: "return-str", + I3: 105, + } + + args.s1.On("Call", ctxArg(), arg).Return(ret, nil) + got, err := args.c1.Call(ctx, arg) + require.NoError(t, err) + assert.Equal(t, ret, got) + }) +} + +func TestRequest(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(nil) + require.NoError(t, args.c1.Simple(ctx)) + }) +} + +func TestRetryRequest(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + count := 0 + args.s1.On("Simple", ctxArg()).Return(tchannel.ErrServerBusy). + Run(func(args mock.Arguments) { + count++ + }) + require.Error(t, args.c1.Simple(ctx), "Simple expected to fail") + assert.Equal(t, 5, count, "Expected Simple to be retried 5 times") + }) +} + +func TestRequestSubChannel(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + tchan := testutils.NewServer(t, testutils.NewOpts().SetServiceName("svc1")) + defer tchan.Close() + + clientCh := testutils.NewClient(t, nil) + defer clientCh.Close() + clientCh.Peers().Add(tchan.PeerInfo().HostPort) + + tests := []tchannel.Registrar{tchan, tchan.GetSubChannel("svc2"), tchan.GetSubChannel("svc3")} + for _, ch := range tests { + mockHandler := new(mocks.TChanSecondService) + server := NewServer(ch) + server.Register(gen.NewTChanSecondServiceServer(mockHandler)) + + client := NewClient(clientCh, ch.ServiceName(), nil) + secondClient := gen.NewTChanSecondServiceClient(client) + + echoArg := ch.ServiceName() + echoRes := echoArg + "-echo" + mockHandler.On("Echo", ctxArg(), echoArg).Return(echoRes, nil) + res, err := secondClient.Echo(ctx, echoArg) + assert.NoError(t, err, "Echo failed") + assert.Equal(t, echoRes, res) + } +} + +func TestLargeRequest(t *testing.T) { + arg := testutils.RandString(100000) + res := strings.ToLower(arg) + + fmt.Println(len(arg)) + withSetup(t, func(ctx Context, args testArgs) { + args.s2.On("Echo", ctxArg(), arg).Return(res, nil) + + got, err := args.c2.Echo(ctx, arg) + if assert.NoError(t, err, "Echo got error") { + assert.Equal(t, res, got, "Echo got unexpected response") + } + }) +} + +func TestThriftError(t *testing.T) { + thriftErr := &gen.SimpleErr{ + Message: "this is the error", + } + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(thriftErr) + got := args.c1.Simple(ctx) + require.Error(t, got) + require.Equal(t, thriftErr, got) + }) +} + +func TestThriftUnknownError(t *testing.T) { + thriftErr := &gen.NewErr_{ + Message: "new error", + } + + withSetup(t, func(ctx Context, args testArgs) { + // When "Simple" is called, actually call a separate similar looking method + // SimpleFuture which has a new exception that the client side of Simple + // does not know how to handle. + args.s1.On("SimpleFuture", ctxArg()).Return(thriftErr) + tClient := NewClient(args.clientCh, args.serverCh.ServiceName(), nil) + rewriteMethodClient := rewriteMethodClient{tClient, "SimpleFuture"} + simpleClient := gen.NewTChanSimpleServiceClient(rewriteMethodClient) + + got := simpleClient.Simple(ctx) + require.Error(t, got) + assert.Contains(t, got.Error(), "no result or unknown exception") + }) +} + +func TestThriftNilErr(t *testing.T) { + var thriftErr *gen.SimpleErr + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(thriftErr) + got := args.c1.Simple(ctx) + require.Error(t, got) + require.Contains(t, got.Error(), "non-nil error type") + require.Contains(t, got.Error(), "nil value") + }) +} + +func TestThriftDecodeEmptyFrameServer(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(nil) + + call, err := args.clientCh.BeginCall(ctx, args.serverCh.PeerInfo().HostPort, args.serverCh.ServiceName(), "SimpleService::Simple", nil) + require.NoError(t, err, "Failed to BeginCall") + + withWriter(t, call.Arg2Writer, func(w tchannel.ArgWriter) error { + if err := WriteHeaders(w, nil); err != nil { + return err + } + + return w.Flush() + }) + + withWriter(t, call.Arg3Writer, func(w tchannel.ArgWriter) error { + if err := WriteStruct(w, &gen.SimpleServiceSimpleArgs{}); err != nil { + return err + } + + return w.Flush() + }) + + response := call.Response() + withReader(t, response.Arg2Reader, func(r tchannel.ArgReader) error { + _, err := ReadHeaders(r) + return err + }) + + var res gen.SimpleServiceSimpleResult + withReader(t, response.Arg3Reader, func(r tchannel.ArgReader) error { + return ReadStruct(r, &res) + }) + + assert.False(t, res.IsSetSimpleErr(), "Expected no error") + }) +} + +func TestThriftDecodeEmptyFrameClient(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + handler := func(ctx context.Context, call *tchannel.InboundCall) { + withReader(t, call.Arg2Reader, func(r tchannel.ArgReader) error { + _, err := ReadHeaders(r) + return err + }) + + withReader(t, call.Arg3Reader, func(r tchannel.ArgReader) error { + req := &gen.SimpleServiceSimpleArgs{} + return ReadStruct(r, req) + }) + + response := call.Response() + withWriter(t, response.Arg2Writer, func(w tchannel.ArgWriter) error { + if err := WriteHeaders(w, nil); err != nil { + return err + } + + return w.Flush() + }) + + withWriter(t, response.Arg3Writer, func(w tchannel.ArgWriter) error { + if err := WriteStruct(w, &gen.SimpleServiceSimpleResult{}); err != nil { + return err + } + + return w.Flush() + }) + } + + args.serverCh.Register(tchannel.HandlerFunc(handler), "SimpleService::Simple") + require.NoError(t, args.c1.Simple(ctx)) + }) +} + +func TestUnknownError(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(errors.New("unexpected err")) + got := args.c1.Simple(ctx) + require.Error(t, got) + require.Equal(t, tchannel.NewSystemError(tchannel.ErrCodeUnexpected, "unexpected err"), got) + }) +} + +func TestMultiple(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(nil) + args.s2.On("Echo", ctxArg(), "test1").Return("test2", nil) + + require.NoError(t, args.c1.Simple(ctx)) + res, err := args.c2.Echo(ctx, "test1") + require.NoError(t, err) + require.Equal(t, "test2", res) + }) +} + +func TestHeaders(t *testing.T) { + reqHeaders := map[string]string{"header1": "value1", "header2": "value2"} + respHeaders := map[string]string{"resp1": "value1-resp", "resp2": "value2-resp"} + + withSetup(t, func(ctx Context, args testArgs) { + args.s1.On("Simple", ctxArg()).Return(nil).Run(func(args mock.Arguments) { + ctx := args.Get(0).(Context) + assert.Equal(t, reqHeaders, ctx.Headers(), "request headers mismatch") + ctx.SetResponseHeaders(respHeaders) + }) + + ctx = WithHeaders(ctx, reqHeaders) + require.NoError(t, args.c1.Simple(ctx)) + assert.Equal(t, respHeaders, ctx.ResponseHeaders(), "response headers mismatch") + }) +} + +func TestClientHostPort(t *testing.T) { + ctx, cancel := NewContext(time.Second) + defer cancel() + + s1ch := testutils.NewServer(t, nil) + s2ch := testutils.NewServer(t, nil) + defer s1ch.Close() + defer s2ch.Close() + + s1ch.Peers().Add(s2ch.PeerInfo().HostPort) + s2ch.Peers().Add(s1ch.PeerInfo().HostPort) + + mock1, mock2 := new(mocks.TChanSecondService), new(mocks.TChanSecondService) + NewServer(s1ch).Register(gen.NewTChanSecondServiceServer(mock1)) + NewServer(s2ch).Register(gen.NewTChanSecondServiceServer(mock2)) + + // When we call using a normal client, it can only call the other server (only peer). + c1 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s2ch.PeerInfo().ServiceName, nil)) + mock2.On("Echo", ctxArg(), "call1").Return("call1", nil) + res, err := c1.Echo(ctx, "call1") + assert.NoError(t, err, "call1 failed") + assert.Equal(t, "call1", res) + + // When we call using a client that specifies host:port, it should call that server. + c2 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s1ch.PeerInfo().ServiceName, &ClientOptions{ + HostPort: s1ch.PeerInfo().HostPort, + })) + mock1.On("Echo", ctxArg(), "call2").Return("call2", nil) + res, err = c2.Echo(ctx, "call2") + assert.NoError(t, err, "call2 failed") + assert.Equal(t, "call2", res) +} + +func TestRegisterPostResponseCB(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + var createdCtx Context + ctxKey := "key" + ctxValue := "value" + + args.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context { + createdCtx = WithHeaders(context.WithValue(ctx, ctxKey, ctxValue), headers) + return createdCtx + }) + + arg := &gen.Data{ + B1: true, + S2: "str", + I3: 102, + } + ret := &gen.Data{ + B1: false, + S2: "return-str", + I3: 105, + } + + called := make(chan struct{}) + cb := func(reqCtx context.Context, method string, response thrift.TStruct) { + assert.Equal(t, "Call", method) + assert.Equal(t, createdCtx, reqCtx) + assert.Equal(t, ctxValue, reqCtx.Value(ctxKey)) + res, ok := response.(*gen.SimpleServiceCallResult) + if assert.True(t, ok, "response type should be Result struct") { + assert.Equal(t, ret, res.GetSuccess(), "result should be returned value") + } + close(called) + } + args.server.Register(gen.NewTChanSimpleServiceServer(args.s1), OptPostResponse(cb)) + + args.s1.On("Call", ctxArg(), arg).Return(ret, nil) + res, err := args.c1.Call(ctx, arg) + require.NoError(t, err, "Call failed") + assert.Equal(t, res, ret, "Call return value wrong") + select { + case <-time.After(time.Second): + t.Errorf("post-response callback not called") + case <-called: + } + }) +} + +func TestRegisterPostResponseCBCalledOnError(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + var createdCtx Context + ctxKey := "key" + ctxValue := "value" + + args.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context { + createdCtx = WithHeaders(context.WithValue(ctx, ctxKey, ctxValue), headers) + return createdCtx + }) + + arg := &gen.Data{ + B1: true, + S2: "str", + I3: 102, + } + + retErr := thrift.NewTProtocolException(fmt.Errorf("expected error")) + + called := make(chan struct{}) + cb := func(reqCtx context.Context, method string, response thrift.TStruct) { + assert.Equal(t, "Call", method) + assert.Equal(t, createdCtx, reqCtx) + assert.Equal(t, ctxValue, reqCtx.Value(ctxKey)) + assert.Nil(t, response) + close(called) + } + args.server.Register(gen.NewTChanSimpleServiceServer(args.s1), OptPostResponse(cb)) + + args.s1.On("Call", ctxArg(), arg).Return(nil, retErr) + res, err := args.c1.Call(ctx, arg) + require.Error(t, err, "Call succeeded instead of failed") + require.Nil(t, res, "Call returned value and an error") + sysErr, ok := err.(tchannel.SystemError) + require.True(t, ok, "Call return error not a system error") + assert.Equal(t, tchannel.ErrCodeBadRequest, sysErr.Code(), "Call return error value wrong") + assert.Equal(t, retErr.Error(), sysErr.Message(), "Call return error value wrong") + select { + case <-time.After(time.Second): + t.Errorf("post-response callback not called") + case <-called: + } + }) +} + +func TestThriftTimeout(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + handler := make(chan struct{}) + + args.s2.On("Echo", ctxArg(), "asd").Return("asd", nil).Run(func(args mock.Arguments) { + time.Sleep(testutils.Timeout(15 * time.Millisecond)) + close(handler) + }) + + ctx, cancel := NewContext(testutils.Timeout(10 * time.Millisecond)) + defer cancel() + + _, err := args.c2.Echo(ctx, "asd") + assert.Equal(t, err, tchannel.ErrTimeout, "Expect call to time out") + + // Wait for the handler to return, otherwise the test ends before the Server gets an error. + select { + case <-handler: + case <-time.After(time.Second): + t.Errorf("Echo handler did not run") + } + }) +} + +func TestThriftContextFn(t *testing.T) { + withSetup(t, func(ctx Context, args testArgs) { + args.server.SetContextFn(func(ctx context.Context, method string, headers map[string]string) Context { + return WithHeaders(ctx, map[string]string{"custom": "headers"}) + }) + + args.s2.On("Echo", ctxArg(), "test").Return("test", nil).Run(func(args mock.Arguments) { + ctx := args.Get(0).(Context) + assert.Equal(t, "headers", ctx.Headers()["custom"], "Custom header is missing") + }) + _, err := args.c2.Echo(ctx, "test") + assert.NoError(t, err, "Echo failed") + }) +} + +func withSetup(t *testing.T, f func(ctx Context, args testArgs)) { + args := testArgs{ + s1: new(mocks.TChanSimpleService), + s2: new(mocks.TChanSecondService), + } + + ctx, cancel := NewContext(time.Second) + defer cancel() + + // Start server + args.serverCh, args.server = setupServer(t, args.s1, args.s2) + defer args.serverCh.Close() + + args.clientCh, args.c1, args.c2 = getClients(t, args.serverCh.PeerInfo(), args.serverCh.ServiceName(), args.clientCh) + + f(ctx, args) + + args.s1.AssertExpectations(t) + args.s2.AssertExpectations(t) +} + +func setupServer(t *testing.T, h *mocks.TChanSimpleService, sh *mocks.TChanSecondService) (*tchannel.Channel, *Server) { + ch := testutils.NewServer(t, nil) + server := NewServer(ch) + server.Register(gen.NewTChanSimpleServiceServer(h)) + server.Register(gen.NewTChanSecondServiceServer(sh)) + return ch, server +} + +func getClients(t *testing.T, serverInfo tchannel.LocalPeerInfo, svcName string, clientCh *tchannel.Channel) (*tchannel.Channel, gen.TChanSimpleService, gen.TChanSecondService) { + ch := testutils.NewClient(t, nil) + + ch.Peers().Add(serverInfo.HostPort) + client := NewClient(ch, svcName, nil) + + simpleClient := gen.NewTChanSimpleServiceClient(client) + secondClient := gen.NewTChanSecondServiceClient(client) + return ch, simpleClient, secondClient +} + +func withReader(t *testing.T, readerFn func() (tchannel.ArgReader, error), f func(r tchannel.ArgReader) error) { + reader, err := readerFn() + require.NoError(t, err, "Failed to get reader") + + err = f(reader) + require.NoError(t, err, "Failed to read contents") + + require.NoError(t, reader.Close(), "Failed to close reader") +} + +func withWriter(t *testing.T, writerFn func() (tchannel.ArgWriter, error), f func(w tchannel.ArgWriter) error) { + writer, err := writerFn() + require.NoError(t, err, "Failed to get writer") + + f(writer) + require.NoError(t, err, "Failed to write contents") + + require.NoError(t, writer.Close(), "Failed to close Writer") +} + +type rewriteMethodClient struct { + client TChanClient + rewriteTo string +} + +func (c rewriteMethodClient) Call(ctx Context, serviceName, methodName string, req, resp thrift.TStruct) (success bool, err error) { + return c.client.Call(ctx, serviceName, c.rewriteTo, req, resp) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/tracing_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/tracing_test.go new file mode 100644 index 00000000..3363717b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/tracing_test.go @@ -0,0 +1,110 @@ +package thrift_test + +import ( + json_encoding "encoding/json" + "testing" + + "github.com/uber/tchannel-go" + . "github.com/uber/tchannel-go/testutils/testtracing" + "github.com/uber/tchannel-go/thrift" + gen "github.com/uber/tchannel-go/thrift/gen-go/test" + + "golang.org/x/net/context" +) + +// ThriftHandler tests tracing over Thrift encoding +type ThriftHandler struct { + gen.TChanSimpleService // leave nil so calls to unimplemented methods panic. + TraceHandler + + thriftClient gen.TChanSimpleService + t *testing.T +} + +func requestFromThrift(req *gen.Data) *TracingRequest { + r := new(TracingRequest) + r.ForwardCount = int(req.I3) + return r +} + +func requestToThrift(r *TracingRequest) *gen.Data { + return &gen.Data{I3: int32(r.ForwardCount)} +} + +func responseFromThrift(t *testing.T, res *gen.Data) (*TracingResponse, error) { + var r TracingResponse + if err := json_encoding.Unmarshal([]byte(res.S2), &r); err != nil { + return nil, err + } + return &r, nil +} + +func responseToThrift(t *testing.T, r *TracingResponse) (*gen.Data, error) { + jsonBytes, err := json_encoding.Marshal(r) + if err != nil { + return nil, err + } + return &gen.Data{S2: string(jsonBytes)}, nil +} + +func (h *ThriftHandler) Call(ctx thrift.Context, arg *gen.Data) (*gen.Data, error) { + req := requestFromThrift(arg) + res, err := h.HandleCall(ctx, req, + func(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + tctx := ctx.(thrift.Context) + res, err := h.thriftClient.Call(tctx, requestToThrift(req)) + if err != nil { + return nil, err + } + return responseFromThrift(h.t, res) + }) + if err != nil { + return nil, err + } + return responseToThrift(h.t, res) +} + +func (h *ThriftHandler) firstCall(ctx context.Context, req *TracingRequest) (*TracingResponse, error) { + tctx := thrift.Wrap(ctx) + res, err := h.thriftClient.Call(tctx, requestToThrift(req)) + if err != nil { + return nil, err + } + return responseFromThrift(h.t, res) +} + +func TestThriftTracingPropagation(t *testing.T) { + suite := &PropagationTestSuite{ + Encoding: EncodingInfo{Format: tchannel.Thrift, HeadersSupported: true}, + Register: func(t *testing.T, ch *tchannel.Channel) TracingCall { + opts := &thrift.ClientOptions{HostPort: ch.PeerInfo().HostPort} + thriftClient := thrift.NewClient(ch, ch.PeerInfo().ServiceName, opts) + handler := &ThriftHandler{ + TraceHandler: TraceHandler{Ch: ch}, + t: t, + thriftClient: gen.NewTChanSimpleServiceClient(thriftClient), + } + + // Register Thrift handler + server := thrift.NewServer(ch) + server.Register(gen.NewTChanSimpleServiceServer(handler)) + + return handler.firstCall + }, + TestCases: map[TracerType][]PropagationTestCase{ + Noop: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: "", ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: "", ExpectedSpanCount: 0}, + }, + Mock: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6}, + }, + Jaeger: { + {ForwardCount: 2, TracingDisabled: true, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 0}, + {ForwardCount: 2, TracingDisabled: false, ExpectedBaggage: BaggageValue, ExpectedSpanCount: 6}, + }, + }, + } + suite.Run(t) +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/transport.go b/vendor/src/github.com/uber/tchannel-go/thrift/transport.go new file mode 100644 index 00000000..829cdb4d --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/transport.go @@ -0,0 +1,122 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "errors" + "io" + "sync" + + "github.com/apache/thrift/lib/go/thrift" +) + +// readerWriterTransport is a transport that reads and writes from the underlying Reader/Writer. +type readWriterTransport struct { + io.Writer + io.Reader + readBuf [1]byte + writeBuf [1]byte +} + +var errNoBytesRead = errors.New("no bytes read") + +func (t *readWriterTransport) Open() error { + return nil +} + +func (t *readWriterTransport) Flush() error { + return nil +} + +func (t *readWriterTransport) IsOpen() bool { + return true +} + +func (t *readWriterTransport) Close() error { + return nil +} + +func (t *readWriterTransport) ReadByte() (byte, error) { + v := t.readBuf[0:1] + + var n int + var err error + + for { + n, err = t.Read(v) + if n > 0 || err != nil { + break + } + } + + if err == io.EOF && n > 0 { + err = nil + } + return v[0], err +} + +func (t *readWriterTransport) WriteByte(b byte) error { + v := t.writeBuf[:1] + + v[0] = b + _, err := t.Write(v) + return err +} + +func (t *readWriterTransport) WriteString(s string) (int, error) { + return io.WriteString(t.Writer, s) +} + +// RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we +// do not know how many bytes we have left. +func (t *readWriterTransport) RemainingBytes() uint64 { + const maxSize = ^uint64(0) + return maxSize +} + +var _ thrift.TRichTransport = &readWriterTransport{} + +type thriftProtocol struct { + transport *readWriterTransport + protocol *thrift.TBinaryProtocol +} + +var thriftProtocolPool = sync.Pool{ + New: func() interface{} { + transport := &readWriterTransport{} + protocol := thrift.NewTBinaryProtocolTransport(transport) + return &thriftProtocol{transport, protocol} + }, +} + +func getProtocolWriter(writer io.Writer) *thriftProtocol { + wp := thriftProtocolPool.Get().(*thriftProtocol) + wp.transport.Reader = nil + wp.transport.Writer = writer + return wp +} + +func getProtocolReader(reader io.Reader) *thriftProtocol { + wp := thriftProtocolPool.Get().(*thriftProtocol) + wp.transport.Reader = reader + wp.transport.Writer = nil + return wp +} diff --git a/vendor/src/github.com/uber/tchannel-go/thrift/transport_test.go b/vendor/src/github.com/uber/tchannel-go/thrift/transport_test.go new file mode 100644 index 00000000..e6a9ec9b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/thrift/transport_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package thrift + +import ( + "bytes" + "io" + "testing" + + "github.com/uber/tchannel-go/testutils/testreader" + "github.com/uber/tchannel-go/testutils/testwriter" + + "github.com/stretchr/testify/assert" +) + +func writeByte(writer io.Writer, b byte) error { + protocol := getProtocolWriter(writer) + return protocol.transport.WriteByte(b) +} + +func TestWriteByteSuccess(t *testing.T) { + writer := &bytes.Buffer{} + assert.NoError(t, writeByte(writer, 'a'), "WriteByte failed") + assert.NoError(t, writeByte(writer, 'b'), "WriteByte failed") + assert.NoError(t, writeByte(writer, 'c'), "WriteByte failed") + assert.Equal(t, []byte("abc"), writer.Bytes(), "Written bytes mismatch") +} + +func TestWriteByteFailed(t *testing.T) { + buf := &bytes.Buffer{} + writer := io.MultiWriter(testwriter.Limited(2), buf) + assert.NoError(t, writeByte(writer, 'a'), "WriteByte failed") + assert.NoError(t, writeByte(writer, 'b'), "WriteByte failed") + assert.Error(t, writeByte(writer, 'c'), "WriteByte should fail due to lack of space") + assert.Equal(t, []byte("ab"), buf.Bytes(), "Written bytes mismatch") +} + +func TestReadByte0Byte(t *testing.T) { + chunkWriter, chunkReader := testreader.ChunkReader() + reader := getProtocolReader(chunkReader) + + chunkWriter <- []byte{} + chunkWriter <- []byte{} + chunkWriter <- []byte{} + chunkWriter <- []byte("abc") + close(chunkWriter) + + b, err := reader.transport.ReadByte() + assert.NoError(t, err, "ReadByte should ignore 0 byte reads") + assert.EqualValues(t, 'a', b) + + b, err = reader.transport.ReadByte() + assert.NoError(t, err, "ReadByte failed") + assert.EqualValues(t, 'b', b) + + b, err = reader.transport.ReadByte() + assert.NoError(t, err, "ReadByte failed") + assert.EqualValues(t, 'c', b) + + b, err = reader.transport.ReadByte() + assert.Equal(t, io.EOF, err, "ReadByte should EOF") +} diff --git a/vendor/src/github.com/uber/tchannel-go/tnet/listener.go b/vendor/src/github.com/uber/tchannel-go/tnet/listener.go new file mode 100644 index 00000000..c2b4db81 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tnet/listener.go @@ -0,0 +1,96 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tnet + +import ( + "net" + "sync" +) + +// Wrap returns a new Listener around the provided net.Listener. +// The returned Listener has a guarantee that when Close returns, it will no longer +// accept any new connections. +// See: https://github.com/uber/tchannel-go/issues/141 +func Wrap(l net.Listener) net.Listener { + return &listener{Listener: l, cond: sync.NewCond(&sync.Mutex{})} +} + +// listener wraps a net.Listener and ensures that once Listener.Close returns, +// the underlying socket has been closed. +// +// The default Listener returns from Close before the underlying socket has been closed +// if another goroutine has an active reference (e.g. is in Accept). +// The following can happen: +// Goroutine 1 is running Accept, and is blocked, waiting for epoll +// Goroutine 2 calls Close. It sees an extra reference, and so cannot destroy +// the socket, but instead decrements a reference, marks the connection as closed +// and unblocks epoll. +// Goroutine 2 returns to the caller, makes a new connection. +// The new connection is sent to the socket (since it hasn't been destroyed) +// Goroutine 1 returns from epoll, and accepts the new connection. +// +// To avoid accepting connections after Close, we block Goroutine 2 from returning from Close +// till Accept returns an error to the user. +type listener struct { + net.Listener + + // cond is used signal Close when there are no references to the listener. + cond *sync.Cond + refs int +} + +func (s *listener) incRef() { + s.cond.L.Lock() + s.refs++ + s.cond.L.Unlock() +} + +func (s *listener) decRef() { + s.cond.L.Lock() + s.refs-- + newRefs := s.refs + s.cond.L.Unlock() + if newRefs == 0 { + s.cond.Broadcast() + } +} + +// Accept waits for and returns the next connection to the listener. +func (s *listener) Accept() (net.Conn, error) { + s.incRef() + defer s.decRef() + return s.Listener.Accept() +} + +// Close closes the listener. +// Any blocked Accept operations will be unblocked and return errors. +func (s *listener) Close() error { + if err := s.Listener.Close(); err != nil { + return err + } + + s.cond.L.Lock() + for s.refs > 0 { + s.cond.Wait() + } + s.cond.L.Unlock() + return nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/tnet/listener_test.go b/vendor/src/github.com/uber/tchannel-go/tnet/listener_test.go new file mode 100644 index 00000000..a31a8cc5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tnet/listener_test.go @@ -0,0 +1,98 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tnet + +import ( + "errors" + "net" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestListenerAcceptAfterClose(t *testing.T) { + var wg sync.WaitGroup + for i := 0; i < 16; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + for i := 0; i < 10; i++ { + runTest(t) + } + }() + } + wg.Wait() +} + +func runTest(t *testing.T) { + const connectionsBeforeClose = 1 + + ln, err := net.Listen("tcp", "127.0.0.1:0") + if !assert.NoError(t, err, "Listen failed") { + return + + } + ln = Wrap(ln) + + addr := ln.Addr().String() + waitForListener := make(chan error) + go func() { + defer close(waitForListener) + + var connCount int + for { + conn, err := ln.Accept() + if err != nil { + return + } + + connCount++ + if connCount > connectionsBeforeClose { + waitForListener <- errors.New("got unexpected conn") + return + } + conn.Close() + } + }() + + for i := 0; i < connectionsBeforeClose; i++ { + err := connect(addr) + if !assert.NoError(t, err, "connect before listener is closed should succeed") { + return + } + } + + ln.Close() + connect(addr) + + err = <-waitForListener + assert.NoError(t, err, "got connection after listener was closed") +} + +func connect(addr string) error { + conn, err := net.Dial("tcp", addr) + if err == nil { + conn.Close() + } + return err +} diff --git a/vendor/src/github.com/uber/tchannel-go/tos/tos.go b/vendor/src/github.com/uber/tchannel-go/tos/tos.go new file mode 100644 index 00000000..0d6389c5 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tos/tos.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tos + +// ToS represents a const value DF, CS3 etc +// Assured Forwarding (x=class, y=drop precedence) (RFC2597) +// Class Selector (RFC 2474) +// IP Precedence (Linux Socket Compat RFC 791 +type ToS uint8 + +// Assured Forwarding (x=class, y=drop precedence) (RFC2597) +// Class Selector (RFC 2474) + +const ( + // CS3 Class Selector 3 + CS3 ToS = 0x18 + // CS4 Class Selector 4 + CS4 ToS = 0x20 + // CS5 Class Selector 5 + CS5 ToS = 0x28 + // CS6 Class Selector 6 + CS6 ToS = 0x30 + // CS7 Class Selector 7 + CS7 ToS = 0x38 + // AF11 Assured Forward 11 + AF11 ToS = 0x0a + // AF12 Assured Forward 11 + AF12 ToS = 0x0c + // AF13 Assured Forward 12 + AF13 ToS = 0x0e + // AF21 Assured Forward 13 + AF21 ToS = 0x12 + // AF22 Assured Forward 21 + AF22 ToS = 0x14 + // AF23 Assured Forward 22 + AF23 ToS = 0x16 + // AF31 Assured Forward 23 + AF31 ToS = 0x1a + // AF32 Assured Forward 31 + AF32 ToS = 0x1c + // AF33 Assured Forward 32 + AF33 ToS = 0x1e + // AF41 Assured Forward 33 + AF41 ToS = 0x22 + // AF42 Assured Forward 41 + AF42 ToS = 0x24 + // AF43 Assured Forward 42 + AF43 ToS = 0x26 + // EF Expedited Forwarding (RFC 3246) + EF ToS = 0x2e + // Lowdelay 10 + Lowdelay ToS = 0x10 + // Throughput 8 + Throughput ToS = 0x08 + // Reliability 4 + Reliability ToS = 0x04 + // Lowcost 2 + Lowcost ToS = 0x02 +) diff --git a/vendor/src/github.com/uber/tchannel-go/tos/tos_string.go b/vendor/src/github.com/uber/tchannel-go/tos/tos_string.go new file mode 100644 index 00000000..13e2d4cf --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tos/tos_string.go @@ -0,0 +1,53 @@ +package tos + +import "fmt" + +var ( + _tosNameToValue map[string]ToS + _tosValueToName = map[ToS]string{ + CS3: "CS3", + CS4: "CS4", + CS5: "CS5", + CS6: "CS6", + CS7: "CS7", + AF11: "AF11", + AF12: "AF12", + AF13: "AF13", + AF21: "AF21", + AF22: "AF22", + AF23: "AF23", + AF31: "AF31", + AF32: "AF32", + AF33: "AF33", + AF41: "AF41", + AF42: "AF42", + AF43: "AF43", + EF: "EF", + Lowdelay: "Lowdelay", + Throughput: "Throughput", + Reliability: "Reliability", + Lowcost: "Lowcost", + } +) + +func init() { + _tosNameToValue = make(map[string]ToS, len(_tosValueToName)) + for tos, tosString := range _tosValueToName { + _tosNameToValue[tosString] = tos + } +} + +// MarshalText implements TextMarshaler from encoding +func (r ToS) MarshalText() ([]byte, error) { + return []byte(_tosValueToName[r]), nil +} + +// UnmarshalText implements TextUnMarshaler from encoding +func (r *ToS) UnmarshalText(data []byte) error { + if v, ok := _tosNameToValue[string(data)]; ok { + *r = v + return nil + } + + return fmt.Errorf("invalid ToS %q", string(data)) +} diff --git a/vendor/src/github.com/uber/tchannel-go/tos/tos_test.go b/vendor/src/github.com/uber/tchannel-go/tos/tos_test.go new file mode 100644 index 00000000..2ae32528 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tos/tos_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tos + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMarshal(t *testing.T) { + for tos, wantMarshalled := range _tosValueToName { + marshalled, err := tos.MarshalText() + require.NoError(t, err, "Failed to marshal %v", tos) + assert.Equal(t, wantMarshalled, string(marshalled)) + + var got ToS + err = got.UnmarshalText(marshalled) + require.NoError(t, err, "Failed to unmarshal %v", string(marshalled)) + assert.Equal(t, tos, got) + } +} + +func TestUnmarshalUnknown(t *testing.T) { + var tos ToS + err := tos.UnmarshalText([]byte("unknown")) + require.Error(t, err, "Should fail to unmarshal unknown value") +} diff --git a/vendor/src/github.com/uber/tchannel-go/trace/doc.go b/vendor/src/github.com/uber/tchannel-go/trace/doc.go new file mode 100644 index 00000000..7173581f --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/trace/doc.go @@ -0,0 +1,30 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/* +Package trace used to contain TChannel's distributed tracing functionality. +It has since been replaced by integration with OpenTracing API. + +See http://opentracing.io + +This package is kept to alleviate problems with `glide update`, which tries +to look for it during the dependencies upgrades. +*/ +package trace diff --git a/vendor/src/github.com/uber/tchannel-go/tracing.go b/vendor/src/github.com/uber/tchannel-go/tracing.go new file mode 100644 index 00000000..703b944e --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tracing.go @@ -0,0 +1,284 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "time" + + "github.com/uber/tchannel-go/trand" + "github.com/uber/tchannel-go/typed" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "golang.org/x/net/context" +) + +// zipkinSpanFormat defines a name for OpenTracing carrier format that tracer may support. +// It is used to extract zipkin-style trace/span IDs from the OpenTracing Span, which are +// otherwise not exposed explicitly. +// NB: the string value is what's actually shared between implementations +const zipkinSpanFormat = "zipkin-span-format" + +// Span is an internal representation of Zipkin-compatible OpenTracing Span. +// It is used as OpenTracing inject/extract Carrier with ZipkinSpanFormat. +type Span struct { + traceID uint64 + parentID uint64 + spanID uint64 + flags byte +} + +var ( + // traceRng is a thread-safe random number generator for generating trace IDs. + traceRng = trand.NewSeeded() + + // emptySpan is returned from CurrentSpan(ctx) when there is no OpenTracing + // Span in ctx, to avoid returning nil. + emptySpan Span +) + +func (s Span) String() string { + return fmt.Sprintf("TraceID=%x,ParentID=%x,SpanID=%x", s.traceID, s.parentID, s.spanID) +} + +func (s *Span) read(r *typed.ReadBuffer) error { + s.spanID = r.ReadUint64() + s.parentID = r.ReadUint64() + s.traceID = r.ReadUint64() + s.flags = r.ReadSingleByte() + return r.Err() +} + +func (s *Span) write(w *typed.WriteBuffer) error { + w.WriteUint64(s.spanID) + w.WriteUint64(s.parentID) + w.WriteUint64(s.traceID) + w.WriteSingleByte(s.flags) + return w.Err() +} + +func (s *Span) initRandom() { + s.traceID = uint64(traceRng.Int63()) + s.spanID = s.traceID + s.parentID = 0 +} + +// TraceID returns the trace id for the entire call graph of requests. Established +// at the outermost edge service and propagated through all calls +func (s Span) TraceID() uint64 { return s.traceID } + +// ParentID returns the id of the parent span in this call graph +func (s Span) ParentID() uint64 { return s.parentID } + +// SpanID returns the id of this specific RPC +func (s Span) SpanID() uint64 { return s.spanID } + +// Flags returns flags bitmap. Interpretation of the bits is up to the tracing system. +func (s Span) Flags() byte { return s.flags } + +type injectableSpan Span + +// SetTraceID sets traceID +func (s *injectableSpan) SetTraceID(traceID uint64) { s.traceID = traceID } + +// SetSpanID sets spanID +func (s *injectableSpan) SetSpanID(spanID uint64) { s.spanID = spanID } + +// SetParentID sets parentID +func (s *injectableSpan) SetParentID(parentID uint64) { s.parentID = parentID } + +// SetFlags sets flags +func (s *injectableSpan) SetFlags(flags byte) { s.flags = flags } + +// initFromOpenTracing initializes injectableSpan fields from an OpenTracing Span, +// assuming the tracing implementation supports Zipkin-style span IDs. +func (s *injectableSpan) initFromOpenTracing(span opentracing.Span) error { + return span.Tracer().Inject(span.Context(), zipkinSpanFormat, s) +} + +// CurrentSpan extracts OpenTracing Span from the Context, and if found tries to +// extract zipkin-style trace/span IDs from it using ZipkinSpanFormat carrier. +// If there is no OpenTracing Span in the Context, an empty span is returned. +func CurrentSpan(ctx context.Context) *Span { + if sp := opentracing.SpanFromContext(ctx); sp != nil { + var injectable injectableSpan + if err := injectable.initFromOpenTracing(sp); err == nil { + span := Span(injectable) + return &span + } + // return empty span on error, instead of possibly a partially filled one + } + return &emptySpan +} + +// startOutboundSpan creates a new tracing span to represent the outbound RPC call. +// If the context already contains a span, it will be used as a parent, otherwise +// a new root span is created. +// +// If the tracer supports Zipkin-style trace IDs, then call.callReq.Tracing is +// initialized with those IDs. Otherwise it is assigned random values. +func (c *Connection) startOutboundSpan(ctx context.Context, serviceName, methodName string, call *OutboundCall, startTime time.Time) opentracing.Span { + var parent opentracing.SpanContext // ok to be nil + if s := opentracing.SpanFromContext(ctx); s != nil { + parent = s.Context() + } + span := c.Tracer().StartSpan( + methodName, + opentracing.ChildOf(parent), + opentracing.StartTime(startTime), + ) + if isTracingDisabled(ctx) { + ext.SamplingPriority.Set(span, 0) + } + ext.SpanKindRPCClient.Set(span) + ext.PeerService.Set(span, serviceName) + c.setPeerHostPort(span) + span.SetTag("as", call.callReq.Headers[ArgScheme]) + var injectable injectableSpan + if err := injectable.initFromOpenTracing(span); err == nil { + call.callReq.Tracing = Span(injectable) + } else { + call.callReq.Tracing.initRandom() + } + return span +} + +// InjectOutboundSpan retrieves OpenTracing Span from `response`, where it is stored +// when the outbound call is initiated. The tracing API is used to serialize the span +// into the application `headers`, which will propagate tracing context to the server. +// Returns modified headers containing serialized tracing context. +// +// Sometimes caller pass a shared instance of the `headers` map, so instead of modifying +// it we clone it into the new map (assuming that Tracer actually injects some tracing keys). +func InjectOutboundSpan(response *OutboundCallResponse, headers map[string]string) map[string]string { + span := response.span + if span == nil { + return headers + } + newHeaders := make(map[string]string) + carrier := tracingHeadersCarrier(newHeaders) + if err := span.Tracer().Inject(span.Context(), opentracing.TextMap, carrier); err != nil { + // Something had to go seriously wrong for Inject to fail, usually a setup problem. + // A good Tracer implementation may also emit a metric. + response.log.WithFields(ErrField(err)).Error("Failed to inject tracing span.") + } + if len(newHeaders) == 0 { + return headers // Tracer did not add any tracing headers, so return the original map + } + for k, v := range headers { + newHeaders[k] = v + } + return newHeaders +} + +// extractInboundSpan attempts to create a new OpenTracing Span for inbound request +// using only trace IDs stored in the frame's tracing field. It only works if the +// tracer understand Zipkin-style trace IDs. If such attempt fails, another attempt +// will be made from the higher level function ExtractInboundSpan() once the +// application headers are read from the wire. +func (c *Connection) extractInboundSpan(callReq *callReq) opentracing.Span { + spanCtx, err := c.Tracer().Extract(zipkinSpanFormat, &callReq.Tracing) + if err != nil { + if err != opentracing.ErrUnsupportedFormat && err != opentracing.ErrSpanContextNotFound { + c.log.WithFields(ErrField(err)).Error("Failed to extract Zipkin-style span.") + } + return nil + } + if spanCtx == nil { + return nil + } + operationName := "" // not known at this point, will be set later + span := c.Tracer().StartSpan(operationName, ext.RPCServerOption(spanCtx)) + span.SetTag("as", callReq.Headers[ArgScheme]) + ext.PeerService.Set(span, callReq.Headers[CallerName]) + c.setPeerHostPort(span) + return span +} + +// ExtractInboundSpan is a higher level version of extractInboundSpan(). +// If the lower-level attempt to create a span from incoming request was +// successful (e.g. when then Tracer supports Zipkin-style trace IDs), +// then the application headers are only used to read the Baggage and add +// it to the existing span. Otherwise, the standard OpenTracing API supported +// by all tracers is used to deserialize the tracing context from the +// application headers and start a new server-side span. +// Once the span is started, it is wrapped in a new Context, which is returned. +func ExtractInboundSpan(ctx context.Context, call *InboundCall, headers map[string]string, tracer opentracing.Tracer) context.Context { + var span = call.Response().span + if span != nil { + if headers != nil { + // extract SpanContext from headers, but do not start another span with it, + // just get the baggage and copy to the already created span + carrier := tracingHeadersCarrier(headers) + if sc, err := tracer.Extract(opentracing.TextMap, carrier); err == nil { + sc.ForeachBaggageItem(func(k, v string) bool { + span.SetBaggageItem(k, v) + return true + }) + } + carrier.RemoveTracingKeys() + } + } else { + var parent opentracing.SpanContext + if headers != nil { + carrier := tracingHeadersCarrier(headers) + if p, err := tracer.Extract(opentracing.TextMap, carrier); err == nil { + parent = p + } + carrier.RemoveTracingKeys() + } + span = tracer.StartSpan(call.MethodString(), ext.RPCServerOption(parent)) + ext.PeerService.Set(span, call.CallerName()) + span.SetTag("as", string(call.Format())) + call.conn.setPeerHostPort(span) + call.Response().span = span + } + return opentracing.ContextWithSpan(ctx, span) +} + +func (c *Connection) setPeerHostPort(span opentracing.Span) { + if c.remotePeerAddress.ipv4 != 0 { + ext.PeerHostIPv4.Set(span, c.remotePeerAddress.ipv4) + } + if c.remotePeerAddress.ipv6 != "" { + ext.PeerHostIPv6.Set(span, c.remotePeerAddress.ipv6) + } + if c.remotePeerAddress.hostname != "" { + ext.PeerHostname.Set(span, c.remotePeerAddress.hostname) + } + if c.remotePeerAddress.port != 0 { + ext.PeerPort.Set(span, c.remotePeerAddress.port) + } +} + +type tracerProvider interface { + Tracer() opentracing.Tracer +} + +// TracerFromRegistrar returns an OpenTracing Tracer embedded in the Registrar, +// assuming that Registrar has a Tracer() method. Otherwise it returns default Global Tracer. +func TracerFromRegistrar(registrar Registrar) opentracing.Tracer { + if tracerProvider, ok := registrar.(tracerProvider); ok { + return tracerProvider.Tracer() + } + return opentracing.GlobalTracer() +} diff --git a/vendor/src/github.com/uber/tchannel-go/tracing_internal_test.go b/vendor/src/github.com/uber/tchannel-go/tracing_internal_test.go new file mode 100644 index 00000000..60e55918 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tracing_internal_test.go @@ -0,0 +1,233 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "fmt" + "net" + "testing" + + "github.com/uber/tchannel-go/typed" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestTracingSpanEncoding(t *testing.T) { + s1 := Span{ + traceID: 1, + parentID: 2, + spanID: 3, + flags: 4, + } + // Encoding is: spanid:8 parentid:8 traceid:8 traceflags:1 + // http://tchannel.readthedocs.io/en/latest/protocol/#tracing + encoded := []byte{ + 0, 0, 0, 0, 0, 0, 0, 3, /* spanID */ + 0, 0, 0, 0, 0, 0, 0, 2, /* parentID */ + 0, 0, 0, 0, 0, 0, 0, 1, /* traceID */ + 4, /* flags */ + } + + buf := make([]byte, len(encoded)) + writer := typed.NewWriteBuffer(buf) + require.NoError(t, s1.write(writer), "Failed to encode span") + + assert.Equal(t, encoded, buf, "Encoded span mismatch") + + var s2 Span + reader := typed.NewReadBuffer(buf) + require.NoError(t, s2.read(reader), "Failed to decode span") + + assert.Equal(t, s1, s2, "Roundtrip of span failed") +} + +func TestTracingInjectorExtractor(t *testing.T) { + tracer := mocktracer.New() + tracer.RegisterInjector(zipkinSpanFormat, new(zipkinInjector)) + tracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor)) + + sp := tracer.StartSpan("x") + var injectable injectableSpan + err := tracer.Inject(sp.Context(), zipkinSpanFormat, &injectable) + require.NoError(t, err) + + tsp := Span(injectable) + assert.NotEqual(t, uint64(0), tsp.TraceID()) + assert.NotEqual(t, uint64(0), tsp.SpanID()) + + sp2, err := tracer.Extract(zipkinSpanFormat, &tsp) + require.NoError(t, err) + require.NotNil(t, sp2) +} + +func TestSpanString(t *testing.T) { + span := Span{traceID: 15} + assert.Equal(t, "TraceID=f,ParentID=0,SpanID=0", span.String()) +} + +func TestSetPeerHostPort(t *testing.T) { + tracer := mocktracer.New() + + ipv6 := []byte{1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16} + assert.Equal(t, net.IPv6len, len(ipv6)) + ipv6hostPort := fmt.Sprintf("[%v]:789", net.IP(ipv6)) + + tests := []struct { + hostPort string + wantHostTag string + wantHost interface{} + wantPort uint16 + }{ + {"adhoc123:bad-port", "peer.hostname", "adhoc123", 0}, + {"adhoc123", "peer.hostname", "adhoc123", 0}, + {"ip123.uswest.aws.com:765", "peer.hostname", "ip123.uswest.aws.com", 765}, + {"localhost:123", "peer.ipv4", uint32(127<<24 | 1), 123}, + {"10.20.30.40:321", "peer.ipv4", uint32(10<<24 | 20<<16 | 30<<8 | 40), 321}, + {ipv6hostPort, "peer.ipv6", "102:300::f10", 789}, + } + + for i, test := range tests { + span := tracer.StartSpan("x") + peerInfo, peerAddress, err := parseRemotePeer(initParams{ + InitParamHostPort: test.hostPort, + InitParamProcessName: "test", + }, &net.IPAddr{IP: net.ParseIP("1.1.1.1")}) + require.NoError(t, err, "Failed to parse remote peer info") + + c := &Connection{ + channelConnectionCommon: channelConnectionCommon{ + log: NullLogger, + }, + remotePeerInfo: peerInfo, + remotePeerAddress: peerAddress, + } + c.setPeerHostPort(span) + span.Finish() + rawSpan := tracer.FinishedSpans()[i] + assert.Equal(t, test.wantHost, rawSpan.Tag(test.wantHostTag), "test %+v", test) + if test.wantPort != 0 { + assert.Equal(t, test.wantPort, rawSpan.Tag(string(ext.PeerPort)), "test %+v", test) + } else { + assert.Nil(t, rawSpan.Tag(string(ext.PeerPort)), "test %+v", test) + } + } +} + +func TestExtractInboundSpanWithZipkinTracer(t *testing.T) { + tracer := mocktracer.New() + callReq := new(callReq) + callReq.Tracing = Span{traceID: 1, spanID: 2, flags: 1} + callReq.Headers = transportHeaders{ + ArgScheme: string(JSON), + CallerName: "caller", + } + peerInfo, peerAddress, err := parseRemotePeer(initParams{ + InitParamHostPort: "host:123", + InitParamProcessName: "test", + }, &net.IPAddr{IP: net.ParseIP("1.1.1.1")}) + require.NoError(t, err, "Failed to parse remote peer info") + c := Connection{ + channelConnectionCommon: channelConnectionCommon{ + log: NullLogger, + tracer: tracer, + }, + remotePeerInfo: peerInfo, + remotePeerAddress: peerAddress, + } + + // fail to extract with zipkin format, as MockTracer does not support it + assert.Nil(t, c.extractInboundSpan(callReq), "zipkin format not available") + + // add zipkin format extractor and try again + tracer.RegisterExtractor(zipkinSpanFormat, new(zipkinExtractor)) + span := c.extractInboundSpan(callReq) + require.NotNil(t, span, "zipkin format available") + + // validate the extracted span was correctly populated + s1, ok := span.(*mocktracer.MockSpan) + require.True(t, ok) + assert.Equal(t, 1, s1.SpanContext.TraceID) + assert.Equal(t, 2, s1.ParentID) + assert.True(t, s1.SpanContext.Sampled) + assert.Equal(t, "", s1.OperationName, "operation name unknown initially") + assert.Equal(t, string(JSON), s1.Tag("as")) + assert.Equal(t, "caller", s1.Tag(string(ext.PeerService))) + assert.Equal(t, "host", s1.Tag(string(ext.PeerHostname))) + assert.Equal(t, uint16(123), s1.Tag(string(ext.PeerPort))) + + // start a temporary span so that we can populate headers with baggage + tempSpan := tracer.StartSpan("test") + tempSpan.SetBaggageItem("x", "y") + headers := make(map[string]string) + carrier := tracingHeadersCarrier(headers) + err = tracer.Inject(tempSpan.Context(), opentracing.TextMap, carrier) + assert.NoError(t, err) + + // run the public ExtractInboundSpan method with application headers + inCall := &InboundCall{ + response: &InboundCallResponse{ + span: span, + }, + } + ctx := context.Background() + ctx2 := ExtractInboundSpan(ctx, inCall, headers, tracer) + span = opentracing.SpanFromContext(ctx2) + s2, ok := span.(*mocktracer.MockSpan) + require.True(t, ok) + assert.Equal(t, s1, s2, "should be the same span started previously") + assert.Equal(t, "y", s2.BaggageItem("x"), "baggage should've been added") +} + +type zipkinInjector struct{} + +func (z *zipkinInjector) Inject(sc mocktracer.MockSpanContext, carrier interface{}) error { + span, ok := carrier.(*injectableSpan) + if !ok { + return opentracing.ErrInvalidCarrier + } + span.SetTraceID(uint64(sc.TraceID)) + span.SetSpanID(uint64(sc.SpanID)) + if sc.Sampled { + span.SetFlags(1) + } else { + span.SetFlags(0) + } + return nil +} + +type zipkinExtractor struct{} + +func (z *zipkinExtractor) Extract(carrier interface{}) (mocktracer.MockSpanContext, error) { + span, ok := carrier.(*Span) + if !ok { + return mocktracer.MockSpanContext{}, opentracing.ErrInvalidCarrier + } + return mocktracer.MockSpanContext{ + TraceID: int(span.traceID), + SpanID: int(span.spanID), + Sampled: span.flags&1 == 1, + }, nil +} diff --git a/vendor/src/github.com/uber/tchannel-go/tracing_keys.go b/vendor/src/github.com/uber/tchannel-go/tracing_keys.go new file mode 100644 index 00000000..bab67b05 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tracing_keys.go @@ -0,0 +1,103 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +import ( + "strings" + "sync" +) + +// tracingKeyPrefix is used to prefix all keys used by the OpenTracing Tracer to represent +// its trace context and baggage. The prefixing is done in order to distinguish tracing +// headers from the actual application headers and to hide the former from the user code. +const tracingKeyPrefix = "$tracing$" + +// tracingKeyMappingSize is the maximum number of tracing key mappings we cache. +const tracingKeyMappingSize = 100 + +type tracingKeysMapping struct { + sync.RWMutex + mapping map[string]string + mapper func(key string) string +} + +var tracingKeyEncoding = &tracingKeysMapping{ + mapping: make(map[string]string), + mapper: func(key string) string { + return tracingKeyPrefix + key + }, +} + +var tracingKeyDecoding = &tracingKeysMapping{ + mapping: make(map[string]string), + mapper: func(key string) string { + return key[len(tracingKeyPrefix):] + }, +} + +func (m *tracingKeysMapping) mapAndCache(key string) string { + m.RLock() + v, ok := m.mapping[key] + m.RUnlock() + if ok { + return v + } + m.Lock() + defer m.Unlock() + if v, ok := m.mapping[key]; ok { + return v + } + mappedKey := m.mapper(key) + if len(m.mapping) < tracingKeyMappingSize { + m.mapping[key] = mappedKey + } + return mappedKey +} + +type tracingHeadersCarrier map[string]string + +// Set implements Set() of opentracing.TextMapWriter +func (c tracingHeadersCarrier) Set(key, val string) { + prefixedKey := tracingKeyEncoding.mapAndCache(key) + c[prefixedKey] = val +} + +// ForeachKey conforms to the TextMapReader interface. +func (c tracingHeadersCarrier) ForeachKey(handler func(key, val string) error) error { + for k, v := range c { + if !strings.HasPrefix(k, tracingKeyPrefix) { + continue + } + noPrefixKey := tracingKeyDecoding.mapAndCache(k) + if err := handler(noPrefixKey, v); err != nil { + return err + } + } + return nil +} + +func (c tracingHeadersCarrier) RemoveTracingKeys() { + for key := range c { + if strings.HasPrefix(key, tracingKeyPrefix) { + delete(c, key) + } + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/tracing_test.go b/vendor/src/github.com/uber/tchannel-go/tracing_test.go new file mode 100644 index 00000000..5311e222 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/tracing_test.go @@ -0,0 +1,182 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "sync" + "testing" + "time" + + . "github.com/uber/tchannel-go" + "github.com/uber/tchannel-go/json" + "github.com/uber/tchannel-go/testutils" + "github.com/uber/tchannel-go/testutils/testtracing" + + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +// JSONHandler tests tracing over JSON encoding +type JSONHandler struct { + testtracing.TraceHandler + t *testing.T +} + +func (h *JSONHandler) callJSON(ctx json.Context, req *testtracing.TracingRequest) (*testtracing.TracingResponse, error) { + resp := new(testtracing.TracingResponse) + resp.ObserveSpan(ctx) + return resp, nil +} + +func (h *JSONHandler) onError(ctx context.Context, err error) { h.t.Errorf("onError %v", err) } + +func TestTracingSpanAttributes(t *testing.T) { + tracer := mocktracer.New() + + opts := &testutils.ChannelOpts{ + ChannelOptions: ChannelOptions{Tracer: tracer}, + DisableRelay: true, + } + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + // Register JSON handler + jsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t} + json.Register(ch, json.Handlers{"call": jsonHandler.callJSON}, jsonHandler.onError) + + span := ch.Tracer().StartSpan("client") + span.SetBaggageItem(testtracing.BaggageKey, testtracing.BaggageValue) + ctx := opentracing.ContextWithSpan(context.Background(), span) + root := new(testtracing.TracingResponse).ObserveSpan(ctx) + + ctx, cancel := NewContextBuilder(2 * time.Second).SetParentContext(ctx).Build() + defer cancel() + + peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort) + var response testtracing.TracingResponse + require.NoError(t, json.CallPeer(json.Wrap(ctx), peer, ch.PeerInfo().ServiceName, + "call", &testtracing.TracingRequest{}, &response)) + + // Spans are finished in inbound.doneSending() or outbound.doneReading(), + // which are called on different go-routines and may execute *after* the + // response has been received by the client. Give them a chance to finish. + for i := 0; i < 1000; i++ { + if spanCount := len(testtracing.MockTracerSampledSpans(tracer)); spanCount == 2 { + break + } + time.Sleep(testutils.Timeout(time.Millisecond)) + } + spans := testtracing.MockTracerSampledSpans(tracer) + spanCount := len(spans) + ch.Logger().Debugf("end span count: %d", spanCount) + + // finish span after taking count of recorded spans + span.Finish() + + require.Equal(t, 2, spanCount, "Wrong span count") + assert.Equal(t, root.TraceID, response.TraceID, "Trace ID must match root span") + assert.Equal(t, testtracing.BaggageValue, response.Luggage, "Baggage must match") + + var parent, child *mocktracer.MockSpan + for _, s := range spans { + if s.Tag("span.kind") == ext.SpanKindRPCClientEnum { + parent = s + ch.Logger().Debugf("Found parent span: %+v", s) + } else if s.Tag("span.kind") == ext.SpanKindRPCServerEnum { + child = s + ch.Logger().Debugf("Found child span: %+v", s) + } + } + + require.NotNil(t, parent) + require.NotNil(t, child) + + traceID := func(s opentracing.Span) int { + return s.Context().(mocktracer.MockSpanContext).TraceID + } + spanID := func(s *mocktracer.MockSpan) int { + return s.Context().(mocktracer.MockSpanContext).SpanID + } + sampled := func(s *mocktracer.MockSpan) bool { + return s.Context().(mocktracer.MockSpanContext).Sampled + } + + require.Equal(t, traceID(span), traceID(parent), "parent must be found") + require.Equal(t, traceID(span), traceID(child), "child must be found") + assert.Equal(t, traceID(parent), traceID(child)) + assert.Equal(t, spanID(parent), child.ParentID) + assert.True(t, sampled(parent), "should be sampled") + assert.True(t, sampled(child), "should be sampled") + assert.Equal(t, "call", parent.OperationName) + assert.Equal(t, "call", child.OperationName) + assert.Equal(t, "testService", parent.Tag("peer.service")) + assert.Equal(t, "testService", child.Tag("peer.service")) + assert.Equal(t, "json", parent.Tag("as")) + assert.Equal(t, "json", child.Tag("as")) + assert.NotNil(t, parent.Tag("peer.ipv4")) + assert.NotNil(t, child.Tag("peer.ipv4")) + assert.NotNil(t, parent.Tag("peer.port")) + assert.NotNil(t, child.Tag("peer.port")) + }) +} + +// Per https://github.com/uber/tchannel-go/issues/505, concurrent client calls +// made with the same shared map used as headers were causing panic due to +// concurrent writes to the map when injecting tracing headers. +func TestReusableHeaders(t *testing.T) { + opts := &testutils.ChannelOpts{ + ChannelOptions: ChannelOptions{Tracer: mocktracer.New()}, + } + WithVerifiedServer(t, opts, func(ch *Channel, hostPort string) { + jsonHandler := &JSONHandler{TraceHandler: testtracing.TraceHandler{Ch: ch}, t: t} + json.Register(ch, json.Handlers{"call": jsonHandler.callJSON}, jsonHandler.onError) + + span := ch.Tracer().StartSpan("client") + traceID := span.(*mocktracer.MockSpan).SpanContext.TraceID // for validation + ctx := opentracing.ContextWithSpan(context.Background(), span) + + sharedHeaders := map[string]string{"life": "42"} + ctx, cancel := NewContextBuilder(2 * time.Second). + SetHeaders(sharedHeaders). + SetParentContext(ctx). + Build() + defer cancel() + + peer := ch.Peers().GetOrAdd(ch.PeerInfo().HostPort) + + var wg sync.WaitGroup + for i := 0; i < 42; i++ { + wg.Add(1) + go func() { + defer wg.Done() + var response testtracing.TracingResponse + err := json.CallPeer(json.Wrap(ctx), peer, ch.ServiceName(), + "call", &testtracing.TracingRequest{}, &response) + assert.NoError(t, err, "json.Call failed") + assert.EqualValues(t, traceID, response.TraceID, "traceID must match") + }() + } + wg.Wait() + assert.Equal(t, map[string]string{"life": "42"}, sharedHeaders, "headers unchanged") + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/trand/rand.go b/vendor/src/github.com/uber/tchannel-go/trand/rand.go new file mode 100644 index 00000000..40feaf10 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/trand/rand.go @@ -0,0 +1,61 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package trand provides a thread-safe random number generator. +package trand + +import ( + "math/rand" + "sync" + "time" +) + +// lockedSource allows a random number generator to be used by multiple goroutines +// concurrently. The code is very similar to math/rand.lockedSource, which is +// unfortunately not exposed. +type lockedSource struct { + sync.Mutex + + src rand.Source +} + +// New returns a rand.Rand that is threadsafe. +func New(seed int64) *rand.Rand { + return rand.New(&lockedSource{src: rand.NewSource(seed)}) +} + +// NewSeeded returns a rand.Rand that's threadsafe and seeded with the current +// time. +func NewSeeded() *rand.Rand { + return New(time.Now().UnixNano()) +} + +func (r *lockedSource) Int63() (n int64) { + r.Lock() + n = r.src.Int63() + r.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.Lock() + r.src.Seed(seed) + r.Unlock() +} diff --git a/vendor/src/github.com/uber/tchannel-go/typed/buffer.go b/vendor/src/github.com/uber/tchannel-go/typed/buffer.go new file mode 100644 index 00000000..af26e78b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/typed/buffer.go @@ -0,0 +1,412 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package typed + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrEOF is returned when trying to read past end of buffer + ErrEOF = errors.New("buffer is too small") + + // ErrBufferFull is returned when trying to write past end of buffer + ErrBufferFull = errors.New("no more room in buffer") +) + +// A ReadBuffer is a wrapper around an underlying []byte with methods to read from +// that buffer in big-endian format. +type ReadBuffer struct { + buffer []byte + remaining []byte + err error +} + +// NewReadBuffer returns a ReadBuffer wrapping a byte slice +func NewReadBuffer(buffer []byte) *ReadBuffer { + return &ReadBuffer{buffer: buffer, remaining: buffer} +} + +// NewReadBufferWithSize returns a ReadBuffer with a given capacity +func NewReadBufferWithSize(size int) *ReadBuffer { + return &ReadBuffer{buffer: make([]byte, size), remaining: nil} +} + +// ReadSingleByte reads the next byte from the buffer +func (r *ReadBuffer) ReadSingleByte() byte { + b, _ := r.ReadByte() + return b +} + +// ReadByte returns the next byte from the buffer. +func (r *ReadBuffer) ReadByte() (byte, error) { + if r.err != nil { + return 0, r.err + } + + if len(r.remaining) < 1 { + r.err = ErrEOF + return 0, r.err + } + + b := r.remaining[0] + r.remaining = r.remaining[1:] + return b, nil +} + +// ReadBytes returns the next n bytes from the buffer +func (r *ReadBuffer) ReadBytes(n int) []byte { + if r.err != nil { + return nil + } + + if len(r.remaining) < n { + r.err = ErrEOF + return nil + } + + b := r.remaining[0:n] + r.remaining = r.remaining[n:] + return b +} + +// ReadString returns a string of size n from the buffer +func (r *ReadBuffer) ReadString(n int) string { + if b := r.ReadBytes(n); b != nil { + // TODO(mmihic): This creates a copy, which sucks + return string(b) + } + + return "" +} + +// ReadUint16 returns the next value in the buffer as a uint16 +func (r *ReadBuffer) ReadUint16() uint16 { + if b := r.ReadBytes(2); b != nil { + return binary.BigEndian.Uint16(b) + } + + return 0 +} + +// ReadUint32 returns the next value in the buffer as a uint32 +func (r *ReadBuffer) ReadUint32() uint32 { + if b := r.ReadBytes(4); b != nil { + return binary.BigEndian.Uint32(b) + } + + return 0 +} + +// ReadUint64 returns the next value in the buffer as a uint64 +func (r *ReadBuffer) ReadUint64() uint64 { + if b := r.ReadBytes(8); b != nil { + return binary.BigEndian.Uint64(b) + } + + return 0 +} + +// ReadUvarint reads an unsigned varint from the buffer. +func (r *ReadBuffer) ReadUvarint() uint64 { + v, _ := binary.ReadUvarint(r) + return v +} + +// ReadLen8String reads an 8-bit length preceded string value +func (r *ReadBuffer) ReadLen8String() string { + n := r.ReadSingleByte() + return r.ReadString(int(n)) +} + +// ReadLen16String reads a 16-bit length preceded string value +func (r *ReadBuffer) ReadLen16String() string { + n := r.ReadUint16() + return r.ReadString(int(n)) +} + +// BytesRemaining returns the number of unconsumed bytes remaining in the buffer +func (r *ReadBuffer) BytesRemaining() int { + return len(r.remaining) +} + +// FillFrom fills the buffer from a reader +func (r *ReadBuffer) FillFrom(ior io.Reader, n int) (int, error) { + if len(r.buffer) < n { + return 0, ErrEOF + } + + r.err = nil + r.remaining = r.buffer[:n] + return io.ReadFull(ior, r.remaining) +} + +// Wrap initializes the buffer to read from the given byte slice +func (r *ReadBuffer) Wrap(b []byte) { + r.buffer = b + r.remaining = b + r.err = nil +} + +// Err returns the error in the ReadBuffer +func (r *ReadBuffer) Err() error { return r.err } + +// A WriteBuffer is a wrapper around an underlying []byte with methods to write to +// that buffer in big-endian format. The buffer is of fixed size, and does not grow. +type WriteBuffer struct { + buffer []byte + remaining []byte + err error +} + +// NewWriteBuffer creates a WriteBuffer wrapping the given slice +func NewWriteBuffer(buffer []byte) *WriteBuffer { + return &WriteBuffer{buffer: buffer, remaining: buffer} +} + +// NewWriteBufferWithSize create a new WriteBuffer using an internal buffer of the given size +func NewWriteBufferWithSize(size int) *WriteBuffer { + return NewWriteBuffer(make([]byte, size)) +} + +// WriteSingleByte writes a single byte to the buffer +func (w *WriteBuffer) WriteSingleByte(n byte) { + if w.err != nil { + return + } + + if len(w.remaining) == 0 { + w.err = ErrBufferFull + return + } + + w.remaining[0] = n + w.remaining = w.remaining[1:] +} + +// WriteBytes writes a slice of bytes to the buffer +func (w *WriteBuffer) WriteBytes(in []byte) { + if b := w.reserve(len(in)); b != nil { + copy(b, in) + } +} + +// WriteUint16 writes a big endian encoded uint16 value to the buffer +func (w *WriteBuffer) WriteUint16(n uint16) { + if b := w.reserve(2); b != nil { + binary.BigEndian.PutUint16(b, n) + } +} + +// WriteUint32 writes a big endian uint32 value to the buffer +func (w *WriteBuffer) WriteUint32(n uint32) { + if b := w.reserve(4); b != nil { + binary.BigEndian.PutUint32(b, n) + } +} + +// WriteUint64 writes a big endian uint64 to the buffer +func (w *WriteBuffer) WriteUint64(n uint64) { + if b := w.reserve(8); b != nil { + binary.BigEndian.PutUint64(b, n) + } +} + +// WriteUvarint writes an unsigned varint to the buffer +func (w *WriteBuffer) WriteUvarint(n uint64) { + // A uvarint could be up to 10 bytes long. + buf := make([]byte, 10) + varBytes := binary.PutUvarint(buf, n) + if b := w.reserve(varBytes); b != nil { + copy(b, buf[0:varBytes]) + } +} + +// WriteString writes a string to the buffer +func (w *WriteBuffer) WriteString(s string) { + // NB(mmihic): Don't just call WriteBytes; that will make a double copy + // of the string due to the cast + if b := w.reserve(len(s)); b != nil { + copy(b, s) + } +} + +// WriteLen8String writes an 8-bit length preceded string +func (w *WriteBuffer) WriteLen8String(s string) { + w.WriteSingleByte(byte(len(s))) + w.WriteString(s) +} + +// WriteLen16String writes a 16-bit length preceded string +func (w *WriteBuffer) WriteLen16String(s string) { + w.WriteUint16(uint16(len(s))) + w.WriteString(s) +} + +// DeferByte reserves space in the buffer for a single byte, and returns a +// reference that can be used to update that byte later +func (w *WriteBuffer) DeferByte() ByteRef { + if len(w.remaining) == 0 { + w.err = ErrBufferFull + return ByteRef(nil) + } + + // Always zero out references, since the caller expects the default to be 0. + w.remaining[0] = 0 + bufRef := ByteRef(w.remaining[0:]) + w.remaining = w.remaining[1:] + return bufRef +} + +// DeferUint16 reserves space in the buffer for a uint16, and returns a +// reference that can be used to update that uint16 +func (w *WriteBuffer) DeferUint16() Uint16Ref { + return Uint16Ref(w.deferred(2)) +} + +// DeferUint32 reserves space in the buffer for a uint32, and returns a +// reference that can be used to update that uint32 +func (w *WriteBuffer) DeferUint32() Uint32Ref { + return Uint32Ref(w.deferred(4)) +} + +// DeferUint64 reserves space in the buffer for a uint64, and returns a +// reference that can be used to update that uint64 +func (w *WriteBuffer) DeferUint64() Uint64Ref { + return Uint64Ref(w.deferred(8)) +} + +// DeferBytes reserves space in the buffer for a fixed sequence of bytes, and +// returns a reference that can be used to update those bytes +func (w *WriteBuffer) DeferBytes(n int) BytesRef { + return BytesRef(w.deferred(n)) +} + +func (w *WriteBuffer) deferred(n int) []byte { + bs := w.reserve(n) + for i := range bs { + bs[i] = 0 + } + return bs +} + +func (w *WriteBuffer) reserve(n int) []byte { + if w.err != nil { + return nil + } + + if len(w.remaining) < n { + w.err = ErrBufferFull + return nil + } + + b := w.remaining[0:n] + w.remaining = w.remaining[n:] + return b +} + +// BytesRemaining returns the number of available bytes remaining in the bufffer +func (w *WriteBuffer) BytesRemaining() int { + return len(w.remaining) +} + +// FlushTo flushes the written buffer to the given writer +func (w *WriteBuffer) FlushTo(iow io.Writer) (int, error) { + dirty := w.buffer[0:w.BytesWritten()] + return iow.Write(dirty) +} + +// BytesWritten returns the number of bytes that have been written to the buffer +func (w *WriteBuffer) BytesWritten() int { return len(w.buffer) - len(w.remaining) } + +// Reset resets the buffer to an empty state, ready for writing +func (w *WriteBuffer) Reset() { + w.remaining = w.buffer + w.err = nil +} + +// Err returns the current error in the buffer +func (w *WriteBuffer) Err() error { return w.err } + +// Wrap initializes the buffer to wrap the given byte slice +func (w *WriteBuffer) Wrap(b []byte) { + w.buffer = b + w.remaining = b +} + +// A ByteRef is a reference to a byte in a bufffer +type ByteRef []byte + +// Update updates the byte in the buffer +func (ref ByteRef) Update(b byte) { + if ref != nil { + ref[0] = b + } +} + +// A Uint16Ref is a reference to a uint16 placeholder in a buffer +type Uint16Ref []byte + +// Update updates the uint16 in the buffer +func (ref Uint16Ref) Update(n uint16) { + if ref != nil { + binary.BigEndian.PutUint16(ref, n) + } +} + +// A Uint32Ref is a reference to a uint32 placeholder in a buffer +type Uint32Ref []byte + +// Update updates the uint32 in the buffer +func (ref Uint32Ref) Update(n uint32) { + if ref != nil { + binary.BigEndian.PutUint32(ref, n) + } +} + +// A Uint64Ref is a reference to a uin64 placeholder in a buffer +type Uint64Ref []byte + +// Update updates the uint64 in the buffer +func (ref Uint64Ref) Update(n uint64) { + if ref != nil { + binary.BigEndian.PutUint64(ref, n) + } +} + +// A BytesRef is a reference to a multi-byte placeholder in a buffer +type BytesRef []byte + +// Update updates the bytes in the buffer +func (ref BytesRef) Update(b []byte) { + if ref != nil { + copy(ref, b) + } +} + +// UpdateString updates the bytes in the buffer from a string +func (ref BytesRef) UpdateString(s string) { + if ref != nil { + copy(ref, s) + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/typed/buffer_test.go b/vendor/src/github.com/uber/tchannel-go/typed/buffer_test.go new file mode 100644 index 00000000..bc8cf837 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/typed/buffer_test.go @@ -0,0 +1,221 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package typed + +import ( + "bytes" + "math" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSimple(t *testing.T) { + buf := make([]byte, 200) + + var r ReadBuffer + var w WriteBuffer + + { + w.Wrap(buf) + w.WriteSingleByte(0xFC) + r.Wrap(buf) + assert.Equal(t, byte(0xFC), r.ReadSingleByte()) + } + + { + w.Wrap(buf) + w.WriteUint16(0xDEAD) + r.Wrap(buf) + assert.Equal(t, uint16(0xDEAD), r.ReadUint16()) + } + + { + w.Wrap(buf) + w.WriteUint32(0xBEEFDEAD) + r.Wrap(buf) + assert.Equal(t, uint32(0xBEEFDEAD), r.ReadUint32()) + } +} + +func TestShortBuffer(t *testing.T) { + r := NewReadBuffer([]byte{23}) + assert.EqualValues(t, 0, r.ReadUint16()) + assert.Equal(t, ErrEOF, r.Err()) +} + +func TestReadWrite(t *testing.T) { + s := "the small brown fix" + bslice := []byte("jumped over the lazy dog") + + w := NewWriteBufferWithSize(1024) + w.WriteUint64(0x0123456789ABCDEF) + w.WriteUint32(0xABCDEF01) + w.WriteUint16(0x2345) + w.WriteUvarint(1) + w.WriteUvarint(math.MaxInt16) + w.WriteUvarint(math.MaxInt32) + w.WriteUvarint(math.MaxInt64) + w.WriteSingleByte(0xFF) + w.WriteString(s) + w.WriteBytes(bslice) + w.WriteLen8String("hello") + w.WriteLen16String("This is a much larger string") + require.NoError(t, w.Err()) + + var b bytes.Buffer + w.FlushTo(&b) + + r := NewReadBufferWithSize(1024) + r.FillFrom(bytes.NewReader(b.Bytes()), len(b.Bytes())) + + assert.Equal(t, uint64(0x0123456789ABCDEF), r.ReadUint64()) + assert.Equal(t, uint32(0xABCDEF01), r.ReadUint32()) + assert.Equal(t, uint16(0x2345), r.ReadUint16()) + assert.Equal(t, uint64(1), r.ReadUvarint()) + assert.Equal(t, uint64(math.MaxInt16), r.ReadUvarint()) + assert.Equal(t, uint64(math.MaxInt32), r.ReadUvarint()) + assert.Equal(t, uint64(math.MaxInt64), r.ReadUvarint()) + assert.Equal(t, byte(0xFF), r.ReadSingleByte()) + assert.Equal(t, s, r.ReadString(len(s))) + assert.Equal(t, bslice, r.ReadBytes(len(bslice))) + assert.Equal(t, "hello", r.ReadLen8String()) + assert.Equal(t, "This is a much larger string", r.ReadLen16String()) + + require.NoError(t, r.Err()) +} + +func TestDeferredWrites(t *testing.T) { + w := NewWriteBufferWithSize(1024) + u16ref := w.DeferUint16() + require.NotNil(t, u16ref) + + u32ref := w.DeferUint32() + require.NotNil(t, u32ref) + + u64ref := w.DeferUint64() + require.NotNil(t, u64ref) + + bref := w.DeferBytes(5) + require.NotNil(t, bref) + + sref := w.DeferBytes(5) + require.NotNil(t, sref) + + byteref := w.DeferByte() + require.NotNil(t, byteref) + + assert.Equal(t, 2+4+8+5+5+1, w.BytesWritten()) + + u16ref.Update(2040) + u32ref.Update(495404) + u64ref.Update(0x40950459) + bref.Update([]byte{0x30, 0x12, 0x45, 0x55, 0x65}) + sref.UpdateString("where") + byteref.Update(0x44) + + var buf bytes.Buffer + w.FlushTo(&buf) + + r := NewReadBuffer(buf.Bytes()) + + u16 := r.ReadUint16() + assert.Equal(t, uint16(2040), u16) + + u32 := r.ReadUint32() + assert.Equal(t, uint32(495404), u32) + + u64 := r.ReadUint64() + assert.Equal(t, uint64(0x40950459), u64) + + b := r.ReadBytes(5) + assert.Equal(t, []byte{0x30, 0x12, 0x45, 0x55, 0x65}, b) + + s := r.ReadString(5) + assert.Equal(t, "where", s) + + u8 := r.ReadSingleByte() + assert.Equal(t, byte(0x44), u8) + assert.NoError(t, r.Err()) +} + +func TestDirtyUnderlyingBuffer(t *testing.T) { + buf := make([]byte, 128) + for i := range buf { + buf[i] = ^byte(0) + } + w := NewWriteBuffer(buf) + + // Defer 1 + 2 + 4 + 8 + 5 = 20 bytes + w.DeferByte() + w.DeferUint16() + w.DeferUint32() + w.DeferUint64() + w.DeferBytes(5) + + defer1 := w.DeferByte() + defer2 := w.DeferUint16() + defer3 := w.DeferUint32() + defer4 := w.DeferUint64() + defer5 := w.DeferBytes(5) + + w.WriteUint16(16) + w.WriteUint32(32) + w.WriteUint64(64) + w.WriteLen16String("len16 string") + w.WriteLen8String("len8 string") + w.WriteString("string") + w.WriteSingleByte(1) + w.WriteBytes([]byte{1, 2, 3, 4, 5}) + + defer1.Update(11) + defer2.Update(116) + defer3.Update(132) + defer4.Update(164) + defer5.Update([]byte{11, 12, 13, 14, 15}) + + r := NewReadBuffer(buf) + + // Deferred unwritten bytes should be 0. + assert.EqualValues(t, 0, r.ReadSingleByte(), "unwritten deferred should be 0") + assert.EqualValues(t, 0, r.ReadUint16(), "unwritten deferred should be 0") + assert.EqualValues(t, 0, r.ReadUint32(), "unwritten deferred should be 0") + assert.EqualValues(t, 0, r.ReadUint64(), "unwritten deferred should be 0") + assert.Equal(t, []byte{0, 0, 0, 0, 0}, r.ReadBytes(5), "unwritten deferred should be 0") + + // Deferred written bytes. + assert.EqualValues(t, 11, r.ReadSingleByte(), "defer byte") + assert.EqualValues(t, 116, r.ReadUint16(), "defer uint16") + assert.EqualValues(t, 132, r.ReadUint32(), "defer uint32") + assert.EqualValues(t, 164, r.ReadUint64(), "defer uint64") + assert.Equal(t, []byte{11, 12, 13, 14, 15}, r.ReadBytes(5), "defer bytes") + + // Normally written bytes. + assert.EqualValues(t, 16, r.ReadUint16(), "uint16") + assert.EqualValues(t, 32, r.ReadUint32(), "uint32") + assert.EqualValues(t, 64, r.ReadUint64(), "uint64") + assert.Equal(t, "len16 string", r.ReadLen16String(), "len16 string") + assert.Equal(t, "len8 string", r.ReadLen8String(), "len 8 string") + assert.Equal(t, "string", r.ReadString(6), "string") + assert.EqualValues(t, 1, r.ReadSingleByte(), "byte") + assert.Equal(t, []byte{1, 2, 3, 4, 5}, r.ReadBytes(5), "bytes") +} diff --git a/vendor/src/github.com/uber/tchannel-go/typed/reader.go b/vendor/src/github.com/uber/tchannel-go/typed/reader.go new file mode 100644 index 00000000..054861d3 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/typed/reader.go @@ -0,0 +1,105 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package typed + +import ( + "encoding/binary" + "io" + "sync" +) + +const maxPoolStringLen = 32 + +// Reader is a reader that reads typed values from an io.Reader. +type Reader struct { + reader io.Reader + err error + buf [maxPoolStringLen]byte +} + +var readerPool = sync.Pool{ + New: func() interface{} { + return &Reader{} + }, +} + +// NewReader returns a reader that reads typed values from the reader. +func NewReader(reader io.Reader) *Reader { + r := readerPool.Get().(*Reader) + r.reader = reader + r.err = nil + return r +} + +// ReadUint16 reads a uint16. +func (r *Reader) ReadUint16() uint16 { + if r.err != nil { + return 0 + } + + buf := r.buf[:2] + + var readN int + readN, r.err = io.ReadFull(r.reader, buf) + if readN < 2 { + return 0 + } + return binary.BigEndian.Uint16(buf) +} + +// ReadString reads a string of length n. +func (r *Reader) ReadString(n int) string { + if r.err != nil { + return "" + } + + var buf []byte + if n <= maxPoolStringLen { + buf = r.buf[:n] + } else { + buf = make([]byte, n) + } + + var readN int + readN, r.err = io.ReadFull(r.reader, buf) + if readN < n { + return "" + } + s := string(buf) + + return s +} + +// ReadLen16String reads a uint16-length prefixed string. +func (r *Reader) ReadLen16String() string { + len := r.ReadUint16() + return r.ReadString(int(len)) +} + +// Err returns any errors hit while reading from the underlying reader. +func (r *Reader) Err() error { + return r.err +} + +// Release puts the Reader back in the pool. +func (r *Reader) Release() { + readerPool.Put(r) +} diff --git a/vendor/src/github.com/uber/tchannel-go/typed/reader_test.go b/vendor/src/github.com/uber/tchannel-go/typed/reader_test.go new file mode 100644 index 00000000..556dd27a --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/typed/reader_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package typed + +import ( + "bytes" + "io" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/uber/tchannel-go/testutils/testreader" +) + +func nString(n int) []byte { + buf := make([]byte, n) + reader := testreader.Looper([]byte{'a', 'b', 'c', 'd', 'e'}) + io.ReadFull(reader, buf) + return buf +} + +func TestReader(t *testing.T) { + s1 := nString(10) + s2 := nString(800) + + var buf []byte + buf = append(buf, 0, 1) // uint16, 1 + buf = append(buf, 0xff, 0xff) // uint16, 65535 + buf = append(buf, 0, 10) // uint16, 10 + buf = append(buf, s1...) // string, 10 bytes + buf = append(buf, 3, 32) // uint16, 800 + buf = append(buf, s2...) // string, 800 bytes + buf = append(buf, 0, 10) // uint16, 10 + + reader := NewReader(bytes.NewReader(buf)) + + assert.Equal(t, uint16(1), reader.ReadUint16()) + assert.Equal(t, uint16(65535), reader.ReadUint16()) + assert.Equal(t, string(s1), reader.ReadLen16String()) + assert.Equal(t, string(s2), reader.ReadLen16String()) + assert.Equal(t, uint16(10), reader.ReadUint16()) +} + +func TestReaderErr(t *testing.T) { + tests := []struct { + chunks [][]byte + validation func(reader *Reader) + }{ + { + chunks: [][]byte{ + {0, 1}, + nil, + {2, 3}, + }, + validation: func(reader *Reader) { + assert.Equal(t, uint16(1), reader.ReadUint16(), "Read unexpected value") + assert.Equal(t, uint16(0), reader.ReadUint16(), "Expected default value") + }, + }, + { + chunks: [][]byte{ + {0, 4}, + []byte("test"), + nil, + {'A', 'b'}, + }, + validation: func(reader *Reader) { + assert.Equal(t, "test", reader.ReadLen16String(), "Read unexpected value") + assert.Equal(t, "", reader.ReadString(2), "Expected default value") + }, + }, + } + + for _, tt := range tests { + writer, chunkReader := testreader.ChunkReader() + reader := NewReader(chunkReader) + defer reader.Release() + + for _, chunk := range tt.chunks { + writer <- chunk + } + close(writer) + + tt.validation(reader) + // Once there's an error, all further calls should fail. + assert.Equal(t, testreader.ErrUser, reader.Err(), "Unexpected error") + assert.Equal(t, uint16(0), reader.ReadUint16(), "Expected default value") + assert.Equal(t, "", reader.ReadString(1), "Expected default value") + assert.Equal(t, "", reader.ReadLen16String(), "Expected default value") + assert.Equal(t, testreader.ErrUser, reader.Err(), "Unexpected error") + } +} diff --git a/vendor/src/github.com/uber/tchannel-go/utils_for_test.go b/vendor/src/github.com/uber/tchannel-go/utils_for_test.go new file mode 100644 index 00000000..4992b66b --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/utils_for_test.go @@ -0,0 +1,84 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +// This file contains functions for tests to access internal tchannel state. +// Since it has a _test.go suffix, it is only compiled with tests in this package. + +import ( + "net" + "time" + + "golang.org/x/net/context" +) + +// MexChannelBufferSize is the size of the message exchange channel buffer. +const MexChannelBufferSize = mexChannelBufferSize + +// SetOnUpdate sets onUpdate for a peer, which is called when the peer's score is +// updated in all peer lists. +func (p *Peer) SetOnUpdate(f func(*Peer)) { + p.Lock() + p.onUpdate = f + p.Unlock() +} + +// GetConnectionRelay exports the getConnectionRelay for tests. +func (p *Peer) GetConnectionRelay(timeout time.Duration) (*Connection, error) { + return p.getConnectionRelay(timeout) +} + +// SetRandomSeed seeds all the random number generators in the channel so that +// tests will be deterministic for a given seed. +func (ch *Channel) SetRandomSeed(seed int64) { + ch.Peers().peerHeap.rng.Seed(seed) + peerRng.Seed(seed) + for _, sc := range ch.subChannels.subchannels { + sc.peers.peerHeap.rng.Seed(seed + int64(len(sc.peers.peersByHostPort))) + } +} + +// Ping sends a ping on the specific connection. +func (c *Connection) Ping(ctx context.Context) error { + return c.ping(ctx) +} + +// Logger returns the logger for the specific connection. +func (c *Connection) Logger() Logger { + return c.log +} + +// OutboundConnection returns the underlying connection for an outbound call. +func OutboundConnection(call *OutboundCall) (*Connection, net.Conn) { + conn := call.conn + return conn, conn.conn +} + +// InboundConnection returns the underlying connection for an incoming call. +func InboundConnection(call IncomingCall) (*Connection, net.Conn) { + inboundCall, ok := call.(*InboundCall) + if !ok { + return nil, nil + } + + conn := inboundCall.conn + return conn, conn.conn +} diff --git a/vendor/src/github.com/uber/tchannel-go/verify_utils_test.go b/vendor/src/github.com/uber/tchannel-go/verify_utils_test.go new file mode 100644 index 00000000..87827ea0 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/verify_utils_test.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel_test + +import ( + "runtime" + "testing" + "time" + + . "github.com/uber/tchannel-go" + + "github.com/uber/tchannel-go/testutils" +) + +func waitForChannelClose(t *testing.T, ch *Channel) bool { + // TODO: remove standalone use (outside testutils.TestServer). + started := time.Now() + + var state ChannelState + for i := 0; i < 50; i++ { + if state = ch.State(); state == ChannelClosed { + return true + } + + runtime.Gosched() + if i < 5 { + continue + } + + sleepFor := time.Duration(i) * 100 * time.Microsecond + time.Sleep(testutils.Timeout(sleepFor)) + } + + // Channel is not closing, fail the test. + sinceStart := time.Since(started) + t.Errorf("Channel did not close after %v, last state: %v", sinceStart, state) + return false +} + +// WithVerifiedServer runs the given test function with a server channel that is verified +// at the end to make sure there are no leaks (e.g. no exchanges leaked). +func WithVerifiedServer(t *testing.T, opts *testutils.ChannelOpts, f func(serverCh *Channel, hostPort string)) { + testutils.WithTestServer(t, opts, func(ts *testutils.TestServer) { + f(ts.Server(), ts.HostPort()) + }) +} diff --git a/vendor/src/github.com/uber/tchannel-go/version.go b/vendor/src/github.com/uber/tchannel-go/version.go new file mode 100644 index 00000000..bedc7441 --- /dev/null +++ b/vendor/src/github.com/uber/tchannel-go/version.go @@ -0,0 +1,26 @@ +// Copyright (c) 2015 Uber Technologies, Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package tchannel + +// VersionInfo identifies the version of the TChannel library. +// Due to lack of proper package management, this version string will +// be maintained manually. +const VersionInfo = "1.7.0" diff --git a/vendor/src/go.uber.org/atomic/LICENSE.txt b/vendor/src/go.uber.org/atomic/LICENSE.txt new file mode 100644 index 00000000..8765c9fb --- /dev/null +++ b/vendor/src/go.uber.org/atomic/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/go.uber.org/atomic/Makefile b/vendor/src/go.uber.org/atomic/Makefile new file mode 100644 index 00000000..dfc63d9d --- /dev/null +++ b/vendor/src/go.uber.org/atomic/Makefile @@ -0,0 +1,64 @@ +PACKAGES := $(shell glide nv) +# Many Go tools take file globs or directories as arguments instead of packages. +PACKAGE_FILES ?= *.go + + +# The linting tools evolve with each Go version, so run them only on the latest +# stable release. +GO_VERSION := $(shell go version | cut -d " " -f 3) +GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION))) +LINTABLE_MINOR_VERSIONS := 7 8 +ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),) +SHOULD_LINT := true +endif + + +export GO15VENDOREXPERIMENT=1 + + +.PHONY: build +build: + go build -i $(PACKAGES) + + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + + +.PHONY: test +test: + go test -cover -race $(PACKAGES) + + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover +ifdef SHOULD_LINT + go get github.com/golang/lint/golint +endif + +.PHONY: lint +lint: +ifdef SHOULD_LINT + @rm -rf lint.log + @echo "Checking formatting..." + @gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log + @echo "Checking vet..." + @$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking lint..." + @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking for unresolved FIXMEs..." + @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log + @[ ! -s lint.log ] +else + @echo "Skipping linters on" $(GO_VERSION) +endif + + +.PHONY: test_ci +test_ci: install_ci build + ./scripts/cover.sh $(shell go list $(PACKAGES)) diff --git a/vendor/src/go.uber.org/atomic/README.md b/vendor/src/go.uber.org/atomic/README.md new file mode 100644 index 00000000..6505abf6 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/README.md @@ -0,0 +1,36 @@ +# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard] + +Simple wrappers for primitive types to enforce atomic access. + +## Installation +`go get -u go.uber.org/atomic` + +## Usage +The standard library's `sync/atomic` is powerful, but it's easy to forget which +variables must be accessed atomically. `go.uber.org/atomic` preserves all the +functionality of the standard library, but wraps the primitive types to +provide a safer, more convenient API. + +```go +var atom atomic.Uint32 +atom.Store(42) +atom.Sub(2) +atom.CAS(40, 11) +``` + +See the [documentation][doc] for a complete API specification. + +## Development Status +Stable. + +
+Released under the [MIT License](LICENSE.txt). + +[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg +[doc]: https://godoc.org/go.uber.org/atomic +[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master +[ci]: https://travis-ci.org/uber-go/atomic +[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/atomic +[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic +[reportcard]: https://goreportcard.com/report/go.uber.org/atomic diff --git a/vendor/src/go.uber.org/atomic/atomic.go b/vendor/src/go.uber.org/atomic/atomic.go new file mode 100644 index 00000000..1ca50dc3 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/atomic.go @@ -0,0 +1,309 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package atomic provides simple wrappers around numerics to enforce atomic +// access. +package atomic + +import ( + "math" + "sync/atomic" +) + +// Int32 is an atomic wrapper around an int32. +type Int32 struct{ v int32 } + +// NewInt32 creates an Int32. +func NewInt32(i int32) *Int32 { + return &Int32{i} +} + +// Load atomically loads the wrapped value. +func (i *Int32) Load() int32 { + return atomic.LoadInt32(&i.v) +} + +// Add atomically adds to the wrapped int32 and returns the new value. +func (i *Int32) Add(n int32) int32 { + return atomic.AddInt32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int32 and returns the new value. +func (i *Int32) Sub(n int32) int32 { + return atomic.AddInt32(&i.v, -n) +} + +// Inc atomically increments the wrapped int32 and returns the new value. +func (i *Int32) Inc() int32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Int32) Dec() int32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int32) CAS(old, new int32) bool { + return atomic.CompareAndSwapInt32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int32) Store(n int32) { + atomic.StoreInt32(&i.v, n) +} + +// Swap atomically swaps the wrapped int32 and returns the old value. +func (i *Int32) Swap(n int32) int32 { + return atomic.SwapInt32(&i.v, n) +} + +// Int64 is an atomic wrapper around an int64. +type Int64 struct{ v int64 } + +// NewInt64 creates an Int64. +func NewInt64(i int64) *Int64 { + return &Int64{i} +} + +// Load atomically loads the wrapped value. +func (i *Int64) Load() int64 { + return atomic.LoadInt64(&i.v) +} + +// Add atomically adds to the wrapped int64 and returns the new value. +func (i *Int64) Add(n int64) int64 { + return atomic.AddInt64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped int64 and returns the new value. +func (i *Int64) Sub(n int64) int64 { + return atomic.AddInt64(&i.v, -n) +} + +// Inc atomically increments the wrapped int64 and returns the new value. +func (i *Int64) Inc() int64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int64 and returns the new value. +func (i *Int64) Dec() int64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Int64) CAS(old, new int64) bool { + return atomic.CompareAndSwapInt64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Int64) Store(n int64) { + atomic.StoreInt64(&i.v, n) +} + +// Swap atomically swaps the wrapped int64 and returns the old value. +func (i *Int64) Swap(n int64) int64 { + return atomic.SwapInt64(&i.v, n) +} + +// Uint32 is an atomic wrapper around an uint32. +type Uint32 struct{ v uint32 } + +// NewUint32 creates a Uint32. +func NewUint32(i uint32) *Uint32 { + return &Uint32{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint32) Load() uint32 { + return atomic.LoadUint32(&i.v) +} + +// Add atomically adds to the wrapped uint32 and returns the new value. +func (i *Uint32) Add(n uint32) uint32 { + return atomic.AddUint32(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint32 and returns the new value. +func (i *Uint32) Sub(n uint32) uint32 { + return atomic.AddUint32(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint32 and returns the new value. +func (i *Uint32) Inc() uint32 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped int32 and returns the new value. +func (i *Uint32) Dec() uint32 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint32) CAS(old, new uint32) bool { + return atomic.CompareAndSwapUint32(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint32) Store(n uint32) { + atomic.StoreUint32(&i.v, n) +} + +// Swap atomically swaps the wrapped uint32 and returns the old value. +func (i *Uint32) Swap(n uint32) uint32 { + return atomic.SwapUint32(&i.v, n) +} + +// Uint64 is an atomic wrapper around a uint64. +type Uint64 struct{ v uint64 } + +// NewUint64 creates a Uint64. +func NewUint64(i uint64) *Uint64 { + return &Uint64{i} +} + +// Load atomically loads the wrapped value. +func (i *Uint64) Load() uint64 { + return atomic.LoadUint64(&i.v) +} + +// Add atomically adds to the wrapped uint64 and returns the new value. +func (i *Uint64) Add(n uint64) uint64 { + return atomic.AddUint64(&i.v, n) +} + +// Sub atomically subtracts from the wrapped uint64 and returns the new value. +func (i *Uint64) Sub(n uint64) uint64 { + return atomic.AddUint64(&i.v, ^(n - 1)) +} + +// Inc atomically increments the wrapped uint64 and returns the new value. +func (i *Uint64) Inc() uint64 { + return i.Add(1) +} + +// Dec atomically decrements the wrapped uint64 and returns the new value. +func (i *Uint64) Dec() uint64 { + return i.Sub(1) +} + +// CAS is an atomic compare-and-swap. +func (i *Uint64) CAS(old, new uint64) bool { + return atomic.CompareAndSwapUint64(&i.v, old, new) +} + +// Store atomically stores the passed value. +func (i *Uint64) Store(n uint64) { + atomic.StoreUint64(&i.v, n) +} + +// Swap atomically swaps the wrapped uint64 and returns the old value. +func (i *Uint64) Swap(n uint64) uint64 { + return atomic.SwapUint64(&i.v, n) +} + +// Bool is an atomic Boolean. +type Bool struct{ v uint32 } + +// NewBool creates a Bool. +func NewBool(initial bool) *Bool { + return &Bool{boolToInt(initial)} +} + +// Load atomically loads the Boolean. +func (b *Bool) Load() bool { + return truthy(atomic.LoadUint32(&b.v)) +} + +// CAS is an atomic compare-and-swap. +func (b *Bool) CAS(old, new bool) bool { + return atomic.CompareAndSwapUint32(&b.v, boolToInt(old), boolToInt(new)) +} + +// Store atomically stores the passed value. +func (b *Bool) Store(new bool) { + atomic.StoreUint32(&b.v, boolToInt(new)) +} + +// Swap sets the given value and returns the previous value. +func (b *Bool) Swap(new bool) bool { + return truthy(atomic.SwapUint32(&b.v, boolToInt(new))) +} + +// Toggle atomically negates the Boolean and returns the previous value. +func (b *Bool) Toggle() bool { + return truthy(atomic.AddUint32(&b.v, 1) - 1) +} + +func truthy(n uint32) bool { + return n&1 == 1 +} + +func boolToInt(b bool) uint32 { + if b { + return 1 + } + return 0 +} + +// Float64 is an atomic wrapper around float64. +type Float64 struct { + v uint64 +} + +// NewFloat64 creates a Float64. +func NewFloat64(f float64) *Float64 { + return &Float64{math.Float64bits(f)} +} + +// Load atomically loads the wrapped value. +func (f *Float64) Load() float64 { + return math.Float64frombits(atomic.LoadUint64(&f.v)) +} + +// Store atomically stores the passed value. +func (f *Float64) Store(s float64) { + atomic.StoreUint64(&f.v, math.Float64bits(s)) +} + +// Add atomically adds to the wrapped float64 and returns the new value. +func (f *Float64) Add(s float64) float64 { + for { + old := f.Load() + new := old + s + if f.CAS(old, new) { + return new + } + } +} + +// Sub atomically subtracts from the wrapped float64 and returns the new value. +func (f *Float64) Sub(s float64) float64 { + return f.Add(-s) +} + +// CAS is an atomic compare-and-swap. +func (f *Float64) CAS(old, new float64) bool { + return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new)) +} + +// Value shadows the type of the same name from sync/atomic +// https://godoc.org/sync/atomic#Value +type Value struct{ atomic.Value } diff --git a/vendor/src/go.uber.org/atomic/atomic_test.go b/vendor/src/go.uber.org/atomic/atomic_test.go new file mode 100644 index 00000000..9f293b7d --- /dev/null +++ b/vendor/src/go.uber.org/atomic/atomic_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInt32(t *testing.T) { + atom := NewInt32(42) + + require.Equal(t, int32(42), atom.Load(), "Load didn't work.") + require.Equal(t, int32(46), atom.Add(4), "Add didn't work.") + require.Equal(t, int32(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, int32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestInt64(t *testing.T) { + atom := NewInt64(42) + + require.Equal(t, int64(42), atom.Load(), "Load didn't work.") + require.Equal(t, int64(46), atom.Add(4), "Add didn't work.") + require.Equal(t, int64(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, int64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, int64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, int64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, int64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, int64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, int64(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint32(t *testing.T) { + atom := NewUint32(42) + + require.Equal(t, uint32(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint32(46), atom.Add(4), "Add didn't work.") + require.Equal(t, uint32(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, uint32(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint32(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint32(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint32(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint32(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint32(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestUint64(t *testing.T) { + atom := NewUint64(42) + + require.Equal(t, uint64(42), atom.Load(), "Load didn't work.") + require.Equal(t, uint64(46), atom.Add(4), "Add didn't work.") + require.Equal(t, uint64(44), atom.Sub(2), "Sub didn't work.") + require.Equal(t, uint64(45), atom.Inc(), "Inc didn't work.") + require.Equal(t, uint64(44), atom.Dec(), "Dec didn't work.") + + require.True(t, atom.CAS(44, 0), "CAS didn't report a swap.") + require.Equal(t, uint64(0), atom.Load(), "CAS didn't set the correct value.") + + require.Equal(t, uint64(0), atom.Swap(1), "Swap didn't return the old value.") + require.Equal(t, uint64(1), atom.Load(), "Swap didn't set the correct value.") + + atom.Store(42) + require.Equal(t, uint64(42), atom.Load(), "Store didn't set the correct value.") +} + +func TestBool(t *testing.T) { + atom := NewBool(false) + require.False(t, atom.Toggle(), "Expected swap to return previous value.") + require.True(t, atom.Load(), "Unexpected state after swap.") + + require.True(t, atom.CAS(true, true), "CAS should swap when old matches") + require.True(t, atom.Load(), "CAS should have no effect") + require.True(t, atom.CAS(true, false), "CAS should swap when old matches") + require.False(t, atom.Load(), "CAS should have modified the value") + require.False(t, atom.CAS(true, false), "CAS should fail on old mismatch") + require.False(t, atom.Load(), "CAS should not have modified the value") + + atom.Store(false) + require.False(t, atom.Load(), "Unexpected state after store.") + + prev := atom.Swap(false) + require.False(t, prev, "Expected Swap to return previous value.") + + prev = atom.Swap(true) + require.False(t, prev, "Expected Swap to return previous value.") +} + +func TestFloat64(t *testing.T) { + atom := NewFloat64(4.2) + + require.Equal(t, float64(4.2), atom.Load(), "Load didn't work.") + + require.True(t, atom.CAS(4.2, 0.5), "CAS didn't report a swap.") + require.Equal(t, float64(0.5), atom.Load(), "CAS didn't set the correct value.") + require.False(t, atom.CAS(0.0, 1.5), "CAS reported a swap.") + + atom.Store(42.0) + require.Equal(t, float64(42.0), atom.Load(), "Store didn't set the correct value.") + require.Equal(t, float64(42.5), atom.Add(0.5), "Add didn't work.") + require.Equal(t, float64(42.0), atom.Sub(0.5), "Sub didn't work.") +} + +func TestValue(t *testing.T) { + var v Value + assert.Nil(t, v.Load(), "initial Value is not nil") + + v.Store(42) + assert.Equal(t, 42, v.Load()) + + v.Store(84) + assert.Equal(t, 84, v.Load()) + + assert.Panics(t, func() { v.Store("foo") }) +} diff --git a/vendor/src/go.uber.org/atomic/example_test.go b/vendor/src/go.uber.org/atomic/example_test.go new file mode 100644 index 00000000..806e11c4 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/example_test.go @@ -0,0 +1,43 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic_test + +import ( + "fmt" + + "go.uber.org/atomic" +) + +func Example() { + // Uint32 is a thin wrapper around the primitive uint32 type. + var atom atomic.Uint32 + + // The wrapper ensures that all operations are atomic. + atom.Store(42) + fmt.Println(atom.Inc()) + fmt.Println(atom.CAS(43, 0)) + fmt.Println(atom.Load()) + + // Output: + // 43 + // true + // 0 +} diff --git a/vendor/src/go.uber.org/atomic/glide.lock b/vendor/src/go.uber.org/atomic/glide.lock new file mode 100644 index 00000000..3c72c599 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/glide.lock @@ -0,0 +1,17 @@ +hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53 +updated: 2016-10-27T00:10:51.16960137-07:00 +imports: [] +testImports: +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: d77da356e56a7428ad25149ca77381849a6a5232 + subpackages: + - assert + - require diff --git a/vendor/src/go.uber.org/atomic/glide.yaml b/vendor/src/go.uber.org/atomic/glide.yaml new file mode 100644 index 00000000..4cf608ec --- /dev/null +++ b/vendor/src/go.uber.org/atomic/glide.yaml @@ -0,0 +1,6 @@ +package: go.uber.org/atomic +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert + - require diff --git a/vendor/src/go.uber.org/atomic/scripts/cover.sh b/vendor/src/go.uber.org/atomic/scripts/cover.sh new file mode 100644 index 00000000..5dfb65e4 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/scripts/cover.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +COVER=cover +ROOT_PKG=go.uber.org/atomic + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | map(select(startswith("'"$ROOT_PKG"'"))) + | map(select(contains("/vendor/") | not)) + | . + ["'"$pkg"'"] + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + go test \ + -coverprofile "$COVER/cover.${i}.out" -coverpkg "$coverpkg" \ + -v "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/go.uber.org/atomic/scripts/test-ubergo.sh b/vendor/src/go.uber.org/atomic/scripts/test-ubergo.sh new file mode 100644 index 00000000..9bc526de --- /dev/null +++ b/vendor/src/go.uber.org/atomic/scripts/test-ubergo.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -euox pipefail +IFS=$'\n\t' + +# This script creates a fake GOPATH, symlinks in the current +# directory as uber-go/atomic and verifies that tests still pass. + +WORK_DIR=`mktemp -d` +function cleanup { + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + + +export GOPATH="$WORK_DIR" +PKG_PARENT="$WORK_DIR/src/github.com/uber-go" +PKG_DIR="$PKG_PARENT/atomic" + +mkdir -p "$PKG_PARENT" +cp -R `pwd` "$PKG_DIR" +cd "$PKG_DIR" + +# The example imports go.uber.org, fix the import. +sed -e 's/go.uber.org\/atomic/github.com\/uber-go\/atomic/' -i="" example_test.go + +make test diff --git a/vendor/src/go.uber.org/atomic/stress_test.go b/vendor/src/go.uber.org/atomic/stress_test.go new file mode 100644 index 00000000..72a65bd4 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/stress_test.go @@ -0,0 +1,154 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "runtime" + "sync" + "testing" +) + +const ( + _parallelism = 4 + _iterations = 1000 +) + +var _stressTests = map[string]func(){ + "i32": stressInt32, + "i64": stressInt64, + "u32": stressUint32, + "u64": stressUint64, + "f64": stressFloat64, + "bool": stressBool, + "string": stressString, +} + +func TestStress(t *testing.T) { + for name, f := range _stressTests { + t.Run(name, func(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(_parallelism)) + + start := make(chan struct{}) + var wg sync.WaitGroup + wg.Add(_parallelism) + for i := 0; i < _parallelism; i++ { + go func() { + defer wg.Done() + <-start + for j := 0; j < _iterations; j++ { + f() + } + }() + } + close(start) + wg.Wait() + }) + } +} + +func BenchmarkStress(b *testing.B) { + for name, f := range _stressTests { + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + f() + } + }) + } +} + +func stressInt32() { + var atom Int32 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressInt64() { + var atom Int64 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressUint32() { + var atom Uint32 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressUint64() { + var atom Uint64 + atom.Load() + atom.Add(1) + atom.Sub(2) + atom.Inc() + atom.Dec() + atom.CAS(1, 0) + atom.Swap(5) + atom.Store(1) +} + +func stressFloat64() { + var atom Float64 + atom.Load() + atom.CAS(1.0, 0.1) + atom.Add(1.1) + atom.Sub(0.2) + atom.Store(1.0) +} + +func stressBool() { + var atom Bool + atom.Load() + atom.Store(false) + atom.Swap(true) + atom.CAS(true, false) + atom.CAS(true, false) + atom.Load() + atom.Toggle() + atom.Toggle() +} + +func stressString() { + var atom String + atom.Load() + atom.Store("abc") + atom.Load() + atom.Store("def") + atom.Load() + atom.Store("") +} diff --git a/vendor/src/go.uber.org/atomic/string.go b/vendor/src/go.uber.org/atomic/string.go new file mode 100644 index 00000000..329fb123 --- /dev/null +++ b/vendor/src/go.uber.org/atomic/string.go @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +// String is an atomic type-safe wrapper around Value for strings. +type String struct{ v Value } + +// NewString creates a String. +func NewString(str string) *String { + s := &String{} + if str != "" { + s.Store(str) + } + return s +} + +// Load atomically loads the wrapped string. +func (s *String) Load() string { + v := s.v.Load() + if v == nil { + return "" + } + return v.(string) +} + +// Store atomically stores the passed string. +// Note: Converting the string to an interface{} to store in the Value +// requires an allocation. +func (s *String) Store(str string) { + if str == "" { + s.v = Value{} + return + } + s.v.Store(str) +} diff --git a/vendor/src/go.uber.org/atomic/string_test.go b/vendor/src/go.uber.org/atomic/string_test.go new file mode 100644 index 00000000..bf72505d --- /dev/null +++ b/vendor/src/go.uber.org/atomic/string_test.go @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package atomic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStringNoInitialValue(t *testing.T) { + atom := &String{} + require.Equal(t, "", atom.Load(), "Initial value should be blank string") +} + +func TestString(t *testing.T) { + atom := NewString("") + require.Equal(t, "", atom.Load(), "Expected Load to return initialized value") + + atom.Store("abc") + require.Equal(t, "abc", atom.Load(), "Unexpected value after Store") + + atom = NewString("bcd") + require.Equal(t, "bcd", atom.Load(), "Expected Load to return initialized value") + + atom.Store("") + require.Equal(t, "", atom.Load(), "Expected Load to return empty value") +} diff --git a/vendor/src/go.uber.org/multierr/CHANGELOG.md b/vendor/src/go.uber.org/multierr/CHANGELOG.md new file mode 100644 index 00000000..898445d0 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/CHANGELOG.md @@ -0,0 +1,28 @@ +Releases +======== + +v1.1.0 (2017-06-30) +=================== + +- Added an `Errors(error) []error` function to extract the underlying list of + errors for a multierr error. + + +v1.0.0 (2017-05-31) +=================== + +No changes since v0.2.0. This release is committing to making no breaking +changes to the current API in the 1.X series. + + +v0.2.0 (2017-04-11) +=================== + +- Repeatedly appending to the same error is now faster due to fewer + allocations. + + +v0.1.0 (2017-31-03) +=================== + +- Initial release diff --git a/vendor/src/go.uber.org/multierr/LICENSE.txt b/vendor/src/go.uber.org/multierr/LICENSE.txt new file mode 100644 index 00000000..858e0247 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/go.uber.org/multierr/Makefile b/vendor/src/go.uber.org/multierr/Makefile new file mode 100644 index 00000000..a7437d06 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/Makefile @@ -0,0 +1,74 @@ +export GO15VENDOREXPERIMENT=1 + +PACKAGES := $(shell glide nv) + +GO_FILES := $(shell \ + find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ + -o -name '*.go' -print | cut -b3-) + +.PHONY: install +install: + glide --version || go get github.com/Masterminds/glide + glide install + +.PHONY: build +build: + go build -i $(PACKAGES) + +.PHONY: test +test: + go test -cover -race $(PACKAGES) + +.PHONY: gofmt +gofmt: + $(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX)) + @gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true + @[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false) + +.PHONY: govet +govet: + $(eval VET_LOG := $(shell mktemp -t govet.XXXXX)) + @go vet $(PACKAGES) 2>&1 \ + | grep -v '^exit status' > $(VET_LOG) || true + @[ ! -s "$(VET_LOG)" ] || (echo "govet failed:" | cat - $(VET_LOG) && false) + +.PHONY: golint +golint: + @go get github.com/golang/lint/golint + $(eval LINT_LOG := $(shell mktemp -t golint.XXXXX)) + @cat /dev/null > $(LINT_LOG) + @$(foreach pkg, $(PACKAGES), golint $(pkg) >> $(LINT_LOG) || true;) + @[ ! -s "$(LINT_LOG)" ] || (echo "golint failed:" | cat - $(LINT_LOG) && false) + +.PHONY: staticcheck +staticcheck: + @go get honnef.co/go/tools/cmd/staticcheck + $(eval STATICCHECK_LOG := $(shell mktemp -t staticcheck.XXXXX)) + @staticcheck $(PACKAGES) 2>&1 > $(STATICCHECK_LOG) || true + @[ ! -s "$(STATICCHECK_LOG)" ] || (echo "staticcheck failed:" | cat - $(STATICCHECK_LOG) && false) + +.PHONY: lint +lint: gofmt govet golint staticcheck + +.PHONY: cover +cover: + ./scripts/cover.sh $(shell go list $(PACKAGES)) + go tool cover -html=cover.out -o cover.html + +update-license: + @go get go.uber.org/tools/update-license + @update-license \ + $(shell go list -json $(PACKAGES) | \ + jq -r '.Dir + "/" + (.GoFiles | .[])') + +############################################################################## + +.PHONY: install_ci +install_ci: install + go get github.com/wadey/gocovmerge + go get github.com/mattn/goveralls + go get golang.org/x/tools/cmd/cover + +.PHONY: test_ci +test_ci: install_ci + ./scripts/cover.sh $(shell go list $(PACKAGES)) diff --git a/vendor/src/go.uber.org/multierr/README.md b/vendor/src/go.uber.org/multierr/README.md new file mode 100644 index 00000000..065088f6 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/README.md @@ -0,0 +1,23 @@ +# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +`multierr` allows combining one or more Go `error`s together. + +## Installation + + go get -u go.uber.org/multierr + +## Status + +Stable: No breaking changes will be made before 2.0. + +------------------------------------------------------------------------------- + +Released under the [MIT License]. + +[MIT License]: LICENSE.txt +[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg +[doc]: https://godoc.org/go.uber.org/multierr +[ci-img]: https://travis-ci.org/uber-go/multierr.svg?branch=master +[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg +[ci]: https://travis-ci.org/uber-go/multierr +[cov]: https://codecov.io/gh/uber-go/multierr diff --git a/vendor/src/go.uber.org/multierr/benchmarks_test.go b/vendor/src/go.uber.org/multierr/benchmarks_test.go new file mode 100644 index 00000000..797f5a0f --- /dev/null +++ b/vendor/src/go.uber.org/multierr/benchmarks_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package multierr + +import ( + "errors" + "fmt" + "testing" +) + +func BenchmarkAppend(b *testing.B) { + errorTypes := []struct { + name string + err error + }{ + { + name: "nil", + err: nil, + }, + { + name: "single error", + err: errors.New("test"), + }, + { + name: "multiple errors", + err: appendN(nil, errors.New("err"), 10), + }, + } + + for _, initial := range errorTypes { + for _, v := range errorTypes { + msg := fmt.Sprintf("append %v to %v", v.name, initial.name) + b.Run(msg, func(b *testing.B) { + for _, appends := range []int{1, 2, 10} { + b.Run(fmt.Sprint(appends), func(b *testing.B) { + for i := 0; i < b.N; i++ { + appendN(initial.err, v.err, appends) + } + }) + } + }) + } + } +} diff --git a/vendor/src/go.uber.org/multierr/error.go b/vendor/src/go.uber.org/multierr/error.go new file mode 100644 index 00000000..de6ce473 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/error.go @@ -0,0 +1,401 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package multierr allows combining one or more errors together. +// +// Overview +// +// Errors can be combined with the use of the Combine function. +// +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// conn.Close(), +// ) +// +// If only two errors are being combined, the Append function may be used +// instead. +// +// err = multierr.Combine(reader.Close(), writer.Close()) +// +// This makes it possible to record resource cleanup failures from deferred +// blocks with the help of named return values. +// +// func sendRequest(req Request) (err error) { +// conn, err := openConnection() +// if err != nil { +// return err +// } +// defer func() { +// err = multierr.Append(err, conn.Close()) +// }() +// // ... +// } +// +// The underlying list of errors for a returned error object may be retrieved +// with the Errors function. +// +// errors := multierr.Errors(err) +// if len(errors) > 0 { +// fmt.Println("The following errors occurred:") +// } +// +// Advanced Usage +// +// Errors returned by Combine and Append MAY implement the following +// interface. +// +// type errorGroup interface { +// // Returns a slice containing the underlying list of errors. +// // +// // This slice MUST NOT be modified by the caller. +// Errors() []error +// } +// +// Note that if you need access to list of errors behind a multierr error, you +// should prefer using the Errors function. That said, if you need cheap +// read-only access to the underlying errors slice, you can attempt to cast +// the error to this interface. You MUST handle the failure case gracefully +// because errors returned by Combine and Append are not guaranteed to +// implement this interface. +// +// var errors []error +// group, ok := err.(errorGroup) +// if ok { +// errors = group.Errors() +// } else { +// errors = []error{err} +// } +package multierr // import "go.uber.org/multierr" + +import ( + "bytes" + "fmt" + "io" + "strings" + "sync" + + "go.uber.org/atomic" +) + +var ( + // Separator for single-line error messages. + _singlelineSeparator = []byte("; ") + + _newline = []byte("\n") + + // Prefix for multi-line messages + _multilinePrefix = []byte("the following errors occurred:") + + // Prefix for the first and following lines of an item in a list of + // multi-line error messages. + // + // For example, if a single item is: + // + // foo + // bar + // + // It will become, + // + // - foo + // bar + _multilineSeparator = []byte("\n - ") + _multilineIndent = []byte(" ") +) + +// _bufferPool is a pool of bytes.Buffers. +var _bufferPool = sync.Pool{ + New: func() interface{} { + return &bytes.Buffer{} + }, +} + +type errorGroup interface { + Errors() []error +} + +// Errors returns a slice containing zero or more errors that the supplied +// error is composed of. If the error is nil, the returned slice is empty. +// +// err := multierr.Append(r.Close(), w.Close()) +// errors := multierr.Errors(err) +// +// If the error is not composed of other errors, the returned slice contains +// just the error that was passed in. +// +// Callers of this function are free to modify the returned slice. +func Errors(err error) []error { + if err == nil { + return nil + } + + // Note that we're casting to multiError, not errorGroup. Our contract is + // that returned errors MAY implement errorGroup. Errors, however, only + // has special behavior for multierr-specific error objects. + // + // This behavior can be expanded in the future but I think it's prudent to + // start with as little as possible in terms of contract and possibility + // of misuse. + eg, ok := err.(*multiError) + if !ok { + return []error{err} + } + + errors := eg.Errors() + result := make([]error, len(errors)) + copy(result, errors) + return result +} + +// multiError is an error that holds one or more errors. +// +// An instance of this is guaranteed to be non-empty and flattened. That is, +// none of the errors inside multiError are other multiErrors. +// +// multiError formats to a semi-colon delimited list of error messages with +// %v and with a more readable multi-line format with %+v. +type multiError struct { + copyNeeded atomic.Bool + errors []error +} + +var _ errorGroup = (*multiError)(nil) + +// Errors returns the list of underlying errors. +// +// This slice MUST NOT be modified. +func (merr *multiError) Errors() []error { + if merr == nil { + return nil + } + return merr.errors +} + +func (merr *multiError) Error() string { + if merr == nil { + return "" + } + + buff := _bufferPool.Get().(*bytes.Buffer) + buff.Reset() + + merr.writeSingleline(buff) + + result := buff.String() + _bufferPool.Put(buff) + return result +} + +func (merr *multiError) Format(f fmt.State, c rune) { + if c == 'v' && f.Flag('+') { + merr.writeMultiline(f) + } else { + merr.writeSingleline(f) + } +} + +func (merr *multiError) writeSingleline(w io.Writer) { + first := true + for _, item := range merr.errors { + if first { + first = false + } else { + w.Write(_singlelineSeparator) + } + io.WriteString(w, item.Error()) + } +} + +func (merr *multiError) writeMultiline(w io.Writer) { + w.Write(_multilinePrefix) + for _, item := range merr.errors { + w.Write(_multilineSeparator) + writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item)) + } +} + +// Writes s to the writer with the given prefix added before each line after +// the first. +func writePrefixLine(w io.Writer, prefix []byte, s string) { + first := true + for len(s) > 0 { + if first { + first = false + } else { + w.Write(prefix) + } + + idx := strings.IndexByte(s, '\n') + if idx < 0 { + idx = len(s) - 1 + } + + io.WriteString(w, s[:idx+1]) + s = s[idx+1:] + } +} + +type inspectResult struct { + // Number of top-level non-nil errors + Count int + + // Total number of errors including multiErrors + Capacity int + + // Index of the first non-nil error in the list. Value is meaningless if + // Count is zero. + FirstErrorIdx int + + // Whether the list contains at least one multiError + ContainsMultiError bool +} + +// Inspects the given slice of errors so that we can efficiently allocate +// space for it. +func inspect(errors []error) (res inspectResult) { + first := true + for i, err := range errors { + if err == nil { + continue + } + + res.Count++ + if first { + first = false + res.FirstErrorIdx = i + } + + if merr, ok := err.(*multiError); ok { + res.Capacity += len(merr.errors) + res.ContainsMultiError = true + } else { + res.Capacity++ + } + } + return +} + +// fromSlice converts the given list of errors into a single error. +func fromSlice(errors []error) error { + res := inspect(errors) + switch res.Count { + case 0: + return nil + case 1: + // only one non-nil entry + return errors[res.FirstErrorIdx] + case len(errors): + if !res.ContainsMultiError { + // already flat + return &multiError{errors: errors} + } + } + + nonNilErrs := make([]error, 0, res.Capacity) + for _, err := range errors[res.FirstErrorIdx:] { + if err == nil { + continue + } + + if nested, ok := err.(*multiError); ok { + nonNilErrs = append(nonNilErrs, nested.errors...) + } else { + nonNilErrs = append(nonNilErrs, err) + } + } + + return &multiError{errors: nonNilErrs} +} + +// Combine combines the passed errors into a single error. +// +// If zero arguments were passed or if all items are nil, a nil error is +// returned. +// +// Combine(nil, nil) // == nil +// +// If only a single error was passed, it is returned as-is. +// +// Combine(err) // == err +// +// Combine skips over nil arguments so this function may be used to combine +// together errors from operations that fail independently of each other. +// +// multierr.Combine( +// reader.Close(), +// writer.Close(), +// pipe.Close(), +// ) +// +// If any of the passed errors is a multierr error, it will be flattened along +// with the other errors. +// +// multierr.Combine(multierr.Combine(err1, err2), err3) +// // is the same as +// multierr.Combine(err1, err2, err3) +// +// The returned error formats into a readable multi-line error message if +// formatted with %+v. +// +// fmt.Sprintf("%+v", multierr.Combine(err1, err2)) +func Combine(errors ...error) error { + return fromSlice(errors) +} + +// Append appends the given errors together. Either value may be nil. +// +// This function is a specialization of Combine for the common case where +// there are only two errors. +// +// err = multierr.Append(reader.Close(), writer.Close()) +// +// The following pattern may also be used to record failure of deferred +// operations without losing information about the original error. +// +// func doSomething(..) (err error) { +// f := acquireResource() +// defer func() { +// err = multierr.Append(err, f.Close()) +// }() +func Append(left error, right error) error { + switch { + case left == nil: + return right + case right == nil: + return left + } + + if _, ok := right.(*multiError); !ok { + if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) { + // Common case where the error on the left is constantly being + // appended to. + errs := append(l.errors, right) + return &multiError{errors: errs} + } else if !ok { + // Both errors are single errors. + return &multiError{errors: []error{left, right}} + } + } + + // Either right or both, left and right, are multiErrors. Rely on usual + // expensive logic. + errors := [2]error{left, right} + return fromSlice(errors[0:]) +} diff --git a/vendor/src/go.uber.org/multierr/error_test.go b/vendor/src/go.uber.org/multierr/error_test.go new file mode 100644 index 00000000..9384ff5a --- /dev/null +++ b/vendor/src/go.uber.org/multierr/error_test.go @@ -0,0 +1,511 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package multierr + +import ( + "errors" + "fmt" + "io" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// richFormatError is an error that prints a different output depending on +// whether %v or %+v was used. +type richFormatError struct{} + +func (r richFormatError) Error() string { + return fmt.Sprint(r) +} + +func (richFormatError) Format(f fmt.State, c rune) { + if c == 'v' && f.Flag('+') { + io.WriteString(f, "multiline\nmessage\nwith plus") + } else { + io.WriteString(f, "without plus") + } +} + +func appendN(initial, err error, n int) error { + errs := initial + for i := 0; i < n; i++ { + errs = Append(errs, err) + } + return errs +} + +func newMultiErr(errors ...error) error { + return &multiError{errors: errors} +} + +func TestCombine(t *testing.T) { + tests := []struct { + // Input + giveErrors []error + + // Resulting error + wantError error + + // %+v and %v string representations + wantMultiline string + wantSingleline string + }{ + { + giveErrors: nil, + wantError: nil, + }, + { + giveErrors: []error{}, + wantError: nil, + }, + { + giveErrors: []error{ + errors.New("foo"), + nil, + newMultiErr( + errors.New("bar"), + ), + nil, + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar", + wantSingleline: "foo; bar", + }, + { + giveErrors: []error{ + errors.New("foo"), + newMultiErr( + errors.New("bar"), + ), + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar", + wantSingleline: "foo; bar", + }, + { + giveErrors: []error{errors.New("great sadness")}, + wantError: errors.New("great sadness"), + wantMultiline: "great sadness", + wantSingleline: "great sadness", + }, + { + giveErrors: []error{ + errors.New("foo"), + errors.New("bar"), + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar", + wantSingleline: "foo; bar", + }, + { + giveErrors: []error{ + errors.New("great sadness"), + errors.New("multi\n line\nerror message"), + errors.New("single line error message"), + }, + wantError: newMultiErr( + errors.New("great sadness"), + errors.New("multi\n line\nerror message"), + errors.New("single line error message"), + ), + wantMultiline: "the following errors occurred:\n" + + " - great sadness\n" + + " - multi\n" + + " line\n" + + " error message\n" + + " - single line error message", + wantSingleline: "great sadness; " + + "multi\n line\nerror message; " + + "single line error message", + }, + { + giveErrors: []error{ + errors.New("foo"), + newMultiErr( + errors.New("bar"), + errors.New("baz"), + ), + errors.New("qux"), + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + errors.New("baz"), + errors.New("qux"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar\n" + + " - baz\n" + + " - qux", + wantSingleline: "foo; bar; baz; qux", + }, + { + giveErrors: []error{ + errors.New("foo"), + nil, + newMultiErr( + errors.New("bar"), + ), + nil, + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar", + wantSingleline: "foo; bar", + }, + { + giveErrors: []error{ + errors.New("foo"), + newMultiErr( + errors.New("bar"), + ), + }, + wantError: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - bar", + wantSingleline: "foo; bar", + }, + { + giveErrors: []error{ + errors.New("foo"), + richFormatError{}, + errors.New("bar"), + }, + wantError: newMultiErr( + errors.New("foo"), + richFormatError{}, + errors.New("bar"), + ), + wantMultiline: "the following errors occurred:\n" + + " - foo\n" + + " - multiline\n" + + " message\n" + + " with plus\n" + + " - bar", + wantSingleline: "foo; without plus; bar", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprint(i), func(t *testing.T) { + err := Combine(tt.giveErrors...) + require.Equal(t, tt.wantError, err) + + if tt.wantMultiline != "" { + t.Run("Sprintf/multiline", func(t *testing.T) { + assert.Equal(t, tt.wantMultiline, fmt.Sprintf("%+v", err)) + }) + } + + if tt.wantSingleline != "" { + t.Run("Sprintf/singleline", func(t *testing.T) { + assert.Equal(t, tt.wantSingleline, fmt.Sprintf("%v", err)) + }) + + t.Run("Error()", func(t *testing.T) { + assert.Equal(t, tt.wantSingleline, err.Error()) + }) + + if s, ok := err.(fmt.Stringer); ok { + t.Run("String()", func(t *testing.T) { + assert.Equal(t, tt.wantSingleline, s.String()) + }) + } + } + }) + } +} + +func TestCombineDoesNotModifySlice(t *testing.T) { + errors := []error{ + errors.New("foo"), + nil, + errors.New("bar"), + } + + assert.NotNil(t, Combine(errors...)) + assert.Len(t, errors, 3) + assert.Nil(t, errors[1], 3) +} + +func TestAppend(t *testing.T) { + tests := []struct { + left error + right error + want error + }{ + { + left: nil, + right: nil, + want: nil, + }, + { + left: nil, + right: errors.New("great sadness"), + want: errors.New("great sadness"), + }, + { + left: errors.New("great sadness"), + right: nil, + want: errors.New("great sadness"), + }, + { + left: errors.New("foo"), + right: errors.New("bar"), + want: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + }, + { + left: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + right: errors.New("baz"), + want: newMultiErr( + errors.New("foo"), + errors.New("bar"), + errors.New("baz"), + ), + }, + { + left: errors.New("baz"), + right: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + want: newMultiErr( + errors.New("baz"), + errors.New("foo"), + errors.New("bar"), + ), + }, + { + left: newMultiErr( + errors.New("foo"), + ), + right: newMultiErr( + errors.New("bar"), + ), + want: newMultiErr( + errors.New("foo"), + errors.New("bar"), + ), + }, + } + + for _, tt := range tests { + assert.Equal(t, tt.want, Append(tt.left, tt.right)) + } +} + +type notMultiErr struct{} + +var _ errorGroup = notMultiErr{} + +func (notMultiErr) Error() string { + return "great sadness" +} + +func (notMultiErr) Errors() []error { + return []error{errors.New("great sadness")} +} + +func TestErrors(t *testing.T) { + tests := []struct { + give error + want []error + + // Don't attempt to cast to errorGroup or *multiError + dontCast bool + }{ + {dontCast: true}, // nil + { + give: errors.New("hi"), + want: []error{errors.New("hi")}, + dontCast: true, + }, + { + // We don't yet support non-multierr errors. + give: notMultiErr{}, + want: []error{notMultiErr{}}, + dontCast: true, + }, + { + give: Combine( + errors.New("foo"), + errors.New("bar"), + ), + want: []error{ + errors.New("foo"), + errors.New("bar"), + }, + }, + { + give: Append( + errors.New("foo"), + errors.New("bar"), + ), + want: []error{ + errors.New("foo"), + errors.New("bar"), + }, + }, + { + give: Append( + errors.New("foo"), + Combine( + errors.New("bar"), + ), + ), + want: []error{ + errors.New("foo"), + errors.New("bar"), + }, + }, + { + give: Combine( + errors.New("foo"), + Append( + errors.New("bar"), + errors.New("baz"), + ), + errors.New("qux"), + ), + want: []error{ + errors.New("foo"), + errors.New("bar"), + errors.New("baz"), + errors.New("qux"), + }, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprint(i), func(t *testing.T) { + t.Run("Errors()", func(t *testing.T) { + require.Equal(t, tt.want, Errors(tt.give)) + }) + + if tt.dontCast { + return + } + + t.Run("multiError", func(t *testing.T) { + require.Equal(t, tt.want, tt.give.(*multiError).Errors()) + }) + + t.Run("errorGroup", func(t *testing.T) { + require.Equal(t, tt.want, tt.give.(errorGroup).Errors()) + }) + }) + } +} + +func createMultiErrWithCapacity() error { + // Create a multiError that has capacity for more errors so Append will + // modify the underlying array that may be shared. + return appendN(nil, errors.New("append"), 50) +} + +func TestAppendDoesNotModify(t *testing.T) { + initial := createMultiErrWithCapacity() + err1 := Append(initial, errors.New("err1")) + err2 := Append(initial, errors.New("err2")) + + // Make sure the error messages match, since we do modify the copyNeeded + // atomic, the values cannot be compared. + assert.EqualError(t, initial, createMultiErrWithCapacity().Error(), "Initial should not be modified") + + assert.EqualError(t, err1, Append(createMultiErrWithCapacity(), errors.New("err1")).Error()) + assert.EqualError(t, err2, Append(createMultiErrWithCapacity(), errors.New("err2")).Error()) +} + +func TestAppendRace(t *testing.T) { + initial := createMultiErrWithCapacity() + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + err := initial + for j := 0; j < 10; j++ { + err = Append(err, errors.New("err")) + } + }() + } + + wg.Wait() +} + +func TestErrorsSliceIsImmutable(t *testing.T) { + err1 := errors.New("err1") + err2 := errors.New("err2") + + err := Append(err1, err2) + gotErrors := Errors(err) + require.Equal(t, []error{err1, err2}, gotErrors, "errors must match") + + gotErrors[0] = nil + gotErrors[1] = errors.New("err3") + + require.Equal(t, []error{err1, err2}, Errors(err), + "errors must match after modification") +} + +func TestNilMultierror(t *testing.T) { + // For safety, all operations on multiError should be safe even if it is + // nil. + var err *multiError + + require.Empty(t, err.Error()) + require.Empty(t, err.Errors()) +} diff --git a/vendor/src/go.uber.org/multierr/example_test.go b/vendor/src/go.uber.org/multierr/example_test.go new file mode 100644 index 00000000..da4d9140 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/example_test.go @@ -0,0 +1,72 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package multierr_test + +import ( + "errors" + "fmt" + + "go.uber.org/multierr" +) + +func ExampleCombine() { + err := multierr.Combine( + errors.New("call 1 failed"), + nil, // successful request + errors.New("call 3 failed"), + nil, // successful request + errors.New("call 5 failed"), + ) + fmt.Printf("%+v", err) + // Output: + // the following errors occurred: + // - call 1 failed + // - call 3 failed + // - call 5 failed +} + +func ExampleAppend() { + var err error + err = multierr.Append(err, errors.New("call 1 failed")) + err = multierr.Append(err, errors.New("call 2 failed")) + fmt.Println(err) + // Output: + // call 1 failed; call 2 failed +} + +func ExampleErrors() { + err := multierr.Combine( + nil, // successful request + errors.New("call 2 failed"), + errors.New("call 3 failed"), + ) + err = multierr.Append(err, nil) // successful request + err = multierr.Append(err, errors.New("call 5 failed")) + + errors := multierr.Errors(err) + for _, err := range errors { + fmt.Println(err) + } + // Output: + // call 2 failed + // call 3 failed + // call 5 failed +} diff --git a/vendor/src/go.uber.org/multierr/glide.lock b/vendor/src/go.uber.org/multierr/glide.lock new file mode 100644 index 00000000..f9ea94c3 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/glide.lock @@ -0,0 +1,19 @@ +hash: b53b5e9a84b9cb3cc4b2d0499e23da2feca1eec318ce9bb717ecf35bf24bf221 +updated: 2017-04-10T13:34:45.671678062-07:00 +imports: +- name: go.uber.org/atomic + version: 3b8db5e93c4c02efbc313e17b2e796b0914a01fb +testImports: +- name: github.com/davecgh/go-spew + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + subpackages: + - spew +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + subpackages: + - assert + - require diff --git a/vendor/src/go.uber.org/multierr/glide.yaml b/vendor/src/go.uber.org/multierr/glide.yaml new file mode 100644 index 00000000..6ef084ec --- /dev/null +++ b/vendor/src/go.uber.org/multierr/glide.yaml @@ -0,0 +1,8 @@ +package: go.uber.org/multierr +import: +- package: go.uber.org/atomic + version: ^1 +testImport: +- package: github.com/stretchr/testify + subpackages: + - assert diff --git a/vendor/src/go.uber.org/multierr/scripts/cover.sh b/vendor/src/go.uber.org/multierr/scripts/cover.sh new file mode 100644 index 00000000..e7f2b886 --- /dev/null +++ b/vendor/src/go.uber.org/multierr/scripts/cover.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e + +COVER=cover +ROOT_PKG=go.uber.org/multierr + +if [[ -d "$COVER" ]]; then + rm -rf "$COVER" +fi +mkdir -p "$COVER" + +i=0 +for pkg in "$@"; do + i=$((i + 1)) + + extracoverpkg="" + if [[ -f "$GOPATH/src/$pkg/.extra-coverpkg" ]]; then + extracoverpkg=$( \ + sed -e "s|^|$pkg/|g" < "$GOPATH/src/$pkg/.extra-coverpkg" \ + | tr '\n' ',') + fi + + coverpkg=$(go list -json "$pkg" | jq -r ' + .Deps + | . + ["'"$pkg"'"] + | map + ( select(startswith("'"$ROOT_PKG"'")) + | select(contains("/vendor/") | not) + ) + | join(",") + ') + if [[ -n "$extracoverpkg" ]]; then + coverpkg="$extracoverpkg$coverpkg" + fi + + go test \ + -coverprofile "$COVER/cover.${i}.out" -coverpkg "$coverpkg" \ + -v "$pkg" +done + +gocovmerge "$COVER"/*.out > cover.out diff --git a/vendor/src/go.uber.org/zap/CHANGELOG.md b/vendor/src/go.uber.org/zap/CHANGELOG.md new file mode 100644 index 00000000..6a1609a2 --- /dev/null +++ b/vendor/src/go.uber.org/zap/CHANGELOG.md @@ -0,0 +1,270 @@ +# Changelog + +## v1.7.1 (25 Sep 2017) + +Bugfixes: +* [#504][]: Store strings when using AddByteString with the map encoder. + +## v1.7.0 (21 Sep 2017) + +Enhancements: + +* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user + to specify the level of the logged messages. + +## v1.6.0 (30 Aug 2017) + +Enhancements: + +* [#491][]: Omit zap stack frames from stacktraces. +* [#490][]: Add a `ContextMap` method to observer logs for simpler + field validation in tests. + +## v1.5.0 (22 Jul 2017) + +Enhancements: + +* [#460][] and [#470][]: Support errors produced by `go.uber.org/multierr`. +* [#465][]: Support user-supplied encoders for logger names. + +Bugfixes: + +* [#477][]: Fix a bug that incorrectly truncated deep stacktraces. + +Thanks to @richard-tunein and @pavius for their contributions to this release. + +## v1.4.1 (08 Jun 2017) + +This release fixes two bugs. + +Bugfixes: + +* [#435][]: Support a variety of case conventions when unmarshaling levels. +* [#444][]: Fix a panic in the observer. + +## v1.4.0 (12 May 2017) + +This release adds a few small features and is fully backward-compatible. + +Enhancements: + +* [#424][]: Add a `LineEnding` field to `EncoderConfig`, allowing users to + override the Unix-style default. +* [#425][]: Preserve time zones when logging times. +* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a + variety of operations a bit simpler. + +## v1.3.0 (25 Apr 2017) + +This release adds an enhancement to zap's testing helpers as well as the +ability to marshal an AtomicLevel. It is fully backward-compatible. + +Enhancements: + +* [#415][]: Add a substring-filtering helper to zap's observer. This is + particularly useful when testing the `SugaredLogger`. +* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`. + +## v1.2.0 (13 Apr 2017) + +This release adds a gRPC compatibility wrapper. It is fully backward-compatible. + +Enhancements: + +* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements + `grpclog.Logger`. + +## v1.1.0 (31 Mar 2017) + +This release fixes two bugs and adds some enhancements to zap's testing helpers. +It is fully backward-compatible. + +Bugfixes: + +* [#385][]: Fix caller path trimming on Windows. +* [#396][]: Fix a panic when attempting to use non-existent directories with + zap's configuration struct. + +Enhancements: + +* [#386][]: Add filtering helpers to zaptest's observing logger. + +Thanks to @moitias for contributing to this release. + +## v1.0.0 (14 Mar 2017) + +This is zap's first stable release. All exported APIs are now final, and no +further breaking changes will be made in the 1.x release series. Anyone using a +semver-aware dependency manager should now pin to `^1`. + +Breaking changes: + +* [#366][]: Add byte-oriented APIs to encoders to log UTF-8 encoded text without + casting from `[]byte` to `string`. +* [#364][]: To support buffering outputs, add `Sync` methods to `zapcore.Core`, + `zap.Logger`, and `zap.SugaredLogger`. +* [#371][]: Rename the `testutils` package to `zaptest`, which is less likely to + clash with other testing helpers. + +Bugfixes: + +* [#362][]: Make the ISO8601 time formatters fixed-width, which is friendlier + for tab-separated console output. +* [#369][]: Remove the automatic locks in `zapcore.NewCore`, which allows zap to + work with concurrency-safe `WriteSyncer` implementations. +* [#347][]: Stop reporting errors when trying to `fsync` standard out on Linux + systems. +* [#373][]: Report the correct caller from zap's standard library + interoperability wrappers. + +Enhancements: + +* [#348][]: Add a registry allowing third-party encodings to work with zap's + built-in `Config`. +* [#327][]: Make the representation of logger callers configurable (like times, + levels, and durations). +* [#376][]: Allow third-party encoders to use their own buffer pools, which + removes the last performance advantage that zap's encoders have over plugins. +* [#346][]: Add `CombineWriteSyncers`, a convenience function to tee multiple + `WriteSyncer`s and lock the result. +* [#365][]: Make zap's stacktraces compatible with mid-stack inlining (coming in + Go 1.9). +* [#372][]: Export zap's observing logger as `zaptest/observer`. This makes it + easier for particularly punctilious users to unit test their application's + logging. + +Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their +contributions to this release. + +## v1.0.0-rc.3 (7 Mar 2017) + +This is the third release candidate for zap's stable release. There are no +breaking changes. + +Bugfixes: + +* [#339][]: Byte slices passed to `zap.Any` are now correctly treated as binary blobs + rather than `[]uint8`. + +Enhancements: + +* [#307][]: Users can opt into colored output for log levels. +* [#353][]: In addition to hijacking the output of the standard library's + package-global logging functions, users can now construct a zap-backed + `log.Logger` instance. +* [#311][]: Frames from common runtime functions and some of zap's internal + machinery are now omitted from stacktraces. + +Thanks to @ansel1 and @suyash for their contributions to this release. + +## v1.0.0-rc.2 (21 Feb 2017) + +This is the second release candidate for zap's stable release. It includes two +breaking changes. + +Breaking changes: + +* [#316][]: Zap's global loggers are now fully concurrency-safe + (previously, users had to ensure that `ReplaceGlobals` was called before the + loggers were in use). However, they must now be accessed via the `L()` and + `S()` functions. Users can update their projects with + + ``` + gofmt -r "zap.L -> zap.L()" -w . + gofmt -r "zap.S -> zap.S()" -w . + ``` +* [#309][] and [#317][]: RC1 was mistakenly shipped with invalid + JSON and YAML struct tags on all config structs. This release fixes the tags + and adds static analysis to prevent similar bugs in the future. + +Bugfixes: + +* [#321][]: Redirecting the standard library's `log` output now + correctly reports the logger's caller. + +Enhancements: + +* [#325][] and [#333][]: Zap now transparently supports non-standard, rich + errors like those produced by `github.com/pkg/errors`. +* [#326][]: Though `New(nil)` continues to return a no-op logger, `NewNop()` is + now preferred. Users can update their projects with `gofmt -r 'zap.New(nil) -> + zap.NewNop()' -w .`. +* [#300][]: Incorrectly importing zap as `github.com/uber-go/zap` now returns a + more informative error. + +Thanks to @skipor and @chapsuk for their contributions to this release. + +## v1.0.0-rc.1 (14 Feb 2017) + +This is the first release candidate for zap's stable release. There are multiple +breaking changes and improvements from the pre-release version. Most notably: + +* **Zap's import path is now "go.uber.org/zap"** — all users will + need to update their code. +* User-facing types and functions remain in the `zap` package. Code relevant + largely to extension authors is now in the `zapcore` package. +* The `zapcore.Core` type makes it easy for third-party packages to use zap's + internals but provide a different user-facing API. +* `Logger` is now a concrete type instead of an interface. +* A less verbose (though slower) logging API is included by default. +* Package-global loggers `L` and `S` are included. +* A human-friendly console encoder is included. +* A declarative config struct allows common logger configurations to be managed + as configuration instead of code. +* Sampling is more accurate, and doesn't depend on the standard library's shared + timer heap. + +## v0.1.0-beta.1 (6 Feb 2017) + +This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and +upgrade at their leisure. Since this is the first tagged release, there are no +backward compatibility concerns and all functionality is new. + +Early zap adopters should pin to the 0.1.x minor version until they're ready to +upgrade to the upcoming stable release. + +[#316]: https://github.com/uber-go/zap/pull/316 +[#309]: https://github.com/uber-go/zap/pull/309 +[#317]: https://github.com/uber-go/zap/pull/317 +[#321]: https://github.com/uber-go/zap/pull/321 +[#325]: https://github.com/uber-go/zap/pull/325 +[#333]: https://github.com/uber-go/zap/pull/333 +[#326]: https://github.com/uber-go/zap/pull/326 +[#300]: https://github.com/uber-go/zap/pull/300 +[#339]: https://github.com/uber-go/zap/pull/339 +[#307]: https://github.com/uber-go/zap/pull/307 +[#353]: https://github.com/uber-go/zap/pull/353 +[#311]: https://github.com/uber-go/zap/pull/311 +[#366]: https://github.com/uber-go/zap/pull/366 +[#364]: https://github.com/uber-go/zap/pull/364 +[#371]: https://github.com/uber-go/zap/pull/371 +[#362]: https://github.com/uber-go/zap/pull/362 +[#369]: https://github.com/uber-go/zap/pull/369 +[#347]: https://github.com/uber-go/zap/pull/347 +[#373]: https://github.com/uber-go/zap/pull/373 +[#348]: https://github.com/uber-go/zap/pull/348 +[#327]: https://github.com/uber-go/zap/pull/327 +[#376]: https://github.com/uber-go/zap/pull/376 +[#346]: https://github.com/uber-go/zap/pull/346 +[#365]: https://github.com/uber-go/zap/pull/365 +[#372]: https://github.com/uber-go/zap/pull/372 +[#385]: https://github.com/uber-go/zap/pull/385 +[#396]: https://github.com/uber-go/zap/pull/396 +[#386]: https://github.com/uber-go/zap/pull/386 +[#402]: https://github.com/uber-go/zap/pull/402 +[#415]: https://github.com/uber-go/zap/pull/415 +[#416]: https://github.com/uber-go/zap/pull/416 +[#424]: https://github.com/uber-go/zap/pull/424 +[#425]: https://github.com/uber-go/zap/pull/425 +[#431]: https://github.com/uber-go/zap/pull/431 +[#435]: https://github.com/uber-go/zap/pull/435 +[#444]: https://github.com/uber-go/zap/pull/444 +[#477]: https://github.com/uber-go/zap/pull/477 +[#465]: https://github.com/uber-go/zap/pull/465 +[#460]: https://github.com/uber-go/zap/pull/460 +[#470]: https://github.com/uber-go/zap/pull/470 +[#487]: https://github.com/uber-go/zap/pull/487 +[#490]: https://github.com/uber-go/zap/pull/490 +[#491]: https://github.com/uber-go/zap/pull/491 +[#491]: https://github.com/uber-go/zap/pull/439 +[#504]: https://github.com/uber-go/zap/pull/504 diff --git a/vendor/src/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/src/go.uber.org/zap/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..e327d9aa --- /dev/null +++ b/vendor/src/go.uber.org/zap/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, +body size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. Representation of a +project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at oss-conduct@uber.com. The project +team will review and investigate all complaints, and will respond in a way +that it deems appropriate to the circumstances. The project team is obligated +to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +[http://contributor-covenant.org/version/1/4][version]. + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/src/go.uber.org/zap/CONTRIBUTING.md b/vendor/src/go.uber.org/zap/CONTRIBUTING.md new file mode 100644 index 00000000..9454bbaf --- /dev/null +++ b/vendor/src/go.uber.org/zap/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +We'd love your help making zap the very best structured logging library in Go! + +If you'd like to add new exported APIs, please [open an issue][open-issue] +describing your proposal — discussing API changes ahead of time makes +pull request review much smoother. In your issue, pull request, and any other +communications, please remember to treat your fellow contributors with +respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. + +Note that you'll need to sign [Uber's Contributor License Agreement][cla] +before we can accept any of your contributions. If necessary, a bot will remind +you to accept the CLA when you open your pull request. + +## Setup + +[Fork][fork], then clone the repository: + +``` +mkdir -p $GOPATH/src/go.uber.org +cd $GOPATH/src/go.uber.org +git clone git@github.com:your_github_username/zap.git +cd zap +git remote add upstream https://github.com/uber-go/zap.git +git fetch upstream +``` + +Install zap's dependencies: + +``` +make dependencies +``` + +Make sure that the tests and the linters pass: + +``` +make test +make lint +``` + +If you're not using the minor version of Go specified in the Makefile's +`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is +fine, but it means that you'll only discover lint failures after you open your +pull request. + +## Making Changes + +Start by creating a new branch for your changes: + +``` +cd $GOPATH/src/go.uber.org/zap +git checkout master +git fetch upstream +git rebase upstream/master +git checkout -b cool_new_feature +``` + +Make your changes, then ensure that `make lint` and `make test` still pass. If +you're satisfied with your changes, push them to your fork. + +``` +git push origin cool_new_feature +``` + +Then use the GitHub UI to open a pull request. + +At this point, you're waiting on us to review your changes. We *try* to respond +to issues and pull requests within a few business days, and we may suggest some +improvements or alternatives. Once your changes are approved, one of the +project maintainers will merge them. + +We're much more likely to approve your changes if you: + +* Add tests for new functionality. +* Write a [good commit message][commit-message]. +* Maintain backward compatibility. + +[fork]: https://github.com/uber-go/zap/fork +[open-issue]: https://github.com/uber-go/zap/issues/new +[cla]: https://cla-assistant.io/uber-go/zap +[commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/vendor/src/go.uber.org/zap/FAQ.md b/vendor/src/go.uber.org/zap/FAQ.md new file mode 100644 index 00000000..96e10121 --- /dev/null +++ b/vendor/src/go.uber.org/zap/FAQ.md @@ -0,0 +1,140 @@ +# Frequently Asked Questions + +## Design + +### Why spend so much effort on logger performance? + +Of course, most applications won't notice the impact of a slow logger: they +already take tens or hundreds of milliseconds for each operation, so an extra +millisecond doesn't matter. + +On the other hand, why *not* make structured logging fast? The `SugaredLogger` +isn't any harder to use than other logging packages, and the `Logger` makes +structured logging possible in performance-sensitive contexts. Across a fleet +of Go microservices, making each application even slightly more efficient adds +up quickly. + +### Why aren't `Logger` and `SugaredLogger` interfaces? + +Unlike the familiar `io.Writer` and `http.Handler`, `Logger` and +`SugaredLogger` interfaces would include *many* methods. As [Rob Pike points +out][go-proverbs], "The bigger the interface, the weaker the abstraction." +Interfaces are also rigid — *any* change requires releasing a new major +version, since it breaks all third-party implementations. + +Making the `Logger` and `SugaredLogger` concrete types doesn't sacrifice much +abstraction, and it lets us add methods without introducing breaking changes. +Your applications should define and depend upon an interface that includes +just the methods you use. + +### Why sample application logs? + +Applications often experience runs of errors, either because of a bug or +because of a misbehaving user. Logging errors is usually a good idea, but it +can easily make this bad situation worse: not only is your application coping +with a flood of errors, it's also spending extra CPU cycles and I/O logging +those errors. Since writes are typically serialized, logging limits throughput +when you need it most. + +Sampling fixes this problem by dropping repetitive log entries. Under normal +conditions, your application writes out every entry. When similar entries are +logged hundreds or thousands of times each second, though, zap begins dropping +duplicates to preserve throughput. + +### Why do the structured logging APIs take a message in addition to fields? + +Subjectively, we find it helpful to accompany structured context with a brief +description. This isn't critical during development, but it makes debugging +and operating unfamiliar systems much easier. + +More concretely, zap's sampling algorithm uses the message to identify +duplicate entries. In our experience, this is a practical middle ground +between random sampling (which often drops the exact entry that you need while +debugging) and hashing the complete entry (which is prohibitively expensive). + +### Why include package-global loggers? + +Since so many other logging packages include a global logger, many +applications aren't designed to accept loggers as explicit parameters. +Changing function signatures is often a breaking change, so zap includes +global loggers to simplify migration. + +Avoid them where possible. + +### Why include dedicated Panic and Fatal log levels? + +In general, application code should handle errors gracefully instead of using +`panic` or `os.Exit`. However, every rule has exceptions, and it's common to +crash when an error is truly unrecoverable. To avoid losing any information +— especially the reason for the crash — the logger must flush any +buffered entries before the process exits. + +Zap makes this easy by offering `Panic` and `Fatal` logging methods that +automatically flush before exiting. Of course, this doesn't guarantee that +logs will never be lost, but it eliminates a common error. + +See the discussion in uber-go/zap#207 for more details. + +### What's `DPanic`? + +`DPanic` stands for "panic in development." In development, it logs at +`PanicLevel`; otherwise, it logs at `ErrorLevel`. `DPanic` makes it easier to +catch errors that are theoretically possible, but shouldn't actually happen, +*without* crashing in production. + +If you've ever written code like this, you need `DPanic`: + +```go +if err != nil { + panic(fmt.Sprintf("shouldn't ever get here: %v", err)) +} +``` + +## Installation + +### What does the error `expects import "go.uber.org/zap"` mean? + +Either zap was installed incorrectly or you're referencing the wrong package +name in your code. + +Zap's source code happens to be hosted on GitHub, but the [import +path][import-path] is `go.uber.org/zap`. This gives us, the project +maintainers, the freedom to move the source code if necessary. However, it +means that you need to take a little care when installing and using the +package. + +If you follow two simple rules, everything should work: install zap with `go +get -u go.uber.org/zap`, and always import it in your code with `import +"go.uber.org/zap"`. Your code shouldn't contain *any* references to +`github.com/uber-go/zap`. + +## Usage + +### Does zap support log rotation? + +Zap doesn't natively support rotating log files, since we prefer to leave this +to an external program like `logrotate`. + +However, it's easy to integrate a log rotation package like +[`gopkg.in/natefinch/lumberjack.v2`][lumberjack] as a `zapcore.WriteSyncer`. + +```go +// lumberjack.Logger is already safe for concurrent use, so we don't need to +// lock it. +w := zapcore.AddSync(&lumberjack.Logger{ + Filename: "/var/log/myapp/foo.log", + MaxSize: 500, // megabytes + MaxBackups: 3, + MaxAge: 28, // days +}) +core := zapcore.NewCore( + zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), + w, + zap.InfoLevel, +) +logger := zap.New(core) +``` + +[go-proverbs]: https://go-proverbs.github.io/ +[import-path]: https://golang.org/cmd/go/#hdr-Remote_import_paths +[lumberjack]: https://godoc.org/gopkg.in/natefinch/lumberjack.v2 diff --git a/vendor/src/go.uber.org/zap/LICENSE.txt b/vendor/src/go.uber.org/zap/LICENSE.txt new file mode 100644 index 00000000..6652bed4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/src/go.uber.org/zap/Makefile b/vendor/src/go.uber.org/zap/Makefile new file mode 100644 index 00000000..8706cd16 --- /dev/null +++ b/vendor/src/go.uber.org/zap/Makefile @@ -0,0 +1,76 @@ +export GO15VENDOREXPERIMENT=1 + +BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem +PKGS ?= $(shell glide novendor) +# Many Go tools take file globs or directories as arguments instead of packages. +PKG_FILES ?= *.go zapcore benchmarks buffer zapgrpc zaptest zaptest/observer internal/bufferpool internal/exit internal/color + +# The linting tools evolve with each Go version, so run them only on the latest +# stable release. +GO_VERSION := $(shell go version | cut -d " " -f 3) +GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION))) +LINTABLE_MINOR_VERSIONS := 8 +ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),) +SHOULD_LINT := true +endif + + +.PHONY: all +all: lint test + +.PHONY: dependencies +dependencies: + @echo "Installing Glide and locked dependencies..." + glide --version || go get -u -f github.com/Masterminds/glide + glide install + @echo "Installing test dependencies..." + go install ./vendor/github.com/axw/gocov/gocov + go install ./vendor/github.com/mattn/goveralls +ifdef SHOULD_LINT + @echo "Installing golint..." + go install ./vendor/github.com/golang/lint/golint +else + @echo "Not installing golint, since we don't expect to lint on" $(GO_VERSION) +endif + +# Disable printf-like invocation checking due to testify.assert.Error() +VET_RULES := -printf=false + +.PHONY: lint +lint: +ifdef SHOULD_LINT + @rm -rf lint.log + @echo "Checking formatting..." + @gofmt -d -s $(PKG_FILES) 2>&1 | tee lint.log + @echo "Installing test dependencies for vet..." + @go test -i $(PKGS) + @echo "Checking vet..." + @$(foreach dir,$(PKG_FILES),go tool vet $(VET_RULES) $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking lint..." + @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;) + @echo "Checking for unresolved FIXMEs..." + @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log + @echo "Checking for license headers..." + @./check_license.sh | tee -a lint.log + @[ ! -s lint.log ] +else + @echo "Skipping linters on" $(GO_VERSION) +endif + +.PHONY: test +test: + go test -race $(PKGS) + +.PHONY: cover +cover: + ./scripts/cover.sh $(PKGS) + +.PHONY: bench +BENCH ?= . +bench: + @$(foreach pkg,$(PKGS),go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) $(pkg);) + +.PHONY: updatereadme +updatereadme: + rm -f README.md + cat .readme.tmpl | go run internal/readme/readme.go > README.md diff --git a/vendor/src/go.uber.org/zap/README.md b/vendor/src/go.uber.org/zap/README.md new file mode 100644 index 00000000..4b2bb9d8 --- /dev/null +++ b/vendor/src/go.uber.org/zap/README.md @@ -0,0 +1,136 @@ +# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +Blazing fast, structured, leveled logging in Go. + +## Installation + +`go get -u go.uber.org/zap` + +Note that zap only supports the two most recent minor versions of Go. + +## Quick Start + +In contexts where performance is nice, but not critical, use the +`SugaredLogger`. It's 4-10x faster than than other structured logging +packages and includes both structured and `printf`-style APIs. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() // flushes buffer, if any +sugar := logger.Sugar() +sugar.Infow("failed to fetch URL", + // Structured context as loosely typed key-value pairs. + "url", url, + "attempt", 3, + "backoff", time.Second, +) +sugar.Infof("Failed to fetch URL: %s", url) +``` + +When performance and type safety are critical, use the `Logger`. It's even +faster than the `SugaredLogger` and allocates far less, but it only supports +structured logging. + +```go +logger, _ := zap.NewProduction() +defer logger.Sync() +logger.Info("failed to fetch URL", + // Structured context as strongly typed Field values. + zap.String("url", url), + zap.Int("attempt", 3), + zap.Duration("backoff", time.Second), +) +``` + +See the [documentation][doc] and [FAQ](FAQ.md) for more details. + +## Performance + +For applications that log in the hot path, reflection-based serialization and +string formatting are prohibitively expensive — they're CPU-intensive +and make many small allocations. Put differently, using `encoding/json` and +`fmt.Fprintf` to log tons of `interface{}`s makes your application slow. + +Zap takes a different approach. It includes a reflection-free, zero-allocation +JSON encoder, and the base `Logger` strives to avoid serialization overhead +and allocations wherever possible. By building the high-level `SugaredLogger` +on that foundation, zap lets users *choose* when they need to count every +allocation and when they'd prefer a more familiar, loosely typed API. + +As measured by its own [benchmarking suite][], not only is zap more performant +than comparable structured logging packages — it's also faster than the +standard library. Like all benchmarks, take these with a grain of salt.[1](#footnote-versions) + +Log a message and 10 fields: + +| Package | Time | Objects Allocated | +| :--- | :---: | :---: | +| :zap: zap | 3131 ns/op | 5 allocs/op | +| :zap: zap (sugared) | 4173 ns/op | 21 allocs/op | +| zerolog | 16154 ns/op | 90 allocs/op | +| lion | 16341 ns/op | 111 allocs/op | +| go-kit | 17049 ns/op | 126 allocs/op | +| logrus | 23662 ns/op | 142 allocs/op | +| log15 | 36351 ns/op | 149 allocs/op | +| apex/log | 42530 ns/op | 126 allocs/op | + +Log a message with a logger that already has 10 fields of context: + +| Package | Time | Objects Allocated | +| :--- | :---: | :---: | +| :zap: zap | 380 ns/op | 0 allocs/op | +| :zap: zap (sugared) | 564 ns/op | 2 allocs/op | +| zerolog | 321 ns/op | 0 allocs/op | +| lion | 7092 ns/op | 39 allocs/op | +| go-kit | 20226 ns/op | 115 allocs/op | +| logrus | 22312 ns/op | 130 allocs/op | +| log15 | 28788 ns/op | 79 allocs/op | +| apex/log | 42063 ns/op | 115 allocs/op | + +Log a static string, without any context or `printf`-style templating: + +| Package | Time | Objects Allocated | +| :--- | :---: | :---: | +| :zap: zap | 361 ns/op | 0 allocs/op | +| :zap: zap (sugared) | 534 ns/op | 2 allocs/op | +| zerolog | 323 ns/op | 0 allocs/op | +| standard library | 575 ns/op | 2 allocs/op | +| go-kit | 922 ns/op | 13 allocs/op | +| lion | 1413 ns/op | 10 allocs/op | +| logrus | 2291 ns/op | 27 allocs/op | +| apex/log | 3690 ns/op | 11 allocs/op | +| log15 | 5954 ns/op | 26 allocs/op | + +## Development Status: Stable + +All APIs are finalized, and no breaking changes will be made in the 1.x series +of releases. Users of semver-aware dependency management systems should pin +zap to `^1`. + +## Contributing + +We encourage and support an active, healthy community of contributors — +including you! Details are in the [contribution guide](CONTRIBUTING.md) and +the [code of conduct](CODE_OF_CONDUCT.md). The zap maintainers keep an eye on +issues and pull requests, but you can also report any negative conduct to +oss-conduct@uber.com. That email list is a private, safe space; even the zap +maintainers don't have access, so don't hesitate to hold us to a high +standard. + +
+ +Released under the [MIT License](LICENSE.txt). + +1 In particular, keep in mind that we may be +benchmarking against slightly older versions of other packages. Versions are +pinned in zap's [glide.lock][] file. [↩](#anchor-versions) + +[doc-img]: https://godoc.org/go.uber.org/zap?status.svg +[doc]: https://godoc.org/go.uber.org/zap +[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master +[ci]: https://travis-ci.org/uber-go/zap +[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg +[cov]: https://codecov.io/gh/uber-go/zap +[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks +[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock diff --git a/vendor/src/go.uber.org/zap/array.go b/vendor/src/go.uber.org/zap/array.go new file mode 100644 index 00000000..3d4d49f4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/array.go @@ -0,0 +1,320 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "time" + + "go.uber.org/zap/zapcore" +) + +// Array constructs a field with the given key and ArrayMarshaler. It provides +// a flexible, but still type-safe and efficient, way to add array-like types +// to the logging context. The struct's MarshalLogArray method is called lazily. +func Array(key string, val zapcore.ArrayMarshaler) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.ArrayMarshalerType, Interface: val} +} + +// Bools constructs a field that carries a slice of bools. +func Bools(key string, bs []bool) zapcore.Field { + return Array(key, bools(bs)) +} + +// ByteStrings constructs a field that carries a slice of []byte, each of which +// must be UTF-8 encoded text. +func ByteStrings(key string, bss [][]byte) zapcore.Field { + return Array(key, byteStringsArray(bss)) +} + +// Complex128s constructs a field that carries a slice of complex numbers. +func Complex128s(key string, nums []complex128) zapcore.Field { + return Array(key, complex128s(nums)) +} + +// Complex64s constructs a field that carries a slice of complex numbers. +func Complex64s(key string, nums []complex64) zapcore.Field { + return Array(key, complex64s(nums)) +} + +// Durations constructs a field that carries a slice of time.Durations. +func Durations(key string, ds []time.Duration) zapcore.Field { + return Array(key, durations(ds)) +} + +// Float64s constructs a field that carries a slice of floats. +func Float64s(key string, nums []float64) zapcore.Field { + return Array(key, float64s(nums)) +} + +// Float32s constructs a field that carries a slice of floats. +func Float32s(key string, nums []float32) zapcore.Field { + return Array(key, float32s(nums)) +} + +// Ints constructs a field that carries a slice of integers. +func Ints(key string, nums []int) zapcore.Field { + return Array(key, ints(nums)) +} + +// Int64s constructs a field that carries a slice of integers. +func Int64s(key string, nums []int64) zapcore.Field { + return Array(key, int64s(nums)) +} + +// Int32s constructs a field that carries a slice of integers. +func Int32s(key string, nums []int32) zapcore.Field { + return Array(key, int32s(nums)) +} + +// Int16s constructs a field that carries a slice of integers. +func Int16s(key string, nums []int16) zapcore.Field { + return Array(key, int16s(nums)) +} + +// Int8s constructs a field that carries a slice of integers. +func Int8s(key string, nums []int8) zapcore.Field { + return Array(key, int8s(nums)) +} + +// Strings constructs a field that carries a slice of strings. +func Strings(key string, ss []string) zapcore.Field { + return Array(key, stringArray(ss)) +} + +// Times constructs a field that carries a slice of time.Times. +func Times(key string, ts []time.Time) zapcore.Field { + return Array(key, times(ts)) +} + +// Uints constructs a field that carries a slice of unsigned integers. +func Uints(key string, nums []uint) zapcore.Field { + return Array(key, uints(nums)) +} + +// Uint64s constructs a field that carries a slice of unsigned integers. +func Uint64s(key string, nums []uint64) zapcore.Field { + return Array(key, uint64s(nums)) +} + +// Uint32s constructs a field that carries a slice of unsigned integers. +func Uint32s(key string, nums []uint32) zapcore.Field { + return Array(key, uint32s(nums)) +} + +// Uint16s constructs a field that carries a slice of unsigned integers. +func Uint16s(key string, nums []uint16) zapcore.Field { + return Array(key, uint16s(nums)) +} + +// Uint8s constructs a field that carries a slice of unsigned integers. +func Uint8s(key string, nums []uint8) zapcore.Field { + return Array(key, uint8s(nums)) +} + +// Uintptrs constructs a field that carries a slice of pointer addresses. +func Uintptrs(key string, us []uintptr) zapcore.Field { + return Array(key, uintptrs(us)) +} + +// Errors constructs a field that carries a slice of errors. +func Errors(key string, errs []error) zapcore.Field { + return Array(key, errArray(errs)) +} + +type bools []bool + +func (bs bools) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range bs { + arr.AppendBool(bs[i]) + } + return nil +} + +type byteStringsArray [][]byte + +func (bss byteStringsArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range bss { + arr.AppendByteString(bss[i]) + } + return nil +} + +type complex128s []complex128 + +func (nums complex128s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendComplex128(nums[i]) + } + return nil +} + +type complex64s []complex64 + +func (nums complex64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendComplex64(nums[i]) + } + return nil +} + +type durations []time.Duration + +func (ds durations) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ds { + arr.AppendDuration(ds[i]) + } + return nil +} + +type float64s []float64 + +func (nums float64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendFloat64(nums[i]) + } + return nil +} + +type float32s []float32 + +func (nums float32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendFloat32(nums[i]) + } + return nil +} + +type ints []int + +func (nums ints) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt(nums[i]) + } + return nil +} + +type int64s []int64 + +func (nums int64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt64(nums[i]) + } + return nil +} + +type int32s []int32 + +func (nums int32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt32(nums[i]) + } + return nil +} + +type int16s []int16 + +func (nums int16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt16(nums[i]) + } + return nil +} + +type int8s []int8 + +func (nums int8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendInt8(nums[i]) + } + return nil +} + +type stringArray []string + +func (ss stringArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ss { + arr.AppendString(ss[i]) + } + return nil +} + +type times []time.Time + +func (ts times) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range ts { + arr.AppendTime(ts[i]) + } + return nil +} + +type uints []uint + +func (nums uints) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint(nums[i]) + } + return nil +} + +type uint64s []uint64 + +func (nums uint64s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint64(nums[i]) + } + return nil +} + +type uint32s []uint32 + +func (nums uint32s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint32(nums[i]) + } + return nil +} + +type uint16s []uint16 + +func (nums uint16s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint16(nums[i]) + } + return nil +} + +type uint8s []uint8 + +func (nums uint8s) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUint8(nums[i]) + } + return nil +} + +type uintptrs []uintptr + +func (nums uintptrs) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range nums { + arr.AppendUintptr(nums[i]) + } + return nil +} diff --git a/vendor/src/go.uber.org/zap/array_test.go b/vendor/src/go.uber.org/zap/array_test.go new file mode 100644 index 00000000..df84d921 --- /dev/null +++ b/vendor/src/go.uber.org/zap/array_test.go @@ -0,0 +1,107 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + "time" + + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" +) + +func BenchmarkBoolsArrayMarshaler(b *testing.B) { + // Keep this benchmark here to capture the overhead of the ArrayMarshaler + // wrapper. + bs := make([]bool, 50) + enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{}) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Bools("array", bs).AddTo(enc.Clone()) + } +} + +func BenchmarkBoolsReflect(b *testing.B) { + bs := make([]bool, 50) + enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{}) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Reflect("array", bs).AddTo(enc.Clone()) + } +} + +func TestArrayWrappers(t *testing.T) { + tests := []struct { + desc string + field zapcore.Field + expected []interface{} + }{ + {"empty bools", Bools("", []bool{}), []interface{}(nil)}, + {"empty byte strings", ByteStrings("", [][]byte{}), []interface{}(nil)}, + {"empty complex128s", Complex128s("", []complex128{}), []interface{}(nil)}, + {"empty complex64s", Complex64s("", []complex64{}), []interface{}(nil)}, + {"empty durations", Durations("", []time.Duration{}), []interface{}(nil)}, + {"empty float64s", Float64s("", []float64{}), []interface{}(nil)}, + {"empty float32s", Float32s("", []float32{}), []interface{}(nil)}, + {"empty ints", Ints("", []int{}), []interface{}(nil)}, + {"empty int64s", Int64s("", []int64{}), []interface{}(nil)}, + {"empty int32s", Int32s("", []int32{}), []interface{}(nil)}, + {"empty int16s", Int16s("", []int16{}), []interface{}(nil)}, + {"empty int8s", Int8s("", []int8{}), []interface{}(nil)}, + {"empty strings", Strings("", []string{}), []interface{}(nil)}, + {"empty times", Times("", []time.Time{}), []interface{}(nil)}, + {"empty uints", Uints("", []uint{}), []interface{}(nil)}, + {"empty uint64s", Uint64s("", []uint64{}), []interface{}(nil)}, + {"empty uint32s", Uint32s("", []uint32{}), []interface{}(nil)}, + {"empty uint16s", Uint16s("", []uint16{}), []interface{}(nil)}, + {"empty uint8s", Uint8s("", []uint8{}), []interface{}(nil)}, + {"empty uintptrs", Uintptrs("", []uintptr{}), []interface{}(nil)}, + {"bools", Bools("", []bool{true, false}), []interface{}{true, false}}, + {"byte strings", ByteStrings("", [][]byte{{1, 2}, {3, 4}}), []interface{}{[]byte{1, 2}, []byte{3, 4}}}, + {"complex128s", Complex128s("", []complex128{1 + 2i, 3 + 4i}), []interface{}{1 + 2i, 3 + 4i}}, + {"complex64s", Complex64s("", []complex64{1 + 2i, 3 + 4i}), []interface{}{complex64(1 + 2i), complex64(3 + 4i)}}, + {"durations", Durations("", []time.Duration{1, 2}), []interface{}{time.Nanosecond, 2 * time.Nanosecond}}, + {"float64s", Float64s("", []float64{1.2, 3.4}), []interface{}{1.2, 3.4}}, + {"float32s", Float32s("", []float32{1.2, 3.4}), []interface{}{float32(1.2), float32(3.4)}}, + {"ints", Ints("", []int{1, 2}), []interface{}{1, 2}}, + {"int64s", Int64s("", []int64{1, 2}), []interface{}{int64(1), int64(2)}}, + {"int32s", Int32s("", []int32{1, 2}), []interface{}{int32(1), int32(2)}}, + {"int16s", Int16s("", []int16{1, 2}), []interface{}{int16(1), int16(2)}}, + {"int8s", Int8s("", []int8{1, 2}), []interface{}{int8(1), int8(2)}}, + {"strings", Strings("", []string{"foo", "bar"}), []interface{}{"foo", "bar"}}, + {"times", Times("", []time.Time{time.Unix(0, 0), time.Unix(0, 0)}), []interface{}{time.Unix(0, 0), time.Unix(0, 0)}}, + {"uints", Uints("", []uint{1, 2}), []interface{}{uint(1), uint(2)}}, + {"uint64s", Uint64s("", []uint64{1, 2}), []interface{}{uint64(1), uint64(2)}}, + {"uint32s", Uint32s("", []uint32{1, 2}), []interface{}{uint32(1), uint32(2)}}, + {"uint16s", Uint16s("", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}}, + {"uint8s", Uint8s("", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}}, + {"uintptrs", Uintptrs("", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}}, + } + + for _, tt := range tests { + enc := zapcore.NewMapObjectEncoder() + tt.field.Key = "k" + tt.field.AddTo(enc) + assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc) + assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields) + } +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/apex_test.go b/vendor/src/go.uber.org/zap/benchmarks/apex_test.go new file mode 100644 index 00000000..67d76463 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/apex_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "github.com/apex/log" + "github.com/apex/log/handlers/json" +) + +func newDisabledApexLog() *log.Logger { + return &log.Logger{ + Handler: json.New(ioutil.Discard), + Level: log.ErrorLevel, + } +} + +func newApexLog() *log.Logger { + return &log.Logger{ + Handler: json.New(ioutil.Discard), + Level: log.DebugLevel, + } +} + +func fakeApexFields() log.Fields { + return log.Fields{ + "int": _tenInts[0], + "ints": _tenInts, + "string": _tenStrings[0], + "strings": _tenStrings, + "time": _tenTimes[0], + "times": _tenTimes, + "user1": _oneUser, + "user2": _oneUser, + "users": _tenUsers, + "error": errExample, + } +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/doc.go b/vendor/src/go.uber.org/zap/benchmarks/doc.go new file mode 100644 index 00000000..b79f79f0 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/doc.go @@ -0,0 +1,23 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package benchmarks contains only benchmarks comparing zap to other +// structured logging libraries. +package benchmarks diff --git a/vendor/src/go.uber.org/zap/benchmarks/kit_test.go b/vendor/src/go.uber.org/zap/benchmarks/kit_test.go new file mode 100644 index 00000000..b9da2933 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/kit_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "github.com/go-kit/kit/log" +) + +func newKitLog(fields ...interface{}) log.Logger { + return log.With(log.NewJSONLogger(ioutil.Discard), fields...) +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/lion_test.go b/vendor/src/go.uber.org/zap/benchmarks/lion_test.go new file mode 100644 index 00000000..6c41cb11 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/lion_test.go @@ -0,0 +1,31 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "go.pedge.io/lion" +) + +func newLion() lion.Logger { + return lion.NewLogger(lion.NewJSONWritePusher(ioutil.Discard)) +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/log15_test.go b/vendor/src/go.uber.org/zap/benchmarks/log15_test.go new file mode 100644 index 00000000..70a60b35 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/log15_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "gopkg.in/inconshreveable/log15.v2" +) + +func newLog15() log15.Logger { + logger := log15.New() + logger.SetHandler(log15.StreamHandler(ioutil.Discard, log15.JsonFormat())) + return logger +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/logrus_test.go b/vendor/src/go.uber.org/zap/benchmarks/logrus_test.go new file mode 100644 index 00000000..ee684a6f --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/logrus_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "github.com/sirupsen/logrus" +) + +func newDisabledLogrus() *logrus.Logger { + logger := newLogrus() + logger.Level = logrus.ErrorLevel + return logger +} + +func newLogrus() *logrus.Logger { + return &logrus.Logger{ + Out: ioutil.Discard, + Formatter: new(logrus.JSONFormatter), + Hooks: make(logrus.LevelHooks), + Level: logrus.DebugLevel, + } +} + +func fakeLogrusFields() logrus.Fields { + return logrus.Fields{ + "int": _tenInts[0], + "ints": _tenInts, + "string": _tenStrings[0], + "strings": _tenStrings, + "time": _tenTimes[0], + "times": _tenTimes, + "user1": _oneUser, + "user2": _oneUser, + "users": _tenUsers, + "error": errExample, + } +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/scenario_bench_test.go b/vendor/src/go.uber.org/zap/benchmarks/scenario_bench_test.go new file mode 100644 index 00000000..4e307d21 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/scenario_bench_test.go @@ -0,0 +1,614 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + "log" + "testing" + + "go.uber.org/zap" +) + +func BenchmarkDisabledWithoutFields(b *testing.B) { + b.Logf("Logging at a disabled level without any structured context.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil { + m.Write() + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.SugarFormatting", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infof("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newDisabledApexLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newDisabledLogrus() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := newDisabledZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msg(getMessage(0)) + } + }) + }) +} + +func BenchmarkDisabledAccumulatedContext(b *testing.B) { + b.Logf("Logging at a disabled level with some accumulated context.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil { + m.Write() + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.SugarFormatting", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).With(fakeFields()...).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infof("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newDisabledApexLog().WithFields(fakeApexFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newDisabledLogrus().WithFields(fakeLogrusFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := fakeZerologContext(newDisabledZerolog().With()).Logger() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msg(getMessage(0)) + } + }) + }) +} + +func BenchmarkDisabledAddingFields(b *testing.B) { + b.Logf("Logging at a disabled level, adding context at each log site.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0), fakeFields()...) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if m := logger.Check(zap.InfoLevel, getMessage(0)); m != nil { + m.Write(fakeFields()...) + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.ErrorLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infow(getMessage(0), fakeSugarFields()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newDisabledApexLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.WithFields(fakeApexFields()).Info(getMessage(0)) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newDisabledLogrus() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.WithFields(fakeLogrusFields()).Info(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := newDisabledZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + fakeZerologFields(logger.Info()).Msg(getMessage(0)) + } + }) + }) +} + +func BenchmarkWithoutFields(b *testing.B) { + b.Logf("Logging without any structured context.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil { + ce.Write() + } + } + }) + }) + b.Run("Zap.CheckSampled", func(b *testing.B) { + logger := newSampledLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + i++ + if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil { + ce.Write() + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.SugarFormatting", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infof("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newApexLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("go-kit/kit/log", func(b *testing.B) { + logger := newKitLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Log(getMessage(0), getMessage(1)) + } + }) + }) + b.Run("inconshreveable/log15", func(b *testing.B) { + logger := newLog15() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newLogrus() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("go.pedge.io/lion", func(b *testing.B) { + logger := newLion() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Printf(getMessage(0)) + } + }) + }) + b.Run("stdlib.Println", func(b *testing.B) { + logger := log.New(ioutil.Discard, "", log.LstdFlags) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Println(getMessage(0)) + } + }) + }) + b.Run("stdlib.Printf", func(b *testing.B) { + logger := log.New(ioutil.Discard, "", log.LstdFlags) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Printf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := newZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msg(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog.Formatting", func(b *testing.B) { + logger := newZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msgf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("rs/zerolog.Check", func(b *testing.B) { + logger := newZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if e := logger.Info(); e.Enabled() { + e.Msg(getMessage(0)) + } + } + }) + }) +} + +func BenchmarkAccumulatedContext(b *testing.B) { + b.Logf("Logging with some accumulated context.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).With(fakeFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).With(fakeFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil { + ce.Write() + } + } + }) + }) + b.Run("Zap.CheckSampled", func(b *testing.B) { + logger := newSampledLogger(zap.DebugLevel).With(fakeFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + i++ + if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil { + ce.Write() + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("Zap.SugarFormatting", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).With(fakeFields()...).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infof("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newApexLog().WithFields(fakeApexFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("go-kit/kit/log", func(b *testing.B) { + logger := newKitLog(fakeSugarFields()...) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Log(getMessage(0), getMessage(1)) + } + }) + }) + b.Run("inconshreveable/log15", func(b *testing.B) { + logger := newLog15().New(fakeSugarFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newLogrus().WithFields(fakeLogrusFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0)) + } + }) + }) + b.Run("go.pedge.io/lion", func(b *testing.B) { + logger := newLion().WithFields(fakeLogrusFields()) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infof(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := fakeZerologContext(newZerolog().With()).Logger() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msg(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog.Check", func(b *testing.B) { + logger := fakeZerologContext(newZerolog().With()).Logger() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if e := logger.Info(); e.Enabled() { + e.Msg(getMessage(0)) + } + } + }) + }) + b.Run("rs/zerolog.Formatting", func(b *testing.B) { + logger := fakeZerologContext(newZerolog().With()).Logger() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info().Msgf("%v %v %v %s %v %v %v %v %v %s\n", fakeFmtArgs()...) + } + }) + }) +} + +func BenchmarkAddingFields(b *testing.B) { + b.Logf("Logging with additional context at each log site.") + b.Run("Zap", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0), fakeFields()...) + } + }) + }) + b.Run("Zap.Check", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if ce := logger.Check(zap.InfoLevel, getMessage(0)); ce != nil { + ce.Write(fakeFields()...) + } + } + }) + }) + b.Run("Zap.CheckSampled", func(b *testing.B) { + logger := newSampledLogger(zap.DebugLevel) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + i++ + if ce := logger.Check(zap.InfoLevel, getMessage(i)); ce != nil { + ce.Write(fakeFields()...) + } + } + }) + }) + b.Run("Zap.Sugar", func(b *testing.B) { + logger := newZapLogger(zap.DebugLevel).Sugar() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Infow(getMessage(0), fakeSugarFields()...) + } + }) + }) + b.Run("apex/log", func(b *testing.B) { + logger := newApexLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.WithFields(fakeApexFields()).Info(getMessage(0)) + } + }) + }) + b.Run("go-kit/kit/log", func(b *testing.B) { + logger := newKitLog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Log(fakeSugarFields()...) + } + }) + }) + b.Run("inconshreveable/log15", func(b *testing.B) { + logger := newLog15() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info(getMessage(0), fakeSugarFields()...) + } + }) + }) + b.Run("sirupsen/logrus", func(b *testing.B) { + logger := newLogrus() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.WithFields(fakeLogrusFields()).Info(getMessage(0)) + } + }) + }) + b.Run("go.pedge.io/lion", func(b *testing.B) { + logger := newLion() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.WithFields(fakeLogrusFields()).Infof(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog", func(b *testing.B) { + logger := newZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + fakeZerologFields(logger.Info()).Msg(getMessage(0)) + } + }) + }) + b.Run("rs/zerolog.Check", func(b *testing.B) { + logger := newZerolog() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if e := logger.Info(); e.Enabled() { + fakeZerologFields(e).Msg(getMessage(0)) + } + } + }) + }) +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/zap_test.go b/vendor/src/go.uber.org/zap/benchmarks/zap_test.go new file mode 100644 index 00000000..294260b1 --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/zap_test.go @@ -0,0 +1,172 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "errors" + "fmt" + "time" + + "go.uber.org/multierr" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +var ( + errExample = errors.New("fail") + + _messages = fakeMessages(1000) + _tenInts = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} + _tenStrings = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"} + _tenTimes = []time.Time{ + time.Unix(0, 0), + time.Unix(1, 0), + time.Unix(2, 0), + time.Unix(3, 0), + time.Unix(4, 0), + time.Unix(5, 0), + time.Unix(6, 0), + time.Unix(7, 0), + time.Unix(8, 0), + time.Unix(9, 0), + } + _oneUser = &user{ + Name: "Jane Doe", + Email: "jane@test.com", + CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC), + } + _tenUsers = users{ + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + _oneUser, + } +) + +func fakeMessages(n int) []string { + messages := make([]string, n) + for i := range messages { + messages[i] = fmt.Sprintf("Test logging, but use a somewhat realistic message length. (#%v)", i) + } + return messages +} + +func getMessage(iter int) string { + return _messages[iter%1000] +} + +type users []*user + +func (uu users) MarshalLogArray(arr zapcore.ArrayEncoder) error { + var err error + for i := range uu { + err = multierr.Append(err, arr.AppendObject(uu[i])) + } + return err +} + +type user struct { + Name string `json:"name"` + Email string `json:"email"` + CreatedAt time.Time `json:"created_at"` +} + +func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("name", u.Name) + enc.AddString("email", u.Email) + enc.AddInt64("createdAt", u.CreatedAt.UnixNano()) + return nil +} + +func newZapLogger(lvl zapcore.Level) *zap.Logger { + ec := zap.NewProductionEncoderConfig() + ec.EncodeDuration = zapcore.NanosDurationEncoder + ec.EncodeTime = zapcore.EpochNanosTimeEncoder + enc := zapcore.NewJSONEncoder(ec) + return zap.New(zapcore.NewCore( + enc, + &zaptest.Discarder{}, + lvl, + )) +} + +func newSampledLogger(lvl zapcore.Level) *zap.Logger { + return zap.New(zapcore.NewSampler( + newZapLogger(zap.DebugLevel).Core(), + 100*time.Millisecond, + 10, // first + 10, // thereafter + )) +} + +func fakeFields() []zapcore.Field { + return []zapcore.Field{ + zap.Int("int", _tenInts[0]), + zap.Ints("ints", _tenInts), + zap.String("string", _tenStrings[0]), + zap.Strings("strings", _tenStrings), + zap.Time("time", _tenTimes[0]), + zap.Times("times", _tenTimes), + zap.Object("user1", _oneUser), + zap.Object("user2", _oneUser), + zap.Array("users", _tenUsers), + zap.Error(errExample), + } +} + +func fakeSugarFields() []interface{} { + return []interface{}{ + "int", _tenInts[0], + "ints", _tenInts, + "string", _tenStrings[0], + "strings", _tenStrings, + "time", _tenTimes[0], + "times", _tenTimes, + "user1", _oneUser, + "user2", _oneUser, + "users", _tenUsers, + "error", errExample, + } +} + +func fakeFmtArgs() []interface{} { + // Need to keep this a function instead of a package-global var so that we + // pay the cast-to-interface{} penalty on each call. + return []interface{}{ + _tenInts[0], + _tenInts, + _tenStrings[0], + _tenStrings, + _tenTimes[0], + _tenTimes, + _oneUser, + _oneUser, + _tenUsers, + errExample, + } +} diff --git a/vendor/src/go.uber.org/zap/benchmarks/zerolog_test.go b/vendor/src/go.uber.org/zap/benchmarks/zerolog_test.go new file mode 100644 index 00000000..b14cd9df --- /dev/null +++ b/vendor/src/go.uber.org/zap/benchmarks/zerolog_test.go @@ -0,0 +1,63 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package benchmarks + +import ( + "io/ioutil" + + "github.com/rs/zerolog" +) + +func newZerolog() zerolog.Logger { + return zerolog.New(ioutil.Discard).With().Timestamp().Logger() +} + +func newDisabledZerolog() zerolog.Logger { + return newZerolog().Level(zerolog.Disabled) +} + +func fakeZerologFields(e *zerolog.Event) *zerolog.Event { + return e. + Int("int", _tenInts[0]). + Interface("ints", _tenInts). + Str("string", _tenStrings[0]). + Interface("strings", _tenStrings). + Time("time", _tenTimes[0]). + Interface("times", _tenTimes). + Interface("user1", _oneUser). + Interface("user2", _oneUser). + Interface("users", _tenUsers). + Err(errExample) +} + +func fakeZerologContext(c zerolog.Context) zerolog.Context { + return c. + Int("int", _tenInts[0]). + Interface("ints", _tenInts). + Str("string", _tenStrings[0]). + Interface("strings", _tenStrings). + Time("time", _tenTimes[0]). + Interface("times", _tenTimes). + Interface("user1", _oneUser). + Interface("user2", _oneUser). + Interface("users", _tenUsers). + Err(errExample) +} diff --git a/vendor/src/go.uber.org/zap/buffer/buffer.go b/vendor/src/go.uber.org/zap/buffer/buffer.go new file mode 100644 index 00000000..ea6fdc8d --- /dev/null +++ b/vendor/src/go.uber.org/zap/buffer/buffer.go @@ -0,0 +1,106 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package buffer provides a thin wrapper around a byte slice. Unlike the +// standard library's bytes.Buffer, it supports a portion of the strconv +// package's zero-allocation formatters. +package buffer + +import "strconv" + +const _size = 1024 // by default, create 1 KiB buffers + +// Buffer is a thin wrapper around a byte slice. It's intended to be pooled, so +// the only way to construct one is via a Pool. +type Buffer struct { + bs []byte + pool Pool +} + +// AppendByte writes a single byte to the Buffer. +func (b *Buffer) AppendByte(v byte) { + b.bs = append(b.bs, v) +} + +// AppendString writes a string to the Buffer. +func (b *Buffer) AppendString(s string) { + b.bs = append(b.bs, s...) +} + +// AppendInt appends an integer to the underlying buffer (assuming base 10). +func (b *Buffer) AppendInt(i int64) { + b.bs = strconv.AppendInt(b.bs, i, 10) +} + +// AppendUint appends an unsigned integer to the underlying buffer (assuming +// base 10). +func (b *Buffer) AppendUint(i uint64) { + b.bs = strconv.AppendUint(b.bs, i, 10) +} + +// AppendBool appends a bool to the underlying buffer. +func (b *Buffer) AppendBool(v bool) { + b.bs = strconv.AppendBool(b.bs, v) +} + +// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN +// or +/- Inf. +func (b *Buffer) AppendFloat(f float64, bitSize int) { + b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize) +} + +// Len returns the length of the underlying byte slice. +func (b *Buffer) Len() int { + return len(b.bs) +} + +// Cap returns the capacity of the underlying byte slice. +func (b *Buffer) Cap() int { + return cap(b.bs) +} + +// Bytes returns a mutable reference to the underlying byte slice. +func (b *Buffer) Bytes() []byte { + return b.bs +} + +// String returns a string copy of the underlying byte slice. +func (b *Buffer) String() string { + return string(b.bs) +} + +// Reset resets the underlying byte slice. Subsequent writes re-use the slice's +// backing array. +func (b *Buffer) Reset() { + b.bs = b.bs[:0] +} + +// Write implements io.Writer. +func (b *Buffer) Write(bs []byte) (int, error) { + b.bs = append(b.bs, bs...) + return len(bs), nil +} + +// Free returns the Buffer to its Pool. +// +// Callers must not retain references to the Buffer after calling Free. +func (b *Buffer) Free() { + b.pool.put(b) +} diff --git a/vendor/src/go.uber.org/zap/buffer/buffer_test.go b/vendor/src/go.uber.org/zap/buffer/buffer_test.go new file mode 100644 index 00000000..59bc08a6 --- /dev/null +++ b/vendor/src/go.uber.org/zap/buffer/buffer_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package buffer + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBufferWrites(t *testing.T) { + buf := NewPool().Get() + + tests := []struct { + desc string + f func() + want string + }{ + {"AppendByte", func() { buf.AppendByte('v') }, "v"}, + {"AppendString", func() { buf.AppendString("foo") }, "foo"}, + {"AppendIntPositive", func() { buf.AppendInt(42) }, "42"}, + {"AppendIntNegative", func() { buf.AppendInt(-42) }, "-42"}, + {"AppendUint", func() { buf.AppendUint(42) }, "42"}, + {"AppendBool", func() { buf.AppendBool(true) }, "true"}, + {"AppendFloat64", func() { buf.AppendFloat(3.14, 64) }, "3.14"}, + // Intenationally introduce some floating-point error. + {"AppendFloat32", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, "3.14"}, + {"AppendWrite", func() { buf.Write([]byte("foo")) }, "foo"}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + buf.Reset() + tt.f() + assert.Equal(t, tt.want, buf.String(), "Unexpected buffer.String().") + assert.Equal(t, tt.want, string(buf.Bytes()), "Unexpected string(buffer.Bytes()).") + assert.Equal(t, len(tt.want), buf.Len(), "Unexpected buffer length.") + // We're not writing more than a kibibyte in tests. + assert.Equal(t, _size, buf.Cap(), "Expected buffer capacity to remain constant.") + }) + } +} + +func BenchmarkBuffers(b *testing.B) { + // Because we use the strconv.AppendFoo functions so liberally, we can't + // use the standard library's bytes.Buffer anyways (without incurring a + // bunch of extra allocations). Nevertheless, let's make sure that we're + // not losing any precious nanoseconds. + str := strings.Repeat("a", 1024) + slice := make([]byte, 1024) + buf := bytes.NewBuffer(slice) + custom := NewPool().Get() + b.Run("ByteSlice", func(b *testing.B) { + for i := 0; i < b.N; i++ { + slice = append(slice, str...) + slice = slice[:0] + } + }) + b.Run("BytesBuffer", func(b *testing.B) { + for i := 0; i < b.N; i++ { + buf.WriteString(str) + buf.Reset() + } + }) + b.Run("CustomBuffer", func(b *testing.B) { + for i := 0; i < b.N; i++ { + custom.AppendString(str) + custom.Reset() + } + }) +} diff --git a/vendor/src/go.uber.org/zap/buffer/pool.go b/vendor/src/go.uber.org/zap/buffer/pool.go new file mode 100644 index 00000000..8fb3e202 --- /dev/null +++ b/vendor/src/go.uber.org/zap/buffer/pool.go @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package buffer + +import "sync" + +// A Pool is a type-safe wrapper around a sync.Pool. +type Pool struct { + p *sync.Pool +} + +// NewPool constructs a new Pool. +func NewPool() Pool { + return Pool{p: &sync.Pool{ + New: func() interface{} { + return &Buffer{bs: make([]byte, 0, _size)} + }, + }} +} + +// Get retrieves a Buffer from the pool, creating one if necessary. +func (p Pool) Get() *Buffer { + buf := p.p.Get().(*Buffer) + buf.Reset() + buf.pool = p + return buf +} + +func (p Pool) put(buf *Buffer) { + p.p.Put(buf) +} diff --git a/vendor/src/go.uber.org/zap/buffer/pool_test.go b/vendor/src/go.uber.org/zap/buffer/pool_test.go new file mode 100644 index 00000000..a219815b --- /dev/null +++ b/vendor/src/go.uber.org/zap/buffer/pool_test.go @@ -0,0 +1,52 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package buffer + +import ( + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuffers(t *testing.T) { + const dummyData = "dummy data" + p := NewPool() + + var wg sync.WaitGroup + for g := 0; g < 10; g++ { + wg.Add(1) + go func() { + for i := 0; i < 100; i++ { + buf := p.Get() + assert.Zero(t, buf.Len(), "Expected truncated buffer") + assert.NotZero(t, buf.Cap(), "Expected non-zero capacity") + + buf.AppendString(dummyData) + assert.Equal(t, buf.Len(), len(dummyData), "Expected buffer to contain dummy data") + + buf.Free() + } + wg.Done() + }() + } + wg.Wait() +} diff --git a/vendor/src/go.uber.org/zap/check_license.sh b/vendor/src/go.uber.org/zap/check_license.sh new file mode 100644 index 00000000..345ac8b8 --- /dev/null +++ b/vendor/src/go.uber.org/zap/check_license.sh @@ -0,0 +1,17 @@ +#!/bin/bash -e + +ERROR_COUNT=0 +while read -r file +do + case "$(head -1 "${file}")" in + *"Copyright (c) "*" Uber Technologies, Inc.") + # everything's cool + ;; + *) + echo "$file is missing license header." + (( ERROR_COUNT++ )) + ;; + esac +done < <(git ls-files "*\.go") + +exit $ERROR_COUNT diff --git a/vendor/src/go.uber.org/zap/common_test.go b/vendor/src/go.uber.org/zap/common_test.go new file mode 100644 index 00000000..b0a4a2e5 --- /dev/null +++ b/vendor/src/go.uber.org/zap/common_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "sync" + "testing" + + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" +) + +func opts(opts ...Option) []Option { + return opts +} + +// Here specifically to introduce an easily-identifiable filename for testing +// stacktraces and caller skips. +func withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*Logger, *observer.ObservedLogs)) { + fac, logs := observer.New(e) + log := New(fac, opts...) + f(log, logs) +} + +func withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *observer.ObservedLogs)) { + withLogger(t, e, opts, func(logger *Logger, logs *observer.ObservedLogs) { f(logger.Sugar(), logs) }) +} + +func runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) { + wg.Add(goroutines) + for g := 0; g < goroutines; g++ { + go func() { + defer wg.Done() + for i := 0; i < iterations; i++ { + f() + } + }() + } +} diff --git a/vendor/src/go.uber.org/zap/config.go b/vendor/src/go.uber.org/zap/config.go new file mode 100644 index 00000000..b0658eda --- /dev/null +++ b/vendor/src/go.uber.org/zap/config.go @@ -0,0 +1,243 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "sort" + "time" + + "go.uber.org/zap/zapcore" +) + +// SamplingConfig sets a sampling strategy for the logger. Sampling caps the +// global CPU and I/O load that logging puts on your process while attempting +// to preserve a representative subset of your logs. +// +// Values configured here are per-second. See zapcore.NewSampler for details. +type SamplingConfig struct { + Initial int `json:"initial" yaml:"initial"` + Thereafter int `json:"thereafter" yaml:"thereafter"` +} + +// Config offers a declarative way to construct a logger. It doesn't do +// anything that can't be done with New, Options, and the various +// zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to +// toggle common options. +// +// Note that Config intentionally supports only the most common options. More +// unusual logging setups (logging to network connections or message queues, +// splitting output between multiple files, etc.) are possible, but require +// direct use of the zapcore package. For sample code, see the package-level +// BasicConfiguration and AdvancedConfiguration examples. +// +// For an example showing runtime log level changes, see the documentation for +// AtomicLevel. +type Config struct { + // Level is the minimum enabled logging level. Note that this is a dynamic + // level, so calling Config.Level.SetLevel will atomically change the log + // level of all loggers descended from this config. + Level AtomicLevel `json:"level" yaml:"level"` + // Development puts the logger in development mode, which changes the + // behavior of DPanicLevel and takes stacktraces more liberally. + Development bool `json:"development" yaml:"development"` + // DisableCaller stops annotating logs with the calling function's file + // name and line number. By default, all logs are annotated. + DisableCaller bool `json:"disableCaller" yaml:"disableCaller"` + // DisableStacktrace completely disables automatic stacktrace capturing. By + // default, stacktraces are captured for WarnLevel and above logs in + // development and ErrorLevel and above in production. + DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"` + // Sampling sets a sampling policy. A nil SamplingConfig disables sampling. + Sampling *SamplingConfig `json:"sampling" yaml:"sampling"` + // Encoding sets the logger's encoding. Valid values are "json" and + // "console", as well as any third-party encodings registered via + // RegisterEncoder. + Encoding string `json:"encoding" yaml:"encoding"` + // EncoderConfig sets options for the chosen encoder. See + // zapcore.EncoderConfig for details. + EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"` + // OutputPaths is a list of paths to write logging output to. See Open for + // details. + OutputPaths []string `json:"outputPaths" yaml:"outputPaths"` + // ErrorOutputPaths is a list of paths to write internal logger errors to. + // The default is standard error. + // + // Note that this setting only affects internal errors; for sample code that + // sends error-level logs to a different location from info- and debug-level + // logs, see the package-level AdvancedConfiguration example. + ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"` + // InitialFields is a collection of fields to add to the root logger. + InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"` +} + +// NewProductionEncoderConfig returns an opinionated EncoderConfig for +// production environments. +func NewProductionEncoderConfig() zapcore.EncoderConfig { + return zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.EpochTimeEncoder, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } +} + +// NewProductionConfig is a reasonable production logging configuration. +// Logging is enabled at InfoLevel and above. +// +// It uses a JSON encoder, writes to standard error, and enables sampling. +// Stacktraces are automatically included on logs of ErrorLevel and above. +func NewProductionConfig() Config { + return Config{ + Level: NewAtomicLevelAt(InfoLevel), + Development: false, + Sampling: &SamplingConfig{ + Initial: 100, + Thereafter: 100, + }, + Encoding: "json", + EncoderConfig: NewProductionEncoderConfig(), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for +// development environments. +func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { + return zapcore.EncoderConfig{ + // Keys can be anything except the empty string. + TimeKey: "T", + LevelKey: "L", + NameKey: "N", + CallerKey: "C", + MessageKey: "M", + StacktraceKey: "S", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } +} + +// NewDevelopmentConfig is a reasonable development logging configuration. +// Logging is enabled at DebugLevel and above. +// +// It enables development mode (which makes DPanicLevel logs panic), uses a +// console encoder, writes to standard error, and disables sampling. +// Stacktraces are automatically included on logs of WarnLevel and above. +func NewDevelopmentConfig() Config { + return Config{ + Level: NewAtomicLevelAt(DebugLevel), + Development: true, + Encoding: "console", + EncoderConfig: NewDevelopmentEncoderConfig(), + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + } +} + +// Build constructs a logger from the Config and Options. +func (cfg Config) Build(opts ...Option) (*Logger, error) { + enc, err := cfg.buildEncoder() + if err != nil { + return nil, err + } + + sink, errSink, err := cfg.openSinks() + if err != nil { + return nil, err + } + + log := New( + zapcore.NewCore(enc, sink, cfg.Level), + cfg.buildOptions(errSink)..., + ) + if len(opts) > 0 { + log = log.WithOptions(opts...) + } + return log, nil +} + +func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option { + opts := []Option{ErrorOutput(errSink)} + + if cfg.Development { + opts = append(opts, Development()) + } + + if !cfg.DisableCaller { + opts = append(opts, AddCaller()) + } + + stackLevel := ErrorLevel + if cfg.Development { + stackLevel = WarnLevel + } + if !cfg.DisableStacktrace { + opts = append(opts, AddStacktrace(stackLevel)) + } + + if cfg.Sampling != nil { + opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewSampler(core, time.Second, int(cfg.Sampling.Initial), int(cfg.Sampling.Thereafter)) + })) + } + + if len(cfg.InitialFields) > 0 { + fs := make([]zapcore.Field, 0, len(cfg.InitialFields)) + keys := make([]string, 0, len(cfg.InitialFields)) + for k := range cfg.InitialFields { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + fs = append(fs, Any(k, cfg.InitialFields[k])) + } + opts = append(opts, Fields(fs...)) + } + + return opts +} + +func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) { + sink, closeOut, err := Open(cfg.OutputPaths...) + if err != nil { + return nil, nil, err + } + errSink, _, err := Open(cfg.ErrorOutputPaths...) + if err != nil { + closeOut() + return nil, nil, err + } + return sink, errSink, nil +} + +func (cfg Config) buildEncoder() (zapcore.Encoder, error) { + return newEncoder(cfg.Encoding, cfg.EncoderConfig) +} diff --git a/vendor/src/go.uber.org/zap/config_test.go b/vendor/src/go.uber.org/zap/config_test.go new file mode 100644 index 00000000..7a875703 --- /dev/null +++ b/vendor/src/go.uber.org/zap/config_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfig(t *testing.T) { + tests := []struct { + desc string + cfg Config + expectN int64 + expectRe string + }{ + { + desc: "production", + cfg: NewProductionConfig(), + expectN: 2 + 100 + 1, // 2 from initial logs, 100 initial sampled logs, 1 from off-by-one in sampler + expectRe: `{"level":"info","caller":"zap/config_test.go:\d+","msg":"info","k":"v","z":"zz"}` + "\n" + + `{"level":"warn","caller":"zap/config_test.go:\d+","msg":"warn","k":"v","z":"zz"}` + "\n", + }, + { + desc: "development", + cfg: NewDevelopmentConfig(), + expectN: 3 + 200, // 3 initial logs, all 200 subsequent logs + expectRe: "DEBUG\tzap/config_test.go:" + `\d+` + "\tdebug\t" + `{"k": "v", "z": "zz"}` + "\n" + + "INFO\tzap/config_test.go:" + `\d+` + "\tinfo\t" + `{"k": "v", "z": "zz"}` + "\n" + + "WARN\tzap/config_test.go:" + `\d+` + "\twarn\t" + `{"k": "v", "z": "zz"}` + "\n" + + `testing.\w+`, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + temp, err := ioutil.TempFile("", "zap-prod-config-test") + require.NoError(t, err, "Failed to create temp file.") + defer os.Remove(temp.Name()) + + tt.cfg.OutputPaths = []string{temp.Name()} + tt.cfg.EncoderConfig.TimeKey = "" // no timestamps in tests + tt.cfg.InitialFields = map[string]interface{}{"z": "zz", "k": "v"} + + hook, count := makeCountingHook() + logger, err := tt.cfg.Build(Hooks(hook)) + require.NoError(t, err, "Unexpected error constructing logger.") + + logger.Debug("debug") + logger.Info("info") + logger.Warn("warn") + + byteContents, err := ioutil.ReadAll(temp) + require.NoError(t, err, "Couldn't read log contents from temp file.") + logs := string(byteContents) + assert.Regexp(t, tt.expectRe, logs, "Unexpected log output.") + + for i := 0; i < 200; i++ { + logger.Info("sampling") + } + assert.Equal(t, tt.expectN, count.Load(), "Hook called an unexpected number of times.") + }) + } +} + +func TestConfigWithInvalidPaths(t *testing.T) { + tests := []struct { + desc string + output string + errOutput string + }{ + {"output directory doesn't exist", "/tmp/not-there/foo.log", "stderr"}, + {"error output directory doesn't exist", "stdout", "/tmp/not-there/foo-errors.log"}, + {"neither output directory exists", "/tmp/not-there/foo.log", "/tmp/not-there/foo-errors.log"}, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + cfg := NewProductionConfig() + cfg.OutputPaths = []string{tt.output} + cfg.ErrorOutputPaths = []string{tt.errOutput} + _, err := cfg.Build() + assert.Error(t, err, "Expected an error opening a non-existent directory.") + }) + } +} diff --git a/vendor/src/go.uber.org/zap/doc.go b/vendor/src/go.uber.org/zap/doc.go new file mode 100644 index 00000000..3f16a8d4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/doc.go @@ -0,0 +1,113 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zap provides fast, structured, leveled logging. +// +// For applications that log in the hot path, reflection-based serialization +// and string formatting are prohibitively expensive - they're CPU-intensive +// and make many small allocations. Put differently, using json.Marshal and +// fmt.Fprintf to log tons of interface{} makes your application slow. +// +// Zap takes a different approach. It includes a reflection-free, +// zero-allocation JSON encoder, and the base Logger strives to avoid +// serialization overhead and allocations wherever possible. By building the +// high-level SugaredLogger on that foundation, zap lets users choose when +// they need to count every allocation and when they'd prefer a more familiar, +// loosely typed API. +// +// Choosing a Logger +// +// In contexts where performance is nice, but not critical, use the +// SugaredLogger. It's 4-10x faster than other structured logging packages and +// supports both structured and printf-style logging. Like log15 and go-kit, +// the SugaredLogger's structured logging APIs are loosely typed and accept a +// variadic number of key-value pairs. (For more advanced use cases, they also +// accept strongly typed fields - see the SugaredLogger.With documentation for +// details.) +// sugar := zap.NewExample().Sugar() +// defer sugar.Sync() +// sugar.Infow("failed to fetch URL", +// "url", "http://example.com", +// "attempt", 3, +// "backoff", time.Second, +// ) +// sugar.Printf("failed to fetch URL: %s", "http://example.com") +// +// By default, loggers are unbuffered. However, since zap's low-level APIs +// allow buffering, calling Sync before letting your process exit is a good +// habit. +// +// In the rare contexts where every microsecond and every allocation matter, +// use the Logger. It's even faster than the SugaredLogger and allocates far +// less, but it only supports strongly-typed, structured logging. +// logger := zap.NewExample() +// defer logger.Sync() +// logger.Info("failed to fetch URL", +// zap.String("url", "http://example.com"), +// zap.Int("attempt", 3), +// zap.Duration("backoff", time.Second), +// ) +// +// Choosing between the Logger and SugaredLogger doesn't need to be an +// application-wide decision: converting between the two is simple and +// inexpensive. +// logger := zap.NewExample() +// defer logger.Sync() +// sugar := logger.Sugar() +// plain := sugar.Desugar() +// +// Configuring Zap +// +// The simplest way to build a Logger is to use zap's opinionated presets: +// NewExample, NewProduction, and NewDevelopment. These presets build a logger +// with a single function call: +// logger, err := zap.NewProduction() +// if err != nil { +// log.Fatalf("can't initialize zap logger: %v", err) +// } +// defer logger.Sync() +// +// Presets are fine for small projects, but larger projects and organizations +// naturally require a bit more customization. For most users, zap's Config +// struct strikes the right balance between flexibility and convenience. See +// the package-level BasicConfiguration example for sample code. +// +// More unusual configurations (splitting output between files, sending logs +// to a message queue, etc.) are possible, but require direct use of +// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration +// example for sample code. +// +// Extending Zap +// +// The zap package itself is a relatively thin wrapper around the interfaces +// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g., +// BSON), a new log sink (e.g., Kafka), or something more exotic (perhaps an +// exception aggregation service, like Sentry or Rollbar) typically requires +// implementing the zapcore.Encoder, zapcore.WriteSyncer, or zapcore.Core +// interfaces. See the zapcore documentation for details. +// +// Similarly, package authors can use the high-performance Encoder and Core +// implementations in the zapcore package to build their own loggers. +// +// Frequently Asked Questions +// +// An FAQ covering everything from installation errors to design decisions is +// available at https://github.com/uber-go/zap/blob/master/FAQ.md. +package zap // import "go.uber.org/zap" diff --git a/vendor/src/go.uber.org/zap/encoder.go b/vendor/src/go.uber.org/zap/encoder.go new file mode 100644 index 00000000..2e9d3c34 --- /dev/null +++ b/vendor/src/go.uber.org/zap/encoder.go @@ -0,0 +1,75 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "fmt" + "sync" + + "go.uber.org/zap/zapcore" +) + +var ( + errNoEncoderNameSpecified = errors.New("no encoder name specified") + + _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){ + "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + return zapcore.NewConsoleEncoder(encoderConfig), nil + }, + "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + return zapcore.NewJSONEncoder(encoderConfig), nil + }, + } + _encoderMutex sync.RWMutex +) + +// RegisterEncoder registers an encoder constructor, which the Config struct +// can then reference. By default, the "json" and "console" encoders are +// registered. +// +// Attempting to register an encoder whose name is already taken returns an +// error. +func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapcore.Encoder, error)) error { + _encoderMutex.Lock() + defer _encoderMutex.Unlock() + if name == "" { + return errNoEncoderNameSpecified + } + if _, ok := _encoderNameToConstructor[name]; ok { + return fmt.Errorf("encoder already registered for name %q", name) + } + _encoderNameToConstructor[name] = constructor + return nil +} + +func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) { + _encoderMutex.RLock() + defer _encoderMutex.RUnlock() + if name == "" { + return nil, errNoEncoderNameSpecified + } + constructor, ok := _encoderNameToConstructor[name] + if !ok { + return nil, fmt.Errorf("no encoder registered for name %q", name) + } + return constructor(encoderConfig) +} diff --git a/vendor/src/go.uber.org/zap/encoder_test.go b/vendor/src/go.uber.org/zap/encoder_test.go new file mode 100644 index 00000000..f6be665b --- /dev/null +++ b/vendor/src/go.uber.org/zap/encoder_test.go @@ -0,0 +1,88 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" +) + +func TestRegisterDefaultEncoders(t *testing.T) { + testEncodersRegistered(t, "console", "json") +} + +func TestRegisterEncoder(t *testing.T) { + testEncoders(func() { + assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo") + testEncodersRegistered(t, "foo") + }) +} + +func TestDuplicateRegisterEncoder(t *testing.T) { + testEncoders(func() { + RegisterEncoder("foo", newNilEncoder) + assert.Error(t, RegisterEncoder("foo", newNilEncoder), "expected an error when registering an encoder with the same name twice") + }) +} + +func TestRegisterEncoderNoName(t *testing.T) { + assert.Equal(t, errNoEncoderNameSpecified, RegisterEncoder("", newNilEncoder), "expected an error when registering an encoder with no name") +} + +func TestNewEncoder(t *testing.T) { + testEncoders(func() { + RegisterEncoder("foo", newNilEncoder) + encoder, err := newEncoder("foo", zapcore.EncoderConfig{}) + assert.NoError(t, err, "could not create an encoder for the registered name foo") + assert.Nil(t, encoder, "the encoder from newNilEncoder is not nil") + }) +} + +func TestNewEncoderNotRegistered(t *testing.T) { + _, err := newEncoder("foo", zapcore.EncoderConfig{}) + assert.Error(t, err, "expected an error when trying to create an encoder of an unregistered name") +} + +func TestNewEncoderNoName(t *testing.T) { + _, err := newEncoder("", zapcore.EncoderConfig{}) + assert.Equal(t, errNoEncoderNameSpecified, err, "expected an error when creating an encoder with no name") +} + +func testEncoders(f func()) { + existing := _encoderNameToConstructor + _encoderNameToConstructor = make(map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error)) + defer func() { _encoderNameToConstructor = existing }() + f() +} + +func testEncodersRegistered(t *testing.T, names ...string) { + assert.Len(t, _encoderNameToConstructor, len(names), "the expected number of registered encoders does not match the actual number") + for _, name := range names { + assert.NotNil(t, _encoderNameToConstructor[name], "no encoder is registered for name %s", name) + } +} + +func newNilEncoder(_ zapcore.EncoderConfig) (zapcore.Encoder, error) { + return nil, nil +} diff --git a/vendor/src/go.uber.org/zap/error.go b/vendor/src/go.uber.org/zap/error.go new file mode 100644 index 00000000..2bff30d8 --- /dev/null +++ b/vendor/src/go.uber.org/zap/error.go @@ -0,0 +1,80 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "sync" + + "go.uber.org/zap/zapcore" +) + +var _errArrayElemPool = sync.Pool{New: func() interface{} { + return &errArrayElem{} +}} + +// Error is shorthand for the common idiom NamedError("error", err). +func Error(err error) zapcore.Field { + return NamedError("error", err) +} + +// NamedError constructs a field that lazily stores err.Error() under the +// provided key. Errors which also implement fmt.Formatter (like those produced +// by github.com/pkg/errors) will also have their verbose representation stored +// under key+"Verbose". If passed a nil error, the field is a no-op. +// +// For the common case in which the key is simply "error", the Error function +// is shorter and less repetitive. +func NamedError(key string, err error) zapcore.Field { + if err == nil { + return Skip() + } + return zapcore.Field{Key: key, Type: zapcore.ErrorType, Interface: err} +} + +type errArray []error + +func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range errs { + if errs[i] == nil { + continue + } + // To represent each error as an object with an "error" attribute and + // potentially an "errorVerbose" attribute, we need to wrap it in a + // type that implements LogObjectMarshaler. To prevent this from + // allocating, pool the wrapper type. + elem := _errArrayElemPool.Get().(*errArrayElem) + elem.error = errs[i] + arr.AppendObject(elem) + elem.error = nil + _errArrayElemPool.Put(elem) + } + return nil +} + +type errArrayElem struct { + error +} + +func (e *errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error { + // Re-use the error field's logic, which supports non-standard error types. + Error(e.error).AddTo(enc) + return nil +} diff --git a/vendor/src/go.uber.org/zap/error_test.go b/vendor/src/go.uber.org/zap/error_test.go new file mode 100644 index 00000000..54ce4bbd --- /dev/null +++ b/vendor/src/go.uber.org/zap/error_test.go @@ -0,0 +1,99 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "testing" + + "go.uber.org/zap/zapcore" + + richErrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestErrorConstructors(t *testing.T) { + fail := errors.New("fail") + + tests := []struct { + name string + field zapcore.Field + expect zapcore.Field + }{ + {"Error", Skip(), Error(nil)}, + {"Error", zapcore.Field{Key: "error", Type: zapcore.ErrorType, Interface: fail}, Error(fail)}, + {"NamedError", Skip(), NamedError("foo", nil)}, + {"NamedError", zapcore.Field{Key: "foo", Type: zapcore.ErrorType, Interface: fail}, NamedError("foo", fail)}, + {"Any:Error", Any("k", errors.New("v")), NamedError("k", errors.New("v"))}, + {"Any:Errors", Any("k", []error{errors.New("v")}), Errors("k", []error{errors.New("v")})}, + } + + for _, tt := range tests { + if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) { + t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface) + } + assertCanBeReused(t, tt.field) + } +} + +func TestErrorArrayConstructor(t *testing.T) { + tests := []struct { + desc string + field zapcore.Field + expected []interface{} + }{ + {"empty errors", Errors("", []error{}), []interface{}(nil)}, + { + "errors", + Errors("", []error{nil, errors.New("foo"), nil, errors.New("bar")}), + []interface{}{map[string]interface{}{"error": "foo"}, map[string]interface{}{"error": "bar"}}, + }, + } + + for _, tt := range tests { + enc := zapcore.NewMapObjectEncoder() + tt.field.Key = "k" + tt.field.AddTo(enc) + assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc) + assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields) + } +} + +func TestErrorsArraysHandleRichErrors(t *testing.T) { + errs := []error{richErrors.New("egad")} + + enc := zapcore.NewMapObjectEncoder() + Errors("k", errs).AddTo(enc) + assert.Equal(t, 1, len(enc.Fields), "Expected only top-level field.") + + val := enc.Fields["k"] + arr, ok := val.([]interface{}) + require.True(t, ok, "Expected top-level field to be an array.") + require.Equal(t, 1, len(arr), "Expected only one error object in array.") + + serialized := arr[0] + errMap, ok := serialized.(map[string]interface{}) + require.True(t, ok, "Expected serialized error to be a map, got %T.", serialized) + assert.Equal(t, "egad", errMap["error"], "Unexpected standard error string.") + assert.Contains(t, errMap["errorVerbose"], "egad", "Verbose error string should be a superset of standard error.") + assert.Contains(t, errMap["errorVerbose"], "TestErrorsArraysHandleRichErrors", "Verbose error string should contain a stacktrace.") +} diff --git a/vendor/src/go.uber.org/zap/example_test.go b/vendor/src/go.uber.org/zap/example_test.go new file mode 100644 index 00000000..b61f153d --- /dev/null +++ b/vendor/src/go.uber.org/zap/example_test.go @@ -0,0 +1,327 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap_test + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func Example_presets() { + // Using zap's preset constructors is the simplest way to get a feel for the + // package, but they don't allow much customization. + logger := zap.NewExample() // or NewProduction, or NewDevelopment + defer logger.Sync() + + const url = "http://example.com" + + // In most circumstances, use the SugaredLogger. It's 4-10x faster than most + // other structured logging packages and has a familiar, loosely-typed API. + sugar := logger.Sugar() + sugar.Infow("Failed to fetch URL.", + // Structured context as loosely typed key-value pairs. + "url", url, + "attempt", 3, + "backoff", time.Second, + ) + sugar.Infof("Failed to fetch URL: %s", url) + + // In the unusual situations where every microsecond matters, use the + // Logger. It's even faster than the SugaredLogger, but only supports + // structured logging. + logger.Info("Failed to fetch URL.", + // Structured context as strongly typed fields. + zap.String("url", url), + zap.Int("attempt", 3), + zap.Duration("backoff", time.Second), + ) + // Output: + // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} + // {"level":"info","msg":"Failed to fetch URL: http://example.com"} + // {"level":"info","msg":"Failed to fetch URL.","url":"http://example.com","attempt":3,"backoff":"1s"} +} + +func Example_basicConfiguration() { + // For some users, the presets offered by the NewProduction, NewDevelopment, + // and NewExample constructors won't be appropriate. For most of those + // users, the bundled Config struct offers the right balance of flexibility + // and convenience. (For more complex needs, see the AdvancedConfiguration + // example.) + // + // See the documentation for Config and zapcore.EncoderConfig for all the + // available options. + rawJSON := []byte(`{ + "level": "debug", + "encoding": "json", + "outputPaths": ["stdout", "/tmp/logs"], + "errorOutputPaths": ["stderr"], + "initialFields": {"foo": "bar"}, + "encoderConfig": { + "messageKey": "message", + "levelKey": "level", + "levelEncoder": "lowercase" + } + }`) + + var cfg zap.Config + if err := json.Unmarshal(rawJSON, &cfg); err != nil { + panic(err) + } + logger, err := cfg.Build() + if err != nil { + panic(err) + } + defer logger.Sync() + + logger.Info("logger construction succeeded") + // Output: + // {"level":"info","message":"logger construction succeeded","foo":"bar"} +} + +func Example_advancedConfiguration() { + // The bundled Config struct only supports the most common configuration + // options. More complex needs, like splitting logs between multiple files + // or writing to non-file outputs, require use of the zapcore package. + // + // In this example, imagine we're both sending our logs to Kafka and writing + // them to the console. We'd like to encode the console output and the Kafka + // topics differently, and we'd also like special treatment for + // high-priority logs. + + // First, define our level-handling logic. + highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= zapcore.ErrorLevel + }) + lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl < zapcore.ErrorLevel + }) + + // Assume that we have clients for two Kafka topics. The clients implement + // zapcore.WriteSyncer and are safe for concurrent use. (If they only + // implement io.Writer, we can use zapcore.AddSync to add a no-op Sync + // method. If they're not safe for concurrent use, we can add a protecting + // mutex with zapcore.Lock.) + topicDebugging := zapcore.AddSync(ioutil.Discard) + topicErrors := zapcore.AddSync(ioutil.Discard) + + // High-priority output should also go to standard error, and low-priority + // output should also go to standard out. + consoleDebugging := zapcore.Lock(os.Stdout) + consoleErrors := zapcore.Lock(os.Stderr) + + // Optimize the Kafka output for machine consumption and the console output + // for human operators. + kafkaEncoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) + consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) + + // Join the outputs, encoders, and level-handling functions into + // zapcore.Cores, then tee the four cores together. + core := zapcore.NewTee( + zapcore.NewCore(kafkaEncoder, topicErrors, highPriority), + zapcore.NewCore(consoleEncoder, consoleErrors, highPriority), + zapcore.NewCore(kafkaEncoder, topicDebugging, lowPriority), + zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority), + ) + + // From a zapcore.Core, it's easy to construct a Logger. + logger := zap.New(core) + defer logger.Sync() + logger.Info("constructed a logger") +} + +func ExampleNamespace() { + logger := zap.NewExample() + defer logger.Sync() + + logger.With( + zap.Namespace("metrics"), + zap.Int("counter", 1), + ).Info("tracked some metrics") + // Output: + // {"level":"info","msg":"tracked some metrics","metrics":{"counter":1}} +} + +func ExampleNewStdLog() { + logger := zap.NewExample() + defer logger.Sync() + + std := zap.NewStdLog(logger) + std.Print("standard logger wrapper") + // Output: + // {"level":"info","msg":"standard logger wrapper"} +} + +func ExampleRedirectStdLog() { + logger := zap.NewExample() + defer logger.Sync() + + undo := zap.RedirectStdLog(logger) + defer undo() + + log.Print("redirected standard library") + // Output: + // {"level":"info","msg":"redirected standard library"} +} + +func ExampleReplaceGlobals() { + logger := zap.NewExample() + defer logger.Sync() + + undo := zap.ReplaceGlobals(logger) + defer undo() + + zap.L().Info("replaced zap's global loggers") + // Output: + // {"level":"info","msg":"replaced zap's global loggers"} +} + +func ExampleAtomicLevel() { + atom := zap.NewAtomicLevel() + + // To keep the example deterministic, disable timestamps in the output. + encoderCfg := zap.NewProductionEncoderConfig() + encoderCfg.TimeKey = "" + + logger := zap.New(zapcore.NewCore( + zapcore.NewJSONEncoder(encoderCfg), + zapcore.Lock(os.Stdout), + atom, + )) + defer logger.Sync() + + logger.Info("info logging enabled") + + atom.SetLevel(zap.ErrorLevel) + logger.Info("info logging disabled") + // Output: + // {"level":"info","msg":"info logging enabled"} +} + +func ExampleAtomicLevel_config() { + // The zap.Config struct includes an AtomicLevel. To use it, keep a + // reference to the Config. + rawJSON := []byte(`{ + "level": "info", + "outputPaths": ["stdout"], + "errorOutputPaths": ["stderr"], + "encoding": "json", + "encoderConfig": { + "messageKey": "message", + "levelKey": "level", + "levelEncoder": "lowercase" + } + }`) + var cfg zap.Config + if err := json.Unmarshal(rawJSON, &cfg); err != nil { + panic(err) + } + logger, err := cfg.Build() + if err != nil { + panic(err) + } + defer logger.Sync() + + logger.Info("info logging enabled") + + cfg.Level.SetLevel(zap.ErrorLevel) + logger.Info("info logging disabled") + // Output: + // {"level":"info","message":"info logging enabled"} +} + +func ExampleLogger_Check() { + logger := zap.NewExample() + defer logger.Sync() + + if ce := logger.Check(zap.DebugLevel, "debugging"); ce != nil { + // If debug-level log output isn't enabled or if zap's sampling would have + // dropped this log entry, we don't allocate the slice that holds these + // fields. + ce.Write( + zap.String("foo", "bar"), + zap.String("baz", "quux"), + ) + } + + // Output: + // {"level":"debug","msg":"debugging","foo":"bar","baz":"quux"} +} + +func ExampleLogger_Named() { + logger := zap.NewExample() + defer logger.Sync() + + // By default, Loggers are unnamed. + logger.Info("no name") + + // The first call to Named sets the Logger name. + main := logger.Named("main") + main.Info("main logger") + + // Additional calls to Named create a period-separated path. + main.Named("subpackage").Info("sub-logger") + // Output: + // {"level":"info","msg":"no name"} + // {"level":"info","logger":"main","msg":"main logger"} + // {"level":"info","logger":"main.subpackage","msg":"sub-logger"} +} + +func ExampleWrapCore_replace() { + // Replacing a Logger's core can alter fundamental behaviors. For example, + // example, it can convert a Logger to a no-op. + nop := zap.WrapCore(func(zapcore.Core) zapcore.Core { + return zapcore.NewNopCore() + }) + + logger := zap.NewExample() + defer logger.Sync() + + logger.Info("working") + logger.WithOptions(nop).Info("no-op") + logger.Info("original logger still works") + // Output: + // {"level":"info","msg":"working"} + // {"level":"info","msg":"original logger still works"} +} + +func ExampleWrapCore_wrap() { + // Wrapping a Logger's core can extend its functionality. As a trivial + // example, it can double-write all logs. + doubled := zap.WrapCore(func(c zapcore.Core) zapcore.Core { + return zapcore.NewTee(c, c) + }) + + logger := zap.NewExample() + defer logger.Sync() + + logger.Info("single") + logger.WithOptions(doubled).Info("doubled") + // Output: + // {"level":"info","msg":"single"} + // {"level":"info","msg":"doubled"} + // {"level":"info","msg":"doubled"} +} diff --git a/vendor/src/go.uber.org/zap/field.go b/vendor/src/go.uber.org/zap/field.go new file mode 100644 index 00000000..20eb487e --- /dev/null +++ b/vendor/src/go.uber.org/zap/field.go @@ -0,0 +1,306 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "math" + "time" + + "go.uber.org/zap/zapcore" +) + +// Skip constructs a no-op field, which is often useful when handling invalid +// inputs in other Field constructors. +func Skip() zapcore.Field { + return zapcore.Field{Type: zapcore.SkipType} +} + +// Binary constructs a field that carries an opaque binary blob. +// +// Binary data is serialized in an encoding-appropriate format. For example, +// zap's JSON encoder base64-encodes binary blobs. To log UTF-8 encoded text, +// use ByteString. +func Binary(key string, val []byte) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.BinaryType, Interface: val} +} + +// Bool constructs a field that carries a bool. +func Bool(key string, val bool) zapcore.Field { + var ival int64 + if val { + ival = 1 + } + return zapcore.Field{Key: key, Type: zapcore.BoolType, Integer: ival} +} + +// ByteString constructs a field that carries UTF-8 encoded text as a []byte. +// To log opaque binary blobs (which aren't necessarily valid UTF-8), use +// Binary. +func ByteString(key string, val []byte) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.ByteStringType, Interface: val} +} + +// Complex128 constructs a field that carries a complex number. Unlike most +// numeric fields, this costs an allocation (to convert the complex128 to +// interface{}). +func Complex128(key string, val complex128) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Complex128Type, Interface: val} +} + +// Complex64 constructs a field that carries a complex number. Unlike most +// numeric fields, this costs an allocation (to convert the complex64 to +// interface{}). +func Complex64(key string, val complex64) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Complex64Type, Interface: val} +} + +// Float64 constructs a field that carries a float64. The way the +// floating-point value is represented is encoder-dependent, so marshaling is +// necessarily lazy. +func Float64(key string, val float64) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))} +} + +// Float32 constructs a field that carries a float32. The way the +// floating-point value is represented is encoder-dependent, so marshaling is +// necessarily lazy. +func Float32(key string, val float32) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))} +} + +// Int constructs a field with the given key and value. +func Int(key string, val int) zapcore.Field { + return Int64(key, int64(val)) +} + +// Int64 constructs a field with the given key and value. +func Int64(key string, val int64) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Int64Type, Integer: val} +} + +// Int32 constructs a field with the given key and value. +func Int32(key string, val int32) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)} +} + +// Int16 constructs a field with the given key and value. +func Int16(key string, val int16) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)} +} + +// Int8 constructs a field with the given key and value. +func Int8(key string, val int8) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)} +} + +// String constructs a field with the given key and value. +func String(key string, val string) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.StringType, String: val} +} + +// Uint constructs a field with the given key and value. +func Uint(key string, val uint) zapcore.Field { + return Uint64(key, uint64(val)) +} + +// Uint64 constructs a field with the given key and value. +func Uint64(key string, val uint64) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)} +} + +// Uint32 constructs a field with the given key and value. +func Uint32(key string, val uint32) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)} +} + +// Uint16 constructs a field with the given key and value. +func Uint16(key string, val uint16) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)} +} + +// Uint8 constructs a field with the given key and value. +func Uint8(key string, val uint8) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)} +} + +// Uintptr constructs a field with the given key and value. +func Uintptr(key string, val uintptr) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)} +} + +// Reflect constructs a field with the given key and an arbitrary object. It uses +// an encoding-appropriate, reflection-based function to lazily serialize nearly +// any object into the logging context, but it's relatively slow and +// allocation-heavy. Outside tests, Any is always a better choice. +// +// If encoding fails (e.g., trying to serialize a map[int]string to JSON), Reflect +// includes the error message in the final log output. +func Reflect(key string, val interface{}) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.ReflectType, Interface: val} +} + +// Namespace creates a named, isolated scope within the logger's context. All +// subsequent fields will be added to the new namespace. +// +// This helps prevent key collisions when injecting loggers into sub-components +// or third-party libraries. +func Namespace(key string) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.NamespaceType} +} + +// Stringer constructs a field with the given key and the output of the value's +// String method. The Stringer's String method is called lazily. +func Stringer(key string, val fmt.Stringer) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.StringerType, Interface: val} +} + +// Time constructs a zapcore.Field with the given key and value. The encoder +// controls how the time is serialized. +func Time(key string, val time.Time) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()} +} + +// Stack constructs a field that stores a stacktrace of the current goroutine +// under provided key. Keep in mind that taking a stacktrace is eager and +// expensive (relatively speaking); this function both makes an allocation and +// takes about two microseconds. +func Stack(key string) zapcore.Field { + // Returning the stacktrace as a string costs an allocation, but saves us + // from expanding the zapcore.Field union struct to include a byte slice. Since + // taking a stacktrace is already so expensive (~10us), the extra allocation + // is okay. + return String(key, takeStacktrace()) +} + +// Duration constructs a field with the given key and value. The encoder +// controls how the duration is serialized. +func Duration(key string, val time.Duration) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)} +} + +// Object constructs a field with the given key and ObjectMarshaler. It +// provides a flexible, but still type-safe and efficient, way to add map- or +// struct-like user-defined types to the logging context. The struct's +// MarshalLogObject method is called lazily. +func Object(key string, val zapcore.ObjectMarshaler) zapcore.Field { + return zapcore.Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} +} + +// Any takes a key and an arbitrary value and chooses the best way to represent +// them as a field, falling back to a reflection-based approach only if +// necessary. +// +// Since byte/uint8 and rune/int32 are aliases, Any can't differentiate between +// them. To minimize suprise, []byte values are treated as binary blobs, byte +// values are treated as uint8, and runes are always treated as integers. +func Any(key string, value interface{}) zapcore.Field { + switch val := value.(type) { + case zapcore.ObjectMarshaler: + return Object(key, val) + case zapcore.ArrayMarshaler: + return Array(key, val) + case bool: + return Bool(key, val) + case []bool: + return Bools(key, val) + case complex128: + return Complex128(key, val) + case []complex128: + return Complex128s(key, val) + case complex64: + return Complex64(key, val) + case []complex64: + return Complex64s(key, val) + case float64: + return Float64(key, val) + case []float64: + return Float64s(key, val) + case float32: + return Float32(key, val) + case []float32: + return Float32s(key, val) + case int: + return Int(key, val) + case []int: + return Ints(key, val) + case int64: + return Int64(key, val) + case []int64: + return Int64s(key, val) + case int32: + return Int32(key, val) + case []int32: + return Int32s(key, val) + case int16: + return Int16(key, val) + case []int16: + return Int16s(key, val) + case int8: + return Int8(key, val) + case []int8: + return Int8s(key, val) + case string: + return String(key, val) + case []string: + return Strings(key, val) + case uint: + return Uint(key, val) + case []uint: + return Uints(key, val) + case uint64: + return Uint64(key, val) + case []uint64: + return Uint64s(key, val) + case uint32: + return Uint32(key, val) + case []uint32: + return Uint32s(key, val) + case uint16: + return Uint16(key, val) + case []uint16: + return Uint16s(key, val) + case uint8: + return Uint8(key, val) + case []byte: + return Binary(key, val) + case uintptr: + return Uintptr(key, val) + case []uintptr: + return Uintptrs(key, val) + case time.Time: + return Time(key, val) + case []time.Time: + return Times(key, val) + case time.Duration: + return Duration(key, val) + case []time.Duration: + return Durations(key, val) + case error: + return NamedError(key, val) + case []error: + return Errors(key, val) + case fmt.Stringer: + return Stringer(key, val) + default: + return Reflect(key, val) + } +} diff --git a/vendor/src/go.uber.org/zap/field_test.go b/vendor/src/go.uber.org/zap/field_test.go new file mode 100644 index 00000000..c13922cd --- /dev/null +++ b/vendor/src/go.uber.org/zap/field_test.go @@ -0,0 +1,159 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "net" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap/zapcore" +) + +type username string + +func (n username) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("username", string(n)) + return nil +} + +func assertCanBeReused(t testing.TB, field zapcore.Field) { + var wg sync.WaitGroup + + for i := 0; i < 100; i++ { + enc := zapcore.NewMapObjectEncoder() + + // Ensure using the field in multiple encoders in separate goroutines + // does not cause any races or panics. + wg.Add(1) + go func() { + defer wg.Done() + assert.NotPanics(t, func() { + field.AddTo(enc) + }, "Reusing a field should not cause issues") + }() + } + + wg.Wait() +} + +func TestFieldConstructors(t *testing.T) { + // Interface types. + addr := net.ParseIP("1.2.3.4") + name := username("phil") + ints := []int{5, 6} + + tests := []struct { + name string + field zapcore.Field + expect zapcore.Field + }{ + {"Skip", zapcore.Field{Type: zapcore.SkipType}, Skip()}, + {"Binary", zapcore.Field{Key: "k", Type: zapcore.BinaryType, Interface: []byte("ab12")}, Binary("k", []byte("ab12"))}, + {"Bool", zapcore.Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)}, + {"Bool", zapcore.Field{Key: "k", Type: zapcore.BoolType, Integer: 1}, Bool("k", true)}, + {"ByteString", zapcore.Field{Key: "k", Type: zapcore.ByteStringType, Interface: []byte("ab12")}, ByteString("k", []byte("ab12"))}, + {"Complex128", zapcore.Field{Key: "k", Type: zapcore.Complex128Type, Interface: 1 + 2i}, Complex128("k", 1+2i)}, + {"Complex64", zapcore.Field{Key: "k", Type: zapcore.Complex64Type, Interface: complex64(1 + 2i)}, Complex64("k", 1+2i)}, + {"Duration", zapcore.Field{Key: "k", Type: zapcore.DurationType, Integer: 1}, Duration("k", 1)}, + {"Int", zapcore.Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int("k", 1)}, + {"Int64", zapcore.Field{Key: "k", Type: zapcore.Int64Type, Integer: 1}, Int64("k", 1)}, + {"Int32", zapcore.Field{Key: "k", Type: zapcore.Int32Type, Integer: 1}, Int32("k", 1)}, + {"Int16", zapcore.Field{Key: "k", Type: zapcore.Int16Type, Integer: 1}, Int16("k", 1)}, + {"Int8", zapcore.Field{Key: "k", Type: zapcore.Int8Type, Integer: 1}, Int8("k", 1)}, + {"String", zapcore.Field{Key: "k", Type: zapcore.StringType, String: "foo"}, String("k", "foo")}, + {"Time", zapcore.Field{Key: "k", Type: zapcore.TimeType, Integer: 0, Interface: time.UTC}, Time("k", time.Unix(0, 0).In(time.UTC))}, + {"Time", zapcore.Field{Key: "k", Type: zapcore.TimeType, Integer: 1000, Interface: time.UTC}, Time("k", time.Unix(0, 1000).In(time.UTC))}, + {"Uint", zapcore.Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint("k", 1)}, + {"Uint64", zapcore.Field{Key: "k", Type: zapcore.Uint64Type, Integer: 1}, Uint64("k", 1)}, + {"Uint32", zapcore.Field{Key: "k", Type: zapcore.Uint32Type, Integer: 1}, Uint32("k", 1)}, + {"Uint16", zapcore.Field{Key: "k", Type: zapcore.Uint16Type, Integer: 1}, Uint16("k", 1)}, + {"Uint8", zapcore.Field{Key: "k", Type: zapcore.Uint8Type, Integer: 1}, Uint8("k", 1)}, + {"Uintptr", zapcore.Field{Key: "k", Type: zapcore.UintptrType, Integer: 10}, Uintptr("k", 0xa)}, + {"Reflect", zapcore.Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)}, + {"Stringer", zapcore.Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)}, + {"Object", zapcore.Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)}, + {"Any:ObjectMarshaler", Any("k", name), Object("k", name)}, + {"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))}, + {"Any:Stringer", Any("k", addr), Stringer("k", addr)}, + {"Any:Bool", Any("k", true), Bool("k", true)}, + {"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})}, + {"Any:Byte", Any("k", byte(1)), Uint8("k", 1)}, + {"Any:Bytes", Any("k", []byte{1}), Binary("k", []byte{1})}, + {"Any:Complex128", Any("k", 1+2i), Complex128("k", 1+2i)}, + {"Any:Complex128s", Any("k", []complex128{1 + 2i}), Complex128s("k", []complex128{1 + 2i})}, + {"Any:Complex64", Any("k", complex64(1+2i)), Complex64("k", 1+2i)}, + {"Any:Complex64s", Any("k", []complex64{1 + 2i}), Complex64s("k", []complex64{1 + 2i})}, + {"Any:Float64", Any("k", 3.14), Float64("k", 3.14)}, + {"Any:Float64s", Any("k", []float64{3.14}), Float64s("k", []float64{3.14})}, + {"Any:Float32", Any("k", float32(3.14)), Float32("k", 3.14)}, + {"Any:Float32s", Any("k", []float32{3.14}), Float32s("k", []float32{3.14})}, + {"Any:Int", Any("k", 1), Int("k", 1)}, + {"Any:Ints", Any("k", []int{1}), Ints("k", []int{1})}, + {"Any:Int64", Any("k", int64(1)), Int64("k", 1)}, + {"Any:Int64s", Any("k", []int64{1}), Int64s("k", []int64{1})}, + {"Any:Int32", Any("k", int32(1)), Int32("k", 1)}, + {"Any:Int32s", Any("k", []int32{1}), Int32s("k", []int32{1})}, + {"Any:Int16", Any("k", int16(1)), Int16("k", 1)}, + {"Any:Int16s", Any("k", []int16{1}), Int16s("k", []int16{1})}, + {"Any:Int8", Any("k", int8(1)), Int8("k", 1)}, + {"Any:Int8s", Any("k", []int8{1}), Int8s("k", []int8{1})}, + {"Any:Rune", Any("k", rune(1)), Int32("k", 1)}, + {"Any:Runes", Any("k", []rune{1}), Int32s("k", []int32{1})}, + {"Any:String", Any("k", "v"), String("k", "v")}, + {"Any:Strings", Any("k", []string{"v"}), Strings("k", []string{"v"})}, + {"Any:Uint", Any("k", uint(1)), Uint("k", 1)}, + {"Any:Uints", Any("k", []uint{1}), Uints("k", []uint{1})}, + {"Any:Uint64", Any("k", uint64(1)), Uint64("k", 1)}, + {"Any:Uint64s", Any("k", []uint64{1}), Uint64s("k", []uint64{1})}, + {"Any:Uint32", Any("k", uint32(1)), Uint32("k", 1)}, + {"Any:Uint32s", Any("k", []uint32{1}), Uint32s("k", []uint32{1})}, + {"Any:Uint16", Any("k", uint16(1)), Uint16("k", 1)}, + {"Any:Uint16s", Any("k", []uint16{1}), Uint16s("k", []uint16{1})}, + {"Any:Uint8", Any("k", uint8(1)), Uint8("k", 1)}, + {"Any:Uint8s", Any("k", []uint8{1}), Binary("k", []uint8{1})}, + {"Any:Uintptr", Any("k", uintptr(1)), Uintptr("k", 1)}, + {"Any:Uintptrs", Any("k", []uintptr{1}), Uintptrs("k", []uintptr{1})}, + {"Any:Time", Any("k", time.Unix(0, 0)), Time("k", time.Unix(0, 0))}, + {"Any:Times", Any("k", []time.Time{time.Unix(0, 0)}), Times("k", []time.Time{time.Unix(0, 0)})}, + {"Any:Duration", Any("k", time.Second), Duration("k", time.Second)}, + {"Any:Durations", Any("k", []time.Duration{time.Second}), Durations("k", []time.Duration{time.Second})}, + {"Any:Fallback", Any("k", struct{}{}), Reflect("k", struct{}{})}, + {"Namespace", Namespace("k"), zapcore.Field{Key: "k", Type: zapcore.NamespaceType}}, + } + + for _, tt := range tests { + if !assert.Equal(t, tt.expect, tt.field, "Unexpected output from convenience field constructor %s.", tt.name) { + t.Logf("type expected: %T\nGot: %T", tt.expect.Interface, tt.field.Interface) + } + assertCanBeReused(t, tt.field) + } +} + +func TestStackField(t *testing.T) { + f := Stack("stacktrace") + assert.Equal(t, "stacktrace", f.Key, "Unexpected field key.") + assert.Equal(t, zapcore.StringType, f.Type, "Unexpected field type.") + assert.Equal(t, takeStacktrace(), f.String, "Unexpected stack trace") + assertCanBeReused(t, f) +} diff --git a/vendor/src/go.uber.org/zap/flag.go b/vendor/src/go.uber.org/zap/flag.go new file mode 100644 index 00000000..13128750 --- /dev/null +++ b/vendor/src/go.uber.org/zap/flag.go @@ -0,0 +1,39 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "flag" + + "go.uber.org/zap/zapcore" +) + +// LevelFlag uses the standard library's flag.Var to declare a global flag +// with the specified name, default, and usage guidance. The returned value is +// a pointer to the value of the flag. +// +// If you don't want to use the flag package's global state, you can use any +// non-nil *Level as a flag.Value with your own *flag.FlagSet. +func LevelFlag(name string, defaultLevel zapcore.Level, usage string) *zapcore.Level { + lvl := defaultLevel + flag.Var(&lvl, name, usage) + return &lvl +} diff --git a/vendor/src/go.uber.org/zap/flag_test.go b/vendor/src/go.uber.org/zap/flag_test.go new file mode 100644 index 00000000..b1698944 --- /dev/null +++ b/vendor/src/go.uber.org/zap/flag_test.go @@ -0,0 +1,102 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "flag" + "io/ioutil" + "testing" + + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" +) + +type flagTestCase struct { + args []string + wantLevel zapcore.Level + wantErr bool +} + +func (tc flagTestCase) runImplicitSet(t testing.TB) { + origCommandLine := flag.CommandLine + flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) + flag.CommandLine.SetOutput(ioutil.Discard) + defer func() { flag.CommandLine = origCommandLine }() + + level := LevelFlag("level", InfoLevel, "") + tc.run(t, flag.CommandLine, level) +} + +func (tc flagTestCase) runExplicitSet(t testing.TB) { + var lvl zapcore.Level + set := flag.NewFlagSet("test", flag.ContinueOnError) + set.Var(&lvl, "level", "minimum enabled logging level") + tc.run(t, set, &lvl) +} + +func (tc flagTestCase) run(t testing.TB, set *flag.FlagSet, actual *zapcore.Level) { + err := set.Parse(tc.args) + if tc.wantErr { + assert.Error(t, err, "Parse(%v) should fail.", tc.args) + return + } + if assert.NoError(t, err, "Parse(%v) should succeed.", tc.args) { + assert.Equal(t, tc.wantLevel, *actual, "Level mismatch.") + } +} + +func TestLevelFlag(t *testing.T) { + tests := []flagTestCase{ + { + args: nil, + wantLevel: zapcore.InfoLevel, + }, + { + args: []string{"--level", "unknown"}, + wantErr: true, + }, + { + args: []string{"--level", "error"}, + wantLevel: zapcore.ErrorLevel, + }, + } + + for _, tt := range tests { + tt.runExplicitSet(t) + tt.runImplicitSet(t) + } +} + +func TestLevelFlagsAreIndependent(t *testing.T) { + origCommandLine := flag.CommandLine + flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError) + flag.CommandLine.SetOutput(ioutil.Discard) + defer func() { flag.CommandLine = origCommandLine }() + + // Make sure that these two flags are independent. + fileLevel := LevelFlag("file-level", InfoLevel, "") + consoleLevel := LevelFlag("console-level", InfoLevel, "") + + assert.NoError(t, flag.CommandLine.Parse([]string{"-file-level", "debug"}), "Unexpected flag-parsing error.") + assert.Equal(t, InfoLevel, *consoleLevel, "Expected file logging level to remain unchanged.") + assert.Equal(t, DebugLevel, *fileLevel, "Expected console logging level to have changed.") +} diff --git a/vendor/src/go.uber.org/zap/glide.lock b/vendor/src/go.uber.org/zap/glide.lock new file mode 100644 index 00000000..881b462c --- /dev/null +++ b/vendor/src/go.uber.org/zap/glide.lock @@ -0,0 +1,76 @@ +hash: f073ba522c06c88ea3075bde32a8aaf0969a840a66cab6318a0897d141ffee92 +updated: 2017-07-22T18:06:49.598185334-07:00 +imports: +- name: go.uber.org/atomic + version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf +- name: go.uber.org/multierr + version: 3c4937480c32f4c13a875a1829af76c98ca3d40a +testImports: +- name: github.com/apex/log + version: d9b960447bfa720077b2da653cc79e533455b499 + subpackages: + - handlers/json +- name: github.com/axw/gocov + version: 3a69a0d2a4ef1f263e2d92b041a69593d6964fe8 + subpackages: + - gocov +- name: github.com/davecgh/go-spew + version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 + subpackages: + - spew +- name: github.com/fatih/color + version: 62e9147c64a1ed519147b62a56a14e83e2be02c1 +- name: github.com/go-kit/kit + version: e10f5bf035be9af21fd5b2fb4469d5716c6ab07d + subpackages: + - log +- name: github.com/go-logfmt/logfmt + version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 +- name: github.com/go-stack/stack + version: 54be5f394ed2c3e19dac9134a40a95ba5a017f7b +- name: github.com/golang/lint + version: c5fb716d6688a859aae56d26d3e6070808df29f7 + subpackages: + - golint +- name: github.com/kr/logfmt + version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 +- name: github.com/mattn/go-colorable + version: 3fa8c76f9daed4067e4a806fb7e4dc86455c6d6a +- name: github.com/mattn/go-isatty + version: fc9e8d8ef48496124e79ae0df75490096eccf6fe +- name: github.com/mattn/goveralls + version: 6efce81852ad1b7567c17ad71b03aeccc9dd9ae0 +- name: github.com/pborman/uuid + version: e790cca94e6cc75c7064b1332e63811d4aae1a53 +- name: github.com/pkg/errors + version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/pmezard/go-difflib + version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + subpackages: + - difflib +- name: github.com/rs/zerolog + version: eed4c2b94d945e0b2456ad6aa518a443986b5f22 +- name: github.com/satori/go.uuid + version: 5bf94b69c6b68ee1b541973bb8e1144db23a194b +- name: github.com/sirupsen/logrus + version: 7dd06bf38e1e13df288d471a57d5adbac106be9e +- name: github.com/stretchr/testify + version: f6abca593680b2315d2075e0f5e2a9751e3f431a + subpackages: + - assert + - require +- name: go.pedge.io/lion + version: 87958e8713f1fa138d993087133b97e976642159 +- name: golang.org/x/sys + version: c4489faa6e5ab84c0ef40d6ee878f7a030281f0f + subpackages: + - unix +- name: golang.org/x/tools + version: 496819729719f9d07692195e0a94d6edd2251389 + subpackages: + - cover +- name: gopkg.in/inconshreveable/log15.v2 + version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f + subpackages: + - stack + - term diff --git a/vendor/src/go.uber.org/zap/glide.yaml b/vendor/src/go.uber.org/zap/glide.yaml new file mode 100644 index 00000000..94412594 --- /dev/null +++ b/vendor/src/go.uber.org/zap/glide.yaml @@ -0,0 +1,35 @@ +package: go.uber.org/zap +license: MIT +import: +- package: go.uber.org/atomic + version: ^1 +- package: go.uber.org/multierr + version: ^1 +testImport: +- package: github.com/satori/go.uuid +- package: github.com/sirupsen/logrus +- package: github.com/apex/log + subpackages: + - handlers/json +- package: github.com/go-kit/kit + subpackages: + - log +- package: github.com/stretchr/testify + subpackages: + - assert + - require +- package: gopkg.in/inconshreveable/log15.v2 +- package: github.com/mattn/goveralls +- package: github.com/pborman/uuid +- package: github.com/pkg/errors +- package: go.pedge.io/lion +- package: github.com/rs/zerolog +- package: golang.org/x/tools + subpackages: + - cover +- package: github.com/golang/lint + subpackages: + - golint +- package: github.com/axw/gocov + subpackages: + - gocov diff --git a/vendor/src/go.uber.org/zap/global.go b/vendor/src/go.uber.org/zap/global.go new file mode 100644 index 00000000..d3454550 --- /dev/null +++ b/vendor/src/go.uber.org/zap/global.go @@ -0,0 +1,139 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "bytes" + "fmt" + "log" + "os" + "sync" + + "go.uber.org/zap/zapcore" +) + +const ( + _stdLogDefaultDepth = 2 + _loggerWriterDepth = 2 +) + +var ( + _globalMu sync.RWMutex + _globalL = NewNop() + _globalS = _globalL.Sugar() +) + +// L returns the global Logger, which can be reconfigured with ReplaceGlobals. +// It's safe for concurrent use. +func L() *Logger { + _globalMu.RLock() + l := _globalL + _globalMu.RUnlock() + return l +} + +// S returns the global SugaredLogger, which can be reconfigured with +// ReplaceGlobals. It's safe for concurrent use. +func S() *SugaredLogger { + _globalMu.RLock() + s := _globalS + _globalMu.RUnlock() + return s +} + +// ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a +// function to restore the original values. It's safe for concurrent use. +func ReplaceGlobals(logger *Logger) func() { + _globalMu.Lock() + prev := _globalL + _globalL = logger + _globalS = logger.Sugar() + _globalMu.Unlock() + return func() { ReplaceGlobals(prev) } +} + +// NewStdLog returns a *log.Logger which writes to the supplied zap Logger at +// InfoLevel. To redirect the standard library's package-global logging +// functions, use RedirectStdLog instead. +func NewStdLog(l *Logger) *log.Logger { + logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) + f := logger.Info + return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */) +} + +// NewStdLogAt returns *log.Logger which writes to supplied zap logger at +// required level. +func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) { + logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth)) + var logFunc func(string, ...zapcore.Field) + switch level { + case DebugLevel: + logFunc = logger.Debug + case InfoLevel: + logFunc = logger.Info + case WarnLevel: + logFunc = logger.Warn + case ErrorLevel: + logFunc = logger.Error + case DPanicLevel: + logFunc = logger.DPanic + case PanicLevel: + logFunc = logger.Panic + case FatalLevel: + logFunc = logger.Fatal + default: + return nil, fmt.Errorf("unrecognized level: %q", level) + } + return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil +} + +// RedirectStdLog redirects output from the standard library's package-global +// logger to the supplied logger at InfoLevel. Since zap already handles caller +// annotations, timestamps, etc., it automatically disables the standard +// library's annotations and prefixing. +// +// It returns a function to restore the original prefix and flags and reset the +// standard library's output to os.Stdout. +func RedirectStdLog(l *Logger) func() { + flags := log.Flags() + prefix := log.Prefix() + log.SetFlags(0) + log.SetPrefix("") + logFunc := l.WithOptions( + AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth), + ).Info + log.SetOutput(&loggerWriter{logFunc}) + return func() { + log.SetFlags(flags) + log.SetPrefix(prefix) + log.SetOutput(os.Stderr) + } +} + +type loggerWriter struct { + logFunc func(msg string, fields ...zapcore.Field) +} + +func (l *loggerWriter) Write(p []byte) (int, error) { + p = bytes.TrimSpace(p) + l.logFunc(string(p)) + return len(p), nil +} diff --git a/vendor/src/go.uber.org/zap/global_test.go b/vendor/src/go.uber.org/zap/global_test.go new file mode 100644 index 00000000..7da3b94e --- /dev/null +++ b/vendor/src/go.uber.org/zap/global_test.go @@ -0,0 +1,189 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "log" + "sync" + "testing" + "time" + + "go.uber.org/zap/internal/exit" + + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/atomic" +) + +func TestReplaceGlobals(t *testing.T) { + initialL := *L() + initialS := *S() + + withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) { + L().Info("no-op") + S().Info("no-op") + assert.Equal(t, 0, logs.Len(), "Expected initial logs to go to default no-op global.") + + defer ReplaceGlobals(l)() + + L().Info("captured") + S().Info("captured") + expected := observer.LoggedEntry{ + Entry: zapcore.Entry{Message: "captured"}, + Context: []zapcore.Field{}, + } + assert.Equal( + t, + []observer.LoggedEntry{expected, expected}, + logs.AllUntimed(), + "Unexpected global log output.", + ) + }) + + assert.Equal(t, initialL, *L(), "Expected func returned from ReplaceGlobals to restore initial L.") + assert.Equal(t, initialS, *S(), "Expected func returned from ReplaceGlobals to restore initial S.") +} + +func TestGlobalsConcurrentUse(t *testing.T) { + var ( + stop atomic.Bool + wg sync.WaitGroup + ) + + for i := 0; i < 100; i++ { + wg.Add(2) + go func() { + for !stop.Load() { + ReplaceGlobals(NewNop()) + } + wg.Done() + }() + go func() { + for !stop.Load() { + L().With(Int("foo", 42)).Named("main").WithOptions(Development()).Info("") + S().Info("") + } + wg.Done() + }() + } + + zaptest.Sleep(100 * time.Millisecond) + stop.Toggle() + wg.Wait() +} + +func TestNewStdLog(t *testing.T) { + withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) { + std := NewStdLog(l) + std.Print("redirected") + checkStdLogMessage(t, "redirected", logs) + }) +} + +func TestNewStdLogAt(t *testing.T) { + // include DPanicLevel here, but do not include Development in options + levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} + for _, level := range levels { + withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) { + std, err := NewStdLogAt(l, level) + require.NoError(t, err, "Unexpected error.") + std.Print("redirected") + checkStdLogMessage(t, "redirected", logs) + }) + } +} + +func TestNewStdLogAtPanics(t *testing.T) { + // include DPanicLevel here and enable Development in options + levels := []zapcore.Level{DPanicLevel, PanicLevel} + for _, level := range levels { + withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) { + std, err := NewStdLogAt(l, level) + require.NoError(t, err, "Unexpected error") + assert.Panics(t, func() { std.Print("redirected") }, "Expected log to panic.") + checkStdLogMessage(t, "redirected", logs) + }) + } +} + +func TestNewStdLogAtFatal(t *testing.T) { + withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) { + stub := exit.WithStub(func() { + std, err := NewStdLogAt(l, FatalLevel) + require.NoError(t, err, "Unexpected error.") + std.Print("redirected") + checkStdLogMessage(t, "redirected", logs) + }) + assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.") + stub.Unstub() + }) +} + +func TestNewStdLogAtInvalid(t *testing.T) { + _, err := NewStdLogAt(NewNop(), zapcore.Level(99)) + assert.Error(t, err, "Expected to get error.") + assert.Contains(t, err.Error(), "99", "Expected level code in error message") +} + +func TestRedirectStdLog(t *testing.T) { + initialFlags := log.Flags() + initialPrefix := log.Prefix() + + withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) { + defer RedirectStdLog(l)() + log.Print("redirected") + + assert.Equal(t, []observer.LoggedEntry{{ + Entry: zapcore.Entry{Message: "redirected"}, + Context: []zapcore.Field{}, + }}, logs.AllUntimed(), "Unexpected global log output.") + }) + + assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.") + assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.") +} + +func TestRedirectStdLogCaller(t *testing.T) { + withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) { + defer RedirectStdLog(l)() + log.Print("redirected") + entries := logs.All() + require.Len(t, entries, 1, "Unexpected number of logs.") + assert.Contains(t, entries[0].Entry.Caller.File, "global_test.go", "Unexpected caller annotation.") + }) +} + +func checkStdLogMessage(t *testing.T, msg string, logs *observer.ObservedLogs) { + require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged") + entry := logs.AllUntimed()[0] + assert.Equal(t, []zapcore.Field{}, entry.Context, "Unexpected entry context.") + assert.Equal(t, "redirected", entry.Entry.Message, "Unexpected entry message.") + assert.Regexp( + t, + `go.uber.org/zap/global_test.go:\d+$`, + entry.Entry.Caller.String(), + "Unexpected caller annotation.", + ) +} diff --git a/vendor/src/go.uber.org/zap/http_handler.go b/vendor/src/go.uber.org/zap/http_handler.go new file mode 100644 index 00000000..f171c384 --- /dev/null +++ b/vendor/src/go.uber.org/zap/http_handler.go @@ -0,0 +1,81 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "encoding/json" + "fmt" + "net/http" + + "go.uber.org/zap/zapcore" +) + +// ServeHTTP is a simple JSON endpoint that can report on or change the current +// logging level. +// +// GET requests return a JSON description of the current logging level. PUT +// requests change the logging level and expect a payload like: +// {"level":"info"} +// +// It's perfectly safe to change the logging level while a program is running. +func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { + type errorResponse struct { + Error string `json:"error"` + } + type payload struct { + Level *zapcore.Level `json:"level"` + } + + enc := json.NewEncoder(w) + + switch r.Method { + + case "GET": + current := lvl.Level() + enc.Encode(payload{Level: ¤t}) + + case "PUT": + var req payload + + if errmess := func() string { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return fmt.Sprintf("Request body must be well-formed JSON: %v", err) + } + if req.Level == nil { + return "Must specify a logging level." + } + return "" + }(); errmess != "" { + w.WriteHeader(http.StatusBadRequest) + enc.Encode(errorResponse{Error: errmess}) + return + } + + lvl.SetLevel(*req.Level) + enc.Encode(req) + + default: + w.WriteHeader(http.StatusMethodNotAllowed) + enc.Encode(errorResponse{ + Error: "Only GET and PUT are supported.", + }) + } +} diff --git a/vendor/src/go.uber.org/zap/http_handler_test.go b/vendor/src/go.uber.org/zap/http_handler_test.go new file mode 100644 index 00000000..474b3c7c --- /dev/null +++ b/vendor/src/go.uber.org/zap/http_handler_test.go @@ -0,0 +1,131 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap_test + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + . "go.uber.org/zap" + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newHandler() (AtomicLevel, *Logger) { + lvl := NewAtomicLevel() + logger := New(zapcore.NewNopCore()) + return lvl, logger +} + +func assertCodeOK(t testing.TB, code int) { + assert.Equal(t, http.StatusOK, code, "Unexpected response status code.") +} + +func assertCodeBadRequest(t testing.TB, code int) { + assert.Equal(t, http.StatusBadRequest, code, "Unexpected response status code.") +} + +func assertCodeMethodNotAllowed(t testing.TB, code int) { + assert.Equal(t, http.StatusMethodNotAllowed, code, "Unexpected response status code.") +} + +func assertResponse(t testing.TB, expectedLevel zapcore.Level, actualBody string) { + assert.Equal(t, fmt.Sprintf(`{"level":"%s"}`, expectedLevel)+"\n", actualBody, "Unexpected response body.") +} + +func assertJSONError(t testing.TB, body string) { + // Don't need to test exact error message, but one should be present. + var payload map[string]interface{} + require.NoError(t, json.Unmarshal([]byte(body), &payload), "Expected error response to be JSON.") + + msg, ok := payload["error"] + require.True(t, ok, "Error message is an unexpected type.") + assert.NotEqual(t, "", msg, "Expected an error message in response.") +} + +func makeRequest(t testing.TB, method string, handler http.Handler, reader io.Reader) (int, string) { + ts := httptest.NewServer(handler) + defer ts.Close() + + req, err := http.NewRequest(method, ts.URL, reader) + require.NoError(t, err, "Error constructing %s request.", method) + + res, err := http.DefaultClient.Do(req) + require.NoError(t, err, "Error making %s request.", method) + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + require.NoError(t, err, "Error reading request body.") + + return res.StatusCode, string(body) +} + +func TestHTTPHandlerGetLevel(t *testing.T) { + lvl, _ := newHandler() + code, body := makeRequest(t, "GET", lvl, nil) + assertCodeOK(t, code) + assertResponse(t, lvl.Level(), body) +} + +func TestHTTPHandlerPutLevel(t *testing.T) { + lvl, _ := newHandler() + + code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{"level":"warn"}`)) + + assertCodeOK(t, code) + assertResponse(t, lvl.Level(), body) +} + +func TestHTTPHandlerPutUnrecognizedLevel(t *testing.T) { + lvl, _ := newHandler() + code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{"level":"unrecognized-level"}`)) + assertCodeBadRequest(t, code) + assertJSONError(t, body) +} + +func TestHTTPHandlerNotJSON(t *testing.T) { + lvl, _ := newHandler() + code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{`)) + assertCodeBadRequest(t, code) + assertJSONError(t, body) +} + +func TestHTTPHandlerNoLevelSpecified(t *testing.T) { + lvl, _ := newHandler() + code, body := makeRequest(t, "PUT", lvl, strings.NewReader(`{}`)) + assertCodeBadRequest(t, code) + assertJSONError(t, body) +} + +func TestHTTPHandlerMethodNotAllowed(t *testing.T) { + lvl, _ := newHandler() + code, body := makeRequest(t, "POST", lvl, strings.NewReader(`{`)) + assertCodeMethodNotAllowed(t, code) + assertJSONError(t, body) +} diff --git a/vendor/src/go.uber.org/zap/internal/bufferpool/bufferpool.go b/vendor/src/go.uber.org/zap/internal/bufferpool/bufferpool.go new file mode 100644 index 00000000..dad583aa --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/bufferpool/bufferpool.go @@ -0,0 +1,31 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package bufferpool houses zap's shared internal buffer pool. Third-party +// packages can recreate the same functionality with buffers.NewPool. +package bufferpool + +import "go.uber.org/zap/buffer" + +var ( + _pool = buffer.NewPool() + // Get retrieves a buffer from the pool, creating one if necessary. + Get = _pool.Get +) diff --git a/vendor/src/go.uber.org/zap/internal/color/color.go b/vendor/src/go.uber.org/zap/internal/color/color.go new file mode 100644 index 00000000..c4d5d02a --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/color/color.go @@ -0,0 +1,44 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package color adds coloring functionality for TTY output. +package color + +import "fmt" + +// Foreground colors. +const ( + Black Color = iota + 30 + Red + Green + Yellow + Blue + Magenta + Cyan + White +) + +// Color represents a text color. +type Color uint8 + +// Add adds the coloring to the given string. +func (c Color) Add(s string) string { + return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) +} diff --git a/vendor/src/go.uber.org/zap/internal/color/color_test.go b/vendor/src/go.uber.org/zap/internal/color/color_test.go new file mode 100644 index 00000000..4982903a --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/color/color_test.go @@ -0,0 +1,36 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package color + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestColorFormatting(t *testing.T) { + assert.Equal( + t, + "\x1b[31mfoo\x1b[0m", + Red.Add("foo"), + "Unexpected colored output.", + ) +} diff --git a/vendor/src/go.uber.org/zap/internal/exit/exit.go b/vendor/src/go.uber.org/zap/internal/exit/exit.go new file mode 100644 index 00000000..dfc5b05f --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/exit/exit.go @@ -0,0 +1,64 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package exit provides stubs so that unit tests can exercise code that calls +// os.Exit(1). +package exit + +import "os" + +var real = func() { os.Exit(1) } + +// Exit normally terminates the process by calling os.Exit(1). If the package +// is stubbed, it instead records a call in the testing spy. +func Exit() { + real() +} + +// A StubbedExit is a testing fake for os.Exit. +type StubbedExit struct { + Exited bool + prev func() +} + +// Stub substitutes a fake for the call to os.Exit(1). +func Stub() *StubbedExit { + s := &StubbedExit{prev: real} + real = s.exit + return s +} + +// WithStub runs the supplied function with Exit stubbed. It returns the stub +// used, so that users can test whether the process would have crashed. +func WithStub(f func()) *StubbedExit { + s := Stub() + defer s.Unstub() + f() + return s +} + +// Unstub restores the previous exit function. +func (se *StubbedExit) Unstub() { + real = se.prev +} + +func (se *StubbedExit) exit() { + se.Exited = true +} diff --git a/vendor/src/go.uber.org/zap/internal/exit/exit_test.go b/vendor/src/go.uber.org/zap/internal/exit/exit_test.go new file mode 100644 index 00000000..300cdc30 --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/exit/exit_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package exit + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStub(t *testing.T) { + tests := []struct { + f func() + want bool + }{ + {Exit, true}, + {func() {}, false}, + } + + for _, tt := range tests { + s := WithStub(tt.f) + assert.Equal(t, tt.want, s.Exited, "Stub captured unexpected exit value.") + } +} diff --git a/vendor/src/go.uber.org/zap/internal/readme/readme.go b/vendor/src/go.uber.org/zap/internal/readme/readme.go new file mode 100644 index 00000000..903a4e1e --- /dev/null +++ b/vendor/src/go.uber.org/zap/internal/readme/readme.go @@ -0,0 +1,212 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "sort" + "strconv" + "strings" + "text/template" + "time" +) + +var ( + libraryNameToMarkdownName = map[string]string{ + "Zap": ":zap: zap", + "Zap.Sugar": ":zap: zap (sugared)", + "stdlib.Println": "standard library", + "sirupsen/logrus": "logrus", + "go-kit/kit/log": "go-kit", + "inconshreveable/log15": "log15", + "apex/log": "apex/log", + "go.pedge.io/lion": "lion", + "rs/zerolog": "zerolog", + } +) + +func main() { + flag.Parse() + if err := do(); err != nil { + log.Fatal(err) + } +} + +func do() error { + tmplData, err := getTmplData() + if err != nil { + return err + } + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return err + } + t, err := template.New("tmpl").Parse(string(data)) + if err != nil { + return err + } + if err := t.Execute(os.Stdout, tmplData); err != nil { + return err + } + return nil +} + +func getTmplData() (*tmplData, error) { + tmplData := &tmplData{} + rows, err := getBenchmarkRows("BenchmarkAddingFields") + if err != nil { + return nil, err + } + tmplData.BenchmarkAddingFields = rows + rows, err = getBenchmarkRows("BenchmarkAccumulatedContext") + if err != nil { + return nil, err + } + tmplData.BenchmarkAccumulatedContext = rows + rows, err = getBenchmarkRows("BenchmarkWithoutFields") + if err != nil { + return nil, err + } + tmplData.BenchmarkWithoutFields = rows + return tmplData, nil +} + +func getBenchmarkRows(benchmarkName string) (string, error) { + benchmarkOutput, err := getBenchmarkOutput(benchmarkName) + if err != nil { + return "", err + } + var benchmarkRows []*benchmarkRow + for libraryName := range libraryNameToMarkdownName { + benchmarkRow, err := getBenchmarkRow(benchmarkOutput, benchmarkName, libraryName) + if err != nil { + return "", err + } + if benchmarkRow == nil { + continue + } + benchmarkRows = append(benchmarkRows, benchmarkRow) + } + sort.Sort(benchmarkRowsByTime(benchmarkRows)) + rows := []string{ + "| Package | Time | Objects Allocated |", + "| :--- | :---: | :---: |", + } + for _, benchmarkRow := range benchmarkRows { + rows = append(rows, benchmarkRow.String()) + } + return strings.Join(rows, "\n"), nil +} + +func getBenchmarkRow(input []string, benchmarkName string, libraryName string) (*benchmarkRow, error) { + line, err := findUniqueSubstring(input, fmt.Sprintf("%s/%s-", benchmarkName, libraryName)) + if err != nil { + return nil, err + } + if line == "" { + return nil, nil + } + split := strings.Split(line, "\t") + if len(split) < 5 { + return nil, fmt.Errorf("unknown benchmark line: %s", line) + } + duration, err := time.ParseDuration(strings.Replace(strings.TrimSuffix(strings.TrimSpace(split[2]), "/op"), " ", "", -1)) + if err != nil { + return nil, err + } + allocatedBytes, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[3]), " B/op")) + if err != nil { + return nil, err + } + allocatedObjects, err := strconv.Atoi(strings.TrimSuffix(strings.TrimSpace(split[4]), " allocs/op")) + if err != nil { + return nil, err + } + return &benchmarkRow{ + libraryNameToMarkdownName[libraryName], + duration, + allocatedBytes, + allocatedObjects, + }, nil +} + +func findUniqueSubstring(input []string, substring string) (string, error) { + var output string + for _, line := range input { + if strings.Contains(line, substring) { + if output != "" { + return "", fmt.Errorf("input has duplicate substring %s", substring) + } + output = line + } + } + return output, nil +} + +func getBenchmarkOutput(benchmarkName string) ([]string, error) { + return getOutput("go", "test", fmt.Sprintf("-bench=%s", benchmarkName), "-benchmem", "./benchmarks") +} + +func getOutput(name string, arg ...string) ([]string, error) { + output, err := exec.Command(name, arg...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error running %s %s: %v\n%s", name, strings.Join(arg, " "), err, string(output)) + } + return strings.Split(string(output), "\n"), nil +} + +type tmplData struct { + BenchmarkAddingFields string + BenchmarkAccumulatedContext string + BenchmarkWithoutFields string +} + +type benchmarkRow struct { + Name string + Time time.Duration + AllocatedBytes int + AllocatedObjects int +} + +func (b *benchmarkRow) String() string { + return fmt.Sprintf("| %s | %d ns/op | %d allocs/op |", b.Name, b.Time.Nanoseconds(), b.AllocatedObjects) +} + +type benchmarkRowsByTime []*benchmarkRow + +func (b benchmarkRowsByTime) Len() int { return len(b) } +func (b benchmarkRowsByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b benchmarkRowsByTime) Less(i, j int) bool { + left, right := b[i], b[j] + leftZap, rightZap := strings.Contains(left.Name, "zap"), strings.Contains(right.Name, "zap") + + // If neither benchmark is for zap or both are, sort by time. + if !(leftZap || rightZap) || (leftZap && rightZap) { + return left.Time.Nanoseconds() < right.Time.Nanoseconds() + } + // Sort zap benchmark first. + return leftZap +} diff --git a/vendor/src/go.uber.org/zap/level.go b/vendor/src/go.uber.org/zap/level.go new file mode 100644 index 00000000..166101f3 --- /dev/null +++ b/vendor/src/go.uber.org/zap/level.go @@ -0,0 +1,132 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "go.uber.org/atomic" + "go.uber.org/zap/zapcore" +) + +const ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel = zapcore.DebugLevel + // InfoLevel is the default logging priority. + InfoLevel = zapcore.InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel = zapcore.WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel = zapcore.ErrorLevel + // DPanicLevel logs are particularly important errors. In development the + // logger panics after writing the message. + DPanicLevel = zapcore.DPanicLevel + // PanicLevel logs a message, then panics. + PanicLevel = zapcore.PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel = zapcore.FatalLevel +) + +// LevelEnablerFunc is a convenient way to implement zapcore.LevelEnabler with +// an anonymous function. +// +// It's particularly useful when splitting log output between different +// outputs (e.g., standard error and standard out). For sample code, see the +// package-level AdvancedConfiguration example. +type LevelEnablerFunc func(zapcore.Level) bool + +// Enabled calls the wrapped function. +func (f LevelEnablerFunc) Enabled(lvl zapcore.Level) bool { return f(lvl) } + +// An AtomicLevel is an atomically changeable, dynamic logging level. It lets +// you safely change the log level of a tree of loggers (the root logger and +// any children created by adding context) at runtime. +// +// The AtomicLevel itself is an http.Handler that serves a JSON endpoint to +// alter its level. +// +// AtomicLevels must be created with the NewAtomicLevel constructor to allocate +// their internal atomic pointer. +type AtomicLevel struct { + l *atomic.Int32 +} + +// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging +// enabled. +func NewAtomicLevel() AtomicLevel { + return AtomicLevel{ + l: atomic.NewInt32(int32(InfoLevel)), + } +} + +// NewAtomicLevelAt is a convienence function that creates an AtomicLevel +// and then calls SetLevel with the given level. +func NewAtomicLevelAt(l zapcore.Level) AtomicLevel { + a := NewAtomicLevel() + a.SetLevel(l) + return a +} + +// Enabled implements the zapcore.LevelEnabler interface, which allows the +// AtomicLevel to be used in place of traditional static levels. +func (lvl AtomicLevel) Enabled(l zapcore.Level) bool { + return lvl.Level().Enabled(l) +} + +// Level returns the minimum enabled log level. +func (lvl AtomicLevel) Level() zapcore.Level { + return zapcore.Level(int8(lvl.l.Load())) +} + +// SetLevel alters the logging level. +func (lvl AtomicLevel) SetLevel(l zapcore.Level) { + lvl.l.Store(int32(l)) +} + +// String returns the string representation of the underlying Level. +func (lvl AtomicLevel) String() string { + return lvl.Level().String() +} + +// UnmarshalText unmarshals the text to an AtomicLevel. It uses the same text +// representations as the static zapcore.Levels ("debug", "info", "warn", +// "error", "dpanic", "panic", and "fatal"). +func (lvl *AtomicLevel) UnmarshalText(text []byte) error { + if lvl.l == nil { + lvl.l = &atomic.Int32{} + } + + var l zapcore.Level + if err := l.UnmarshalText(text); err != nil { + return err + } + + lvl.SetLevel(l) + return nil +} + +// MarshalText marshals the AtomicLevel to a byte slice. It uses the same +// text representation as the static zapcore.Levels ("debug", "info", "warn", +// "error", "dpanic", "panic", and "fatal"). +func (lvl AtomicLevel) MarshalText() (text []byte, err error) { + return lvl.Level().MarshalText() +} diff --git a/vendor/src/go.uber.org/zap/level_test.go b/vendor/src/go.uber.org/zap/level_test.go new file mode 100644 index 00000000..a8fb650c --- /dev/null +++ b/vendor/src/go.uber.org/zap/level_test.go @@ -0,0 +1,117 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "sync" + "testing" + + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" +) + +func TestLevelEnablerFunc(t *testing.T) { + enab := LevelEnablerFunc(func(l zapcore.Level) bool { return l == zapcore.InfoLevel }) + tests := []struct { + level zapcore.Level + enabled bool + }{ + {DebugLevel, false}, + {InfoLevel, true}, + {WarnLevel, false}, + {ErrorLevel, false}, + {DPanicLevel, false}, + {PanicLevel, false}, + {FatalLevel, false}, + } + for _, tt := range tests { + assert.Equal(t, tt.enabled, enab.Enabled(tt.level), "Unexpected result applying LevelEnablerFunc to %s", tt.level) + } +} + +func TestNewAtomicLevel(t *testing.T) { + lvl := NewAtomicLevel() + assert.Equal(t, InfoLevel, lvl.Level(), "Unexpected initial level.") + lvl.SetLevel(ErrorLevel) + assert.Equal(t, ErrorLevel, lvl.Level(), "Unexpected level after SetLevel.") + lvl = NewAtomicLevelAt(WarnLevel) + assert.Equal(t, WarnLevel, lvl.Level(), "Unexpected level after SetLevel.") +} + +func TestAtomicLevelMutation(t *testing.T) { + lvl := NewAtomicLevel() + lvl.SetLevel(WarnLevel) + // Trigger races for non-atomic level mutations. + proceed := make(chan struct{}) + wg := &sync.WaitGroup{} + runConcurrently(10, 100, wg, func() { + <-proceed + assert.Equal(t, WarnLevel, lvl.Level()) + }) + runConcurrently(10, 100, wg, func() { + <-proceed + lvl.SetLevel(WarnLevel) + }) + close(proceed) + wg.Wait() +} + +func TestAtomicLevelText(t *testing.T) { + tests := []struct { + text string + expect zapcore.Level + err bool + }{ + {"debug", DebugLevel, false}, + {"info", InfoLevel, false}, + {"", InfoLevel, false}, + {"warn", WarnLevel, false}, + {"error", ErrorLevel, false}, + {"dpanic", DPanicLevel, false}, + {"panic", PanicLevel, false}, + {"fatal", FatalLevel, false}, + {"foobar", InfoLevel, true}, + } + + for _, tt := range tests { + var lvl AtomicLevel + // Test both initial unmarshaling and overwriting existing value. + for i := 0; i < 2; i++ { + if tt.err { + assert.Error(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to fail.", tt.text) + } else { + assert.NoError(t, lvl.UnmarshalText([]byte(tt.text)), "Expected unmarshaling %q to succeed.", tt.text) + } + assert.Equal(t, tt.expect, lvl.Level(), "Unexpected level after unmarshaling.") + lvl.SetLevel(InfoLevel) + } + + // Test marshalling + if tt.text != "" && !tt.err { + lvl.SetLevel(tt.expect) + marshaled, err := lvl.MarshalText() + assert.NoError(t, err, `Unexpected error marshalling level "%v" to text.`, tt.expect) + assert.Equal(t, tt.text, string(marshaled), "Expected marshaled text to match") + assert.Equal(t, tt.text, lvl.String(), "Expected Stringer call to match") + } + } +} diff --git a/vendor/src/go.uber.org/zap/logger.go b/vendor/src/go.uber.org/zap/logger.go new file mode 100644 index 00000000..7d8824b4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/logger.go @@ -0,0 +1,305 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "strings" + "time" + + "go.uber.org/zap/zapcore" +) + +// A Logger provides fast, leveled, structured logging. All methods are safe +// for concurrent use. +// +// The Logger is designed for contexts in which every microsecond and every +// allocation matters, so its API intentionally favors performance and type +// safety over brevity. For most applications, the SugaredLogger strikes a +// better balance between performance and ergonomics. +type Logger struct { + core zapcore.Core + + development bool + name string + errorOutput zapcore.WriteSyncer + + addCaller bool + addStack zapcore.LevelEnabler + + callerSkip int +} + +// New constructs a new Logger from the provided zapcore.Core and Options. If +// the passed zapcore.Core is nil, it falls back to using a no-op +// implementation. +// +// This is the most flexible way to construct a Logger, but also the most +// verbose. For typical use cases, the highly-opinionated presets +// (NewProduction, NewDevelopment, and NewExample) or the Config struct are +// more convenient. +// +// For sample code, see the package-level AdvancedConfiguration example. +func New(core zapcore.Core, options ...Option) *Logger { + if core == nil { + return NewNop() + } + log := &Logger{ + core: core, + errorOutput: zapcore.Lock(os.Stderr), + addStack: zapcore.FatalLevel + 1, + } + return log.WithOptions(options...) +} + +// NewNop returns a no-op Logger. It never writes out logs or internal errors, +// and it never runs user-defined hooks. +// +// Using WithOptions to replace the Core or error output of a no-op Logger can +// re-enable logging. +func NewNop() *Logger { + return &Logger{ + core: zapcore.NewNopCore(), + errorOutput: zapcore.AddSync(ioutil.Discard), + addStack: zapcore.FatalLevel + 1, + } +} + +// NewProduction builds a sensible production Logger that writes InfoLevel and +// above logs to standard error as JSON. +// +// It's a shortcut for NewProductionConfig().Build(...Option). +func NewProduction(options ...Option) (*Logger, error) { + return NewProductionConfig().Build(options...) +} + +// NewDevelopment builds a development Logger that writes DebugLevel and above +// logs to standard error in a human-friendly format. +// +// It's a shortcut for NewDevelopmentConfig().Build(...Option). +func NewDevelopment(options ...Option) (*Logger, error) { + return NewDevelopmentConfig().Build(options...) +} + +// NewExample builds a Logger that's designed for use in zap's testable +// examples. It writes DebugLevel and above logs to standard out as JSON, but +// omits the timestamp and calling function to keep example output +// short and deterministic. +func NewExample(options ...Option) *Logger { + encoderCfg := zapcore.EncoderConfig{ + MessageKey: "msg", + LevelKey: "level", + NameKey: "logger", + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeDuration: zapcore.StringDurationEncoder, + } + core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel) + return New(core).WithOptions(options...) +} + +// Sugar wraps the Logger to provide a more ergonomic, but slightly slower, +// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a +// single application to use both Loggers and SugaredLoggers, converting +// between them on the boundaries of performance-sensitive code. +func (log *Logger) Sugar() *SugaredLogger { + core := log.clone() + core.callerSkip += 2 + return &SugaredLogger{core} +} + +// Named adds a new path segment to the logger's name. Segments are joined by +// periods. By default, Loggers are unnamed. +func (log *Logger) Named(s string) *Logger { + if s == "" { + return log + } + l := log.clone() + if log.name == "" { + l.name = s + } else { + l.name = strings.Join([]string{l.name, s}, ".") + } + return l +} + +// WithOptions clones the current Logger, applies the supplied Options, and +// returns the resulting Logger. It's safe to use concurrently. +func (log *Logger) WithOptions(opts ...Option) *Logger { + c := log.clone() + for _, opt := range opts { + opt.apply(c) + } + return c +} + +// With creates a child logger and adds structured context to it. Fields added +// to the child don't affect the parent, and vice versa. +func (log *Logger) With(fields ...zapcore.Field) *Logger { + if len(fields) == 0 { + return log + } + l := log.clone() + l.core = l.core.With(fields) + return l +} + +// Check returns a CheckedEntry if logging a message at the specified level +// is enabled. It's a completely optional optimization; in high-performance +// applications, Check can help avoid allocating a slice to hold fields. +func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { + return log.check(lvl, msg) +} + +// Debug logs a message at DebugLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Debug(msg string, fields ...zapcore.Field) { + if ce := log.check(DebugLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Info logs a message at InfoLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Info(msg string, fields ...zapcore.Field) { + if ce := log.check(InfoLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Warn logs a message at WarnLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Warn(msg string, fields ...zapcore.Field) { + if ce := log.check(WarnLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Error logs a message at ErrorLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +func (log *Logger) Error(msg string, fields ...zapcore.Field) { + if ce := log.check(ErrorLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// DPanic logs a message at DPanicLevel. The message includes any fields +// passed at the log site, as well as any fields accumulated on the logger. +// +// If the logger is in development mode, it then panics (DPanic means +// "development panic"). This is useful for catching errors that are +// recoverable, but shouldn't ever happen. +func (log *Logger) DPanic(msg string, fields ...zapcore.Field) { + if ce := log.check(DPanicLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Panic logs a message at PanicLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +// +// The logger then panics, even if logging at PanicLevel is disabled. +func (log *Logger) Panic(msg string, fields ...zapcore.Field) { + if ce := log.check(PanicLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Fatal logs a message at FatalLevel. The message includes any fields passed +// at the log site, as well as any fields accumulated on the logger. +// +// The logger then calls os.Exit(1), even if logging at FatalLevel is +// disabled. +func (log *Logger) Fatal(msg string, fields ...zapcore.Field) { + if ce := log.check(FatalLevel, msg); ce != nil { + ce.Write(fields...) + } +} + +// Sync calls the underlying Core's Sync method, flushing any buffered log +// entries. Applications should take care to call Sync before exiting. +func (log *Logger) Sync() error { + return log.core.Sync() +} + +// Core returns the Logger's underlying zapcore.Core. +func (log *Logger) Core() zapcore.Core { + return log.core +} + +func (log *Logger) clone() *Logger { + copy := *log + return © +} + +func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { + // check must always be called directly by a method in the Logger interface + // (e.g., Check, Info, Fatal). + const callerSkipOffset = 2 + + // Create basic checked entry thru the core; this will be non-nil if the + // log message will actually be written somewhere. + ent := zapcore.Entry{ + LoggerName: log.name, + Time: time.Now(), + Level: lvl, + Message: msg, + } + ce := log.core.Check(ent, nil) + willWrite := ce != nil + + // Set up any required terminal behavior. + switch ent.Level { + case zapcore.PanicLevel: + ce = ce.Should(ent, zapcore.WriteThenPanic) + case zapcore.FatalLevel: + ce = ce.Should(ent, zapcore.WriteThenFatal) + case zapcore.DPanicLevel: + if log.development { + ce = ce.Should(ent, zapcore.WriteThenPanic) + } + } + + // Only do further annotation if we're going to write this message; checked + // entries that exist only for terminal behavior don't benefit from + // annotation. + if !willWrite { + return ce + } + + // Thread the error output through to the CheckedEntry. + ce.ErrorOutput = log.errorOutput + if log.addCaller { + ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(log.callerSkip + callerSkipOffset)) + if !ce.Entry.Caller.Defined { + fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC()) + log.errorOutput.Sync() + } + } + if log.addStack.Enabled(ce.Entry.Level) { + ce.Entry.Stack = Stack("").String + } + + return ce +} diff --git a/vendor/src/go.uber.org/zap/logger_bench_test.go b/vendor/src/go.uber.org/zap/logger_bench_test.go new file mode 100644 index 00000000..1ba7c759 --- /dev/null +++ b/vendor/src/go.uber.org/zap/logger_bench_test.go @@ -0,0 +1,222 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "testing" + "time" + + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +type user struct { + Name string + Email string + CreatedAt time.Time +} + +func (u *user) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("name", u.Name) + enc.AddString("email", u.Email) + enc.AddInt64("created_at", u.CreatedAt.UnixNano()) + return nil +} + +var _jane = &user{ + Name: "Jane Doe", + Email: "jane@test.com", + CreatedAt: time.Date(1980, 1, 1, 12, 0, 0, 0, time.UTC), +} + +func withBenchedLogger(b *testing.B, f func(*Logger)) { + logger := New( + zapcore.NewCore( + zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig), + &zaptest.Discarder{}, + DebugLevel, + )) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f(logger) + } + }) +} + +func BenchmarkNoContext(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("No context.") + }) +} + +func BenchmarkBoolField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Boolean.", Bool("foo", true)) + }) +} + +func BenchmarkByteStringField(b *testing.B) { + val := []byte("bar") + withBenchedLogger(b, func(log *Logger) { + log.Info("ByteString.", ByteString("foo", val)) + }) +} + +func BenchmarkFloat64Field(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Floating point.", Float64("foo", 3.14)) + }) +} + +func BenchmarkIntField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Integer.", Int("foo", 42)) + }) +} + +func BenchmarkInt64Field(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("64-bit integer.", Int64("foo", 42)) + }) +} + +func BenchmarkStringField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Strings.", String("foo", "bar")) + }) +} + +func BenchmarkStringerField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Level.", Stringer("foo", InfoLevel)) + }) +} + +func BenchmarkTimeField(b *testing.B) { + t := time.Unix(0, 0) + withBenchedLogger(b, func(log *Logger) { + log.Info("Time.", Time("foo", t)) + }) +} + +func BenchmarkDurationField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Duration", Duration("foo", time.Second)) + }) +} + +func BenchmarkErrorField(b *testing.B) { + err := errors.New("egad") + withBenchedLogger(b, func(log *Logger) { + log.Info("Error.", Error(err)) + }) +} + +func BenchmarkErrorsField(b *testing.B) { + errs := []error{ + errors.New("egad"), + errors.New("oh no"), + errors.New("dear me"), + errors.New("such fail"), + } + withBenchedLogger(b, func(log *Logger) { + log.Info("Errors.", Errors("errors", errs)) + }) +} + +func BenchmarkStackField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Error.", Stack("stacktrace")) + }) +} + +func BenchmarkObjectField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Arbitrary ObjectMarshaler.", Object("user", _jane)) + }) +} + +func BenchmarkReflectField(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Reflection-based serialization.", Reflect("user", _jane)) + }) +} + +func BenchmarkAddCallerHook(b *testing.B) { + logger := New( + zapcore.NewCore( + zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig), + &zaptest.Discarder{}, + InfoLevel, + ), + AddCaller(), + ) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + logger.Info("Caller.") + } + }) +} + +func Benchmark10Fields(b *testing.B) { + withBenchedLogger(b, func(log *Logger) { + log.Info("Ten fields, passed at the log site.", + Int("one", 1), + Int("two", 2), + Int("three", 3), + Int("four", 4), + Int("five", 5), + Int("six", 6), + Int("seven", 7), + Int("eight", 8), + Int("nine", 9), + Int("ten", 10), + ) + }) +} + +func Benchmark100Fields(b *testing.B) { + const batchSize = 50 + logger := New(zapcore.NewCore( + zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig), + &zaptest.Discarder{}, + DebugLevel, + )) + + // Don't include allocating these helper slices in the benchmark. Since + // access to them isn't synchronized, we can't run the benchmark in + // parallel. + first := make([]zapcore.Field, batchSize) + second := make([]zapcore.Field, batchSize) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for i := 0; i < batchSize; i++ { + // We're duplicating keys, but that doesn't affect performance. + first[i] = Int("foo", i) + second[i] = Int("foo", i+batchSize) + } + logger.With(first...).Info("Child loggers with lots of context.", second...) + } +} diff --git a/vendor/src/go.uber.org/zap/logger_test.go b/vendor/src/go.uber.org/zap/logger_test.go new file mode 100644 index 00000000..0b0cf62f --- /dev/null +++ b/vendor/src/go.uber.org/zap/logger_test.go @@ -0,0 +1,432 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "errors" + "sync" + "testing" + + "go.uber.org/zap/internal/exit" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/atomic" +) + +func makeCountingHook() (func(zapcore.Entry) error, *atomic.Int64) { + count := &atomic.Int64{} + h := func(zapcore.Entry) error { + count.Inc() + return nil + } + return h, count +} + +func TestLoggerAtomicLevel(t *testing.T) { + // Test that the dynamic level applies to all ancestors and descendants. + dl := NewAtomicLevel() + + withLogger(t, dl, nil, func(grandparent *Logger, _ *observer.ObservedLogs) { + parent := grandparent.With(Int("generation", 1)) + child := parent.With(Int("generation", 2)) + + tests := []struct { + setLevel zapcore.Level + testLevel zapcore.Level + enabled bool + }{ + {DebugLevel, DebugLevel, true}, + {InfoLevel, DebugLevel, false}, + {WarnLevel, PanicLevel, true}, + } + + for _, tt := range tests { + dl.SetLevel(tt.setLevel) + for _, logger := range []*Logger{grandparent, parent, child} { + if tt.enabled { + assert.NotNil( + t, + logger.Check(tt.testLevel, ""), + "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel, + ) + } else { + assert.Nil( + t, + logger.Check(tt.testLevel, ""), + "Expected level %s to be enabled after setting level %s.", tt.testLevel, tt.setLevel, + ) + } + } + } + }) +} + +func TestLoggerInitialFields(t *testing.T) { + fieldOpts := opts(Fields(Int("foo", 42), String("bar", "baz"))) + withLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) { + logger.Info("") + assert.Equal( + t, + observer.LoggedEntry{Context: []zapcore.Field{Int("foo", 42), String("bar", "baz")}}, + logs.AllUntimed()[0], + "Unexpected output with initial fields set.", + ) + }) +} + +func TestLoggerWith(t *testing.T) { + fieldOpts := opts(Fields(Int("foo", 42))) + withLogger(t, DebugLevel, fieldOpts, func(logger *Logger, logs *observer.ObservedLogs) { + // Child loggers should have copy-on-write semantics, so two children + // shouldn't stomp on each other's fields or affect the parent's fields. + logger.With(String("one", "two")).Info("") + logger.With(String("three", "four")).Info("") + logger.Info("") + + assert.Equal(t, []observer.LoggedEntry{ + {Context: []zapcore.Field{Int("foo", 42), String("one", "two")}}, + {Context: []zapcore.Field{Int("foo", 42), String("three", "four")}}, + {Context: []zapcore.Field{Int("foo", 42)}}, + }, logs.AllUntimed(), "Unexpected cross-talk between child loggers.") + }) +} + +func TestLoggerLogPanic(t *testing.T) { + for _, tt := range []struct { + do func(*Logger) + should bool + expected string + }{ + {func(logger *Logger) { logger.Check(PanicLevel, "bar").Write() }, true, "bar"}, + {func(logger *Logger) { logger.Panic("baz") }, true, "baz"}, + } { + withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + if tt.should { + assert.Panics(t, func() { tt.do(logger) }, "Expected panic") + } else { + assert.NotPanics(t, func() { tt.do(logger) }, "Expected no panic") + } + + output := logs.AllUntimed() + assert.Equal(t, 1, len(output), "Unexpected number of logs.") + assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.") + assert.Equal( + t, + zapcore.Entry{Message: tt.expected, Level: PanicLevel}, + output[0].Entry, + "Unexpected output from panic-level Log.", + ) + }) + } +} + +func TestLoggerLogFatal(t *testing.T) { + for _, tt := range []struct { + do func(*Logger) + expected string + }{ + {func(logger *Logger) { logger.Check(FatalLevel, "bar").Write() }, "bar"}, + {func(logger *Logger) { logger.Fatal("baz") }, "baz"}, + } { + withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + stub := exit.WithStub(func() { + tt.do(logger) + }) + assert.True(t, stub.Exited, "Expected Fatal logger call to terminate process.") + output := logs.AllUntimed() + assert.Equal(t, 1, len(output), "Unexpected number of logs.") + assert.Equal(t, 0, len(output[0].Context), "Unexpected context on first log.") + assert.Equal( + t, + zapcore.Entry{Message: tt.expected, Level: FatalLevel}, + output[0].Entry, + "Unexpected output from fatal-level Log.", + ) + }) + } +} + +func TestLoggerLeveledMethods(t *testing.T) { + withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + tests := []struct { + method func(string, ...zapcore.Field) + expectedLevel zapcore.Level + }{ + {logger.Debug, DebugLevel}, + {logger.Info, InfoLevel}, + {logger.Warn, WarnLevel}, + {logger.Error, ErrorLevel}, + {logger.DPanic, DPanicLevel}, + } + for i, tt := range tests { + tt.method("") + output := logs.AllUntimed() + assert.Equal(t, i+1, len(output), "Unexpected number of logs.") + assert.Equal(t, 0, len(output[i].Context), "Unexpected context on first log.") + assert.Equal( + t, + zapcore.Entry{Level: tt.expectedLevel}, + output[i].Entry, + "Unexpected output from %s-level logger method.", tt.expectedLevel) + } + }) +} + +func TestLoggerAlwaysPanics(t *testing.T) { + // Users can disable writing out panic-level logs, but calls to logger.Panic() + // should still call panic(). + withLogger(t, FatalLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + msg := "Even if output is disabled, logger.Panic should always panic." + assert.Panics(t, func() { logger.Panic("foo") }, msg) + assert.Panics(t, func() { + if ce := logger.Check(PanicLevel, "foo"); ce != nil { + ce.Write() + } + }, msg) + assert.Equal(t, 0, logs.Len(), "Panics shouldn't be written out if PanicLevel is disabled.") + }) +} + +func TestLoggerAlwaysFatals(t *testing.T) { + // Users can disable writing out fatal-level logs, but calls to logger.Fatal() + // should still terminate the process. + withLogger(t, FatalLevel+1, nil, func(logger *Logger, logs *observer.ObservedLogs) { + stub := exit.WithStub(func() { logger.Fatal("") }) + assert.True(t, stub.Exited, "Expected calls to logger.Fatal to terminate process.") + + stub = exit.WithStub(func() { + if ce := logger.Check(FatalLevel, ""); ce != nil { + ce.Write() + } + }) + assert.True(t, stub.Exited, "Expected calls to logger.Check(FatalLevel, ...) to terminate process.") + + assert.Equal(t, 0, logs.Len(), "Shouldn't write out logs when fatal-level logging is disabled.") + }) +} + +func TestLoggerDPanic(t *testing.T) { + withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + assert.NotPanics(t, func() { logger.DPanic("") }) + assert.Equal( + t, + []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []zapcore.Field{}}}, + logs.AllUntimed(), + "Unexpected log output from DPanic in production mode.", + ) + }) + withLogger(t, DebugLevel, opts(Development()), func(logger *Logger, logs *observer.ObservedLogs) { + assert.Panics(t, func() { logger.DPanic("") }) + assert.Equal( + t, + []observer.LoggedEntry{{Entry: zapcore.Entry{Level: DPanicLevel}, Context: []zapcore.Field{}}}, + logs.AllUntimed(), + "Unexpected log output from DPanic in development mode.", + ) + }) +} + +func TestLoggerNoOpsDisabledLevels(t *testing.T) { + withLogger(t, WarnLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + logger.Info("silence!") + assert.Equal( + t, + []observer.LoggedEntry{}, + logs.AllUntimed(), + "Expected logging at a disabled level to produce no output.", + ) + }) +} + +func TestLoggerNames(t *testing.T) { + tests := []struct { + names []string + expected string + }{ + {nil, ""}, + {[]string{""}, ""}, + {[]string{"foo"}, "foo"}, + {[]string{"foo", ""}, "foo"}, + {[]string{"foo", "bar"}, "foo.bar"}, + {[]string{"foo.bar", "baz"}, "foo.bar.baz"}, + // Garbage in, garbage out. + {[]string{"foo.", "bar"}, "foo..bar"}, + {[]string{"foo", ".bar"}, "foo..bar"}, + {[]string{"foo.", ".bar"}, "foo...bar"}, + } + + for _, tt := range tests { + withLogger(t, DebugLevel, nil, func(log *Logger, logs *observer.ObservedLogs) { + for _, n := range tt.names { + log = log.Named(n) + } + log.Info("") + require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.") + assert.Equal(t, tt.expected, logs.AllUntimed()[0].Entry.LoggerName, "Unexpected logger name.") + }) + withSugar(t, DebugLevel, nil, func(log *SugaredLogger, logs *observer.ObservedLogs) { + for _, n := range tt.names { + log = log.Named(n) + } + log.Infow("") + require.Equal(t, 1, logs.Len(), "Expected only one log entry to be written.") + assert.Equal(t, tt.expected, logs.AllUntimed()[0].Entry.LoggerName, "Unexpected logger name.") + }) + } +} + +func TestLoggerWriteFailure(t *testing.T) { + errSink := &zaptest.Buffer{} + logger := New( + zapcore.NewCore( + zapcore.NewJSONEncoder(NewProductionConfig().EncoderConfig), + zapcore.Lock(zapcore.AddSync(zaptest.FailWriter{})), + DebugLevel, + ), + ErrorOutput(errSink), + ) + + logger.Info("foo") + // Should log the error. + assert.Regexp(t, `write error: failed`, errSink.Stripped(), "Expected to log the error to the error output.") + assert.True(t, errSink.Called(), "Expected logging an internal error to call Sync the error sink.") +} + +func TestLoggerSync(t *testing.T) { + withLogger(t, DebugLevel, nil, func(logger *Logger, _ *observer.ObservedLogs) { + assert.NoError(t, logger.Sync(), "Expected syncing a test logger to succeed.") + assert.NoError(t, logger.Sugar().Sync(), "Expected syncing a sugared logger to succeed.") + }) +} + +func TestLoggerSyncFail(t *testing.T) { + noSync := &zaptest.Buffer{} + err := errors.New("fail") + noSync.SetError(err) + logger := New(zapcore.NewCore( + zapcore.NewJSONEncoder(zapcore.EncoderConfig{}), + noSync, + DebugLevel, + )) + assert.Equal(t, err, logger.Sync(), "Expected Logger.Sync to propagate errors.") + assert.Equal(t, err, logger.Sugar().Sync(), "Expected SugaredLogger.Sync to propagate errors.") +} + +func TestLoggerAddCaller(t *testing.T) { + tests := []struct { + options []Option + pat string + }{ + {opts(AddCaller()), `.+/logger_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/zap/logger_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1)), `.+/zap/common_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(3)), `.+/src/runtime/.*:[\d]+$`}, + } + for _, tt := range tests { + withLogger(t, DebugLevel, tt.options, func(logger *Logger, logs *observer.ObservedLogs) { + // Make sure that sugaring and desugaring resets caller skip properly. + logger = logger.Sugar().Desugar() + logger.Info("") + output := logs.AllUntimed() + assert.Equal(t, 1, len(output), "Unexpected number of logs written out.") + assert.Regexp( + t, + tt.pat, + output[0].Entry.Caller, + "Expected to find package name and file name in output.", + ) + }) + } +} + +func TestLoggerAddCallerFail(t *testing.T) { + errBuf := &zaptest.Buffer{} + withLogger(t, DebugLevel, opts(AddCaller(), ErrorOutput(errBuf)), func(log *Logger, logs *observer.ObservedLogs) { + log.callerSkip = 1e3 + log.Info("Failure.") + assert.Regexp( + t, + `Logger.check error: failed to get caller`, + errBuf.String(), + "Didn't find expected failure message.", + ) + assert.Equal( + t, + logs.AllUntimed()[0].Entry.Message, + "Failure.", + "Expected original message to survive failures in runtime.Caller.") + }) +} + +func TestLoggerReplaceCore(t *testing.T) { + replace := WrapCore(func(zapcore.Core) zapcore.Core { + return zapcore.NewNopCore() + }) + withLogger(t, DebugLevel, opts(replace), func(logger *Logger, logs *observer.ObservedLogs) { + logger.Debug("") + logger.Info("") + logger.Warn("") + assert.Equal(t, 0, logs.Len(), "Expected no-op core to write no logs.") + }) +} + +func TestLoggerHooks(t *testing.T) { + hook, seen := makeCountingHook() + withLogger(t, DebugLevel, opts(Hooks(hook)), func(logger *Logger, logs *observer.ObservedLogs) { + logger.Debug("") + logger.Info("") + }) + assert.Equal(t, int64(2), seen.Load(), "Hook saw an unexpected number of logs.") +} + +func TestLoggerConcurrent(t *testing.T) { + withLogger(t, DebugLevel, nil, func(logger *Logger, logs *observer.ObservedLogs) { + child := logger.With(String("foo", "bar")) + + wg := &sync.WaitGroup{} + runConcurrently(5, 10, wg, func() { + logger.Info("", String("foo", "bar")) + }) + runConcurrently(5, 10, wg, func() { + child.Info("") + }) + + wg.Wait() + + // Make sure the output doesn't contain interspersed entries. + assert.Equal(t, 100, logs.Len(), "Unexpected number of logs written out.") + for _, obs := range logs.AllUntimed() { + assert.Equal( + t, + observer.LoggedEntry{ + Entry: zapcore.Entry{Level: InfoLevel}, + Context: []zapcore.Field{String("foo", "bar")}, + }, + obs, + "Unexpected log output.", + ) + } + }) +} diff --git a/vendor/src/go.uber.org/zap/options.go b/vendor/src/go.uber.org/zap/options.go new file mode 100644 index 00000000..d0f9422d --- /dev/null +++ b/vendor/src/go.uber.org/zap/options.go @@ -0,0 +1,109 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import "go.uber.org/zap/zapcore" + +// An Option configures a Logger. +type Option interface { + apply(*Logger) +} + +// optionFunc wraps a func so it satisfies the Option interface. +type optionFunc func(*Logger) + +func (f optionFunc) apply(log *Logger) { + f(log) +} + +// WrapCore wraps or replaces the Logger's underlying zapcore.Core. +func WrapCore(f func(zapcore.Core) zapcore.Core) Option { + return optionFunc(func(log *Logger) { + log.core = f(log.core) + }) +} + +// Hooks registers functions which will be called each time the Logger writes +// out an Entry. Repeated use of Hooks is additive. +// +// Hooks are useful for simple side effects, like capturing metrics for the +// number of emitted logs. More complex side effects, including anything that +// requires access to the Entry's structured fields, should be implemented as +// a zapcore.Core instead. See zapcore.RegisterHooks for details. +func Hooks(hooks ...func(zapcore.Entry) error) Option { + return optionFunc(func(log *Logger) { + log.core = zapcore.RegisterHooks(log.core, hooks...) + }) +} + +// Fields adds fields to the Logger. +func Fields(fs ...zapcore.Field) Option { + return optionFunc(func(log *Logger) { + log.core = log.core.With(fs) + }) +} + +// ErrorOutput sets the destination for errors generated by the Logger. Note +// that this option only affects internal errors; for sample code that sends +// error-level logs to a different location from info- and debug-level logs, +// see the package-level AdvancedConfiguration example. +// +// The supplied WriteSyncer must be safe for concurrent use. The Open and +// zapcore.Lock functions are the simplest ways to protect files with a mutex. +func ErrorOutput(w zapcore.WriteSyncer) Option { + return optionFunc(func(log *Logger) { + log.errorOutput = w + }) +} + +// Development puts the logger in development mode, which makes DPanic-level +// logs panic instead of simply logging an error. +func Development() Option { + return optionFunc(func(log *Logger) { + log.development = true + }) +} + +// AddCaller configures the Logger to annotate each message with the filename +// and line number of zap's caller. +func AddCaller() Option { + return optionFunc(func(log *Logger) { + log.addCaller = true + }) +} + +// AddCallerSkip increases the number of callers skipped by caller annotation +// (as enabled by the AddCaller option). When building wrappers around the +// Logger and SugaredLogger, supplying this Option prevents zap from always +// reporting the wrapper code as the caller. +func AddCallerSkip(skip int) Option { + return optionFunc(func(log *Logger) { + log.callerSkip += skip + }) +} + +// AddStacktrace configures the Logger to record a stack trace for all messages at +// or above a given level. +func AddStacktrace(lvl zapcore.LevelEnabler) Option { + return optionFunc(func(log *Logger) { + log.addStack = lvl + }) +} diff --git a/vendor/src/go.uber.org/zap/scripts/cover.sh b/vendor/src/go.uber.org/zap/scripts/cover.sh new file mode 100644 index 00000000..0da503cb --- /dev/null +++ b/vendor/src/go.uber.org/zap/scripts/cover.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e +echo "" > cover.out + +for d in $(go list $@); do + go test -race -coverprofile=profile.out $d + if [ -f profile.out ]; then + cat profile.out >> cover.out + rm profile.out + fi +done diff --git a/vendor/src/go.uber.org/zap/stacktrace.go b/vendor/src/go.uber.org/zap/stacktrace.go new file mode 100644 index 00000000..100fac21 --- /dev/null +++ b/vendor/src/go.uber.org/zap/stacktrace.go @@ -0,0 +1,126 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "runtime" + "strings" + "sync" + + "go.uber.org/zap/internal/bufferpool" +) + +const _zapPackage = "go.uber.org/zap" + +var ( + _stacktracePool = sync.Pool{ + New: func() interface{} { + return newProgramCounters(64) + }, + } + + // We add "." and "/" suffixes to the package name to ensure we only match + // the exact package and not any package with the same prefix. + _zapStacktracePrefixes = addPrefix(_zapPackage, ".", "/") + _zapStacktraceVendorContains = addPrefix("/vendor/", _zapStacktracePrefixes...) +) + +func takeStacktrace() string { + buffer := bufferpool.Get() + defer buffer.Free() + programCounters := _stacktracePool.Get().(*programCounters) + defer _stacktracePool.Put(programCounters) + + var numFrames int + for { + // Skip the call to runtime.Counters and takeStacktrace so that the + // program counters start at the caller of takeStacktrace. + numFrames = runtime.Callers(2, programCounters.pcs) + if numFrames < len(programCounters.pcs) { + break + } + // Don't put the too-short counter slice back into the pool; this lets + // the pool adjust if we consistently take deep stacktraces. + programCounters = newProgramCounters(len(programCounters.pcs) * 2) + } + + i := 0 + skipZapFrames := true // skip all consecutive zap frames at the beginning. + frames := runtime.CallersFrames(programCounters.pcs[:numFrames]) + + // Note: On the last iteration, frames.Next() returns false, with a valid + // frame, but we ignore this frame. The last frame is a a runtime frame which + // adds noise, since it's only either runtime.main or runtime.goexit. + for frame, more := frames.Next(); more; frame, more = frames.Next() { + if skipZapFrames && isZapFrame(frame.Function) { + continue + } else { + skipZapFrames = false + } + + if i != 0 { + buffer.AppendByte('\n') + } + i++ + buffer.AppendString(frame.Function) + buffer.AppendByte('\n') + buffer.AppendByte('\t') + buffer.AppendString(frame.File) + buffer.AppendByte(':') + buffer.AppendInt(int64(frame.Line)) + } + + return buffer.String() +} + +func isZapFrame(function string) bool { + for _, prefix := range _zapStacktracePrefixes { + if strings.HasPrefix(function, prefix) { + return true + } + } + + // We can't use a prefix match here since the location of the vendor + // directory affects the prefix. Instead we do a contains match. + for _, contains := range _zapStacktraceVendorContains { + if strings.Contains(function, contains) { + return true + } + } + + return false +} + +type programCounters struct { + pcs []uintptr +} + +func newProgramCounters(size int) *programCounters { + return &programCounters{make([]uintptr, size)} +} + +func addPrefix(prefix string, ss ...string) []string { + withPrefix := make([]string, len(ss)) + for i, s := range ss { + withPrefix[i] = prefix + s + } + return withPrefix +} diff --git a/vendor/src/go.uber.org/zap/stacktrace_ext_test.go b/vendor/src/go.uber.org/zap/stacktrace_ext_test.go new file mode 100644 index 00000000..0edf5e1e --- /dev/null +++ b/vendor/src/go.uber.org/zap/stacktrace_ext_test.go @@ -0,0 +1,161 @@ +// Copyright (c) 2016, 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap_test + +import ( + "bytes" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// _zapPackages are packages that we search for in the logging output to match a +// zap stack frame. It is different from _zapStacktracePrefixes which is only +// intended to match on the function name, while this is on the full output +// which includes filenames. +var _zapPackages = []string{ + "go.uber.org/zap.", + "go.uber.org/zap/zapcore.", +} + +func TestStacktraceFiltersZapLog(t *testing.T) { + withLogger(t, func(logger *zap.Logger, out *bytes.Buffer) { + logger.Error("test log") + logger.Sugar().Error("sugar test log") + + require.Contains(t, out.String(), "TestStacktraceFiltersZapLog", "Should not strip out non-zap import") + verifyNoZap(t, out.String()) + }) +} + +func TestStacktraceFiltersZapMarshal(t *testing.T) { + withLogger(t, func(logger *zap.Logger, out *bytes.Buffer) { + marshal := func(enc zapcore.ObjectEncoder) error { + logger.Warn("marshal caused warn") + enc.AddString("f", "v") + return nil + } + logger.Error("test log", zap.Object("obj", zapcore.ObjectMarshalerFunc(marshal))) + + logs := out.String() + + // The marshal function (which will be under the test function) should not be stripped. + const marshalFnPrefix = "TestStacktraceFiltersZapMarshal." + require.Contains(t, logs, marshalFnPrefix, "Should not strip out marshal call") + + // There should be no zap stack traces before that point. + marshalIndex := strings.Index(logs, marshalFnPrefix) + verifyNoZap(t, logs[:marshalIndex]) + + // After that point, there should be zap stack traces - we don't want to strip out + // the Marshal caller information. + for _, fnPrefix := range _zapPackages { + require.Contains(t, logs[marshalIndex:], fnPrefix, "Missing zap caller stack for Marshal") + } + }) +} + +func TestStacktraceFiltersVendorZap(t *testing.T) { + // We need to simulate a zap as a vendor library, so we're going to create a fake GOPATH + // and run the above test which will contain zap in the vendor directory. + withGoPath(t, func(goPath string) { + curDir, err := os.Getwd() + require.NoError(t, err, "Failed to get current directory") + + testDir := filepath.Join(goPath, "src/go.uber.org/zap_test/") + vendorDir := filepath.Join(testDir, "vendor") + require.NoError(t, os.MkdirAll(testDir, 0777), "Failed to create source director") + + curFile := getSelfFilename(t) + //copyFile(t, curFile, filepath.Join(testDir, curFile)) + setupSymlink(t, curFile, filepath.Join(testDir, curFile)) + + // Set up symlinks for zap, and for any test dependencies. + setupSymlink(t, curDir, filepath.Join(vendorDir, "go.uber.org/zap")) + for _, testDep := range []string{"github.com/stretchr/testify"} { + setupSymlink(t, filepath.Join(curDir, "vendor", testDep), filepath.Join(vendorDir, testDep)) + } + + // Now run the above test which ensures we filter out zap stacktraces, but this time + // zap is in a vendor + cmd := exec.Command("go", "test", "-v", "-run", "TestStacktraceFiltersZap") + cmd.Dir = testDir + out, err := cmd.CombinedOutput() + require.NoError(t, err, "Failed to run test in vendor directory, output: %s", out) + assert.Contains(t, string(out), "PASS") + }) +} + +// withLogger sets up a logger with a real encoder set up, so that any marshal functions are called. +// The inbuilt observer does not call Marshal for objects/arrays, which we need for some tests. +func withLogger(t *testing.T, fn func(logger *zap.Logger, out *bytes.Buffer)) { + buf := &bytes.Buffer{} + encoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) + core := zapcore.NewCore(encoder, zapcore.AddSync(buf), zapcore.DebugLevel) + logger := zap.New(core, zap.AddStacktrace(zap.DebugLevel)) + fn(logger, buf) +} + +func verifyNoZap(t *testing.T, logs string) { + for _, fnPrefix := range _zapPackages { + require.NotContains(t, logs, fnPrefix, "Should not strip out marshal call") + } +} + +func withGoPath(t *testing.T, f func(goPath string)) { + goPath, err := ioutil.TempDir("", "gopath") + require.NoError(t, err, "Failed to create temporary directory for GOPATH") + //defer os.RemoveAll(goPath) + + os.Setenv("GOPATH", goPath) + defer os.Setenv("GOPATH", os.Getenv("GOPATH")) + + f(goPath) +} + +func getSelfFilename(t *testing.T) string { + _, file, _, ok := runtime.Caller(0) + require.True(t, ok, "Failed to get caller information to identify local file") + + return filepath.Base(file) +} + +func setupSymlink(t *testing.T, src, dst string) { + // Make sure the destination directory exists. + os.MkdirAll(filepath.Dir(dst), 0777) + + // Get absolute path of the source for the symlink, otherwise we can create a symlink + // that uses relative paths. + srcAbs, err := filepath.Abs(src) + require.NoError(t, err, "Failed to get absolute path") + + require.NoError(t, os.Symlink(srcAbs, dst), "Failed to set up symlink") +} diff --git a/vendor/src/go.uber.org/zap/stacktrace_test.go b/vendor/src/go.uber.org/zap/stacktrace_test.go new file mode 100644 index 00000000..3c9a41cf --- /dev/null +++ b/vendor/src/go.uber.org/zap/stacktrace_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTakeStacktrace(t *testing.T) { + trace := takeStacktrace() + lines := strings.Split(trace, "\n") + require.True(t, len(lines) > 0, "Expected stacktrace to have at least one frame.") + assert.Contains( + t, + lines[0], + "testing.", + "Expected stacktrace to start with the test runner (zap frames are filtered out) %s.", lines[0], + ) +} + +func TestIsZapFrame(t *testing.T) { + zapFrames := []string{ + "go.uber.org/zap.Stack", + "go.uber.org/zap.(*SugaredLogger).log", + "go.uber.org/zap/zapcore.(ArrayMarshalerFunc).MarshalLogArray", + "github.com/uber/tchannel-go/vendor/go.uber.org/zap.Stack", + "github.com/uber/tchannel-go/vendor/go.uber.org/zap.(*SugaredLogger).log", + "github.com/uber/tchannel-go/vendor/go.uber.org/zap/zapcore.(ArrayMarshalerFunc).MarshalLogArray", + } + nonZapFrames := []string{ + "github.com/uber/tchannel-go.NewChannel", + "go.uber.org/not-zap.New", + "go.uber.org/zapext.ctx", + "go.uber.org/zap_ext/ctx.New", + } + + t.Run("zap frames", func(t *testing.T) { + for _, f := range zapFrames { + require.True(t, isZapFrame(f), f) + } + }) + t.Run("non-zap frames", func(t *testing.T) { + for _, f := range nonZapFrames { + require.False(t, isZapFrame(f), f) + } + }) +} + +func BenchmarkTakeStacktrace(b *testing.B) { + for i := 0; i < b.N; i++ { + takeStacktrace() + } +} diff --git a/vendor/src/go.uber.org/zap/sugar.go b/vendor/src/go.uber.org/zap/sugar.go new file mode 100644 index 00000000..9cda0096 --- /dev/null +++ b/vendor/src/go.uber.org/zap/sugar.go @@ -0,0 +1,304 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "fmt" + + "go.uber.org/zap/zapcore" + + "go.uber.org/multierr" +) + +const ( + _oddNumberErrMsg = "Ignored key without a value." + _nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys." +) + +// A SugaredLogger wraps the base Logger functionality in a slower, but less +// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar +// method. +// +// Unlike the Logger, the SugaredLogger doesn't insist on structured logging. +// For each log level, it exposes three methods: one for loosely-typed +// structured logging, one for println-style formatting, and one for +// printf-style formatting. For example, SugaredLoggers can produce InfoLevel +// output with Infow ("info with" structured context), Info, or Infof. +type SugaredLogger struct { + base *Logger +} + +// Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring +// is quite inexpensive, so it's reasonable for a single application to use +// both Loggers and SugaredLoggers, converting between them on the boundaries +// of performance-sensitive code. +func (s *SugaredLogger) Desugar() *Logger { + base := s.base.clone() + base.callerSkip -= 2 + return base +} + +// Named adds a sub-scope to the logger's name. See Logger.Named for details. +func (s *SugaredLogger) Named(name string) *SugaredLogger { + return &SugaredLogger{base: s.base.Named(name)} +} + +// With adds a variadic number of fields to the logging context. It accepts a +// mix of strongly-typed zapcore.Field objects and loosely-typed key-value +// pairs. When processing pairs, the first element of the pair is used as the +// field key and the second as the field value. +// +// For example, +// sugaredLogger.With( +// "hello", "world", +// "failure", errors.New("oh no"), +// Stack(), +// "count", 42, +// "user", User{Name: "alice"}, +// ) +// is the equivalent of +// unsugared.With( +// String("hello", "world"), +// String("failure", "oh no"), +// Stack(), +// Int("count", 42), +// Object("user", User{Name: "alice"}), +// ) +// +// Note that the keys in key-value pairs should be strings. In development, +// passing a non-string key panics. In production, the logger is more +// forgiving: a separate error is logged, but the key-value pair is skipped +// and execution continues. Passing an orphaned key triggers similar behavior: +// panics in development and errors in production. +func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger { + return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)} +} + +// Debug uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Debug(args ...interface{}) { + s.log(DebugLevel, "", args, nil) +} + +// Info uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Info(args ...interface{}) { + s.log(InfoLevel, "", args, nil) +} + +// Warn uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Warn(args ...interface{}) { + s.log(WarnLevel, "", args, nil) +} + +// Error uses fmt.Sprint to construct and log a message. +func (s *SugaredLogger) Error(args ...interface{}) { + s.log(ErrorLevel, "", args, nil) +} + +// DPanic uses fmt.Sprint to construct and log a message. In development, the +// logger then panics. (See DPanicLevel for details.) +func (s *SugaredLogger) DPanic(args ...interface{}) { + s.log(DPanicLevel, "", args, nil) +} + +// Panic uses fmt.Sprint to construct and log a message, then panics. +func (s *SugaredLogger) Panic(args ...interface{}) { + s.log(PanicLevel, "", args, nil) +} + +// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit. +func (s *SugaredLogger) Fatal(args ...interface{}) { + s.log(FatalLevel, "", args, nil) +} + +// Debugf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Debugf(template string, args ...interface{}) { + s.log(DebugLevel, template, args, nil) +} + +// Infof uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Infof(template string, args ...interface{}) { + s.log(InfoLevel, template, args, nil) +} + +// Warnf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Warnf(template string, args ...interface{}) { + s.log(WarnLevel, template, args, nil) +} + +// Errorf uses fmt.Sprintf to log a templated message. +func (s *SugaredLogger) Errorf(template string, args ...interface{}) { + s.log(ErrorLevel, template, args, nil) +} + +// DPanicf uses fmt.Sprintf to log a templated message. In development, the +// logger then panics. (See DPanicLevel for details.) +func (s *SugaredLogger) DPanicf(template string, args ...interface{}) { + s.log(DPanicLevel, template, args, nil) +} + +// Panicf uses fmt.Sprintf to log a templated message, then panics. +func (s *SugaredLogger) Panicf(template string, args ...interface{}) { + s.log(PanicLevel, template, args, nil) +} + +// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit. +func (s *SugaredLogger) Fatalf(template string, args ...interface{}) { + s.log(FatalLevel, template, args, nil) +} + +// Debugw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +// +// When debug-level logging is disabled, this is much faster than +// s.With(keysAndValues).Debug(msg) +func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) { + s.log(DebugLevel, msg, nil, keysAndValues) +} + +// Infow logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) { + s.log(InfoLevel, msg, nil, keysAndValues) +} + +// Warnw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) { + s.log(WarnLevel, msg, nil, keysAndValues) +} + +// Errorw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) { + s.log(ErrorLevel, msg, nil, keysAndValues) +} + +// DPanicw logs a message with some additional context. In development, the +// logger then panics. (See DPanicLevel for details.) The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) { + s.log(DPanicLevel, msg, nil, keysAndValues) +} + +// Panicw logs a message with some additional context, then panics. The +// variadic key-value pairs are treated as they are in With. +func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) { + s.log(PanicLevel, msg, nil, keysAndValues) +} + +// Fatalw logs a message with some additional context, then calls os.Exit. The +// variadic key-value pairs are treated as they are in With. +func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) { + s.log(FatalLevel, msg, nil, keysAndValues) +} + +// Sync flushes any buffered log entries. +func (s *SugaredLogger) Sync() error { + return s.base.Sync() +} + +func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) { + // If logging at this level is completely disabled, skip the overhead of + // string formatting. + if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) { + return + } + + // Format with Sprint, Sprintf, or neither. + msg := template + if msg == "" && len(fmtArgs) > 0 { + msg = fmt.Sprint(fmtArgs...) + } else if msg != "" && len(fmtArgs) > 0 { + msg = fmt.Sprintf(template, fmtArgs...) + } + + if ce := s.base.Check(lvl, msg); ce != nil { + ce.Write(s.sweetenFields(context)...) + } +} + +func (s *SugaredLogger) sweetenFields(args []interface{}) []zapcore.Field { + if len(args) == 0 { + return nil + } + + // Allocate enough space for the worst case; if users pass only structured + // fields, we shouldn't penalize them with extra allocations. + fields := make([]zapcore.Field, 0, len(args)) + var invalid invalidPairs + + for i := 0; i < len(args); { + // This is a strongly-typed field. Consume it and move on. + if f, ok := args[i].(zapcore.Field); ok { + fields = append(fields, f) + i++ + continue + } + + // Make sure this element isn't a dangling key. + if i == len(args)-1 { + s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i])) + break + } + + // Consume this value and the next, treating them as a key-value pair. If the + // key isn't a string, add this pair to the slice of invalid pairs. + key, val := args[i], args[i+1] + if keyStr, ok := key.(string); !ok { + // Subsequent errors are likely, so allocate once up front. + if cap(invalid) == 0 { + invalid = make(invalidPairs, 0, len(args)/2) + } + invalid = append(invalid, invalidPair{i, key, val}) + } else { + fields = append(fields, Any(keyStr, val)) + } + i += 2 + } + + // If we encountered any invalid key-value pairs, log an error. + if len(invalid) > 0 { + s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid)) + } + return fields +} + +type invalidPair struct { + position int + key, value interface{} +} + +func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddInt64("position", int64(p.position)) + Any("key", p.key).AddTo(enc) + Any("value", p.value).AddTo(enc) + return nil +} + +type invalidPairs []invalidPair + +func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error { + var err error + for i := range ps { + err = multierr.Append(err, enc.AppendObject(ps[i])) + } + return err +} diff --git a/vendor/src/go.uber.org/zap/sugar_test.go b/vendor/src/go.uber.org/zap/sugar_test.go new file mode 100644 index 00000000..69eb1fa6 --- /dev/null +++ b/vendor/src/go.uber.org/zap/sugar_test.go @@ -0,0 +1,374 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + + "go.uber.org/zap/internal/exit" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSugarWith(t *testing.T) { + // Convenience functions to create expected error logs. + ignored := func(msg interface{}) observer.LoggedEntry { + return observer.LoggedEntry{ + Entry: zapcore.Entry{Level: DPanicLevel, Message: _oddNumberErrMsg}, + Context: []zapcore.Field{Any("ignored", msg)}, + } + } + nonString := func(pairs ...invalidPair) observer.LoggedEntry { + return observer.LoggedEntry{ + Entry: zapcore.Entry{Level: DPanicLevel, Message: _nonStringKeyErrMsg}, + Context: []zapcore.Field{Array("invalid", invalidPairs(pairs))}, + } + } + + tests := []struct { + desc string + args []interface{} + expected []zapcore.Field + errLogs []observer.LoggedEntry + }{ + { + desc: "nil args", + args: nil, + expected: []zapcore.Field{}, + errLogs: nil, + }, + { + desc: "empty slice of args", + args: []interface{}{}, + expected: []zapcore.Field{}, + errLogs: nil, + }, + { + desc: "just a dangling key", + args: []interface{}{"should ignore"}, + expected: []zapcore.Field{}, + errLogs: []observer.LoggedEntry{ignored("should ignore")}, + }, + { + desc: "well-formed key-value pairs", + args: []interface{}{"foo", 42, "true", "bar"}, + expected: []zapcore.Field{Int("foo", 42), String("true", "bar")}, + errLogs: nil, + }, + { + desc: "just a structured field", + args: []interface{}{Int("foo", 42)}, + expected: []zapcore.Field{Int("foo", 42)}, + errLogs: nil, + }, + { + desc: "structured field and a dangling key", + args: []interface{}{Int("foo", 42), "dangling"}, + expected: []zapcore.Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "structured field and a dangling non-string key", + args: []interface{}{Int("foo", 42), 13}, + expected: []zapcore.Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored(13)}, + }, + { + desc: "key-value pair and a dangling key", + args: []interface{}{"foo", 42, "dangling"}, + expected: []zapcore.Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "pairs, a structured field, and a dangling key", + args: []interface{}{"first", "field", Int("foo", 42), "baz", "quux", "dangling"}, + expected: []zapcore.Field{String("first", "field"), Int("foo", 42), String("baz", "quux")}, + errLogs: []observer.LoggedEntry{ignored("dangling")}, + }, + { + desc: "one non-string key", + args: []interface{}{"foo", 42, true, "bar"}, + expected: []zapcore.Field{Int("foo", 42)}, + errLogs: []observer.LoggedEntry{nonString(invalidPair{2, true, "bar"})}, + }, + { + desc: "pairs, structured fields, non-string keys, and a dangling key", + args: []interface{}{"foo", 42, true, "bar", Int("structure", 11), 42, "reversed", "baz", "quux", "dangling"}, + expected: []zapcore.Field{Int("foo", 42), Int("structure", 11), String("baz", "quux")}, + errLogs: []observer.LoggedEntry{ + ignored("dangling"), + nonString(invalidPair{2, true, "bar"}, invalidPair{5, 42, "reversed"}), + }, + }, + } + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(tt.args...).Info("") + output := logs.AllUntimed() + if len(tt.errLogs) > 0 { + for i := range tt.errLogs { + assert.Equal(t, tt.errLogs[i], output[i], "Unexpected error log at position %d for scenario %s.", i, tt.desc) + } + } + assert.Equal(t, len(tt.errLogs)+1, len(output), "Expected only one non-error message to be logged in scenario %s.", tt.desc) + assert.Equal(t, tt.expected, output[len(tt.errLogs)].Context, "Unexpected message context in scenario %s.", tt.desc) + }) + } +} + +func TestSugarFieldsInvalidPairs(t *testing.T) { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(42, "foo", []string{"bar"}, "baz").Info("") + output := logs.AllUntimed() + + // Double-check that the actual message was logged. + require.Equal(t, 2, len(output), "Unexpected number of entries logged.") + require.Equal(t, observer.LoggedEntry{Context: []zapcore.Field{}}, output[1], "Unexpected non-error log entry.") + + // Assert that the error message's structured fields serialize properly. + require.Equal(t, 1, len(output[0].Context), "Expected one field in error entry context.") + enc := zapcore.NewMapObjectEncoder() + output[0].Context[0].AddTo(enc) + assert.Equal(t, []interface{}{ + map[string]interface{}{"position": int64(0), "key": int64(42), "value": "foo"}, + map[string]interface{}{"position": int64(2), "key": []interface{}{"bar"}, "value": "baz"}, + }, enc.Fields["invalid"], "Unexpected output when logging invalid key-value pairs.") + }) +} + +type stringerF func() string + +func (f stringerF) String() string { return f() } + +func TestSugarStructuredLogging(t *testing.T) { + tests := []struct { + msg string + expectMsg string + }{ + {"foo", "foo"}, + {"", ""}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + extra := []interface{}{"baz", false} + expectedFields := []zapcore.Field{String("foo", "bar"), Bool("baz", false)} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debugw(tt.msg, extra...) + logger.With(context...).Infow(tt.msg, extra...) + logger.With(context...).Warnw(tt.msg, extra...) + logger.With(context...).Errorw(tt.msg, extra...) + logger.With(context...).DPanicw(tt.msg, extra...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expectMsg, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarConcatenatingLogging(t *testing.T) { + tests := []struct { + args []interface{} + expect string + }{ + {[]interface{}{nil}, ""}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + expectedFields := []zapcore.Field{String("foo", "bar")} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debug(tt.args...) + logger.With(context...).Info(tt.args...) + logger.With(context...).Warn(tt.args...) + logger.With(context...).Error(tt.args...) + logger.With(context...).DPanic(tt.args...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expect, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarTemplatedLogging(t *testing.T) { + tests := []struct { + format string + args []interface{} + expect string + }{ + {"", nil, ""}, + {"foo", nil, "foo"}, + // If the user fails to pass a template, degrade to fmt.Sprint. + {"", []interface{}{"foo"}, "foo"}, + } + + // Common to all test cases. + context := []interface{}{"foo", "bar"} + expectedFields := []zapcore.Field{String("foo", "bar")} + + for _, tt := range tests { + withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.With(context...).Debugf(tt.format, tt.args...) + logger.With(context...).Infof(tt.format, tt.args...) + logger.With(context...).Warnf(tt.format, tt.args...) + logger.With(context...).Errorf(tt.format, tt.args...) + logger.With(context...).DPanicf(tt.format, tt.args...) + + expected := make([]observer.LoggedEntry, 5) + for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} { + expected[i] = observer.LoggedEntry{ + Entry: zapcore.Entry{Message: tt.expect, Level: lvl}, + Context: expectedFields, + } + } + assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.") + }) + } +} + +func TestSugarPanicLogging(t *testing.T) { + tests := []struct { + loggerLevel zapcore.Level + f func(*SugaredLogger) + expectedMsg string + }{ + {FatalLevel, func(s *SugaredLogger) { s.Panic("foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panic("foo") }, "foo"}, + {FatalLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panicf("%s", "foo") }, "foo"}, + {FatalLevel, func(s *SugaredLogger) { s.Panicw("foo") }, ""}, + {PanicLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"}, + } + + for _, tt := range tests { + withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) { + assert.Panics(t, func() { tt.f(sugar) }, "Expected panic-level logger calls to panic.") + if tt.expectedMsg != "" { + assert.Equal(t, []observer.LoggedEntry{{ + Context: []zapcore.Field{}, + Entry: zapcore.Entry{Message: tt.expectedMsg, Level: PanicLevel}, + }}, logs.AllUntimed(), "Unexpected log output.") + } else { + assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.") + } + }) + } +} + +func TestSugarFatalLogging(t *testing.T) { + tests := []struct { + loggerLevel zapcore.Level + f func(*SugaredLogger) + expectedMsg string + }{ + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatal("foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatal("foo") }, "foo"}, + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatalf("%s", "foo") }, "foo"}, + {FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw("foo") }, ""}, + {FatalLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"}, + {DebugLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"}, + } + + for _, tt := range tests { + withSugar(t, tt.loggerLevel, nil, func(sugar *SugaredLogger, logs *observer.ObservedLogs) { + stub := exit.WithStub(func() { tt.f(sugar) }) + assert.True(t, stub.Exited, "Expected all calls to fatal logger methods to exit process.") + if tt.expectedMsg != "" { + assert.Equal(t, []observer.LoggedEntry{{ + Context: []zapcore.Field{}, + Entry: zapcore.Entry{Message: tt.expectedMsg, Level: FatalLevel}, + }}, logs.AllUntimed(), "Unexpected log output.") + } else { + assert.Equal(t, 0, logs.Len(), "Didn't expect any log output.") + } + }) + } +} + +func TestSugarAddCaller(t *testing.T) { + tests := []struct { + options []Option + pat string + }{ + {opts(AddCaller()), `.+/sugar_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(-1)), `.+/zap/sugar_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1)), `.+/zap/common_test.go:[\d]+$`}, + {opts(AddCaller(), AddCallerSkip(1), AddCallerSkip(5)), `.+/src/runtime/.*:[\d]+$`}, + } + for _, tt := range tests { + withSugar(t, DebugLevel, tt.options, func(logger *SugaredLogger, logs *observer.ObservedLogs) { + logger.Info("") + output := logs.AllUntimed() + assert.Equal(t, 1, len(output), "Unexpected number of logs written out.") + assert.Regexp( + t, + tt.pat, + output[0].Entry.Caller, + "Expected to find package name and file name in output.", + ) + }) + } +} + +func TestSugarAddCallerFail(t *testing.T) { + errBuf := &zaptest.Buffer{} + withSugar(t, DebugLevel, opts(AddCaller(), AddCallerSkip(1e3), ErrorOutput(errBuf)), func(log *SugaredLogger, logs *observer.ObservedLogs) { + log.Info("Failure.") + assert.Regexp( + t, + `Logger.check error: failed to get caller`, + errBuf.String(), + "Didn't find expected failure message.", + ) + assert.Equal( + t, + logs.AllUntimed()[0].Entry.Message, + "Failure.", + "Expected original message to survive failures in runtime.Caller.") + }) +} diff --git a/vendor/src/go.uber.org/zap/time.go b/vendor/src/go.uber.org/zap/time.go new file mode 100644 index 00000000..c5a1f162 --- /dev/null +++ b/vendor/src/go.uber.org/zap/time.go @@ -0,0 +1,27 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import "time" + +func timeToMillis(t time.Time) int64 { + return t.UnixNano() / int64(time.Millisecond) +} diff --git a/vendor/src/go.uber.org/zap/time_test.go b/vendor/src/go.uber.org/zap/time_test.go new file mode 100644 index 00000000..cb993ab1 --- /dev/null +++ b/vendor/src/go.uber.org/zap/time_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestTimeToMillis(t *testing.T) { + tests := []struct { + t time.Time + stamp int64 + }{ + {t: time.Unix(0, 0), stamp: 0}, + {t: time.Unix(1, 0), stamp: 1000}, + {t: time.Unix(1, int64(500*time.Millisecond)), stamp: 1500}, + } + for _, tt := range tests { + assert.Equal(t, tt.stamp, timeToMillis(tt.t), "Unexpected timestamp for time %v.", tt.t) + } +} diff --git a/vendor/src/go.uber.org/zap/writer.go b/vendor/src/go.uber.org/zap/writer.go new file mode 100644 index 00000000..16f55ce4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/writer.go @@ -0,0 +1,96 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "io/ioutil" + "os" + + "go.uber.org/zap/zapcore" + + "go.uber.org/multierr" +) + +// Open is a high-level wrapper that takes a variadic number of paths, opens or +// creates each of the specified files, and combines them into a locked +// WriteSyncer. It also returns any error encountered and a function to close +// any opened files. +// +// Passing no paths returns a no-op WriteSyncer. The special paths "stdout" and +// "stderr" are interpreted as os.Stdout and os.Stderr, respectively. +func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { + writers, close, err := open(paths) + if err != nil { + return nil, nil, err + } + + writer := CombineWriteSyncers(writers...) + return writer, close, nil +} + +func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { + var openErr error + writers := make([]zapcore.WriteSyncer, 0, len(paths)) + files := make([]*os.File, 0, len(paths)) + close := func() { + for _, f := range files { + f.Close() + } + } + for _, path := range paths { + switch path { + case "stdout": + writers = append(writers, os.Stdout) + // Don't close standard out. + continue + case "stderr": + writers = append(writers, os.Stderr) + // Don't close standard error. + continue + } + f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) + openErr = multierr.Append(openErr, err) + if err == nil { + writers = append(writers, f) + files = append(files, f) + } + } + + if openErr != nil { + close() + return writers, nil, openErr + } + + return writers, close, nil +} + +// CombineWriteSyncers is a utility that combines multiple WriteSyncers into a +// single, locked WriteSyncer. If no inputs are supplied, it returns a no-op +// WriteSyncer. +// +// It's provided purely as a convenience; the result is no different from +// using zapcore.NewMultiWriteSyncer and zapcore.Lock individually. +func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { + if len(writers) == 0 { + return zapcore.AddSync(ioutil.Discard) + } + return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) +} diff --git a/vendor/src/go.uber.org/zap/writer_test.go b/vendor/src/go.uber.org/zap/writer_test.go new file mode 100644 index 00000000..1293832a --- /dev/null +++ b/vendor/src/go.uber.org/zap/writer_test.go @@ -0,0 +1,125 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zap + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" +) + +func TestOpenNoPaths(t *testing.T) { + ws, cleanup, err := Open() + defer cleanup() + + assert.NoError(t, err, "Expected opening no paths to succeed.") + assert.Equal( + t, + zapcore.AddSync(ioutil.Discard), + ws, + "Expected opening no paths to return a no-op WriteSyncer.", + ) +} + +func TestOpen(t *testing.T) { + temp, err := ioutil.TempFile("", "zap-open-test") + require.NoError(t, err, "Couldn't create a temporary file for test.") + defer os.Remove(temp.Name()) + + tests := []struct { + paths []string + filenames []string + error string + }{ + {[]string{"stdout"}, []string{os.Stdout.Name()}, ""}, + {[]string{"stderr"}, []string{os.Stderr.Name()}, ""}, + {[]string{temp.Name()}, []string{temp.Name()}, ""}, + {[]string{"/foo/bar/baz"}, []string{}, "open /foo/bar/baz: no such file or directory"}, + { + paths: []string{"stdout", "/foo/bar/baz", temp.Name(), "/baz/quux"}, + filenames: []string{os.Stdout.Name(), temp.Name()}, + error: "open /foo/bar/baz: no such file or directory; open /baz/quux: no such file or directory", + }, + } + + for _, tt := range tests { + wss, cleanup, err := open(tt.paths) + if err == nil { + defer cleanup() + } + + if tt.error == "" { + assert.NoError(t, err, "Unexpected error opening paths %v.", tt.paths) + } else { + assert.Equal(t, tt.error, err.Error(), "Unexpected error opening paths %v.", tt.paths) + } + names := make([]string, len(wss)) + for i, ws := range wss { + f, ok := ws.(*os.File) + require.True(t, ok, "Expected all WriteSyncers returned from open() to be files.") + names[i] = f.Name() + } + assert.Equal(t, tt.filenames, names, "Opened unexpected files given paths %v.", tt.paths) + } +} + +func TestOpenFails(t *testing.T) { + tests := []struct { + paths []string + }{ + { + paths: []string{"./non-existent-dir/file"}, + }, + { + paths: []string{"stdout", "./non-existent-dir/file"}, + }, + } + + for _, tt := range tests { + _, cleanup, err := Open(tt.paths...) + require.Nil(t, cleanup, "Cleanup function should never be nil") + assert.Error(t, err, "Open with non-existent directory should fail") + } +} + +type testWriter struct { + expected string + t testing.TB +} + +func (w *testWriter) Write(actual []byte) (int, error) { + assert.Equal(w.t, []byte(w.expected), actual, "Unexpected write error.") + return len(actual), nil +} + +func (w *testWriter) Sync() error { + return nil +} + +func TestCombineWriteSyncers(t *testing.T) { + tw := &testWriter{"test", t} + w := CombineWriteSyncers(tw) + w.Write([]byte("test")) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/console_encoder.go b/vendor/src/go.uber.org/zap/zapcore/console_encoder.go new file mode 100644 index 00000000..b7875966 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/console_encoder.go @@ -0,0 +1,147 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "sync" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/internal/bufferpool" +) + +var _sliceEncoderPool = sync.Pool{ + New: func() interface{} { + return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)} + }, +} + +func getSliceEncoder() *sliceArrayEncoder { + return _sliceEncoderPool.Get().(*sliceArrayEncoder) +} + +func putSliceEncoder(e *sliceArrayEncoder) { + e.elems = e.elems[:0] + _sliceEncoderPool.Put(e) +} + +type consoleEncoder struct { + *jsonEncoder +} + +// NewConsoleEncoder creates an encoder whose output is designed for human - +// rather than machine - consumption. It serializes the core log entry data +// (message, level, timestamp, etc.) in a plain-text format and leaves the +// structured context as JSON. +// +// Note that although the console encoder doesn't use the keys specified in the +// encoder configuration, it will omit any element whose key is set to the empty +// string. +func NewConsoleEncoder(cfg EncoderConfig) Encoder { + return consoleEncoder{newJSONEncoder(cfg, true)} +} + +func (c consoleEncoder) Clone() Encoder { + return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)} +} + +func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { + line := bufferpool.Get() + + // We don't want the entry's metadata to be quoted and escaped (if it's + // encoded as strings), which means that we can't use the JSON encoder. The + // simplest option is to use the memory encoder and fmt.Fprint. + // + // If this ever becomes a performance bottleneck, we can implement + // ArrayEncoder for our plain-text format. + arr := getSliceEncoder() + if c.TimeKey != "" && c.EncodeTime != nil { + c.EncodeTime(ent.Time, arr) + } + if c.LevelKey != "" && c.EncodeLevel != nil { + c.EncodeLevel(ent.Level, arr) + } + if ent.LoggerName != "" && c.NameKey != "" { + nameEncoder := c.EncodeName + + if nameEncoder == nil { + // Fall back to FullNameEncoder for backward compatibility. + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, arr) + } + if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil { + c.EncodeCaller(ent.Caller, arr) + } + for i := range arr.elems { + if i > 0 { + line.AppendByte('\t') + } + fmt.Fprint(line, arr.elems[i]) + } + putSliceEncoder(arr) + + // Add the message itself. + if c.MessageKey != "" { + c.addTabIfNecessary(line) + line.AppendString(ent.Message) + } + + // Add any structured context. + c.writeContext(line, fields) + + // If there's no stacktrace key, honor that; this allows users to force + // single-line output. + if ent.Stack != "" && c.StacktraceKey != "" { + line.AppendByte('\n') + line.AppendString(ent.Stack) + } + + if c.LineEnding != "" { + line.AppendString(c.LineEnding) + } else { + line.AppendString(DefaultLineEnding) + } + return line, nil +} + +func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) { + context := c.jsonEncoder.Clone().(*jsonEncoder) + defer context.buf.Free() + + addFields(context, extra) + context.closeOpenNamespaces() + if context.buf.Len() == 0 { + return + } + + c.addTabIfNecessary(line) + line.AppendByte('{') + line.Write(context.buf.Bytes()) + line.AppendByte('}') +} + +func (c consoleEncoder) addTabIfNecessary(line *buffer.Buffer) { + if line.Len() > 0 { + line.AppendByte('\t') + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/console_encoder_bench_test.go b/vendor/src/go.uber.org/zap/zapcore/console_encoder_bench_test.go new file mode 100644 index 00000000..62feaea7 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/console_encoder_bench_test.go @@ -0,0 +1,49 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "testing" + + . "go.uber.org/zap/zapcore" +) + +func BenchmarkZapConsole(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + enc := NewConsoleEncoder(humanEncoderConfig()) + enc.AddString("str", "foo") + enc.AddInt64("int64-1", 1) + enc.AddInt64("int64-2", 2) + enc.AddFloat64("float64", 1.0) + enc.AddString("string1", "\n") + enc.AddString("string2", "💩") + enc.AddString("string3", "🤔") + enc.AddString("string4", "🙊") + enc.AddBool("bool", true) + buf, _ := enc.EncodeEntry(Entry{ + Message: "fake", + Level: DebugLevel, + }, nil) + buf.Free() + } + }) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/core.go b/vendor/src/go.uber.org/zap/zapcore/core.go new file mode 100644 index 00000000..a1ef8b03 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/core.go @@ -0,0 +1,113 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +// Core is a minimal, fast logger interface. It's designed for library authors +// to wrap in a more user-friendly API. +type Core interface { + LevelEnabler + + // With adds structured context to the Core. + With([]Field) Core + // Check determines whether the supplied Entry should be logged (using the + // embedded LevelEnabler and possibly some extra logic). If the entry + // should be logged, the Core adds itself to the CheckedEntry and returns + // the result. + // + // Callers must use Check before calling Write. + Check(Entry, *CheckedEntry) *CheckedEntry + // Write serializes the Entry and any Fields supplied at the log site and + // writes them to their destination. + // + // If called, Write should always log the Entry and Fields; it should not + // replicate the logic of Check. + Write(Entry, []Field) error + // Sync flushes buffered logs (if any). + Sync() error +} + +type nopCore struct{} + +// NewNopCore returns a no-op Core. +func NewNopCore() Core { return nopCore{} } +func (nopCore) Enabled(Level) bool { return false } +func (n nopCore) With([]Field) Core { return n } +func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce } +func (nopCore) Write(Entry, []Field) error { return nil } +func (nopCore) Sync() error { return nil } + +// NewCore creates a Core that writes logs to a WriteSyncer. +func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core { + return &ioCore{ + LevelEnabler: enab, + enc: enc, + out: ws, + } +} + +type ioCore struct { + LevelEnabler + enc Encoder + out WriteSyncer +} + +func (c *ioCore) With(fields []Field) Core { + clone := c.clone() + addFields(clone.enc, fields) + return clone +} + +func (c *ioCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + if c.Enabled(ent.Level) { + return ce.AddCore(ent, c) + } + return ce +} + +func (c *ioCore) Write(ent Entry, fields []Field) error { + buf, err := c.enc.EncodeEntry(ent, fields) + if err != nil { + return err + } + _, err = c.out.Write(buf.Bytes()) + buf.Free() + if err != nil { + return err + } + if ent.Level > ErrorLevel { + // Since we may be crashing the program, sync the output. Ignore Sync + // errors, pending a clean solution to issue #370. + c.Sync() + } + return nil +} + +func (c *ioCore) Sync() error { + return c.out.Sync() +} + +func (c *ioCore) clone() *ioCore { + return &ioCore{ + LevelEnabler: c.LevelEnabler, + enc: c.enc.Clone(), + out: c.out, + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/core_test.go b/vendor/src/go.uber.org/zap/zapcore/core_test.go new file mode 100644 index 00000000..20a4720b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/core_test.go @@ -0,0 +1,163 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "errors" + "io/ioutil" + "os" + "testing" + "time" + + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func makeInt64Field(key string, val int) Field { + return Field{Type: Int64Type, Integer: int64(val), Key: key} +} + +func TestNopCore(t *testing.T) { + entry := Entry{ + Message: "test", + Level: InfoLevel, + Time: time.Now(), + LoggerName: "main", + Stack: "fake-stack", + } + ce := &CheckedEntry{} + + allLevels := []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + DPanicLevel, + PanicLevel, + FatalLevel, + } + core := NewNopCore() + assert.Equal(t, core, core.With([]Field{makeInt64Field("k", 42)}), "Expected no-op With.") + for _, level := range allLevels { + assert.False(t, core.Enabled(level), "Expected all levels to be disabled in no-op core.") + assert.Equal(t, ce, core.Check(entry, ce), "Expected no-op Check to return checked entry unchanged.") + assert.NoError(t, core.Write(entry, nil), "Expected no-op Writes to always succeed.") + assert.NoError(t, core.Sync(), "Expected no-op Syncs to always succeed.") + } +} + +func TestIOCore(t *testing.T) { + temp, err := ioutil.TempFile("", "zapcore-test-iocore") + require.NoError(t, err, "Failed to create temp file.") + defer os.Remove(temp.Name()) + + // Drop timestamps for simpler assertions (timestamp encoding is tested + // elsewhere). + cfg := testEncoderConfig() + cfg.TimeKey = "" + + core := NewCore( + NewJSONEncoder(cfg), + temp, + InfoLevel, + ).With([]Field{makeInt64Field("k", 1)}) + defer assert.NoError(t, core.Sync(), "Expected Syncing a temp file to succeed.") + + if ce := core.Check(Entry{Level: DebugLevel, Message: "debug"}, nil); ce != nil { + ce.Write(makeInt64Field("k", 2)) + } + if ce := core.Check(Entry{Level: InfoLevel, Message: "info"}, nil); ce != nil { + ce.Write(makeInt64Field("k", 3)) + } + if ce := core.Check(Entry{Level: WarnLevel, Message: "warn"}, nil); ce != nil { + ce.Write(makeInt64Field("k", 4)) + } + + logged, err := ioutil.ReadFile(temp.Name()) + require.NoError(t, err, "Failed to read from temp file.") + require.Equal( + t, + `{"level":"info","msg":"info","k":1,"k":3}`+"\n"+ + `{"level":"warn","msg":"warn","k":1,"k":4}`+"\n", + string(logged), + "Unexpected log output.", + ) +} + +func TestIOCoreSyncFail(t *testing.T) { + sink := &zaptest.Discarder{} + err := errors.New("failed") + sink.SetError(err) + + core := NewCore( + NewJSONEncoder(testEncoderConfig()), + sink, + DebugLevel, + ) + + assert.Equal( + t, + err, + core.Sync(), + "Expected core.Sync to return errors from underlying WriteSyncer.", + ) +} + +func TestIOCoreSyncsOutput(t *testing.T) { + tests := []struct { + entry Entry + shouldSync bool + }{ + {Entry{Level: DebugLevel}, false}, + {Entry{Level: InfoLevel}, false}, + {Entry{Level: WarnLevel}, false}, + {Entry{Level: ErrorLevel}, false}, + {Entry{Level: DPanicLevel}, true}, + {Entry{Level: PanicLevel}, true}, + {Entry{Level: FatalLevel}, true}, + } + + for _, tt := range tests { + sink := &zaptest.Discarder{} + core := NewCore( + NewJSONEncoder(testEncoderConfig()), + sink, + DebugLevel, + ) + + core.Write(tt.entry, nil) + assert.Equal(t, tt.shouldSync, sink.Called(), "Incorrect Sync behavior.") + } +} + +func TestIOCoreWriteFailure(t *testing.T) { + core := NewCore( + NewJSONEncoder(testEncoderConfig()), + Lock(&zaptest.FailWriter{}), + DebugLevel, + ) + err := core.Write(Entry{}, nil) + // Should log the error. + assert.Error(t, err, "Expected writing Entry to fail.") +} diff --git a/vendor/src/go.uber.org/zap/zapcore/doc.go b/vendor/src/go.uber.org/zap/zapcore/doc.go new file mode 100644 index 00000000..31000e91 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/doc.go @@ -0,0 +1,24 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zapcore defines and implements the low-level interfaces upon which +// zap is built. By providing alternate implementations of these interfaces, +// external packages can extend zap's capabilities. +package zapcore // import "go.uber.org/zap/zapcore" diff --git a/vendor/src/go.uber.org/zap/zapcore/encoder.go b/vendor/src/go.uber.org/zap/zapcore/encoder.go new file mode 100644 index 00000000..f0509522 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/encoder.go @@ -0,0 +1,348 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "time" + + "go.uber.org/zap/buffer" +) + +// DefaultLineEnding defines the default line ending when writing logs. +// Alternate line endings specified in EncoderConfig can override this +// behavior. +const DefaultLineEnding = "\n" + +// A LevelEncoder serializes a Level to a primitive type. +type LevelEncoder func(Level, PrimitiveArrayEncoder) + +// LowercaseLevelEncoder serializes a Level to a lowercase string. For example, +// InfoLevel is serialized to "info". +func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + enc.AppendString(l.String()) +} + +// LowercaseColorLevelEncoder serializes a Level to a lowercase string and adds coloring. +// For example, InfoLevel is serialized to "info" and colored blue. +func LowercaseColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + s, ok := _levelToLowercaseColorString[l] + if !ok { + s = _unknownLevelColor.Add(l.String()) + } + enc.AppendString(s) +} + +// CapitalLevelEncoder serializes a Level to an all-caps string. For example, +// InfoLevel is serialized to "INFO". +func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + enc.AppendString(l.CapitalString()) +} + +// CapitalColorLevelEncoder serializes a Level to an all-caps string and adds color. +// For example, InfoLevel is serialized to "INFO" and colored blue. +func CapitalColorLevelEncoder(l Level, enc PrimitiveArrayEncoder) { + s, ok := _levelToCapitalColorString[l] + if !ok { + s = _unknownLevelColor.Add(l.CapitalString()) + } + enc.AppendString(s) +} + +// UnmarshalText unmarshals text to a LevelEncoder. "capital" is unmarshaled to +// CapitalLevelEncoder, "coloredCapital" is unmarshaled to CapitalColorLevelEncoder, +// "colored" is unmarshaled to LowercaseColorLevelEncoder, and anything else +// is unmarshaled to LowercaseLevelEncoder. +func (e *LevelEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "capital": + *e = CapitalLevelEncoder + case "capitalColor": + *e = CapitalColorLevelEncoder + case "color": + *e = LowercaseColorLevelEncoder + default: + *e = LowercaseLevelEncoder + } + return nil +} + +// A TimeEncoder serializes a time.Time to a primitive type. +type TimeEncoder func(time.Time, PrimitiveArrayEncoder) + +// EpochTimeEncoder serializes a time.Time to a floating-point number of seconds +// since the Unix epoch. +func EpochTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + nanos := t.UnixNano() + sec := float64(nanos) / float64(time.Second) + enc.AppendFloat64(sec) +} + +// EpochMillisTimeEncoder serializes a time.Time to a floating-point number of +// milliseconds since the Unix epoch. +func EpochMillisTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + nanos := t.UnixNano() + millis := float64(nanos) / float64(time.Millisecond) + enc.AppendFloat64(millis) +} + +// EpochNanosTimeEncoder serializes a time.Time to an integer number of +// nanoseconds since the Unix epoch. +func EpochNanosTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + enc.AppendInt64(t.UnixNano()) +} + +// ISO8601TimeEncoder serializes a time.Time to an ISO8601-formatted string +// with millisecond precision. +func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02T15:04:05.000Z0700")) +} + +// UnmarshalText unmarshals text to a TimeEncoder. "iso8601" and "ISO8601" are +// unmarshaled to ISO8601TimeEncoder, "millis" is unmarshaled to +// EpochMillisTimeEncoder, and anything else is unmarshaled to EpochTimeEncoder. +func (e *TimeEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "iso8601", "ISO8601": + *e = ISO8601TimeEncoder + case "millis": + *e = EpochMillisTimeEncoder + case "nanos": + *e = EpochNanosTimeEncoder + default: + *e = EpochTimeEncoder + } + return nil +} + +// A DurationEncoder serializes a time.Duration to a primitive type. +type DurationEncoder func(time.Duration, PrimitiveArrayEncoder) + +// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed. +func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendFloat64(float64(d) / float64(time.Second)) +} + +// NanosDurationEncoder serializes a time.Duration to an integer number of +// nanoseconds elapsed. +func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendInt64(int64(d)) +} + +// StringDurationEncoder serializes a time.Duration using its built-in String +// method. +func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) { + enc.AppendString(d.String()) +} + +// UnmarshalText unmarshals text to a DurationEncoder. "string" is unmarshaled +// to StringDurationEncoder, and anything else is unmarshaled to +// NanosDurationEncoder. +func (e *DurationEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "string": + *e = StringDurationEncoder + case "nanos": + *e = NanosDurationEncoder + default: + *e = SecondsDurationEncoder + } + return nil +} + +// A CallerEncoder serializes an EntryCaller to a primitive type. +type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder) + +// FullCallerEncoder serializes a caller in /full/path/to/package/file:line +// format. +func FullCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { + // TODO: consider using a byte-oriented API to save an allocation. + enc.AppendString(caller.String()) +} + +// ShortCallerEncoder serializes a caller in package/file:line format, trimming +// all but the final directory from the full path. +func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) { + // TODO: consider using a byte-oriented API to save an allocation. + enc.AppendString(caller.TrimmedPath()) +} + +// UnmarshalText unmarshals text to a CallerEncoder. "full" is unmarshaled to +// FullCallerEncoder and anything else is unmarshaled to ShortCallerEncoder. +func (e *CallerEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "full": + *e = FullCallerEncoder + default: + *e = ShortCallerEncoder + } + return nil +} + +// A NameEncoder serializes a period-separated logger name to a primitive +// type. +type NameEncoder func(string, PrimitiveArrayEncoder) + +// FullNameEncoder serializes the logger name as-is. +func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(loggerName) +} + +// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is +// unmarshaled to FullNameEncoder. +func (e *NameEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "full": + *e = FullNameEncoder + default: + *e = FullNameEncoder + } + return nil +} + +// An EncoderConfig allows users to configure the concrete encoders supplied by +// zapcore. +type EncoderConfig struct { + // Set the keys used for each log entry. If any key is empty, that portion + // of the entry is omitted. + MessageKey string `json:"messageKey" yaml:"messageKey"` + LevelKey string `json:"levelKey" yaml:"levelKey"` + TimeKey string `json:"timeKey" yaml:"timeKey"` + NameKey string `json:"nameKey" yaml:"nameKey"` + CallerKey string `json:"callerKey" yaml:"callerKey"` + StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"` + LineEnding string `json:"lineEnding" yaml:"lineEnding"` + // Configure the primitive representations of common complex types. For + // example, some users may want all time.Times serialized as floating-point + // seconds since epoch, while others may prefer ISO8601 strings. + EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` + EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` + EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` + EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` + // Unlike the other primitive type encoders, EncodeName is optional. The + // zero value falls back to FullNameEncoder. + EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` +} + +// ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a +// map- or struct-like object to the logging context. Like maps, ObjectEncoders +// aren't safe for concurrent use (though typical use shouldn't require locks). +type ObjectEncoder interface { + // Logging-specific marshalers. + AddArray(key string, marshaler ArrayMarshaler) error + AddObject(key string, marshaler ObjectMarshaler) error + + // Built-in types. + AddBinary(key string, value []byte) // for arbitrary bytes + AddByteString(key string, value []byte) // for UTF-8 encoded bytes + AddBool(key string, value bool) + AddComplex128(key string, value complex128) + AddComplex64(key string, value complex64) + AddDuration(key string, value time.Duration) + AddFloat64(key string, value float64) + AddFloat32(key string, value float32) + AddInt(key string, value int) + AddInt64(key string, value int64) + AddInt32(key string, value int32) + AddInt16(key string, value int16) + AddInt8(key string, value int8) + AddString(key, value string) + AddTime(key string, value time.Time) + AddUint(key string, value uint) + AddUint64(key string, value uint64) + AddUint32(key string, value uint32) + AddUint16(key string, value uint16) + AddUint8(key string, value uint8) + AddUintptr(key string, value uintptr) + + // AddReflected uses reflection to serialize arbitrary objects, so it's slow + // and allocation-heavy. + AddReflected(key string, value interface{}) error + // OpenNamespace opens an isolated namespace where all subsequent fields will + // be added. Applications can use namespaces to prevent key collisions when + // injecting loggers into sub-components or third-party libraries. + OpenNamespace(key string) +} + +// ArrayEncoder is a strongly-typed, encoding-agnostic interface for adding +// array-like objects to the logging context. Of note, it supports mixed-type +// arrays even though they aren't typical in Go. Like slices, ArrayEncoders +// aren't safe for concurrent use (though typical use shouldn't require locks). +type ArrayEncoder interface { + // Built-in types. + PrimitiveArrayEncoder + + // Time-related types. + AppendDuration(time.Duration) + AppendTime(time.Time) + + // Logging-specific marshalers. + AppendArray(ArrayMarshaler) error + AppendObject(ObjectMarshaler) error + + // AppendReflected uses reflection to serialize arbitrary objects, so it's + // slow and allocation-heavy. + AppendReflected(value interface{}) error +} + +// PrimitiveArrayEncoder is the subset of the ArrayEncoder interface that deals +// only in Go's built-in types. It's included only so that Duration- and +// TimeEncoders cannot trigger infinite recursion. +type PrimitiveArrayEncoder interface { + // Built-in types. + AppendBool(bool) + AppendByteString([]byte) // for UTF-8 encoded bytes + AppendComplex128(complex128) + AppendComplex64(complex64) + AppendFloat64(float64) + AppendFloat32(float32) + AppendInt(int) + AppendInt64(int64) + AppendInt32(int32) + AppendInt16(int16) + AppendInt8(int8) + AppendString(string) + AppendUint(uint) + AppendUint64(uint64) + AppendUint32(uint32) + AppendUint16(uint16) + AppendUint8(uint8) + AppendUintptr(uintptr) +} + +// Encoder is a format-agnostic interface for all log entry marshalers. Since +// log encoders don't need to support the same wide range of use cases as +// general-purpose marshalers, it's possible to make them faster and +// lower-allocation. +// +// Implementations of the ObjectEncoder interface's methods can, of course, +// freely modify the receiver. However, the Clone and EncodeEntry methods will +// be called concurrently and shouldn't modify the receiver. +type Encoder interface { + ObjectEncoder + + // Clone copies the encoder, ensuring that adding fields to the copy doesn't + // affect the original. + Clone() Encoder + + // EncodeEntry encodes an entry and fields, along with any accumulated + // context, into a byte buffer and returns it. + EncodeEntry(Entry, []Field) (*buffer.Buffer, error) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/encoder_test.go b/vendor/src/go.uber.org/zap/zapcore/encoder_test.go new file mode 100644 index 00000000..04641678 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/encoder_test.go @@ -0,0 +1,636 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + . "go.uber.org/zap/zapcore" +) + +var ( + _epoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + _testEntry = Entry{ + LoggerName: "main", + Level: InfoLevel, + Message: `hello`, + Time: _epoch, + Stack: "fake-stack", + Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42}, + } +) + +func testEncoderConfig() EncoderConfig { + return EncoderConfig{ + MessageKey: "msg", + LevelKey: "level", + NameKey: "name", + TimeKey: "ts", + CallerKey: "caller", + StacktraceKey: "stacktrace", + LineEnding: "\n", + EncodeTime: EpochTimeEncoder, + EncodeLevel: LowercaseLevelEncoder, + EncodeDuration: SecondsDurationEncoder, + EncodeCaller: ShortCallerEncoder, + } +} + +func humanEncoderConfig() EncoderConfig { + cfg := testEncoderConfig() + cfg.EncodeTime = ISO8601TimeEncoder + cfg.EncodeLevel = CapitalLevelEncoder + cfg.EncodeDuration = StringDurationEncoder + return cfg +} + +func withJSONEncoder(f func(Encoder)) { + f(NewJSONEncoder(testEncoderConfig())) +} + +func withConsoleEncoder(f func(Encoder)) { + f(NewConsoleEncoder(humanEncoderConfig())) +} + +func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(strings.ToUpper(loggerName)) +} + +func TestEncoderConfiguration(t *testing.T) { + base := testEncoderConfig() + + tests := []struct { + desc string + cfg EncoderConfig + amendEntry func(Entry) Entry + extra func(Encoder) + expectedJSON string + expectedConsole string + }{ + { + desc: "messages to be escaped", + cfg: base, + amendEntry: func(ent Entry) Entry { + ent.Message = `hello\` + return ent + }, + expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","msg":"hello\\","stacktrace":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\\\nfake-stack\n", + }, + { + desc: "use custom entry keys in JSON output and ignore them in console output", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "skip level if LevelKey is omitted", + cfg: EncoderConfig{ + LevelKey: "", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "skip timestamp if TimeKey is omitted", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "skip message if MessageKey is omitted", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack\n", + }, + { + desc: "skip name if NameKey is omitted", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "skip caller if CallerKey is omitted", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", + }, + { + desc: "skip stacktrace if StacktraceKey is omitted", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n", + }, + { + desc: "use the supplied EncodeTime, for both the entry and any times added", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) }, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + extra: func(enc Encoder) { + enc.AddTime("extra", _epoch) + enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error { + enc.AppendTime(_epoch) + return nil + })) + }, + expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n", + expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\thello\t" + // plain-text preamble + `{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` + // JSON context + "\nfake-stack\n", // stacktrace after newline + }, + { + desc: "use the supplied EncodeDuration for any durations added", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: StringDurationEncoder, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + extra: func(enc Encoder) { + enc.AddDuration("extra", time.Second) + enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error { + enc.AppendDuration(time.Minute) + return nil + })) + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + // preamble + `{"extra": "1s", "extras": ["1m0s"]}` + // context + "\nfake-stack\n", // stacktrace + }, + { + desc: "use the supplied EncodeLevel", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: CapitalLevelEncoder, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "use the supplied EncodeName", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeName: capitalNameEncoder, + }, + expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "close all open namespaces", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + extra: func(enc Encoder) { + enc.OpenNamespace("outer") + enc.OpenNamespace("inner") + enc.AddString("foo", "bar") + enc.OpenNamespace("innermost") + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + + `{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` + + "\nfake-stack\n", + }, + { + desc: "handle no-op EncodeTime", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(time.Time, PrimitiveArrayEncoder) {}, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n", + expectedConsole: "info\tmain\tfoo.go:42\thello\t" + `{"sometime": 100}` + "\nfake-stack\n", + }, + { + desc: "handle no-op EncodeDuration", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {}, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n", + }, + { + desc: "handle no-op EncodeLevel", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: func(Level, PrimitiveArrayEncoder) {}, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "handle no-op EncodeCaller", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {}, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", + }, + { + desc: "handle no-op EncodeName", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeName: func(string, PrimitiveArrayEncoder) {}, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", + }, + { + desc: "use custom line separator", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: "\r\n", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\r\n", + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\r\n", + }, + { + desc: "omit line separator definition - fall back to default", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + DefaultLineEnding, + expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack" + DefaultLineEnding, + }, + } + + for i, tt := range tests { + json := NewJSONEncoder(tt.cfg) + console := NewConsoleEncoder(tt.cfg) + if tt.extra != nil { + tt.extra(json) + tt.extra(console) + } + entry := _testEntry + if tt.amendEntry != nil { + entry = tt.amendEntry(_testEntry) + } + jsonOut, jsonErr := json.EncodeEntry(entry, nil) + if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) { + assert.Equal( + t, + tt.expectedJSON, + jsonOut.String(), + "Unexpected JSON output: expected to %v.", tt.desc, + ) + } + consoleOut, consoleErr := console.EncodeEntry(entry, nil) + if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) { + assert.Equal( + t, + tt.expectedConsole, + consoleOut.String(), + "Unexpected console output: expected to %v.", tt.desc, + ) + } + } +} + +func TestLevelEncoders(t *testing.T) { + tests := []struct { + name string + expected interface{} // output of encoding InfoLevel + }{ + {"capital", "INFO"}, + {"lower", "info"}, + {"", "info"}, + {"something-random", "info"}, + } + + for _, tt := range tests { + var le LevelEncoder + require.NoError(t, le.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { le(InfoLevel, arr) }, + "Unexpected output serializing InfoLevel with %q.", tt.name, + ) + } +} + +func TestTimeEncoders(t *testing.T) { + moment := time.Unix(100, 50005000).UTC() + tests := []struct { + name string + expected interface{} // output of serializing moment + }{ + {"iso8601", "1970-01-01T00:01:40.050Z"}, + {"ISO8601", "1970-01-01T00:01:40.050Z"}, + {"millis", 100050.005}, + {"nanos", int64(100050005000)}, + {"", 100.050005}, + {"something-random", 100.050005}, + } + + for _, tt := range tests { + var te TimeEncoder + require.NoError(t, te.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { te(moment, arr) }, + "Unexpected output serializing %v with %q.", moment, tt.name, + ) + } +} + +func TestDurationEncoders(t *testing.T) { + elapsed := time.Second + 500*time.Nanosecond + tests := []struct { + name string + expected interface{} // output of serializing elapsed + }{ + {"string", "1.0000005s"}, + {"nanos", int64(1000000500)}, + {"", 1.0000005}, + {"something-random", 1.0000005}, + } + + for _, tt := range tests { + var de DurationEncoder + require.NoError(t, de.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { de(elapsed, arr) }, + "Unexpected output serializing %v with %q.", elapsed, tt.name, + ) + } +} + +func TestCallerEncoders(t *testing.T) { + caller := EntryCaller{Defined: true, File: "/home/jack/src/github.com/foo/foo.go", Line: 42} + tests := []struct { + name string + expected interface{} // output of serializing caller + }{ + {"", "foo/foo.go:42"}, + {"something-random", "foo/foo.go:42"}, + {"short", "foo/foo.go:42"}, + {"full", "/home/jack/src/github.com/foo/foo.go:42"}, + } + + for _, tt := range tests { + var ce CallerEncoder + require.NoError(t, ce.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { ce(caller, arr) }, + "Unexpected output serializing file name as %v with %q.", tt.expected, tt.name, + ) + } +} + +func TestNameEncoders(t *testing.T) { + tests := []struct { + name string + expected interface{} // output of encoding InfoLevel + }{ + {"", "main"}, + {"full", "main"}, + {"something-random", "main"}, + } + + for _, tt := range tests { + var ne NameEncoder + require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { ne("main", arr) }, + "Unexpected output serializing logger name with %q.", tt.name, + ) + } +} + +func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) { + mem := NewMapObjectEncoder() + mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error { + f(arr) + return nil + })) + arr := mem.Fields["k"].([]interface{}) + require.Equal(t, 1, len(arr), "Expected to append exactly one element to array.") + assert.Equal(t, expected, arr[0], msgAndArgs...) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/entry.go b/vendor/src/go.uber.org/zap/zapcore/entry.go new file mode 100644 index 00000000..7d9893f3 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/entry.go @@ -0,0 +1,257 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "strings" + "sync" + "time" + + "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/exit" + + "go.uber.org/multierr" +) + +var ( + _cePool = sync.Pool{New: func() interface{} { + // Pre-allocate some space for cores. + return &CheckedEntry{ + cores: make([]Core, 4), + } + }} +) + +func getCheckedEntry() *CheckedEntry { + ce := _cePool.Get().(*CheckedEntry) + ce.reset() + return ce +} + +func putCheckedEntry(ce *CheckedEntry) { + if ce == nil { + return + } + _cePool.Put(ce) +} + +// NewEntryCaller makes an EntryCaller from the return signature of +// runtime.Caller. +func NewEntryCaller(pc uintptr, file string, line int, ok bool) EntryCaller { + if !ok { + return EntryCaller{} + } + return EntryCaller{ + PC: pc, + File: file, + Line: line, + Defined: true, + } +} + +// EntryCaller represents the caller of a logging function. +type EntryCaller struct { + Defined bool + PC uintptr + File string + Line int +} + +// String returns the full path and line number of the caller. +func (ec EntryCaller) String() string { + return ec.FullPath() +} + +// FullPath returns a /full/path/to/package/file:line description of the +// caller. +func (ec EntryCaller) FullPath() string { + if !ec.Defined { + return "undefined" + } + buf := bufferpool.Get() + buf.AppendString(ec.File) + buf.AppendByte(':') + buf.AppendInt(int64(ec.Line)) + caller := buf.String() + buf.Free() + return caller +} + +// TrimmedPath returns a package/file:line description of the caller, +// preserving only the leaf directory name and file name. +func (ec EntryCaller) TrimmedPath() string { + if !ec.Defined { + return "undefined" + } + // nb. To make sure we trim the path correctly on Windows too, we + // counter-intuitively need to use '/' and *not* os.PathSeparator here, + // because the path given originates from Go stdlib, specifically + // runtime.Caller() which (as of Mar/17) returns forward slashes even on + // Windows. + // + // See https://github.com/golang/go/issues/3335 + // and https://github.com/golang/go/issues/18151 + // + // for discussion on the issue on Go side. + // + // Find the last separator. + // + idx := strings.LastIndexByte(ec.File, '/') + if idx == -1 { + return ec.FullPath() + } + // Find the penultimate separator. + idx = strings.LastIndexByte(ec.File[:idx], '/') + if idx == -1 { + return ec.FullPath() + } + buf := bufferpool.Get() + // Keep everything after the penultimate separator. + buf.AppendString(ec.File[idx+1:]) + buf.AppendByte(':') + buf.AppendInt(int64(ec.Line)) + caller := buf.String() + buf.Free() + return caller +} + +// An Entry represents a complete log message. The entry's structured context +// is already serialized, but the log level, time, message, and call site +// information are available for inspection and modification. +// +// Entries are pooled, so any functions that accept them MUST be careful not to +// retain references to them. +type Entry struct { + Level Level + Time time.Time + LoggerName string + Message string + Caller EntryCaller + Stack string +} + +// CheckWriteAction indicates what action to take after a log entry is +// processed. Actions are ordered in increasing severity. +type CheckWriteAction uint8 + +const ( + // WriteThenNoop indicates that nothing special needs to be done. It's the + // default behavior. + WriteThenNoop CheckWriteAction = iota + // WriteThenPanic causes a panic after Write. + WriteThenPanic + // WriteThenFatal causes a fatal os.Exit after Write. + WriteThenFatal +) + +// CheckedEntry is an Entry together with a collection of Cores that have +// already agreed to log it. +// +// CheckedEntry references should be created by calling AddCore or Should on a +// nil *CheckedEntry. References are returned to a pool after Write, and MUST +// NOT be retained after calling their Write method. +type CheckedEntry struct { + Entry + ErrorOutput WriteSyncer + dirty bool // best-effort detection of pool misuse + should CheckWriteAction + cores []Core +} + +func (ce *CheckedEntry) reset() { + ce.Entry = Entry{} + ce.ErrorOutput = nil + ce.dirty = false + ce.should = WriteThenNoop + for i := range ce.cores { + // don't keep references to cores + ce.cores[i] = nil + } + ce.cores = ce.cores[:0] +} + +// Write writes the entry to the stored Cores, returns any errors, and returns +// the CheckedEntry reference to a pool for immediate re-use. Finally, it +// executes any required CheckWriteAction. +func (ce *CheckedEntry) Write(fields ...Field) { + if ce == nil { + return + } + + if ce.dirty { + if ce.ErrorOutput != nil { + // Make a best effort to detect unsafe re-use of this CheckedEntry. + // If the entry is dirty, log an internal error; because the + // CheckedEntry is being used after it was returned to the pool, + // the message may be an amalgamation from multiple call sites. + fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", time.Now(), ce.Entry) + ce.ErrorOutput.Sync() + } + return + } + ce.dirty = true + + var err error + for i := range ce.cores { + err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) + } + if ce.ErrorOutput != nil { + if err != nil { + fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", time.Now(), err) + ce.ErrorOutput.Sync() + } + } + + should, msg := ce.should, ce.Message + putCheckedEntry(ce) + + switch should { + case WriteThenPanic: + panic(msg) + case WriteThenFatal: + exit.Exit() + } +} + +// AddCore adds a Core that has agreed to log this CheckedEntry. It's intended to be +// used by Core.Check implementations, and is safe to call on nil CheckedEntry +// references. +func (ce *CheckedEntry) AddCore(ent Entry, core Core) *CheckedEntry { + if ce == nil { + ce = getCheckedEntry() + ce.Entry = ent + } + ce.cores = append(ce.cores, core) + return ce +} + +// Should sets this CheckedEntry's CheckWriteAction, which controls whether a +// Core will panic or fatal after writing this log entry. Like AddCore, it's +// safe to call on nil CheckedEntry references. +func (ce *CheckedEntry) Should(ent Entry, should CheckWriteAction) *CheckedEntry { + if ce == nil { + ce = getCheckedEntry() + ce.Entry = ent + } + ce.should = should + return ce +} diff --git a/vendor/src/go.uber.org/zap/zapcore/entry_test.go b/vendor/src/go.uber.org/zap/zapcore/entry_test.go new file mode 100644 index 00000000..569c4e1e --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/entry_test.go @@ -0,0 +1,107 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "sync" + "testing" + + "go.uber.org/zap/internal/exit" + + "github.com/stretchr/testify/assert" +) + +func TestPutNilEntry(t *testing.T) { + // Pooling nil entries defeats the purpose. + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + putCheckedEntry(nil) + } + }() + + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + ce := getCheckedEntry() + assert.NotNil(t, ce, "Expected only non-nil CheckedEntries in pool.") + assert.False(t, ce.dirty, "Unexpected dirty bit set.") + assert.Nil(t, ce.ErrorOutput, "Non-nil ErrorOutput.") + assert.Equal(t, WriteThenNoop, ce.should, "Unexpected terminal behavior.") + assert.Equal(t, 0, len(ce.cores), "Expected empty slice of cores.") + assert.True(t, cap(ce.cores) > 0, "Expected pooled CheckedEntries to pre-allocate slice of Cores.") + } + }() + + wg.Wait() +} + +func TestEntryCaller(t *testing.T) { + tests := []struct { + caller EntryCaller + full string + short string + }{ + { + caller: NewEntryCaller(100, "/path/to/foo.go", 42, false), + full: "undefined", + short: "undefined", + }, + { + caller: NewEntryCaller(100, "/path/to/foo.go", 42, true), + full: "/path/to/foo.go:42", + short: "to/foo.go:42", + }, + { + caller: NewEntryCaller(100, "to/foo.go", 42, true), + full: "to/foo.go:42", + short: "to/foo.go:42", + }, + } + + for _, tt := range tests { + assert.Equal(t, tt.full, tt.caller.String(), "Unexpected string from EntryCaller.") + assert.Equal(t, tt.full, tt.caller.FullPath(), "Unexpected FullPath from EntryCaller.") + assert.Equal(t, tt.short, tt.caller.TrimmedPath(), "Unexpected TrimmedPath from EntryCaller.") + } +} + +func TestCheckedEntryWrite(t *testing.T) { + // Nil checked entries are safe. + var ce *CheckedEntry + assert.NotPanics(t, func() { ce.Write() }, "Unexpected panic writing nil CheckedEntry.") + + // WriteThenPanic + ce = ce.Should(Entry{}, WriteThenPanic) + assert.Panics(t, func() { ce.Write() }, "Expected to panic when WriteThenPanic is set.") + ce.reset() + + // WriteThenFatal + ce = ce.Should(Entry{}, WriteThenFatal) + stub := exit.WithStub(func() { + ce.Write() + }) + assert.True(t, stub.Exited, "Expected to exit when WriteThenFatal is set.") + ce.reset() +} diff --git a/vendor/src/go.uber.org/zap/zapcore/error.go b/vendor/src/go.uber.org/zap/zapcore/error.go new file mode 100644 index 00000000..a67c7bac --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/error.go @@ -0,0 +1,120 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "fmt" + "sync" +) + +// Encodes the given error into fields of an object. A field with the given +// name is added for the error message. +// +// If the error implements fmt.Formatter, a field with the name ${key}Verbose +// is also added with the full verbose error message. +// +// Finally, if the error implements errorGroup (from go.uber.org/multierr) or +// causer (from github.com/pkg/errors), a ${key}Causes field is added with an +// array of objects containing the errors this error was comprised of. +// +// { +// "error": err.Error(), +// "errorVerbose": fmt.Sprintf("%+v", err), +// "errorCauses": [ +// ... +// ], +// } +func encodeError(key string, err error, enc ObjectEncoder) error { + basic := err.Error() + enc.AddString(key, basic) + + switch e := err.(type) { + case errorGroup: + return enc.AddArray(key+"Causes", errArray(e.Errors())) + case fmt.Formatter: + verbose := fmt.Sprintf("%+v", e) + if verbose != basic { + // This is a rich error type, like those produced by + // github.com/pkg/errors. + enc.AddString(key+"Verbose", verbose) + } + } + return nil +} + +type errorGroup interface { + // Provides read-only access to the underlying list of errors, preferably + // without causing any allocs. + Errors() []error +} + +type causer interface { + // Provides access to the error that caused this error. + Cause() error +} + +// Note that errArry and errArrayElem are very similar to the version +// implemented in the top-level error.go file. We can't re-use this because +// that would require exporting errArray as part of the zapcore API. + +// Encodes a list of errors using the standard error encoding logic. +type errArray []error + +func (errs errArray) MarshalLogArray(arr ArrayEncoder) error { + for i := range errs { + if errs[i] == nil { + continue + } + + el := newErrArrayElem(errs[i]) + arr.AppendObject(el) + el.Free() + } + return nil +} + +var _errArrayElemPool = sync.Pool{New: func() interface{} { + return &errArrayElem{} +}} + +// Encodes any error into a {"error": ...} re-using the same errors logic. +// +// May be passed in place of an array to build a single-element array. +type errArrayElem struct{ err error } + +func newErrArrayElem(err error) *errArrayElem { + e := _errArrayElemPool.Get().(*errArrayElem) + e.err = err + return e +} + +func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error { + return arr.AppendObject(e) +} + +func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error { + return encodeError("error", e.err, enc) +} + +func (e *errArrayElem) Free() { + e.err = nil + _errArrayElemPool.Put(e) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/error_test.go b/vendor/src/go.uber.org/zap/zapcore/error_test.go new file mode 100644 index 00000000..900f5202 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/error_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "errors" + "fmt" + "io" + "testing" + + richErrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + + "go.uber.org/multierr" + . "go.uber.org/zap/zapcore" +) + +type errTooManyUsers int + +func (e errTooManyUsers) Error() string { + return fmt.Sprintf("%d too many users", int(e)) +} + +func (e errTooManyUsers) Format(s fmt.State, verb rune) { + // Implement fmt.Formatter, but don't add any information beyond the basic + // Error method. + if verb == 'v' && s.Flag('+') { + io.WriteString(s, e.Error()) + } +} + +type customMultierr struct{} + +func (e customMultierr) Error() string { + return "great sadness" +} + +func (e customMultierr) Errors() []error { + return []error{ + errors.New("foo"), + nil, + multierr.Append( + errors.New("bar"), + errors.New("baz"), + ), + } +} + +func TestErrorEncoding(t *testing.T) { + tests := []struct { + k string + t FieldType // defaults to ErrorType + iface interface{} + want map[string]interface{} + }{ + { + k: "k", + iface: errTooManyUsers(2), + want: map[string]interface{}{ + "k": "2 too many users", + }, + }, + { + k: "err", + iface: multierr.Combine( + errors.New("foo"), + errors.New("bar"), + errors.New("baz"), + ), + want: map[string]interface{}{ + "err": "foo; bar; baz", + "errCauses": []interface{}{ + map[string]interface{}{"error": "foo"}, + map[string]interface{}{"error": "bar"}, + map[string]interface{}{"error": "baz"}, + }, + }, + }, + { + k: "e", + iface: customMultierr{}, + want: map[string]interface{}{ + "e": "great sadness", + "eCauses": []interface{}{ + map[string]interface{}{"error": "foo"}, + map[string]interface{}{ + "error": "bar; baz", + "errorCauses": []interface{}{ + map[string]interface{}{"error": "bar"}, + map[string]interface{}{"error": "baz"}, + }, + }, + }, + }, + }, + { + k: "k", + iface: richErrors.WithMessage(errors.New("egad"), "failed"), + want: map[string]interface{}{ + "k": "failed: egad", + "kVerbose": "egad\nfailed", + }, + }, + { + k: "error", + iface: multierr.Combine( + richErrors.WithMessage( + multierr.Combine(errors.New("foo"), errors.New("bar")), + "hello", + ), + errors.New("baz"), + richErrors.WithMessage(errors.New("qux"), "world"), + ), + want: map[string]interface{}{ + "error": "hello: foo; bar; baz; world: qux", + "errorCauses": []interface{}{ + map[string]interface{}{ + "error": "hello: foo; bar", + "errorVerbose": "the following errors occurred:\n" + + " - foo\n" + + " - bar\n" + + "hello", + }, + map[string]interface{}{"error": "baz"}, + map[string]interface{}{"error": "world: qux", "errorVerbose": "qux\nworld"}, + }, + }, + }, + } + + for _, tt := range tests { + if tt.t == UnknownType { + tt.t = ErrorType + } + + enc := NewMapObjectEncoder() + f := Field{Key: tt.k, Type: tt.t, Interface: tt.iface} + f.AddTo(enc) + assert.Equal(t, tt.want, enc.Fields, "Unexpected output from field %+v.", f) + } +} + +func TestRichErrorSupport(t *testing.T) { + f := Field{ + Type: ErrorType, + Interface: richErrors.WithMessage(richErrors.New("egad"), "failed"), + Key: "k", + } + enc := NewMapObjectEncoder() + f.AddTo(enc) + assert.Equal(t, "failed: egad", enc.Fields["k"], "Unexpected basic error message.") + + serialized := enc.Fields["kVerbose"] + // Don't assert the exact format used by a third-party package, but ensure + // that some critical elements are present. + assert.Regexp(t, `egad`, serialized, "Expected original error message to be present.") + assert.Regexp(t, `failed`, serialized, "Expected error annotation to be present.") + assert.Regexp(t, `TestRichErrorSupport`, serialized, "Expected calling function to be present in stacktrace.") +} diff --git a/vendor/src/go.uber.org/zap/zapcore/field.go b/vendor/src/go.uber.org/zap/zapcore/field.go new file mode 100644 index 00000000..6a5e33e2 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/field.go @@ -0,0 +1,201 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "fmt" + "math" + "reflect" + "time" +) + +// A FieldType indicates which member of the Field union struct should be used +// and how it should be serialized. +type FieldType uint8 + +const ( + // UnknownType is the default field type. Attempting to add it to an encoder will panic. + UnknownType FieldType = iota + // ArrayMarshalerType indicates that the field carries an ArrayMarshaler. + ArrayMarshalerType + // ObjectMarshalerType indicates that the field carries an ObjectMarshaler. + ObjectMarshalerType + // BinaryType indicates that the field carries an opaque binary blob. + BinaryType + // BoolType indicates that the field carries a bool. + BoolType + // ByteStringType indicates that the field carries UTF-8 encoded bytes. + ByteStringType + // Complex128Type indicates that the field carries a complex128. + Complex128Type + // Complex64Type indicates that the field carries a complex128. + Complex64Type + // DurationType indicates that the field carries a time.Duration. + DurationType + // Float64Type indicates that the field carries a float64. + Float64Type + // Float32Type indicates that the field carries a float32. + Float32Type + // Int64Type indicates that the field carries an int64. + Int64Type + // Int32Type indicates that the field carries an int32. + Int32Type + // Int16Type indicates that the field carries an int16. + Int16Type + // Int8Type indicates that the field carries an int8. + Int8Type + // StringType indicates that the field carries a string. + StringType + // TimeType indicates that the field carries a time.Time. + TimeType + // Uint64Type indicates that the field carries a uint64. + Uint64Type + // Uint32Type indicates that the field carries a uint32. + Uint32Type + // Uint16Type indicates that the field carries a uint16. + Uint16Type + // Uint8Type indicates that the field carries a uint8. + Uint8Type + // UintptrType indicates that the field carries a uintptr. + UintptrType + // ReflectType indicates that the field carries an interface{}, which should + // be serialized using reflection. + ReflectType + // NamespaceType signals the beginning of an isolated namespace. All + // subsequent fields should be added to the new namespace. + NamespaceType + // StringerType indicates that the field carries a fmt.Stringer. + StringerType + // ErrorType indicates that the field carries an error. + ErrorType + // SkipType indicates that the field is a no-op. + SkipType +) + +// A Field is a marshaling operation used to add a key-value pair to a logger's +// context. Most fields are lazily marshaled, so it's inexpensive to add fields +// to disabled debug-level log statements. +type Field struct { + Key string + Type FieldType + Integer int64 + String string + Interface interface{} +} + +// AddTo exports a field through the ObjectEncoder interface. It's primarily +// useful to library authors, and shouldn't be necessary in most applications. +func (f Field) AddTo(enc ObjectEncoder) { + var err error + + switch f.Type { + case ArrayMarshalerType: + err = enc.AddArray(f.Key, f.Interface.(ArrayMarshaler)) + case ObjectMarshalerType: + err = enc.AddObject(f.Key, f.Interface.(ObjectMarshaler)) + case BinaryType: + enc.AddBinary(f.Key, f.Interface.([]byte)) + case BoolType: + enc.AddBool(f.Key, f.Integer == 1) + case ByteStringType: + enc.AddByteString(f.Key, f.Interface.([]byte)) + case Complex128Type: + enc.AddComplex128(f.Key, f.Interface.(complex128)) + case Complex64Type: + enc.AddComplex64(f.Key, f.Interface.(complex64)) + case DurationType: + enc.AddDuration(f.Key, time.Duration(f.Integer)) + case Float64Type: + enc.AddFloat64(f.Key, math.Float64frombits(uint64(f.Integer))) + case Float32Type: + enc.AddFloat32(f.Key, math.Float32frombits(uint32(f.Integer))) + case Int64Type: + enc.AddInt64(f.Key, f.Integer) + case Int32Type: + enc.AddInt32(f.Key, int32(f.Integer)) + case Int16Type: + enc.AddInt16(f.Key, int16(f.Integer)) + case Int8Type: + enc.AddInt8(f.Key, int8(f.Integer)) + case StringType: + enc.AddString(f.Key, f.String) + case TimeType: + if f.Interface != nil { + enc.AddTime(f.Key, time.Unix(0, f.Integer).In(f.Interface.(*time.Location))) + } else { + // Fall back to UTC if location is nil. + enc.AddTime(f.Key, time.Unix(0, f.Integer)) + } + case Uint64Type: + enc.AddUint64(f.Key, uint64(f.Integer)) + case Uint32Type: + enc.AddUint32(f.Key, uint32(f.Integer)) + case Uint16Type: + enc.AddUint16(f.Key, uint16(f.Integer)) + case Uint8Type: + enc.AddUint8(f.Key, uint8(f.Integer)) + case UintptrType: + enc.AddUintptr(f.Key, uintptr(f.Integer)) + case ReflectType: + err = enc.AddReflected(f.Key, f.Interface) + case NamespaceType: + enc.OpenNamespace(f.Key) + case StringerType: + enc.AddString(f.Key, f.Interface.(fmt.Stringer).String()) + case ErrorType: + encodeError(f.Key, f.Interface.(error), enc) + case SkipType: + break + default: + panic(fmt.Sprintf("unknown field type: %v", f)) + } + + if err != nil { + enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error()) + } +} + +// Equals returns whether two fields are equal. For non-primitive types such as +// errors, marshalers, or reflect types, it uses reflect.DeepEqual. +func (f Field) Equals(other Field) bool { + if f.Type != other.Type { + return false + } + if f.Key != other.Key { + return false + } + + switch f.Type { + case BinaryType, ByteStringType: + return bytes.Equal(f.Interface.([]byte), other.Interface.([]byte)) + case ArrayMarshalerType, ObjectMarshalerType, ErrorType, ReflectType: + return reflect.DeepEqual(f.Interface, other.Interface) + default: + return f == other + } +} + +func addFields(enc ObjectEncoder, fields []Field) { + for i := range fields { + fields[i].AddTo(enc) + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/field_test.go b/vendor/src/go.uber.org/zap/zapcore/field_test.go new file mode 100644 index 00000000..fb722b4b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/field_test.go @@ -0,0 +1,231 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "errors" + "fmt" + "math" + "testing" + "time" + + "go.uber.org/zap" + + "github.com/stretchr/testify/assert" + + . "go.uber.org/zap/zapcore" +) + +type users int + +func (u users) String() string { + return fmt.Sprintf("%d users", int(u)) +} + +func (u users) MarshalLogObject(enc ObjectEncoder) error { + if int(u) < 0 { + return errors.New("too few users") + } + enc.AddInt("users", int(u)) + return nil +} + +func (u users) MarshalLogArray(enc ArrayEncoder) error { + if int(u) < 0 { + return errors.New("too few users") + } + for i := 0; i < int(u); i++ { + enc.AppendString("user") + } + return nil +} + +func TestUnknownFieldType(t *testing.T) { + unknown := Field{Key: "k", String: "foo"} + assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.") + assert.Panics(t, func() { + unknown.AddTo(NewMapObjectEncoder()) + }, "Expected using a field with unknown type to panic.") +} + +func TestFieldAddingError(t *testing.T) { + tests := []struct { + t FieldType + want interface{} + }{ + {ArrayMarshalerType, []interface{}(nil)}, + {ObjectMarshalerType, map[string]interface{}{}}, + } + for _, tt := range tests { + f := Field{Key: "k", Interface: users(-1), Type: tt.t} + enc := NewMapObjectEncoder() + assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.") + assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.") + assert.Equal(t, "too few users", enc.Fields["kError"], "Expected error message in log context.") + } +} + +func TestFields(t *testing.T) { + tests := []struct { + t FieldType + i int64 + s string + iface interface{} + want interface{} + }{ + {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}}, + {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}}, + {t: BinaryType, iface: []byte("foo"), want: []byte("foo")}, + {t: BoolType, i: 0, want: false}, + {t: ByteStringType, iface: []byte("foo"), want: "foo"}, + {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i}, + {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)}, + {t: DurationType, i: 1000, want: time.Microsecond}, + {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14}, + {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)}, + {t: Int64Type, i: 42, want: int64(42)}, + {t: Int32Type, i: 42, want: int32(42)}, + {t: Int16Type, i: 42, want: int16(42)}, + {t: Int8Type, i: 42, want: int8(42)}, + {t: StringType, s: "foo", want: "foo"}, + {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)}, + {t: TimeType, i: 1000, want: time.Unix(0, 1000)}, + {t: Uint64Type, i: 42, want: uint64(42)}, + {t: Uint32Type, i: 42, want: uint32(42)}, + {t: Uint16Type, i: 42, want: uint16(42)}, + {t: Uint8Type, i: 42, want: uint8(42)}, + {t: UintptrType, i: 42, want: uintptr(42)}, + {t: ReflectType, iface: users(2), want: users(2)}, + {t: NamespaceType, want: map[string]interface{}{}}, + {t: StringerType, iface: users(2), want: "2 users"}, + {t: SkipType, want: interface{}(nil)}, + } + + for _, tt := range tests { + enc := NewMapObjectEncoder() + f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s} + f.AddTo(enc) + assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f) + + delete(enc.Fields, "k") + assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.") + + assert.True(t, f.Equals(f), "Field does not equal itself") + } +} + +func TestEquals(t *testing.T) { + tests := []struct { + a, b Field + want bool + }{ + { + a: zap.Int16("a", 1), + b: zap.Int32("a", 1), + want: false, + }, + { + a: zap.String("k", "a"), + b: zap.String("k", "a"), + want: true, + }, + { + a: zap.String("k", "a"), + b: zap.String("k2", "a"), + want: false, + }, + { + a: zap.String("k", "a"), + b: zap.String("k", "b"), + want: false, + }, + { + a: zap.Time("k", time.Unix(1000, 1000)), + b: zap.Time("k", time.Unix(1000, 1000)), + want: true, + }, + { + a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)), + b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))), + want: false, + }, + { + a: zap.Time("k", time.Unix(1000, 1000)), + b: zap.Time("k", time.Unix(1000, 2000)), + want: false, + }, + { + a: zap.Binary("k", []byte{1, 2}), + b: zap.Binary("k", []byte{1, 2}), + want: true, + }, + { + a: zap.Binary("k", []byte{1, 2}), + b: zap.Binary("k", []byte{1, 3}), + want: false, + }, + { + a: zap.ByteString("k", []byte("abc")), + b: zap.ByteString("k", []byte("abc")), + want: true, + }, + { + a: zap.ByteString("k", []byte("abc")), + b: zap.ByteString("k", []byte("abd")), + want: false, + }, + { + a: zap.Ints("k", []int{1, 2}), + b: zap.Ints("k", []int{1, 2}), + want: true, + }, + { + a: zap.Ints("k", []int{1, 2}), + b: zap.Ints("k", []int{1, 3}), + want: false, + }, + { + a: zap.Object("k", users(10)), + b: zap.Object("k", users(10)), + want: true, + }, + { + a: zap.Object("k", users(10)), + b: zap.Object("k", users(20)), + want: false, + }, + { + a: zap.Any("k", map[string]string{"a": "b"}), + b: zap.Any("k", map[string]string{"a": "b"}), + want: true, + }, + { + a: zap.Any("k", map[string]string{"a": "b"}), + b: zap.Any("k", map[string]string{"a": "d"}), + want: false, + }, + } + + for _, tt := range tests { + assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b) + assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b) + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/hook.go b/vendor/src/go.uber.org/zap/zapcore/hook.go new file mode 100644 index 00000000..5db4afb3 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/hook.go @@ -0,0 +1,68 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/multierr" + +type hooked struct { + Core + funcs []func(Entry) error +} + +// RegisterHooks wraps a Core and runs a collection of user-defined callback +// hooks each time a message is logged. Execution of the callbacks is blocking. +// +// This offers users an easy way to register simple callbacks (e.g., metrics +// collection) without implementing the full Core interface. +func RegisterHooks(core Core, hooks ...func(Entry) error) Core { + funcs := append([]func(Entry) error{}, hooks...) + return &hooked{ + Core: core, + funcs: funcs, + } +} + +func (h *hooked) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + // Let the wrapped Core decide whether to log this message or not. This + // also gives the downstream a chance to register itself directly with the + // CheckedEntry. + if downstream := h.Core.Check(ent, ce); downstream != nil { + return downstream.AddCore(ent, h) + } + return ce +} + +func (h *hooked) With(fields []Field) Core { + return &hooked{ + Core: h.Core.With(fields), + funcs: h.funcs, + } +} + +func (h *hooked) Write(ent Entry, _ []Field) error { + // Since our downstream had a chance to register itself directly with the + // CheckedMessage, we don't need to call it here. + var err error + for i := range h.funcs { + err = multierr.Append(err, h.funcs[i](ent)) + } + return err +} diff --git a/vendor/src/go.uber.org/zap/zapcore/hook_test.go b/vendor/src/go.uber.org/zap/zapcore/hook_test.go new file mode 100644 index 00000000..0764888a --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/hook_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "testing" + + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" +) + +func TestHooks(t *testing.T) { + tests := []struct { + entryLevel Level + coreLevel Level + expectCall bool + }{ + {DebugLevel, InfoLevel, false}, + {InfoLevel, InfoLevel, true}, + {WarnLevel, InfoLevel, true}, + } + + for _, tt := range tests { + fac, logs := observer.New(tt.coreLevel) + intField := makeInt64Field("foo", 42) + ent := Entry{Message: "bar", Level: tt.entryLevel} + + var called int + f := func(e Entry) error { + called++ + assert.Equal(t, ent, e, "Hook called with unexpected Entry.") + return nil + } + + h := RegisterHooks(fac, f) + if ce := h.With([]Field{intField}).Check(ent, nil); ce != nil { + ce.Write() + } + + if tt.expectCall { + assert.Equal(t, 1, called, "Expected to call hook once.") + assert.Equal( + t, + []observer.LoggedEntry{{Entry: ent, Context: []Field{intField}}}, + logs.AllUntimed(), + "Unexpected logs written out.", + ) + } else { + assert.Equal(t, 0, called, "Didn't expect to call hook.") + assert.Equal(t, 0, logs.Len(), "Unexpected logs written out.") + } + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/json_encoder.go b/vendor/src/go.uber.org/zap/zapcore/json_encoder.go new file mode 100644 index 00000000..1006ba2b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/json_encoder.go @@ -0,0 +1,480 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "encoding/base64" + "encoding/json" + "math" + "sync" + "time" + "unicode/utf8" + + "go.uber.org/zap/buffer" + "go.uber.org/zap/internal/bufferpool" +) + +// For JSON-escaping; see jsonEncoder.safeAddString below. +const _hex = "0123456789abcdef" + +var _jsonPool = sync.Pool{New: func() interface{} { + return &jsonEncoder{} +}} + +func getJSONEncoder() *jsonEncoder { + return _jsonPool.Get().(*jsonEncoder) +} + +func putJSONEncoder(enc *jsonEncoder) { + enc.EncoderConfig = nil + enc.buf = nil + enc.spaced = false + enc.openNamespaces = 0 + _jsonPool.Put(enc) +} + +type jsonEncoder struct { + *EncoderConfig + buf *buffer.Buffer + spaced bool // include spaces after colons and commas + openNamespaces int +} + +// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder +// appropriately escapes all field keys and values. +// +// Note that the encoder doesn't deduplicate keys, so it's possible to produce +// a message like +// {"foo":"bar","foo":"baz"} +// This is permitted by the JSON specification, but not encouraged. Many +// libraries will ignore duplicate key-value pairs (typically keeping the last +// pair) when unmarshaling, but users should attempt to avoid adding duplicate +// keys. +func NewJSONEncoder(cfg EncoderConfig) Encoder { + return newJSONEncoder(cfg, false) +} + +func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder { + return &jsonEncoder{ + EncoderConfig: &cfg, + buf: bufferpool.Get(), + spaced: spaced, + } +} + +func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error { + enc.addKey(key) + return enc.AppendArray(arr) +} + +func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error { + enc.addKey(key) + return enc.AppendObject(obj) +} + +func (enc *jsonEncoder) AddBinary(key string, val []byte) { + enc.AddString(key, base64.StdEncoding.EncodeToString(val)) +} + +func (enc *jsonEncoder) AddByteString(key string, val []byte) { + enc.addKey(key) + enc.AppendByteString(val) +} + +func (enc *jsonEncoder) AddBool(key string, val bool) { + enc.addKey(key) + enc.AppendBool(val) +} + +func (enc *jsonEncoder) AddComplex128(key string, val complex128) { + enc.addKey(key) + enc.AppendComplex128(val) +} + +func (enc *jsonEncoder) AddDuration(key string, val time.Duration) { + enc.addKey(key) + enc.AppendDuration(val) +} + +func (enc *jsonEncoder) AddFloat64(key string, val float64) { + enc.addKey(key) + enc.AppendFloat64(val) +} + +func (enc *jsonEncoder) AddInt64(key string, val int64) { + enc.addKey(key) + enc.AppendInt64(val) +} + +func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error { + marshaled, err := json.Marshal(obj) + if err != nil { + return err + } + enc.addKey(key) + _, err = enc.buf.Write(marshaled) + return err +} + +func (enc *jsonEncoder) OpenNamespace(key string) { + enc.addKey(key) + enc.buf.AppendByte('{') + enc.openNamespaces++ +} + +func (enc *jsonEncoder) AddString(key, val string) { + enc.addKey(key) + enc.AppendString(val) +} + +func (enc *jsonEncoder) AddTime(key string, val time.Time) { + enc.addKey(key) + enc.AppendTime(val) +} + +func (enc *jsonEncoder) AddUint64(key string, val uint64) { + enc.addKey(key) + enc.AppendUint64(val) +} + +func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error { + enc.addElementSeparator() + enc.buf.AppendByte('[') + err := arr.MarshalLogArray(enc) + enc.buf.AppendByte(']') + return err +} + +func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error { + enc.addElementSeparator() + enc.buf.AppendByte('{') + err := obj.MarshalLogObject(enc) + enc.buf.AppendByte('}') + return err +} + +func (enc *jsonEncoder) AppendBool(val bool) { + enc.addElementSeparator() + enc.buf.AppendBool(val) +} + +func (enc *jsonEncoder) AppendByteString(val []byte) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddByteString(val) + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendComplex128(val complex128) { + enc.addElementSeparator() + // Cast to a platform-independent, fixed-size type. + r, i := float64(real(val)), float64(imag(val)) + enc.buf.AppendByte('"') + // Because we're always in a quoted string, we can use strconv without + // special-casing NaN and +/-Inf. + enc.buf.AppendFloat(r, 64) + enc.buf.AppendByte('+') + enc.buf.AppendFloat(i, 64) + enc.buf.AppendByte('i') + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendDuration(val time.Duration) { + cur := enc.buf.Len() + enc.EncodeDuration(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep + // JSON valid. + enc.AppendInt64(int64(val)) + } +} + +func (enc *jsonEncoder) AppendInt64(val int64) { + enc.addElementSeparator() + enc.buf.AppendInt(val) +} + +func (enc *jsonEncoder) AppendReflected(val interface{}) error { + marshaled, err := json.Marshal(val) + if err != nil { + return err + } + enc.addElementSeparator() + _, err = enc.buf.Write(marshaled) + return err +} + +func (enc *jsonEncoder) AppendString(val string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(val) + enc.buf.AppendByte('"') +} + +func (enc *jsonEncoder) AppendTime(val time.Time) { + cur := enc.buf.Len() + enc.EncodeTime(val, enc) + if cur == enc.buf.Len() { + // User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep + // output JSON valid. + enc.AppendInt64(val.UnixNano()) + } +} + +func (enc *jsonEncoder) AppendUint64(val uint64) { + enc.addElementSeparator() + enc.buf.AppendUint(val) +} + +func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) } +func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) } +func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) } +func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) } +func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) } +func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) } +func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) } +func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) } +func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) } +func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) } + +func (enc *jsonEncoder) Clone() Encoder { + clone := enc.clone() + clone.buf.Write(enc.buf.Bytes()) + return clone +} + +func (enc *jsonEncoder) clone() *jsonEncoder { + clone := getJSONEncoder() + clone.EncoderConfig = enc.EncoderConfig + clone.spaced = enc.spaced + clone.openNamespaces = enc.openNamespaces + clone.buf = bufferpool.Get() + return clone +} + +func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { + final := enc.clone() + final.buf.AppendByte('{') + + if final.LevelKey != "" { + final.addKey(final.LevelKey) + cur := final.buf.Len() + final.EncodeLevel(ent.Level, final) + if cur == final.buf.Len() { + // User-supplied EncodeLevel was a no-op. Fall back to strings to keep + // output JSON valid. + final.AppendString(ent.Level.String()) + } + } + if final.TimeKey != "" { + final.AddTime(final.TimeKey, ent.Time) + } + if ent.LoggerName != "" && final.NameKey != "" { + final.addKey(final.NameKey) + cur := final.buf.Len() + nameEncoder := final.EncodeName + + // if no name encoder provided, fall back to FullNameEncoder for backwards + // compatibility + if nameEncoder == nil { + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, final) + if cur == final.buf.Len() { + // User-supplied EncodeName was a no-op. Fall back to strings to + // keep output JSON valid. + final.AppendString(ent.LoggerName) + } + } + if ent.Caller.Defined && final.CallerKey != "" { + final.addKey(final.CallerKey) + cur := final.buf.Len() + final.EncodeCaller(ent.Caller, final) + if cur == final.buf.Len() { + // User-supplied EncodeCaller was a no-op. Fall back to strings to + // keep output JSON valid. + final.AppendString(ent.Caller.String()) + } + } + if final.MessageKey != "" { + final.addKey(enc.MessageKey) + final.AppendString(ent.Message) + } + if enc.buf.Len() > 0 { + final.addElementSeparator() + final.buf.Write(enc.buf.Bytes()) + } + addFields(final, fields) + final.closeOpenNamespaces() + if ent.Stack != "" && final.StacktraceKey != "" { + final.AddString(final.StacktraceKey, ent.Stack) + } + final.buf.AppendByte('}') + if final.LineEnding != "" { + final.buf.AppendString(final.LineEnding) + } else { + final.buf.AppendString(DefaultLineEnding) + } + + ret := final.buf + putJSONEncoder(final) + return ret, nil +} + +func (enc *jsonEncoder) truncate() { + enc.buf.Reset() +} + +func (enc *jsonEncoder) closeOpenNamespaces() { + for i := 0; i < enc.openNamespaces; i++ { + enc.buf.AppendByte('}') + } +} + +func (enc *jsonEncoder) addKey(key string) { + enc.addElementSeparator() + enc.buf.AppendByte('"') + enc.safeAddString(key) + enc.buf.AppendByte('"') + enc.buf.AppendByte(':') + if enc.spaced { + enc.buf.AppendByte(' ') + } +} + +func (enc *jsonEncoder) addElementSeparator() { + last := enc.buf.Len() - 1 + if last < 0 { + return + } + switch enc.buf.Bytes()[last] { + case '{', '[', ':', ',', ' ': + return + default: + enc.buf.AppendByte(',') + if enc.spaced { + enc.buf.AppendByte(' ') + } + } +} + +func (enc *jsonEncoder) appendFloat(val float64, bitSize int) { + enc.addElementSeparator() + switch { + case math.IsNaN(val): + enc.buf.AppendString(`"NaN"`) + case math.IsInf(val, 1): + enc.buf.AppendString(`"+Inf"`) + case math.IsInf(val, -1): + enc.buf.AppendString(`"-Inf"`) + default: + enc.buf.AppendFloat(val, bitSize) + } +} + +// safeAddString JSON-escapes a string and appends it to the internal buffer. +// Unlike the standard library's encoder, it doesn't attempt to protect the +// user from browser vulnerabilities or JSONP-related problems. +func (enc *jsonEncoder) safeAddString(s string) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRuneInString(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.AppendString(s[i : i+size]) + i += size + } +} + +// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. +func (enc *jsonEncoder) safeAddByteString(s []byte) { + for i := 0; i < len(s); { + if enc.tryAddRuneSelf(s[i]) { + i++ + continue + } + r, size := utf8.DecodeRune(s[i:]) + if enc.tryAddRuneError(r, size) { + i++ + continue + } + enc.buf.Write(s[i : i+size]) + i += size + } +} + +// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. +func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool { + if b >= utf8.RuneSelf { + return false + } + if 0x20 <= b && b != '\\' && b != '"' { + enc.buf.AppendByte(b) + return true + } + switch b { + case '\\', '"': + enc.buf.AppendByte('\\') + enc.buf.AppendByte(b) + case '\n': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('n') + case '\r': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('r') + case '\t': + enc.buf.AppendByte('\\') + enc.buf.AppendByte('t') + default: + // Encode bytes < 0x20, except for the escape sequences above. + enc.buf.AppendString(`\u00`) + enc.buf.AppendByte(_hex[b>>4]) + enc.buf.AppendByte(_hex[b&0xF]) + } + return true +} + +func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool { + if r == utf8.RuneError && size == 1 { + enc.buf.AppendString(`\ufffd`) + return true + } + return false +} diff --git a/vendor/src/go.uber.org/zap/zapcore/json_encoder_bench_test.go b/vendor/src/go.uber.org/zap/zapcore/json_encoder_bench_test.go new file mode 100644 index 00000000..4bd5033b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/json_encoder_bench_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "encoding/json" + "testing" + "time" + + . "go.uber.org/zap/zapcore" +) + +func BenchmarkJSONLogMarshalerFunc(b *testing.B) { + for i := 0; i < b.N; i++ { + enc := NewJSONEncoder(testEncoderConfig()) + enc.AddObject("nested", ObjectMarshalerFunc(func(enc ObjectEncoder) error { + enc.AddInt64("i", int64(i)) + return nil + })) + } +} + +func BenchmarkZapJSON(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + enc := NewJSONEncoder(testEncoderConfig()) + enc.AddString("str", "foo") + enc.AddInt64("int64-1", 1) + enc.AddInt64("int64-2", 2) + enc.AddFloat64("float64", 1.0) + enc.AddString("string1", "\n") + enc.AddString("string2", "💩") + enc.AddString("string3", "🤔") + enc.AddString("string4", "🙊") + enc.AddBool("bool", true) + buf, _ := enc.EncodeEntry(Entry{ + Message: "fake", + Level: DebugLevel, + }, nil) + buf.Free() + } + }) +} + +func BenchmarkStandardJSON(b *testing.B) { + record := struct { + Level string `json:"level"` + Message string `json:"msg"` + Time time.Time `json:"ts"` + Fields map[string]interface{} `json:"fields"` + }{ + Level: "debug", + Message: "fake", + Time: time.Unix(0, 0), + Fields: map[string]interface{}{ + "str": "foo", + "int64-1": int64(1), + "int64-2": int64(1), + "float64": float64(1.0), + "string1": "\n", + "string2": "💩", + "string3": "🤔", + "string4": "🙊", + "bool": true, + }, + } + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + json.Marshal(record) + } + }) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/json_encoder_impl_test.go b/vendor/src/go.uber.org/zap/zapcore/json_encoder_impl_test.go new file mode 100644 index 00000000..a2dd7228 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/json_encoder_impl_test.go @@ -0,0 +1,475 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "encoding/json" + "errors" + "math" + "math/rand" + "reflect" + "testing" + "testing/quick" + "time" + + "go.uber.org/zap/internal/bufferpool" + + "github.com/stretchr/testify/assert" + "go.uber.org/multierr" +) + +func TestJSONClone(t *testing.T) { + // The parent encoder is created with plenty of excess capacity. + parent := &jsonEncoder{buf: bufferpool.Get()} + clone := parent.Clone() + + // Adding to the parent shouldn't affect the clone, and vice versa. + parent.AddString("foo", "bar") + clone.AddString("baz", "bing") + + assertJSON(t, `"foo":"bar"`, parent) + assertJSON(t, `"baz":"bing"`, clone.(*jsonEncoder)) +} + +func TestJSONEscaping(t *testing.T) { + enc := &jsonEncoder{buf: bufferpool.Get()} + // Test all the edge cases of JSON escaping directly. + cases := map[string]string{ + // ASCII. + `foo`: `foo`, + // Special-cased characters. + `"`: `\"`, + `\`: `\\`, + // Special-cased characters within everyday ASCII. + `foo"foo`: `foo\"foo`, + "foo\n": `foo\n`, + // Special-cased control characters. + "\n": `\n`, + "\r": `\r`, + "\t": `\t`, + // \b and \f are sometimes backslash-escaped, but this representation is also + // conformant. + "\b": `\u0008`, + "\f": `\u000c`, + // The standard lib special-cases angle brackets and ampersands by default, + // because it wants to protect users from browser exploits. In a logging + // context, we shouldn't special-case these characters. + "<": "<", + ">": ">", + "&": "&", + // ASCII bell - not special-cased. + string(byte(0x07)): `\u0007`, + // Astral-plane unicode. + `☃`: `☃`, + // Decodes to (RuneError, 1) + "\xed\xa0\x80": `\ufffd\ufffd\ufffd`, + "foo\xed\xa0\x80": `foo\ufffd\ufffd\ufffd`, + } + + t.Run("String", func(t *testing.T) { + for input, output := range cases { + enc.truncate() + enc.safeAddString(input) + assertJSON(t, output, enc) + } + }) + + t.Run("ByteString", func(t *testing.T) { + for input, output := range cases { + enc.truncate() + enc.safeAddByteString([]byte(input)) + assertJSON(t, output, enc) + } + }) +} + +func TestJSONEncoderObjectFields(t *testing.T) { + tests := []struct { + desc string + expected string + f func(Encoder) + }{ + {"binary", `"k":"YWIxMg=="`, func(e Encoder) { e.AddBinary("k", []byte("ab12")) }}, + {"bool", `"k\\":true`, func(e Encoder) { e.AddBool(`k\`, true) }}, // test key escaping once + {"bool", `"k":true`, func(e Encoder) { e.AddBool("k", true) }}, + {"bool", `"k":false`, func(e Encoder) { e.AddBool("k", false) }}, + {"byteString", `"k":"v\\"`, func(e Encoder) { e.AddByteString(`k`, []byte(`v\`)) }}, + {"byteString", `"k":"v"`, func(e Encoder) { e.AddByteString("k", []byte("v")) }}, + {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", []byte{}) }}, + {"byteString", `"k":""`, func(e Encoder) { e.AddByteString("k", nil) }}, + {"complex128", `"k":"1+2i"`, func(e Encoder) { e.AddComplex128("k", 1+2i) }}, + {"complex64", `"k":"1+2i"`, func(e Encoder) { e.AddComplex64("k", 1+2i) }}, + {"duration", `"k":0.000000001`, func(e Encoder) { e.AddDuration("k", 1) }}, + {"float64", `"k":1`, func(e Encoder) { e.AddFloat64("k", 1.0) }}, + {"float64", `"k":10000000000`, func(e Encoder) { e.AddFloat64("k", 1e10) }}, + {"float64", `"k":"NaN"`, func(e Encoder) { e.AddFloat64("k", math.NaN()) }}, + {"float64", `"k":"+Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(1)) }}, + {"float64", `"k":"-Inf"`, func(e Encoder) { e.AddFloat64("k", math.Inf(-1)) }}, + {"float32", `"k":1`, func(e Encoder) { e.AddFloat32("k", 1.0) }}, + {"float32", `"k":10000000000`, func(e Encoder) { e.AddFloat32("k", 1e10) }}, + {"float32", `"k":"NaN"`, func(e Encoder) { e.AddFloat32("k", float32(math.NaN())) }}, + {"float32", `"k":"+Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(1))) }}, + {"float32", `"k":"-Inf"`, func(e Encoder) { e.AddFloat32("k", float32(math.Inf(-1))) }}, + {"int", `"k":42`, func(e Encoder) { e.AddInt("k", 42) }}, + {"int64", `"k":42`, func(e Encoder) { e.AddInt64("k", 42) }}, + {"int32", `"k":42`, func(e Encoder) { e.AddInt32("k", 42) }}, + {"int16", `"k":42`, func(e Encoder) { e.AddInt16("k", 42) }}, + {"int8", `"k":42`, func(e Encoder) { e.AddInt8("k", 42) }}, + {"string", `"k":"v\\"`, func(e Encoder) { e.AddString(`k`, `v\`) }}, + {"string", `"k":"v"`, func(e Encoder) { e.AddString("k", "v") }}, + {"string", `"k":""`, func(e Encoder) { e.AddString("k", "") }}, + {"time", `"k":1`, func(e Encoder) { e.AddTime("k", time.Unix(1, 0)) }}, + {"uint", `"k":42`, func(e Encoder) { e.AddUint("k", 42) }}, + {"uint64", `"k":42`, func(e Encoder) { e.AddUint64("k", 42) }}, + {"uint32", `"k":42`, func(e Encoder) { e.AddUint32("k", 42) }}, + {"uint16", `"k":42`, func(e Encoder) { e.AddUint16("k", 42) }}, + {"uint8", `"k":42`, func(e Encoder) { e.AddUint8("k", 42) }}, + {"uintptr", `"k":42`, func(e Encoder) { e.AddUintptr("k", 42) }}, + { + desc: "object (success)", + expected: `"k":{"loggable":"yes"}`, + f: func(e Encoder) { + assert.NoError(t, e.AddObject("k", loggable{true}), "Unexpected error calling MarshalLogObject.") + }, + }, + { + desc: "object (error)", + expected: `"k":{}`, + f: func(e Encoder) { + assert.Error(t, e.AddObject("k", loggable{false}), "Expected an error calling MarshalLogObject.") + }, + }, + { + desc: "object (with nested array)", + expected: `"turducken":{"ducks":[{"in":"chicken"},{"in":"chicken"}]}`, + f: func(e Encoder) { + assert.NoError( + t, + e.AddObject("turducken", turducken{}), + "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.", + ) + }, + }, + { + desc: "array (with nested object)", + expected: `"turduckens":[{"ducks":[{"in":"chicken"},{"in":"chicken"}]},{"ducks":[{"in":"chicken"},{"in":"chicken"}]}]`, + f: func(e Encoder) { + assert.NoError( + t, + e.AddArray("turduckens", turduckens(2)), + "Unexpected error calling MarshalLogObject with nested ObjectMarshalers and ArrayMarshalers.", + ) + }, + }, + { + desc: "array (success)", + expected: `"k":[true]`, + f: func(e Encoder) { + assert.NoError(t, e.AddArray(`k`, loggable{true}), "Unexpected error calling MarshalLogArray.") + }, + }, + { + desc: "array (error)", + expected: `"k":[]`, + f: func(e Encoder) { + assert.Error(t, e.AddArray("k", loggable{false}), "Expected an error calling MarshalLogArray.") + }, + }, + { + desc: "reflect (success)", + expected: `"k":{"loggable":"yes"}`, + f: func(e Encoder) { + assert.NoError(t, e.AddReflected("k", map[string]string{"loggable": "yes"}), "Unexpected error JSON-serializing a map.") + }, + }, + { + desc: "reflect (failure)", + expected: "", + f: func(e Encoder) { + assert.Error(t, e.AddReflected("k", noJSON{}), "Unexpected success JSON-serializing a noJSON.") + }, + }, + { + desc: "namespace", + // EncodeEntry is responsible for closing all open namespaces. + expected: `"outermost":{"outer":{"foo":1,"inner":{"foo":2,"innermost":{`, + f: func(e Encoder) { + e.OpenNamespace("outermost") + e.OpenNamespace("outer") + e.AddInt("foo", 1) + e.OpenNamespace("inner") + e.AddInt("foo", 2) + e.OpenNamespace("innermost") + }, + }, + } + + for _, tt := range tests { + assertOutput(t, tt.desc, tt.expected, tt.f) + } +} + +func TestJSONEncoderArrays(t *testing.T) { + tests := []struct { + desc string + expected string // expect f to be called twice + f func(ArrayEncoder) + }{ + {"bool", `[true,true]`, func(e ArrayEncoder) { e.AppendBool(true) }}, + {"byteString", `["k","k"]`, func(e ArrayEncoder) { e.AppendByteString([]byte("k")) }}, + {"byteString", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendByteString([]byte(`k\`)) }}, + {"complex128", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }}, + {"complex64", `["1+2i","1+2i"]`, func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }}, + {"durations", `[0.000000002,0.000000002]`, func(e ArrayEncoder) { e.AppendDuration(2) }}, + {"float64", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat64(3.14) }}, + {"float32", `[3.14,3.14]`, func(e ArrayEncoder) { e.AppendFloat32(3.14) }}, + {"int", `[42,42]`, func(e ArrayEncoder) { e.AppendInt(42) }}, + {"int64", `[42,42]`, func(e ArrayEncoder) { e.AppendInt64(42) }}, + {"int32", `[42,42]`, func(e ArrayEncoder) { e.AppendInt32(42) }}, + {"int16", `[42,42]`, func(e ArrayEncoder) { e.AppendInt16(42) }}, + {"int8", `[42,42]`, func(e ArrayEncoder) { e.AppendInt8(42) }}, + {"string", `["k","k"]`, func(e ArrayEncoder) { e.AppendString("k") }}, + {"string", `["k\\","k\\"]`, func(e ArrayEncoder) { e.AppendString(`k\`) }}, + {"times", `[1,1]`, func(e ArrayEncoder) { e.AppendTime(time.Unix(1, 0)) }}, + {"uint", `[42,42]`, func(e ArrayEncoder) { e.AppendUint(42) }}, + {"uint64", `[42,42]`, func(e ArrayEncoder) { e.AppendUint64(42) }}, + {"uint32", `[42,42]`, func(e ArrayEncoder) { e.AppendUint32(42) }}, + {"uint16", `[42,42]`, func(e ArrayEncoder) { e.AppendUint16(42) }}, + {"uint8", `[42,42]`, func(e ArrayEncoder) { e.AppendUint8(42) }}, + {"uintptr", `[42,42]`, func(e ArrayEncoder) { e.AppendUintptr(42) }}, + { + desc: "arrays (success)", + expected: `[[true],[true]]`, + f: func(arr ArrayEncoder) { + assert.NoError(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error { + inner.AppendBool(true) + return nil + })), "Unexpected error appending an array.") + }, + }, + { + desc: "arrays (error)", + expected: `[[true],[true]]`, + f: func(arr ArrayEncoder) { + assert.Error(t, arr.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error { + inner.AppendBool(true) + return errors.New("fail") + })), "Expected an error appending an array.") + }, + }, + { + desc: "objects (success)", + expected: `[{"loggable":"yes"},{"loggable":"yes"}]`, + f: func(arr ArrayEncoder) { + assert.NoError(t, arr.AppendObject(loggable{true}), "Unexpected error appending an object.") + }, + }, + { + desc: "objects (error)", + expected: `[{},{}]`, + f: func(arr ArrayEncoder) { + assert.Error(t, arr.AppendObject(loggable{false}), "Expected an error appending an object.") + }, + }, + { + desc: "reflect (success)", + expected: `[{"foo":5},{"foo":5}]`, + f: func(arr ArrayEncoder) { + assert.NoError( + t, + arr.AppendReflected(map[string]int{"foo": 5}), + "Unexpected an error appending an object with reflection.", + ) + }, + }, + { + desc: "reflect (error)", + expected: `[]`, + f: func(arr ArrayEncoder) { + assert.Error( + t, + arr.AppendReflected(noJSON{}), + "Unexpected an error appending an object with reflection.", + ) + }, + }, + } + + for _, tt := range tests { + f := func(enc Encoder) error { + return enc.AddArray("array", ArrayMarshalerFunc(func(arr ArrayEncoder) error { + tt.f(arr) + tt.f(arr) + return nil + })) + } + assertOutput(t, tt.desc, `"array":`+tt.expected, func(enc Encoder) { + err := f(enc) + assert.NoError(t, err, "Unexpected error adding array to JSON encoder.") + }) + } +} + +func assertJSON(t *testing.T, expected string, enc *jsonEncoder) { + assert.Equal(t, expected, enc.buf.String(), "Encoded JSON didn't match expectations.") +} + +func assertOutput(t testing.TB, desc string, expected string, f func(Encoder)) { + enc := &jsonEncoder{buf: bufferpool.Get(), EncoderConfig: &EncoderConfig{ + EncodeTime: EpochTimeEncoder, + EncodeDuration: SecondsDurationEncoder, + }} + f(enc) + assert.Equal(t, expected, enc.buf.String(), "Unexpected encoder output after adding a %s.", desc) + + enc.truncate() + enc.AddString("foo", "bar") + f(enc) + expectedPrefix := `"foo":"bar"` + if expected != "" { + // If we expect output, it should be comma-separated from the previous + // field. + expectedPrefix += "," + } + assert.Equal(t, expectedPrefix+expected, enc.buf.String(), "Unexpected encoder output after adding a %s as a second field.", desc) +} + +// Nested Array- and ObjectMarshalers. +type turducken struct{} + +func (t turducken) MarshalLogObject(enc ObjectEncoder) error { + return enc.AddArray("ducks", ArrayMarshalerFunc(func(arr ArrayEncoder) error { + for i := 0; i < 2; i++ { + arr.AppendObject(ObjectMarshalerFunc(func(inner ObjectEncoder) error { + inner.AddString("in", "chicken") + return nil + })) + } + return nil + })) +} + +type turduckens int + +func (t turduckens) MarshalLogArray(enc ArrayEncoder) error { + var err error + tur := turducken{} + for i := 0; i < int(t); i++ { + err = multierr.Append(err, enc.AppendObject(tur)) + } + return err +} + +type loggable struct{ bool } + +func (l loggable) MarshalLogObject(enc ObjectEncoder) error { + if !l.bool { + return errors.New("can't marshal") + } + enc.AddString("loggable", "yes") + return nil +} + +func (l loggable) MarshalLogArray(enc ArrayEncoder) error { + if !l.bool { + return errors.New("can't marshal") + } + enc.AppendBool(true) + return nil +} + +type noJSON struct{} + +func (nj noJSON) MarshalJSON() ([]byte, error) { + return nil, errors.New("no") +} + +func zapEncode(encode func(*jsonEncoder, string)) func(s string) []byte { + return func(s string) []byte { + enc := &jsonEncoder{buf: bufferpool.Get()} + // Escape and quote a string using our encoder. + var ret []byte + encode(enc, s) + ret = make([]byte, 0, enc.buf.Len()+2) + ret = append(ret, '"') + ret = append(ret, enc.buf.Bytes()...) + ret = append(ret, '"') + return ret + } +} + +func roundTripsCorrectly(encode func(string) []byte, original string) bool { + // Encode using our encoder, decode using the standard library, and assert + // that we haven't lost any information. + encoded := encode(original) + + var decoded string + err := json.Unmarshal(encoded, &decoded) + if err != nil { + return false + } + return original == decoded +} + +func roundTripsCorrectlyString(original string) bool { + return roundTripsCorrectly(zapEncode((*jsonEncoder).safeAddString), original) +} + +func roundTripsCorrectlyByteString(original string) bool { + return roundTripsCorrectly( + zapEncode(func(enc *jsonEncoder, s string) { + enc.safeAddByteString([]byte(s)) + }), + original) +} + +type ASCII string + +func (s ASCII) Generate(r *rand.Rand, size int) reflect.Value { + bs := make([]byte, size) + for i := range bs { + bs[i] = byte(r.Intn(128)) + } + a := ASCII(bs) + return reflect.ValueOf(a) +} + +func asciiRoundTripsCorrectlyString(s ASCII) bool { + return roundTripsCorrectlyString(string(s)) +} + +func asciiRoundTripsCorrectlyByteString(s ASCII) bool { + return roundTripsCorrectlyByteString(string(s)) +} + +func TestJSONQuick(t *testing.T) { + check := func(f interface{}) { + err := quick.Check(f, &quick.Config{MaxCountScale: 100.0}) + assert.NoError(t, err) + } + // Test the full range of UTF-8 strings. + check(roundTripsCorrectlyString) + check(roundTripsCorrectlyByteString) + + // Focus on ASCII strings. + check(asciiRoundTripsCorrectlyString) + check(asciiRoundTripsCorrectlyByteString) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/level.go b/vendor/src/go.uber.org/zap/zapcore/level.go new file mode 100644 index 00000000..e575c9f4 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/level.go @@ -0,0 +1,175 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "errors" + "fmt" +) + +var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level") + +// A Level is a logging priority. Higher levels are more important. +type Level int8 + +const ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel Level = iota - 1 + // InfoLevel is the default logging priority. + InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel + // DPanicLevel logs are particularly important errors. In development the + // logger panics after writing the message. + DPanicLevel + // PanicLevel logs a message, then panics. + PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel + + _minLevel = DebugLevel + _maxLevel = FatalLevel +) + +// String returns a lower-case ASCII representation of the log level. +func (l Level) String() string { + switch l { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + case ErrorLevel: + return "error" + case DPanicLevel: + return "dpanic" + case PanicLevel: + return "panic" + case FatalLevel: + return "fatal" + default: + return fmt.Sprintf("Level(%d)", l) + } +} + +// CapitalString returns an all-caps ASCII representation of the log level. +func (l Level) CapitalString() string { + // Printing levels in all-caps is common enough that we should export this + // functionality. + switch l { + case DebugLevel: + return "DEBUG" + case InfoLevel: + return "INFO" + case WarnLevel: + return "WARN" + case ErrorLevel: + return "ERROR" + case DPanicLevel: + return "DPANIC" + case PanicLevel: + return "PANIC" + case FatalLevel: + return "FATAL" + default: + return fmt.Sprintf("LEVEL(%d)", l) + } +} + +// MarshalText marshals the Level to text. Note that the text representation +// drops the -Level suffix (see example). +func (l Level) MarshalText() ([]byte, error) { + return []byte(l.String()), nil +} + +// UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText +// expects the text representation of a Level to drop the -Level suffix (see +// example). +// +// In particular, this makes it easy to configure logging levels using YAML, +// TOML, or JSON files. +func (l *Level) UnmarshalText(text []byte) error { + if l == nil { + return errUnmarshalNilLevel + } + if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { + return fmt.Errorf("unrecognized level: %q", text) + } + return nil +} + +func (l *Level) unmarshalText(text []byte) bool { + switch string(text) { + case "debug", "DEBUG": + *l = DebugLevel + case "info", "INFO", "": // make the zero value useful + *l = InfoLevel + case "warn", "WARN": + *l = WarnLevel + case "error", "ERROR": + *l = ErrorLevel + case "dpanic", "DPANIC": + *l = DPanicLevel + case "panic", "PANIC": + *l = PanicLevel + case "fatal", "FATAL": + *l = FatalLevel + default: + return false + } + return true +} + +// Set sets the level for the flag.Value interface. +func (l *Level) Set(s string) error { + return l.UnmarshalText([]byte(s)) +} + +// Get gets the level for the flag.Getter interface. +func (l *Level) Get() interface{} { + return *l +} + +// Enabled returns true if the given level is at or above this level. +func (l Level) Enabled(lvl Level) bool { + return lvl >= l +} + +// LevelEnabler decides whether a given logging level is enabled when logging a +// message. +// +// Enablers are intended to be used to implement deterministic filters; +// concerns like sampling are better implemented as a Core. +// +// Each concrete Level value implements a static LevelEnabler which returns +// true for itself and all higher logging levels. For example WarnLevel.Enabled() +// will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and +// FatalLevel, but return false for InfoLevel and DebugLevel. +type LevelEnabler interface { + Enabled(Level) bool +} diff --git a/vendor/src/go.uber.org/zap/zapcore/level_strings.go b/vendor/src/go.uber.org/zap/zapcore/level_strings.go new file mode 100644 index 00000000..7af8dadc --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/level_strings.go @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/zap/internal/color" + +var ( + _levelToColor = map[Level]color.Color{ + DebugLevel: color.Magenta, + InfoLevel: color.Blue, + WarnLevel: color.Yellow, + ErrorLevel: color.Red, + DPanicLevel: color.Red, + PanicLevel: color.Red, + FatalLevel: color.Red, + } + _unknownLevelColor = color.Red + + _levelToLowercaseColorString = make(map[Level]string, len(_levelToColor)) + _levelToCapitalColorString = make(map[Level]string, len(_levelToColor)) +) + +func init() { + for level, color := range _levelToColor { + _levelToLowercaseColorString[level] = color.Add(level.String()) + _levelToCapitalColorString[level] = color.Add(level.CapitalString()) + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/level_strings_test.go b/vendor/src/go.uber.org/zap/zapcore/level_strings_test.go new file mode 100644 index 00000000..14b0bac6 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/level_strings_test.go @@ -0,0 +1,38 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAllLevelsCoveredByLevelString(t *testing.T) { + numLevels := int((_maxLevel - _minLevel) + 1) + + isComplete := func(m map[Level]string) bool { + return len(m) == numLevels + } + + assert.True(t, isComplete(_levelToLowercaseColorString), "Colored lowercase strings don't cover all levels.") + assert.True(t, isComplete(_levelToCapitalColorString), "Colored capital strings don't cover all levels.") +} diff --git a/vendor/src/go.uber.org/zap/zapcore/level_test.go b/vendor/src/go.uber.org/zap/zapcore/level_test.go new file mode 100644 index 00000000..28b75b37 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/level_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "flag" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLevelString(t *testing.T) { + tests := map[Level]string{ + DebugLevel: "debug", + InfoLevel: "info", + WarnLevel: "warn", + ErrorLevel: "error", + DPanicLevel: "dpanic", + PanicLevel: "panic", + FatalLevel: "fatal", + Level(-42): "Level(-42)", + } + + for lvl, stringLevel := range tests { + assert.Equal(t, stringLevel, lvl.String(), "Unexpected lowercase level string.") + assert.Equal(t, strings.ToUpper(stringLevel), lvl.CapitalString(), "Unexpected all-caps level string.") + } +} + +func TestLevelText(t *testing.T) { + tests := []struct { + text string + level Level + }{ + {"debug", DebugLevel}, + {"info", InfoLevel}, + {"", InfoLevel}, // make the zero value useful + {"warn", WarnLevel}, + {"error", ErrorLevel}, + {"dpanic", DPanicLevel}, + {"panic", PanicLevel}, + {"fatal", FatalLevel}, + } + for _, tt := range tests { + if tt.text != "" { + lvl := tt.level + marshaled, err := lvl.MarshalText() + assert.NoError(t, err, "Unexpected error marshaling level %v to text.", &lvl) + assert.Equal(t, tt.text, string(marshaled), "Marshaling level %v to text yielded unexpected result.", &lvl) + } + + var unmarshaled Level + err := unmarshaled.UnmarshalText([]byte(tt.text)) + assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text) + assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text) + } +} + +func TestCapitalLevelsParse(t *testing.T) { + tests := []struct { + text string + level Level + }{ + {"DEBUG", DebugLevel}, + {"INFO", InfoLevel}, + {"WARN", WarnLevel}, + {"ERROR", ErrorLevel}, + {"DPANIC", DPanicLevel}, + {"PANIC", PanicLevel}, + {"FATAL", FatalLevel}, + } + for _, tt := range tests { + var unmarshaled Level + err := unmarshaled.UnmarshalText([]byte(tt.text)) + assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text) + assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text) + } +} + +func TestWeirdLevelsParse(t *testing.T) { + tests := []struct { + text string + level Level + }{ + // I guess... + {"Debug", DebugLevel}, + {"Info", InfoLevel}, + {"Warn", WarnLevel}, + {"Error", ErrorLevel}, + {"Dpanic", DPanicLevel}, + {"Panic", PanicLevel}, + {"Fatal", FatalLevel}, + + // What even is... + {"DeBuG", DebugLevel}, + {"InFo", InfoLevel}, + {"WaRn", WarnLevel}, + {"ErRor", ErrorLevel}, + {"DpAnIc", DPanicLevel}, + {"PaNiC", PanicLevel}, + {"FaTaL", FatalLevel}, + } + for _, tt := range tests { + var unmarshaled Level + err := unmarshaled.UnmarshalText([]byte(tt.text)) + assert.NoError(t, err, `Unexpected error unmarshaling text %q to level.`, tt.text) + assert.Equal(t, tt.level, unmarshaled, `Text %q unmarshaled to an unexpected level.`, tt.text) + } +} + +func TestLevelNils(t *testing.T) { + var l *Level + + // The String() method will not handle nil level properly. + assert.Panics(t, func() { + assert.Equal(t, "Level(nil)", l.String(), "Unexpected result stringifying nil *Level.") + }, "Level(nil).String() should panic") + + assert.Panics(t, func() { + l.MarshalText() + }, "Expected to panic when marshalling a nil level.") + + err := l.UnmarshalText([]byte("debug")) + assert.Equal(t, errUnmarshalNilLevel, err, "Expected to error unmarshalling into a nil Level.") +} + +func TestLevelUnmarshalUnknownText(t *testing.T) { + var l Level + err := l.UnmarshalText([]byte("foo")) + assert.Contains(t, err.Error(), "unrecognized level", "Expected unmarshaling arbitrary text to fail.") +} + +func TestLevelAsFlagValue(t *testing.T) { + var ( + buf bytes.Buffer + lvl Level + ) + fs := flag.NewFlagSet("levelTest", flag.ContinueOnError) + fs.SetOutput(&buf) + fs.Var(&lvl, "level", "log level") + + for _, expected := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} { + assert.NoError(t, fs.Parse([]string{"-level", expected.String()})) + assert.Equal(t, expected, lvl, "Unexpected level after parsing flag.") + assert.Equal(t, expected, lvl.Get(), "Unexpected output using flag.Getter API.") + assert.Empty(t, buf.String(), "Unexpected error output parsing level flag.") + buf.Reset() + } + + assert.Error(t, fs.Parse([]string{"-level", "nope"})) + assert.Equal( + t, + `invalid value "nope" for flag -level: unrecognized level: "nope"`, + strings.Split(buf.String(), "\n")[0], // second line is help message + "Unexpected error output from invalid flag input.", + ) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/marshaler.go b/vendor/src/go.uber.org/zap/zapcore/marshaler.go new file mode 100644 index 00000000..2627a653 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/marshaler.go @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +// ObjectMarshaler allows user-defined types to efficiently add themselves to the +// logging context, and to selectively omit information which shouldn't be +// included in logs (e.g., passwords). +type ObjectMarshaler interface { + MarshalLogObject(ObjectEncoder) error +} + +// ObjectMarshalerFunc is a type adapter that turns a function into an +// ObjectMarshaler. +type ObjectMarshalerFunc func(ObjectEncoder) error + +// MarshalLogObject calls the underlying function. +func (f ObjectMarshalerFunc) MarshalLogObject(enc ObjectEncoder) error { + return f(enc) +} + +// ArrayMarshaler allows user-defined types to efficiently add themselves to the +// logging context, and to selectively omit information which shouldn't be +// included in logs (e.g., passwords). +type ArrayMarshaler interface { + MarshalLogArray(ArrayEncoder) error +} + +// ArrayMarshalerFunc is a type adapter that turns a function into an +// ArrayMarshaler. +type ArrayMarshalerFunc func(ArrayEncoder) error + +// MarshalLogArray calls the underlying function. +func (f ArrayMarshalerFunc) MarshalLogArray(enc ArrayEncoder) error { + return f(enc) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/memory_encoder.go b/vendor/src/go.uber.org/zap/zapcore/memory_encoder.go new file mode 100644 index 00000000..5c46bc13 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/memory_encoder.go @@ -0,0 +1,179 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "time" + +// MapObjectEncoder is an ObjectEncoder backed by a simple +// map[string]interface{}. It's not fast enough for production use, but it's +// helpful in tests. +type MapObjectEncoder struct { + // Fields contains the entire encoded log context. + Fields map[string]interface{} + // cur is a pointer to the namespace we're currently writing to. + cur map[string]interface{} +} + +// NewMapObjectEncoder creates a new map-backed ObjectEncoder. +func NewMapObjectEncoder() *MapObjectEncoder { + m := make(map[string]interface{}) + return &MapObjectEncoder{ + Fields: m, + cur: m, + } +} + +// AddArray implements ObjectEncoder. +func (m *MapObjectEncoder) AddArray(key string, v ArrayMarshaler) error { + arr := &sliceArrayEncoder{} + err := v.MarshalLogArray(arr) + m.cur[key] = arr.elems + return err +} + +// AddObject implements ObjectEncoder. +func (m *MapObjectEncoder) AddObject(k string, v ObjectMarshaler) error { + newMap := NewMapObjectEncoder() + m.cur[k] = newMap.Fields + return v.MarshalLogObject(newMap) +} + +// AddBinary implements ObjectEncoder. +func (m *MapObjectEncoder) AddBinary(k string, v []byte) { m.cur[k] = v } + +// AddByteString implements ObjectEncoder. +func (m *MapObjectEncoder) AddByteString(k string, v []byte) { m.cur[k] = string(v) } + +// AddBool implements ObjectEncoder. +func (m *MapObjectEncoder) AddBool(k string, v bool) { m.cur[k] = v } + +// AddDuration implements ObjectEncoder. +func (m MapObjectEncoder) AddDuration(k string, v time.Duration) { m.cur[k] = v } + +// AddComplex128 implements ObjectEncoder. +func (m *MapObjectEncoder) AddComplex128(k string, v complex128) { m.cur[k] = v } + +// AddComplex64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddComplex64(k string, v complex64) { m.cur[k] = v } + +// AddFloat64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddFloat64(k string, v float64) { m.cur[k] = v } + +// AddFloat32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddFloat32(k string, v float32) { m.cur[k] = v } + +// AddInt implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt(k string, v int) { m.cur[k] = v } + +// AddInt64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt64(k string, v int64) { m.cur[k] = v } + +// AddInt32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt32(k string, v int32) { m.cur[k] = v } + +// AddInt16 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt16(k string, v int16) { m.cur[k] = v } + +// AddInt8 implements ObjectEncoder. +func (m *MapObjectEncoder) AddInt8(k string, v int8) { m.cur[k] = v } + +// AddString implements ObjectEncoder. +func (m *MapObjectEncoder) AddString(k string, v string) { m.cur[k] = v } + +// AddTime implements ObjectEncoder. +func (m MapObjectEncoder) AddTime(k string, v time.Time) { m.cur[k] = v } + +// AddUint implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint(k string, v uint) { m.cur[k] = v } + +// AddUint64 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint64(k string, v uint64) { m.cur[k] = v } + +// AddUint32 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint32(k string, v uint32) { m.cur[k] = v } + +// AddUint16 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint16(k string, v uint16) { m.cur[k] = v } + +// AddUint8 implements ObjectEncoder. +func (m *MapObjectEncoder) AddUint8(k string, v uint8) { m.cur[k] = v } + +// AddUintptr implements ObjectEncoder. +func (m *MapObjectEncoder) AddUintptr(k string, v uintptr) { m.cur[k] = v } + +// AddReflected implements ObjectEncoder. +func (m *MapObjectEncoder) AddReflected(k string, v interface{}) error { + m.cur[k] = v + return nil +} + +// OpenNamespace implements ObjectEncoder. +func (m *MapObjectEncoder) OpenNamespace(k string) { + ns := make(map[string]interface{}) + m.cur[k] = ns + m.cur = ns +} + +// sliceArrayEncoder is an ArrayEncoder backed by a simple []interface{}. Like +// the MapObjectEncoder, it's not designed for production use. +type sliceArrayEncoder struct { + elems []interface{} +} + +func (s *sliceArrayEncoder) AppendArray(v ArrayMarshaler) error { + enc := &sliceArrayEncoder{} + err := v.MarshalLogArray(enc) + s.elems = append(s.elems, enc.elems) + return err +} + +func (s *sliceArrayEncoder) AppendObject(v ObjectMarshaler) error { + m := NewMapObjectEncoder() + err := v.MarshalLogObject(m) + s.elems = append(s.elems, m.Fields) + return err +} + +func (s *sliceArrayEncoder) AppendReflected(v interface{}) error { + s.elems = append(s.elems, v) + return nil +} + +func (s *sliceArrayEncoder) AppendBool(v bool) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendByteString(v []byte) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendComplex128(v complex128) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendComplex64(v complex64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendDuration(v time.Duration) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendFloat64(v float64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendFloat32(v float32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt(v int) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt64(v int64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt32(v int32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt16(v int16) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendInt8(v int8) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendString(v string) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendTime(v time.Time) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint(v uint) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint64(v uint64) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint32(v uint32) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint16(v uint16) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUint8(v uint8) { s.elems = append(s.elems, v) } +func (s *sliceArrayEncoder) AppendUintptr(v uintptr) { s.elems = append(s.elems, v) } diff --git a/vendor/src/go.uber.org/zap/zapcore/memory_encoder_test.go b/vendor/src/go.uber.org/zap/zapcore/memory_encoder_test.go new file mode 100644 index 00000000..e63454a3 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/memory_encoder_test.go @@ -0,0 +1,283 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestMapObjectEncoderAdd(t *testing.T) { + // Expected output of a turducken. + wantTurducken := map[string]interface{}{ + "ducks": []interface{}{ + map[string]interface{}{"in": "chicken"}, + map[string]interface{}{"in": "chicken"}, + }, + } + + tests := []struct { + desc string + f func(ObjectEncoder) + expected interface{} + }{ + { + desc: "AddObject", + f: func(e ObjectEncoder) { + assert.NoError(t, e.AddObject("k", loggable{true}), "Expected AddObject to succeed.") + }, + expected: map[string]interface{}{"loggable": "yes"}, + }, + { + desc: "AddObject (nested)", + f: func(e ObjectEncoder) { + assert.NoError(t, e.AddObject("k", turducken{}), "Expected AddObject to succeed.") + }, + expected: wantTurducken, + }, + { + desc: "AddArray", + f: func(e ObjectEncoder) { + assert.NoError(t, e.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error { + arr.AppendBool(true) + arr.AppendBool(false) + arr.AppendBool(true) + return nil + })), "Expected AddArray to succeed.") + }, + expected: []interface{}{true, false, true}, + }, + { + desc: "AddArray (nested)", + f: func(e ObjectEncoder) { + assert.NoError(t, e.AddArray("k", turduckens(2)), "Expected AddArray to succeed.") + }, + expected: []interface{}{wantTurducken, wantTurducken}, + }, + { + desc: "AddBinary", + f: func(e ObjectEncoder) { e.AddBinary("k", []byte("foo")) }, + expected: []byte("foo"), + }, + { + desc: "AddBool", + f: func(e ObjectEncoder) { e.AddBool("k", true) }, + expected: true, + }, + { + desc: "AddComplex128", + f: func(e ObjectEncoder) { e.AddComplex128("k", 1+2i) }, + expected: 1 + 2i, + }, + { + desc: "AddComplex64", + f: func(e ObjectEncoder) { e.AddComplex64("k", 1+2i) }, + expected: complex64(1 + 2i), + }, + { + desc: "AddDuration", + f: func(e ObjectEncoder) { e.AddDuration("k", time.Millisecond) }, + expected: time.Millisecond, + }, + { + desc: "AddFloat64", + f: func(e ObjectEncoder) { e.AddFloat64("k", 3.14) }, + expected: 3.14, + }, + { + desc: "AddFloat32", + f: func(e ObjectEncoder) { e.AddFloat32("k", 3.14) }, + expected: float32(3.14), + }, + { + desc: "AddInt", + f: func(e ObjectEncoder) { e.AddInt("k", 42) }, + expected: 42, + }, + { + desc: "AddInt64", + f: func(e ObjectEncoder) { e.AddInt64("k", 42) }, + expected: int64(42), + }, + { + desc: "AddInt32", + f: func(e ObjectEncoder) { e.AddInt32("k", 42) }, + expected: int32(42), + }, + { + desc: "AddInt16", + f: func(e ObjectEncoder) { e.AddInt16("k", 42) }, + expected: int16(42), + }, + { + desc: "AddInt8", + f: func(e ObjectEncoder) { e.AddInt8("k", 42) }, + expected: int8(42), + }, + { + desc: "AddString", + f: func(e ObjectEncoder) { e.AddString("k", "v") }, + expected: "v", + }, + { + desc: "AddTime", + f: func(e ObjectEncoder) { e.AddTime("k", time.Unix(0, 100)) }, + expected: time.Unix(0, 100), + }, + { + desc: "AddUint", + f: func(e ObjectEncoder) { e.AddUint("k", 42) }, + expected: uint(42), + }, + { + desc: "AddUint64", + f: func(e ObjectEncoder) { e.AddUint64("k", 42) }, + expected: uint64(42), + }, + { + desc: "AddUint32", + f: func(e ObjectEncoder) { e.AddUint32("k", 42) }, + expected: uint32(42), + }, + { + desc: "AddUint16", + f: func(e ObjectEncoder) { e.AddUint16("k", 42) }, + expected: uint16(42), + }, + { + desc: "AddUint8", + f: func(e ObjectEncoder) { e.AddUint8("k", 42) }, + expected: uint8(42), + }, + { + desc: "AddUintptr", + f: func(e ObjectEncoder) { e.AddUintptr("k", 42) }, + expected: uintptr(42), + }, + { + desc: "AddReflected", + f: func(e ObjectEncoder) { + assert.NoError(t, e.AddReflected("k", map[string]interface{}{"foo": 5}), "Expected AddReflected to succeed.") + }, + expected: map[string]interface{}{"foo": 5}, + }, + { + desc: "OpenNamespace", + f: func(e ObjectEncoder) { + e.OpenNamespace("k") + e.AddInt("foo", 1) + e.OpenNamespace("middle") + e.AddInt("foo", 2) + e.OpenNamespace("inner") + e.AddInt("foo", 3) + }, + expected: map[string]interface{}{ + "foo": 1, + "middle": map[string]interface{}{ + "foo": 2, + "inner": map[string]interface{}{ + "foo": 3, + }, + }, + }, + }, + } + + for _, tt := range tests { + enc := NewMapObjectEncoder() + tt.f(enc) + assert.Equal(t, tt.expected, enc.Fields["k"], "Unexpected encoder output.") + } +} +func TestSliceArrayEncoderAppend(t *testing.T) { + tests := []struct { + desc string + f func(ArrayEncoder) + expected interface{} + }{ + // AppendObject and AppendArray are covered by the AddObject (nested) and + // AddArray (nested) cases above. + {"AppendBool", func(e ArrayEncoder) { e.AppendBool(true) }, true}, + {"AppendComplex128", func(e ArrayEncoder) { e.AppendComplex128(1 + 2i) }, 1 + 2i}, + {"AppendComplex64", func(e ArrayEncoder) { e.AppendComplex64(1 + 2i) }, complex64(1 + 2i)}, + {"AppendDuration", func(e ArrayEncoder) { e.AppendDuration(time.Second) }, time.Second}, + {"AppendFloat64", func(e ArrayEncoder) { e.AppendFloat64(3.14) }, 3.14}, + {"AppendFloat32", func(e ArrayEncoder) { e.AppendFloat32(3.14) }, float32(3.14)}, + {"AppendInt", func(e ArrayEncoder) { e.AppendInt(42) }, 42}, + {"AppendInt64", func(e ArrayEncoder) { e.AppendInt64(42) }, int64(42)}, + {"AppendInt32", func(e ArrayEncoder) { e.AppendInt32(42) }, int32(42)}, + {"AppendInt16", func(e ArrayEncoder) { e.AppendInt16(42) }, int16(42)}, + {"AppendInt8", func(e ArrayEncoder) { e.AppendInt8(42) }, int8(42)}, + {"AppendString", func(e ArrayEncoder) { e.AppendString("foo") }, "foo"}, + {"AppendTime", func(e ArrayEncoder) { e.AppendTime(time.Unix(0, 100)) }, time.Unix(0, 100)}, + {"AppendUint", func(e ArrayEncoder) { e.AppendUint(42) }, uint(42)}, + {"AppendUint64", func(e ArrayEncoder) { e.AppendUint64(42) }, uint64(42)}, + {"AppendUint32", func(e ArrayEncoder) { e.AppendUint32(42) }, uint32(42)}, + {"AppendUint16", func(e ArrayEncoder) { e.AppendUint16(42) }, uint16(42)}, + {"AppendUint8", func(e ArrayEncoder) { e.AppendUint8(42) }, uint8(42)}, + {"AppendUintptr", func(e ArrayEncoder) { e.AppendUintptr(42) }, uintptr(42)}, + { + desc: "AppendReflected", + f: func(e ArrayEncoder) { e.AppendReflected(map[string]interface{}{"foo": 5}) }, + expected: map[string]interface{}{"foo": 5}, + }, + { + desc: "AppendArray (arrays of arrays)", + f: func(e ArrayEncoder) { + e.AppendArray(ArrayMarshalerFunc(func(inner ArrayEncoder) error { + inner.AppendBool(true) + inner.AppendBool(false) + return nil + })) + }, + expected: []interface{}{true, false}, + }, + } + + for _, tt := range tests { + enc := NewMapObjectEncoder() + assert.NoError(t, enc.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error { + tt.f(arr) + tt.f(arr) + return nil + })), "Expected AddArray to succeed.") + + arr, ok := enc.Fields["k"].([]interface{}) + if !ok { + t.Errorf("Test case %s didn't encode an array.", tt.desc) + continue + } + assert.Equal(t, []interface{}{tt.expected, tt.expected}, arr, "Unexpected encoder output.") + } +} + +func TestMapObjectEncoderReflectionFailures(t *testing.T) { + enc := NewMapObjectEncoder() + assert.Error(t, enc.AddObject("object", loggable{false}), "Expected AddObject to fail.") + assert.Equal( + t, + map[string]interface{}{"object": map[string]interface{}{}}, + enc.Fields, + "Expected encoder to use empty values on errors.", + ) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/sampler.go b/vendor/src/go.uber.org/zap/zapcore/sampler.go new file mode 100644 index 00000000..e3164186 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/sampler.go @@ -0,0 +1,134 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "time" + + "go.uber.org/atomic" +) + +const ( + _numLevels = _maxLevel - _minLevel + 1 + _countersPerLevel = 4096 +) + +type counter struct { + resetAt atomic.Int64 + counter atomic.Uint64 +} + +type counters [_numLevels][_countersPerLevel]counter + +func newCounters() *counters { + return &counters{} +} + +func (cs *counters) get(lvl Level, key string) *counter { + i := lvl - _minLevel + j := fnv32a(key) % _countersPerLevel + return &cs[i][j] +} + +// fnv32a, adapted from "hash/fnv", but without a []byte(string) alloc +func fnv32a(s string) uint32 { + const ( + offset32 = 2166136261 + prime32 = 16777619 + ) + hash := uint32(offset32) + for i := 0; i < len(s); i++ { + hash ^= uint32(s[i]) + hash *= prime32 + } + return hash +} + +func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 { + tn := t.UnixNano() + resetAfter := c.resetAt.Load() + if resetAfter > tn { + return c.counter.Inc() + } + + c.counter.Store(1) + + newResetAfter := tn + tick.Nanoseconds() + if !c.resetAt.CAS(resetAfter, newResetAfter) { + // We raced with another goroutine trying to reset, and it also reset + // the counter to 1, so we need to reincrement the counter. + return c.counter.Inc() + } + + return 1 +} + +type sampler struct { + Core + + counts *counters + tick time.Duration + first, thereafter uint64 +} + +// NewSampler creates a Core that samples incoming entries, which caps the CPU +// and I/O load of logging while attempting to preserve a representative subset +// of your logs. +// +// Zap samples by logging the first N entries with a given level and message +// each tick. If more Entries with the same level and message are seen during +// the same interval, every Mth message is logged and the rest are dropped. +// +// Keep in mind that zap's sampling implementation is optimized for speed over +// absolute precision; under load, each tick may be slightly over- or +// under-sampled. +func NewSampler(core Core, tick time.Duration, first, thereafter int) Core { + return &sampler{ + Core: core, + tick: tick, + counts: newCounters(), + first: uint64(first), + thereafter: uint64(thereafter), + } +} + +func (s *sampler) With(fields []Field) Core { + return &sampler{ + Core: s.Core.With(fields), + tick: s.tick, + counts: s.counts, + first: s.first, + thereafter: s.thereafter, + } +} + +func (s *sampler) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + if !s.Enabled(ent.Level) { + return ce + } + + counter := s.counts.get(ent.Level, ent.Message) + n := counter.IncCheckReset(ent.Time, s.tick) + if n > s.first && (n-s.first)%s.thereafter != 0 { + return ce + } + return s.Core.Check(ent, ce) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/sampler_bench_test.go b/vendor/src/go.uber.org/zap/zapcore/sampler_bench_test.go new file mode 100644 index 00000000..3c79932b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/sampler_bench_test.go @@ -0,0 +1,230 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "fmt" + "testing" + "time" + + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +var counterTestCases = [][]string{ + // some stuff I made up + { + "foo", + "bar", + "baz", + "alpha", + "bravo", + "charlie", + "delta", + }, + + // shuf -n50 /usr/share/dict/words + { + "unbracing", + "stereotomy", + "supranervian", + "moaning", + "exchangeability", + "gunyang", + "sulcation", + "dariole", + "archheresy", + "synchronistically", + "clips", + "unsanctioned", + "Argoan", + "liparomphalus", + "layship", + "Fregatae", + "microzoology", + "glaciaria", + "Frugivora", + "patterist", + "Grossulariaceae", + "lithotint", + "bargander", + "opisthographical", + "cacography", + "chalkstone", + "nonsubstantialism", + "sardonicism", + "calamiform", + "lodginghouse", + "predisposedly", + "topotypic", + "broideress", + "outrange", + "gingivolabial", + "monoazo", + "sparlike", + "concameration", + "untoothed", + "Camorrism", + "reissuer", + "soap", + "palaiotype", + "countercharm", + "yellowbird", + "palterly", + "writinger", + "boatfalls", + "tuglike", + "underbitten", + }, + + // shuf -n100 /usr/share/dict/words + { + "rooty", + "malcultivation", + "degrade", + "pseudoindependent", + "stillatory", + "antiseptize", + "protoamphibian", + "antiar", + "Esther", + "pseudelminth", + "superfluitance", + "teallite", + "disunity", + "spirignathous", + "vergency", + "myliobatid", + "inosic", + "overabstemious", + "patriarchally", + "foreimagine", + "coetaneity", + "hemimellitene", + "hyperspatial", + "aulophyte", + "electropoion", + "antitrope", + "Amarantus", + "smaltine", + "lighthead", + "syntonically", + "incubous", + "versation", + "cirsophthalmia", + "Ulidian", + "homoeography", + "Velella", + "Hecatean", + "serfage", + "Spermaphyta", + "palatoplasty", + "electroextraction", + "aconite", + "avirulence", + "initiator", + "besmear", + "unrecognizably", + "euphoniousness", + "balbuties", + "pascuage", + "quebracho", + "Yakala", + "auriform", + "sevenbark", + "superorganism", + "telesterion", + "ensand", + "nagaika", + "anisuria", + "etching", + "soundingly", + "grumpish", + "drillmaster", + "perfumed", + "dealkylate", + "anthracitiferous", + "predefiance", + "sulphoxylate", + "freeness", + "untucking", + "misworshiper", + "Nestorianize", + "nonegoistical", + "construe", + "upstroke", + "teated", + "nasolachrymal", + "Mastodontidae", + "gallows", + "radioluminescent", + "uncourtierlike", + "phasmatrope", + "Clunisian", + "drainage", + "sootless", + "brachyfacial", + "antiheroism", + "irreligionize", + "ked", + "unfact", + "nonprofessed", + "milady", + "conjecture", + "Arctomys", + "guapilla", + "Sassenach", + "emmetrope", + "rosewort", + "raphidiferous", + "pooh", + "Tyndallize", + }, +} + +func BenchmarkSampler_Check(b *testing.B) { + for _, keys := range counterTestCases { + b.Run(fmt.Sprintf("%v keys", len(keys)), func(b *testing.B) { + fac := NewSampler( + NewCore( + NewJSONEncoder(testEncoderConfig()), + &zaptest.Discarder{}, + DebugLevel, + ), + time.Millisecond, 1, 1000) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + ent := Entry{ + Level: DebugLevel + Level(i%4), + Message: keys[i], + } + _ = fac.Check(ent, nil) + i++ + if n := len(keys); i >= n { + i -= n + } + } + }) + }) + } +} diff --git a/vendor/src/go.uber.org/zap/zapcore/sampler_test.go b/vendor/src/go.uber.org/zap/zapcore/sampler_test.go new file mode 100644 index 00000000..82588c9a --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/sampler_test.go @@ -0,0 +1,225 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "fmt" + "sync" + "testing" + "time" + + "go.uber.org/atomic" + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func fakeSampler(lvl LevelEnabler, tick time.Duration, first, thereafter int) (Core, *observer.ObservedLogs) { + core, logs := observer.New(lvl) + core = NewSampler(core, tick, first, thereafter) + return core, logs +} + +func assertSequence(t testing.TB, logs []observer.LoggedEntry, lvl Level, seq ...int64) { + seen := make([]int64, len(logs)) + for i, entry := range logs { + require.Equal(t, "", entry.Message, "Message wasn't created by writeSequence.") + require.Equal(t, 1, len(entry.Context), "Unexpected number of fields.") + require.Equal(t, lvl, entry.Level, "Unexpected level.") + f := entry.Context[0] + require.Equal(t, "iter", f.Key, "Unexpected field key.") + require.Equal(t, Int64Type, f.Type, "Unexpected field type") + seen[i] = f.Integer + } + assert.Equal(t, seq, seen, "Unexpected sequence logged at level %v.", lvl) +} + +func writeSequence(core Core, n int, lvl Level) { + // All tests using writeSequence verify that counters are shared between + // parent and child cores. + core = core.With([]Field{makeInt64Field("iter", n)}) + if ce := core.Check(Entry{Level: lvl, Time: time.Now()}, nil); ce != nil { + ce.Write() + } +} + +func TestSampler(t *testing.T) { + for _, lvl := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} { + sampler, logs := fakeSampler(DebugLevel, time.Minute, 2, 3) + + // Ensure that counts aren't shared between levels. + probeLevel := DebugLevel + if lvl == DebugLevel { + probeLevel = InfoLevel + } + for i := 0; i < 10; i++ { + writeSequence(sampler, 1, probeLevel) + } + // Clear any output. + logs.TakeAll() + + for i := 1; i < 10; i++ { + writeSequence(sampler, i, lvl) + } + assertSequence(t, logs.TakeAll(), lvl, 1, 2, 5, 8) + } +} + +func TestSamplerDisabledLevels(t *testing.T) { + sampler, logs := fakeSampler(InfoLevel, time.Minute, 1, 100) + + // Shouldn't be counted, because debug logging isn't enabled. + writeSequence(sampler, 1, DebugLevel) + writeSequence(sampler, 2, InfoLevel) + assertSequence(t, logs.TakeAll(), InfoLevel, 2) +} + +func TestSamplerTicking(t *testing.T) { + // Ensure that we're resetting the sampler's counter every tick. + sampler, logs := fakeSampler(DebugLevel, 10*time.Millisecond, 5, 10) + + // If we log five or fewer messages every tick, none of them should be + // dropped. + for tick := 0; tick < 2; tick++ { + for i := 1; i <= 5; i++ { + writeSequence(sampler, i, InfoLevel) + } + zaptest.Sleep(15 * time.Millisecond) + } + assertSequence( + t, + logs.TakeAll(), + InfoLevel, + 1, 2, 3, 4, 5, // first tick + 1, 2, 3, 4, 5, // second tick + ) + + // If we log quickly, we should drop some logs. The first five statements + // each tick should be logged, then every tenth. + for tick := 0; tick < 3; tick++ { + for i := 1; i < 18; i++ { + writeSequence(sampler, i, InfoLevel) + } + zaptest.Sleep(10 * time.Millisecond) + } + + assertSequence( + t, + logs.TakeAll(), + InfoLevel, + 1, 2, 3, 4, 5, 15, // first tick + 1, 2, 3, 4, 5, 15, // second tick + 1, 2, 3, 4, 5, 15, // third tick + ) +} + +type countingCore struct { + logs atomic.Uint32 +} + +func (c *countingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + return ce.AddCore(ent, c) +} + +func (c *countingCore) Write(Entry, []Field) error { + c.logs.Inc() + return nil +} + +func (c *countingCore) With([]Field) Core { return c } +func (*countingCore) Enabled(Level) bool { return true } +func (*countingCore) Sync() error { return nil } + +func TestSamplerConcurrent(t *testing.T) { + const ( + logsPerTick = 10 + numMessages = 5 + numTicks = 25 + numGoroutines = 10 + expectedCount = numMessages * logsPerTick * numTicks + ) + + tick := zaptest.Timeout(10 * time.Millisecond) + cc := &countingCore{} + sampler := NewSampler(cc, tick, logsPerTick, 100000) + + var ( + done atomic.Bool + wg sync.WaitGroup + ) + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + + for { + if done.Load() { + return + } + msg := fmt.Sprintf("msg%v", i%numMessages) + ent := Entry{Level: DebugLevel, Message: msg, Time: time.Now()} + if ce := sampler.Check(ent, nil); ce != nil { + ce.Write() + } + + // Give a chance for other goroutines to run. + time.Sleep(time.Microsecond) + } + }(i) + } + + time.AfterFunc(numTicks*tick, func() { + done.Store(true) + }) + wg.Wait() + + assert.InDelta( + t, + expectedCount, + cc.logs.Load(), + expectedCount/10, + "Unexpected number of logs", + ) +} + +func TestSamplerRaces(t *testing.T) { + sampler, _ := fakeSampler(DebugLevel, time.Minute, 1, 1000) + + var wg sync.WaitGroup + start := make(chan struct{}) + + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + <-start + for j := 0; j < 100; j++ { + writeSequence(sampler, j, InfoLevel) + } + wg.Done() + }() + } + + close(start) + wg.Wait() +} diff --git a/vendor/src/go.uber.org/zap/zapcore/tee.go b/vendor/src/go.uber.org/zap/zapcore/tee.go new file mode 100644 index 00000000..07a32eef --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/tee.go @@ -0,0 +1,81 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import "go.uber.org/multierr" + +type multiCore []Core + +// NewTee creates a Core that duplicates log entries into two or more +// underlying Cores. +// +// Calling it with a single Core returns the input unchanged, and calling +// it with no input returns a no-op Core. +func NewTee(cores ...Core) Core { + switch len(cores) { + case 0: + return NewNopCore() + case 1: + return cores[0] + default: + return multiCore(cores) + } +} + +func (mc multiCore) With(fields []Field) Core { + clone := make(multiCore, len(mc)) + for i := range mc { + clone[i] = mc[i].With(fields) + } + return clone +} + +func (mc multiCore) Enabled(lvl Level) bool { + for i := range mc { + if mc[i].Enabled(lvl) { + return true + } + } + return false +} + +func (mc multiCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry { + for i := range mc { + ce = mc[i].Check(ent, ce) + } + return ce +} + +func (mc multiCore) Write(ent Entry, fields []Field) error { + var err error + for i := range mc { + err = multierr.Append(err, mc[i].Write(ent, fields)) + } + return err +} + +func (mc multiCore) Sync() error { + var err error + for i := range mc { + err = multierr.Append(err, mc[i].Sync()) + } + return err +} diff --git a/vendor/src/go.uber.org/zap/zapcore/tee_logger_bench_test.go b/vendor/src/go.uber.org/zap/zapcore/tee_logger_bench_test.go new file mode 100644 index 00000000..041f10ac --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/tee_logger_bench_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "testing" + + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +func withBenchedTee(b *testing.B, f func(Core)) { + fac := NewTee( + NewCore(NewJSONEncoder(testEncoderConfig()), &zaptest.Discarder{}, DebugLevel), + NewCore(NewJSONEncoder(testEncoderConfig()), &zaptest.Discarder{}, InfoLevel), + ) + b.ResetTimer() + f(fac) +} + +func BenchmarkTeeCheck(b *testing.B) { + cases := []struct { + lvl Level + msg string + }{ + {DebugLevel, "foo"}, + {InfoLevel, "bar"}, + {WarnLevel, "baz"}, + {ErrorLevel, "babble"}, + } + withBenchedTee(b, func(core Core) { + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + tt := cases[i] + entry := Entry{Level: tt.lvl, Message: tt.msg} + if cm := core.Check(entry, nil); cm != nil { + cm.Write(Field{Key: "i", Integer: int64(i), Type: Int64Type}) + } + i = (i + 1) % len(cases) + } + }) + }) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/tee_test.go b/vendor/src/go.uber.org/zap/zapcore/tee_test.go new file mode 100644 index 00000000..754abbb8 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/tee_test.go @@ -0,0 +1,153 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore_test + +import ( + "errors" + "testing" + + . "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/assert" +) + +func withTee(f func(core Core, debugLogs, warnLogs *observer.ObservedLogs)) { + debugLogger, debugLogs := observer.New(DebugLevel) + warnLogger, warnLogs := observer.New(WarnLevel) + tee := NewTee(debugLogger, warnLogger) + f(tee, debugLogs, warnLogs) +} + +func TestTeeUnusualInput(t *testing.T) { + // Verify that Tee handles receiving one and no inputs correctly. + t.Run("one input", func(t *testing.T) { + obs, _ := observer.New(DebugLevel) + assert.Equal(t, obs, NewTee(obs), "Expected to return single inputs unchanged.") + }) + t.Run("no input", func(t *testing.T) { + assert.Equal(t, NewNopCore(), NewTee(), "Expected to return NopCore.") + }) +} + +func TestTeeCheck(t *testing.T) { + withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) { + debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"} + infoEntry := Entry{Level: InfoLevel, Message: "log-at-info"} + warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"} + errorEntry := Entry{Level: ErrorLevel, Message: "log-at-error"} + for _, ent := range []Entry{debugEntry, infoEntry, warnEntry, errorEntry} { + if ce := tee.Check(ent, nil); ce != nil { + ce.Write() + } + } + + assert.Equal(t, []observer.LoggedEntry{ + {Entry: debugEntry, Context: []Field{}}, + {Entry: infoEntry, Context: []Field{}}, + {Entry: warnEntry, Context: []Field{}}, + {Entry: errorEntry, Context: []Field{}}, + }, debugLogs.All()) + + assert.Equal(t, []observer.LoggedEntry{ + {Entry: warnEntry, Context: []Field{}}, + {Entry: errorEntry, Context: []Field{}}, + }, warnLogs.All()) + }) +} + +func TestTeeWrite(t *testing.T) { + // Calling the tee's Write method directly should always log, regardless of + // the configured level. + withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) { + debugEntry := Entry{Level: DebugLevel, Message: "log-at-debug"} + warnEntry := Entry{Level: WarnLevel, Message: "log-at-warn"} + for _, ent := range []Entry{debugEntry, warnEntry} { + tee.Write(ent, nil) + } + + for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} { + assert.Equal(t, []observer.LoggedEntry{ + {Entry: debugEntry, Context: []Field{}}, + {Entry: warnEntry, Context: []Field{}}, + }, logs.All()) + } + }) +} + +func TestTeeWith(t *testing.T) { + withTee(func(tee Core, debugLogs, warnLogs *observer.ObservedLogs) { + f := makeInt64Field("k", 42) + tee = tee.With([]Field{f}) + ent := Entry{Level: WarnLevel, Message: "log-at-warn"} + if ce := tee.Check(ent, nil); ce != nil { + ce.Write() + } + + for _, logs := range []*observer.ObservedLogs{debugLogs, warnLogs} { + assert.Equal(t, []observer.LoggedEntry{ + {Entry: ent, Context: []Field{f}}, + }, logs.All()) + } + }) +} + +func TestTeeEnabled(t *testing.T) { + infoLogger, _ := observer.New(InfoLevel) + warnLogger, _ := observer.New(WarnLevel) + tee := NewTee(infoLogger, warnLogger) + tests := []struct { + lvl Level + enabled bool + }{ + {DebugLevel, false}, + {InfoLevel, true}, + {WarnLevel, true}, + {ErrorLevel, true}, + {DPanicLevel, true}, + {PanicLevel, true}, + {FatalLevel, true}, + } + + for _, tt := range tests { + assert.Equal(t, tt.enabled, tee.Enabled(tt.lvl), "Unexpected Enabled result for level %s.", tt.lvl) + } +} + +func TestTeeSync(t *testing.T) { + infoLogger, _ := observer.New(InfoLevel) + warnLogger, _ := observer.New(WarnLevel) + tee := NewTee(infoLogger, warnLogger) + assert.NoError(t, tee.Sync(), "Unexpected error from Syncing a tee.") + + sink := &zaptest.Discarder{} + err := errors.New("failed") + sink.SetError(err) + + noSync := NewCore( + NewJSONEncoder(testEncoderConfig()), + sink, + DebugLevel, + ) + tee = NewTee(tee, noSync) + assert.Equal(t, err, tee.Sync(), "Expected an error when part of tee can't Sync.") +} diff --git a/vendor/src/go.uber.org/zap/zapcore/write_syncer.go b/vendor/src/go.uber.org/zap/zapcore/write_syncer.go new file mode 100644 index 00000000..209e25fe --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/write_syncer.go @@ -0,0 +1,123 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "io" + "sync" + + "go.uber.org/multierr" +) + +// A WriteSyncer is an io.Writer that can also flush any buffered data. Note +// that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer. +type WriteSyncer interface { + io.Writer + Sync() error +} + +// AddSync converts an io.Writer to a WriteSyncer. It attempts to be +// intelligent: if the concrete type of the io.Writer implements WriteSyncer, +// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync. +func AddSync(w io.Writer) WriteSyncer { + switch w := w.(type) { + case WriteSyncer: + return w + default: + return writerWrapper{w} + } +} + +type lockedWriteSyncer struct { + sync.Mutex + ws WriteSyncer +} + +// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In +// particular, *os.Files must be locked before use. +func Lock(ws WriteSyncer) WriteSyncer { + if _, ok := ws.(*lockedWriteSyncer); ok { + // no need to layer on another lock + return ws + } + return &lockedWriteSyncer{ws: ws} +} + +func (s *lockedWriteSyncer) Write(bs []byte) (int, error) { + s.Lock() + n, err := s.ws.Write(bs) + s.Unlock() + return n, err +} + +func (s *lockedWriteSyncer) Sync() error { + s.Lock() + err := s.ws.Sync() + s.Unlock() + return err +} + +type writerWrapper struct { + io.Writer +} + +func (w writerWrapper) Sync() error { + return nil +} + +type multiWriteSyncer []WriteSyncer + +// NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes +// and sync calls, much like io.MultiWriter. +func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { + if len(ws) == 1 { + return ws[0] + } + // Copy to protect against https://github.com/golang/go/issues/7809 + return multiWriteSyncer(append([]WriteSyncer(nil), ws...)) +} + +// See https://golang.org/src/io/multi.go +// When not all underlying syncers write the same number of bytes, +// the smallest number is returned even though Write() is called on +// all of them. +func (ws multiWriteSyncer) Write(p []byte) (int, error) { + var writeErr error + nWritten := 0 + for _, w := range ws { + n, err := w.Write(p) + writeErr = multierr.Append(writeErr, err) + if nWritten == 0 && n != 0 { + nWritten = n + } else if n < nWritten { + nWritten = n + } + } + return nWritten, writeErr +} + +func (ws multiWriteSyncer) Sync() error { + var err error + for _, w := range ws { + err = multierr.Append(err, w.Sync()) + } + return err +} diff --git a/vendor/src/go.uber.org/zap/zapcore/write_syncer_bench_test.go b/vendor/src/go.uber.org/zap/zapcore/write_syncer_bench_test.go new file mode 100644 index 00000000..0adcad85 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/write_syncer_bench_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "testing" + + "go.uber.org/zap/zaptest" +) + +func BenchmarkMultiWriteSyncer(b *testing.B) { + b.Run("2", func(b *testing.B) { + w := NewMultiWriteSyncer( + &zaptest.Discarder{}, + &zaptest.Discarder{}, + ) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + w.Write([]byte("foobarbazbabble")) + } + }) + }) + b.Run("4", func(b *testing.B) { + w := NewMultiWriteSyncer( + &zaptest.Discarder{}, + &zaptest.Discarder{}, + &zaptest.Discarder{}, + &zaptest.Discarder{}, + ) + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + w.Write([]byte("foobarbazbabble")) + } + }) + }) +} diff --git a/vendor/src/go.uber.org/zap/zapcore/write_syncer_test.go b/vendor/src/go.uber.org/zap/zapcore/write_syncer_test.go new file mode 100644 index 00000000..084b94aa --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapcore/write_syncer_test.go @@ -0,0 +1,137 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapcore + +import ( + "bytes" + "errors" + "testing" + + "io" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +type writeSyncSpy struct { + io.Writer + zaptest.Syncer +} + +func requireWriteWorks(t testing.TB, ws WriteSyncer) { + n, err := ws.Write([]byte("foo")) + require.NoError(t, err, "Unexpected error writing to WriteSyncer.") + require.Equal(t, 3, n, "Wrote an unexpected number of bytes.") +} + +func TestAddSyncWriteSyncer(t *testing.T) { + buf := &bytes.Buffer{} + concrete := &writeSyncSpy{Writer: buf} + ws := AddSync(concrete) + requireWriteWorks(t, ws) + + require.NoError(t, ws.Sync(), "Unexpected error syncing a WriteSyncer.") + require.True(t, concrete.Called(), "Expected to dispatch to concrete type's Sync method.") + + concrete.SetError(errors.New("fail")) + assert.Error(t, ws.Sync(), "Expected to propagate errors from concrete type's Sync method.") +} + +func TestAddSyncWriter(t *testing.T) { + // If we pass a plain io.Writer, make sure that we still get a WriteSyncer + // with a no-op Sync. + buf := &bytes.Buffer{} + ws := AddSync(buf) + requireWriteWorks(t, ws) + assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.") +} + +func TestNewMultiWriteSyncerWorksForSingleWriter(t *testing.T) { + w := &zaptest.Buffer{} + + ws := NewMultiWriteSyncer(w) + assert.Equal(t, w, ws, "Expected NewMultiWriteSyncer to return the same WriteSyncer object for a single argument.") + + ws.Sync() + assert.True(t, w.Called(), "Expected Sync to be called on the created WriteSyncer") +} + +func TestMultiWriteSyncerWritesBoth(t *testing.T) { + first := &bytes.Buffer{} + second := &bytes.Buffer{} + ws := NewMultiWriteSyncer(AddSync(first), AddSync(second)) + + msg := []byte("dumbledore") + n, err := ws.Write(msg) + require.NoError(t, err, "Expected successful buffer write") + assert.Equal(t, len(msg), n) + + assert.Equal(t, msg, first.Bytes()) + assert.Equal(t, msg, second.Bytes()) +} + +func TestMultiWriteSyncerFailsWrite(t *testing.T) { + ws := NewMultiWriteSyncer(AddSync(&zaptest.FailWriter{})) + _, err := ws.Write([]byte("test")) + assert.Error(t, err, "Write error should propagate") +} + +func TestMultiWriteSyncerFailsShortWrite(t *testing.T) { + ws := NewMultiWriteSyncer(AddSync(&zaptest.ShortWriter{})) + n, err := ws.Write([]byte("test")) + assert.NoError(t, err, "Expected fake-success from short write") + assert.Equal(t, 3, n, "Expected byte count to return from underlying writer") +} + +func TestWritestoAllSyncs_EvenIfFirstErrors(t *testing.T) { + failer := &zaptest.FailWriter{} + second := &bytes.Buffer{} + ws := NewMultiWriteSyncer(AddSync(failer), AddSync(second)) + + _, err := ws.Write([]byte("fail")) + assert.Error(t, err, "Expected error from call to a writer that failed") + assert.Equal(t, []byte("fail"), second.Bytes(), "Expected second sink to be written after first error") +} + +func TestMultiWriteSyncerSync_PropagatesErrors(t *testing.T) { + badsink := &zaptest.Buffer{} + badsink.SetError(errors.New("sink is full")) + ws := NewMultiWriteSyncer(&zaptest.Discarder{}, badsink) + + assert.Error(t, ws.Sync(), "Expected sync error to propagate") +} + +func TestMultiWriteSyncerSync_NoErrorsOnDiscard(t *testing.T) { + ws := NewMultiWriteSyncer(&zaptest.Discarder{}) + assert.NoError(t, ws.Sync(), "Expected error-free sync to /dev/null") +} + +func TestMultiWriteSyncerSync_AllCalled(t *testing.T) { + failed, second := &zaptest.Buffer{}, &zaptest.Buffer{} + + failed.SetError(errors.New("disposal broken")) + ws := NewMultiWriteSyncer(failed, second) + + assert.Error(t, ws.Sync(), "Expected first sink to fail") + assert.True(t, failed.Called(), "Expected first sink to have Sync method called.") + assert.True(t, second.Called(), "Expected call to Sync even with first failure.") +} diff --git a/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc.go b/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc.go new file mode 100644 index 00000000..1181e6a0 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc.go @@ -0,0 +1,100 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zapgrpc provides a logger that is compatible with grpclog. +package zapgrpc // import "go.uber.org/zap/zapgrpc" + +import "go.uber.org/zap" + +// An Option overrides a Logger's default configuration. +type Option interface { + apply(*Logger) +} + +type optionFunc func(*Logger) + +func (f optionFunc) apply(log *Logger) { + f(log) +} + +// WithDebug configures a Logger to print at zap's DebugLevel instead of +// InfoLevel. +func WithDebug() Option { + return optionFunc(func(logger *Logger) { + logger.print = (*zap.SugaredLogger).Debug + logger.printf = (*zap.SugaredLogger).Debugf + }) +} + +// NewLogger returns a new Logger. +// +// By default, Loggers print at zap's InfoLevel. +func NewLogger(l *zap.Logger, options ...Option) *Logger { + logger := &Logger{ + log: l.Sugar(), + fatal: (*zap.SugaredLogger).Fatal, + fatalf: (*zap.SugaredLogger).Fatalf, + print: (*zap.SugaredLogger).Info, + printf: (*zap.SugaredLogger).Infof, + } + for _, option := range options { + option.apply(logger) + } + return logger +} + +// Logger adapts zap's Logger to be compatible with grpclog.Logger. +type Logger struct { + log *zap.SugaredLogger + fatal func(*zap.SugaredLogger, ...interface{}) + fatalf func(*zap.SugaredLogger, string, ...interface{}) + print func(*zap.SugaredLogger, ...interface{}) + printf func(*zap.SugaredLogger, string, ...interface{}) +} + +// Fatal implements grpclog.Logger. +func (l *Logger) Fatal(args ...interface{}) { + l.fatal(l.log, args...) +} + +// Fatalf implements grpclog.Logger. +func (l *Logger) Fatalf(format string, args ...interface{}) { + l.fatalf(l.log, format, args...) +} + +// Fatalln implements grpclog.Logger. +func (l *Logger) Fatalln(args ...interface{}) { + l.fatal(l.log, args...) +} + +// Print implements grpclog.Logger. +func (l *Logger) Print(args ...interface{}) { + l.print(l.log, args...) +} + +// Printf implements grpclog.Logger. +func (l *Logger) Printf(format string, args ...interface{}) { + l.printf(l.log, format, args...) +} + +// Println implements grpclog.Logger. +func (l *Logger) Println(args ...interface{}) { + l.print(l.log, args...) +} diff --git a/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc_test.go b/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc_test.go new file mode 100644 index 00000000..036f3d76 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zapgrpc/zapgrpc_test.go @@ -0,0 +1,115 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zapgrpc + +import ( + "testing" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" + + "github.com/stretchr/testify/require" +) + +func TestLoggerInfoExpected(t *testing.T) { + checkMessages(t, zapcore.DebugLevel, nil, zapcore.InfoLevel, []string{ + "hello", + "world", + "foo", + }, func(logger *Logger) { + logger.Print("hello") + logger.Printf("world") + logger.Println("foo") + }) +} + +func TestLoggerDebugExpected(t *testing.T) { + checkMessages(t, zapcore.DebugLevel, []Option{WithDebug()}, zapcore.DebugLevel, []string{ + "hello", + "world", + "foo", + }, func(logger *Logger) { + logger.Print("hello") + logger.Printf("world") + logger.Println("foo") + }) +} + +func TestLoggerDebugSuppressed(t *testing.T) { + checkMessages(t, zapcore.InfoLevel, []Option{WithDebug()}, zapcore.DebugLevel, nil, func(logger *Logger) { + logger.Print("hello") + logger.Printf("world") + logger.Println("foo") + }) +} + +func TestLoggerFatalExpected(t *testing.T) { + checkMessages(t, zapcore.DebugLevel, nil, zapcore.FatalLevel, []string{ + "hello", + "world", + "foo", + }, func(logger *Logger) { + logger.Fatal("hello") + logger.Fatalf("world") + logger.Fatalln("foo") + }) +} + +func checkMessages( + t testing.TB, + enab zapcore.LevelEnabler, + opts []Option, + expectedLevel zapcore.Level, + expectedMessages []string, + f func(*Logger), +) { + if expectedLevel == zapcore.FatalLevel { + expectedLevel = zapcore.WarnLevel + } + withLogger(enab, opts, func(logger *Logger, observedLogs *observer.ObservedLogs) { + f(logger) + logEntries := observedLogs.All() + require.Equal(t, len(expectedMessages), len(logEntries)) + for i, logEntry := range logEntries { + require.Equal(t, expectedLevel, logEntry.Level) + require.Equal(t, expectedMessages[i], logEntry.Message) + } + }) +} + +func withLogger( + enab zapcore.LevelEnabler, + opts []Option, + f func(*Logger, *observer.ObservedLogs), +) { + core, observedLogs := observer.New(enab) + f(NewLogger(zap.New(core), append(opts, withWarn())...), observedLogs) +} + +// withWarn redirects the fatal level to the warn level, which makes testing +// easier. +func withWarn() Option { + return optionFunc(func(logger *Logger) { + logger.fatal = (*zap.SugaredLogger).Warn + logger.fatalf = (*zap.SugaredLogger).Warnf + }) +} diff --git a/vendor/src/go.uber.org/zap/zaptest/doc.go b/vendor/src/go.uber.org/zap/zaptest/doc.go new file mode 100644 index 00000000..464f5898 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/doc.go @@ -0,0 +1,27 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package zaptest provides low-level helpers for testing log output. These +// utilities are helpful in zap's own unit tests, but any assertions using +// them are strongly coupled to a single encoding. +// +// Most users should use go.uber.org/zap/zaptest/observer instead of this +// package. +package zaptest // import "go.uber.org/zap/zaptest" diff --git a/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry.go b/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry.go new file mode 100644 index 00000000..a4ea7ec3 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry.go @@ -0,0 +1,39 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package observer + +import "go.uber.org/zap/zapcore" + +// An LoggedEntry is an encoding-agnostic representation of a log message. +// Field availability is context dependant. +type LoggedEntry struct { + zapcore.Entry + Context []zapcore.Field +} + +// ContextMap returns a map for all fields in Context. +func (e LoggedEntry) ContextMap() map[string]interface{} { + encoder := zapcore.NewMapObjectEncoder() + for _, f := range e.Context { + f.AddTo(encoder) + } + return encoder.Fields +} diff --git a/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry_test.go b/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry_test.go new file mode 100644 index 00000000..50f6123b --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/observer/logged_entry_test.go @@ -0,0 +1,88 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package observer + +import ( + "testing" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + + "github.com/stretchr/testify/assert" +) + +func TestLoggedEntryContextMap(t *testing.T) { + tests := []struct { + msg string + fields []zapcore.Field + want map[string]interface{} + }{ + { + msg: "no fields", + fields: nil, + want: map[string]interface{}{}, + }, + { + msg: "simple", + fields: []zapcore.Field{ + zap.String("k1", "v"), + zap.Int64("k2", 10), + }, + want: map[string]interface{}{ + "k1": "v", + "k2": int64(10), + }, + }, + { + msg: "overwrite", + fields: []zapcore.Field{ + zap.String("k1", "v1"), + zap.String("k1", "v2"), + }, + want: map[string]interface{}{ + "k1": "v2", + }, + }, + { + msg: "nested", + fields: []zapcore.Field{ + zap.String("k1", "v1"), + zap.Namespace("nested"), + zap.String("k2", "v2"), + }, + want: map[string]interface{}{ + "k1": "v1", + "nested": map[string]interface{}{ + "k2": "v2", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.msg, func(t *testing.T) { + entry := LoggedEntry{ + Context: tt.fields, + } + assert.Equal(t, tt.want, entry.ContextMap()) + }) + } +} diff --git a/vendor/src/go.uber.org/zap/zaptest/observer/observer.go b/vendor/src/go.uber.org/zap/zaptest/observer/observer.go new file mode 100644 index 00000000..78f5be45 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/observer/observer.go @@ -0,0 +1,167 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Package observer provides a zapcore.Core that keeps an in-memory, +// encoding-agnostic repesentation of log entries. It's useful for +// applications that want to unit test their log output without tying their +// tests to a particular output encoding. +package observer // import "go.uber.org/zap/zaptest/observer" + +import ( + "strings" + "sync" + "time" + + "go.uber.org/zap/zapcore" +) + +// ObservedLogs is a concurrency-safe, ordered collection of observed logs. +type ObservedLogs struct { + mu sync.RWMutex + logs []LoggedEntry +} + +// Len returns the number of items in the collection. +func (o *ObservedLogs) Len() int { + o.mu.RLock() + n := len(o.logs) + o.mu.RUnlock() + return n +} + +// All returns a copy of all the observed logs. +func (o *ObservedLogs) All() []LoggedEntry { + o.mu.RLock() + ret := make([]LoggedEntry, len(o.logs)) + for i := range o.logs { + ret[i] = o.logs[i] + } + o.mu.RUnlock() + return ret +} + +// TakeAll returns a copy of all the observed logs, and truncates the observed +// slice. +func (o *ObservedLogs) TakeAll() []LoggedEntry { + o.mu.Lock() + ret := o.logs + o.logs = nil + o.mu.Unlock() + return ret +} + +// AllUntimed returns a copy of all the observed logs, but overwrites the +// observed timestamps with time.Time's zero value. This is useful when making +// assertions in tests. +func (o *ObservedLogs) AllUntimed() []LoggedEntry { + ret := o.All() + for i := range ret { + ret[i].Time = time.Time{} + } + return ret +} + +// FilterMessage filters entries to those that have the specified message. +func (o *ObservedLogs) FilterMessage(msg string) *ObservedLogs { + return o.filter(func(e LoggedEntry) bool { + return e.Message == msg + }) +} + +// FilterMessageSnippet filters entries to those that have a message containing the specified snippet. +func (o *ObservedLogs) FilterMessageSnippet(snippet string) *ObservedLogs { + return o.filter(func(e LoggedEntry) bool { + return strings.Contains(e.Message, snippet) + }) +} + +// FilterField filters entries to those that have the specified field. +func (o *ObservedLogs) FilterField(field zapcore.Field) *ObservedLogs { + return o.filter(func(e LoggedEntry) bool { + for _, ctxField := range e.Context { + if ctxField.Equals(field) { + return true + } + } + return false + }) +} + +func (o *ObservedLogs) filter(match func(LoggedEntry) bool) *ObservedLogs { + o.mu.RLock() + defer o.mu.RUnlock() + + var filtered []LoggedEntry + for _, entry := range o.logs { + if match(entry) { + filtered = append(filtered, entry) + } + } + return &ObservedLogs{logs: filtered} +} + +func (o *ObservedLogs) add(log LoggedEntry) { + o.mu.Lock() + o.logs = append(o.logs, log) + o.mu.Unlock() +} + +// New creates a new Core that buffers logs in memory (without any encoding). +// It's particularly useful in tests. +func New(enab zapcore.LevelEnabler) (zapcore.Core, *ObservedLogs) { + ol := &ObservedLogs{} + return &contextObserver{ + LevelEnabler: enab, + logs: ol, + }, ol +} + +type contextObserver struct { + zapcore.LevelEnabler + logs *ObservedLogs + context []zapcore.Field +} + +func (co *contextObserver) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if co.Enabled(ent.Level) { + return ce.AddCore(ent, co) + } + return ce +} + +func (co *contextObserver) With(fields []zapcore.Field) zapcore.Core { + return &contextObserver{ + LevelEnabler: co.LevelEnabler, + logs: co.logs, + context: append(co.context[:len(co.context):len(co.context)], fields...), + } +} + +func (co *contextObserver) Write(ent zapcore.Entry, fields []zapcore.Field) error { + all := make([]zapcore.Field, 0, len(fields)+len(co.context)) + all = append(all, co.context...) + all = append(all, fields...) + co.logs.add(LoggedEntry{ent, all}) + return nil +} + +func (co *contextObserver) Sync() error { + return nil +} diff --git a/vendor/src/go.uber.org/zap/zaptest/observer/observer_test.go b/vendor/src/go.uber.org/zap/zaptest/observer/observer_test.go new file mode 100644 index 00000000..e1a0da78 --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/observer/observer_test.go @@ -0,0 +1,215 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package observer_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + . "go.uber.org/zap/zaptest/observer" +) + +func assertEmpty(t testing.TB, logs *ObservedLogs) { + assert.Equal(t, 0, logs.Len(), "Expected empty ObservedLogs to have zero length.") + assert.Equal(t, []LoggedEntry{}, logs.All(), "Unexpected LoggedEntries in empty ObservedLogs.") +} + +func TestObserver(t *testing.T) { + observer, logs := New(zap.InfoLevel) + assertEmpty(t, logs) + + assert.NoError(t, observer.Sync(), "Unexpected failure in no-op Sync") + + obs := zap.New(observer).With(zap.Int("i", 1)) + obs.Info("foo") + obs.Debug("bar") + want := []LoggedEntry{{ + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "foo"}, + Context: []zapcore.Field{zap.Int("i", 1)}, + }} + + assert.Equal(t, 1, logs.Len(), "Unexpected observed logs Len.") + assert.Equal(t, want, logs.AllUntimed(), "Unexpected contents from AllUntimed.") + + all := logs.All() + require.Equal(t, 1, len(all), "Unexpected numbed of LoggedEntries returned from All.") + assert.NotEqual(t, time.Time{}, all[0].Time, "Expected non-zero time on LoggedEntry.") + + // copy & zero time for stable assertions + untimed := append([]LoggedEntry{}, all...) + untimed[0].Time = time.Time{} + assert.Equal(t, want, untimed, "Unexpected LoggedEntries from All.") + + assert.Equal(t, all, logs.TakeAll(), "Expected All and TakeAll to return identical results.") + assertEmpty(t, logs) +} + +func TestObserverWith(t *testing.T) { + sf1, logs := New(zap.InfoLevel) + + // need to pad out enough initial fields so that the underlying slice cap() + // gets ahead of its len() so that the sf3/4 With append's could choose + // not to copy (if the implementation doesn't force them) + sf1 = sf1.With([]zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)}) + + sf2 := sf1.With([]zapcore.Field{zap.Int("c", 3)}) + sf3 := sf2.With([]zapcore.Field{zap.Int("d", 4)}) + sf4 := sf2.With([]zapcore.Field{zap.Int("e", 5)}) + ent := zapcore.Entry{Level: zap.InfoLevel, Message: "hello"} + + for i, core := range []zapcore.Core{sf2, sf3, sf4} { + if ce := core.Check(ent, nil); ce != nil { + ce.Write(zap.Int("i", i)) + } + } + + assert.Equal(t, []LoggedEntry{ + { + Entry: ent, + Context: []zapcore.Field{ + zap.Int("a", 1), + zap.Int("b", 2), + zap.Int("c", 3), + zap.Int("i", 0), + }, + }, + { + Entry: ent, + Context: []zapcore.Field{ + zap.Int("a", 1), + zap.Int("b", 2), + zap.Int("c", 3), + zap.Int("d", 4), + zap.Int("i", 1), + }, + }, + { + Entry: ent, + Context: []zapcore.Field{ + zap.Int("a", 1), + zap.Int("b", 2), + zap.Int("c", 3), + zap.Int("e", 5), + zap.Int("i", 2), + }, + }, + }, logs.All(), "expected no field sharing between With siblings") +} + +func TestFilters(t *testing.T) { + logs := []LoggedEntry{ + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"}, + Context: []zapcore.Field{zap.String("fStr", "1"), zap.Int("a", 1)}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log a"}, + Context: []zapcore.Field{zap.String("fStr", "2"), zap.Int("b", 2)}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log b"}, + Context: []zapcore.Field{zap.Int("a", 1), zap.Int("b", 2)}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "log c"}, + Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns"), zap.Int("a", 2)}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "msg 1"}, + Context: []zapcore.Field{zap.Int("a", 1), zap.Namespace("ns")}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any map"}, + Context: []zapcore.Field{zap.Any("map", map[string]string{"a": "b"})}, + }, + { + Entry: zapcore.Entry{Level: zap.InfoLevel, Message: "any slice"}, + Context: []zapcore.Field{zap.Any("slice", []string{"a"})}, + }, + } + + logger, sink := New(zap.InfoLevel) + for _, log := range logs { + logger.Write(log.Entry, log.Context) + } + + tests := []struct { + msg string + filtered *ObservedLogs + want []LoggedEntry + }{ + { + msg: "filter by message", + filtered: sink.FilterMessage("log a"), + want: logs[0:2], + }, + { + msg: "filter by field", + filtered: sink.FilterField(zap.String("fStr", "1")), + want: logs[0:1], + }, + { + msg: "filter by message and field", + filtered: sink.FilterMessage("log a").FilterField(zap.Int("b", 2)), + want: logs[1:2], + }, + { + msg: "filter by field with duplicate fields", + filtered: sink.FilterField(zap.Int("a", 2)), + want: logs[3:4], + }, + { + msg: "filter doesn't match any messages", + filtered: sink.FilterMessage("no match"), + want: []LoggedEntry{}, + }, + { + msg: "filter by snippet", + filtered: sink.FilterMessageSnippet("log"), + want: logs[0:4], + }, + { + msg: "filter by snippet and field", + filtered: sink.FilterMessageSnippet("a").FilterField(zap.Int("b", 2)), + want: logs[1:2], + }, + { + msg: "filter for map", + filtered: sink.FilterField(zap.Any("map", map[string]string{"a": "b"})), + want: logs[5:6], + }, + { + msg: "filter for slice", + filtered: sink.FilterField(zap.Any("slice", []string{"a"})), + want: logs[6:7], + }, + } + + for _, tt := range tests { + got := tt.filtered.AllUntimed() + assert.Equal(t, tt.want, got, tt.msg) + } +} diff --git a/vendor/src/go.uber.org/zap/zaptest/timeout.go b/vendor/src/go.uber.org/zap/zaptest/timeout.go new file mode 100644 index 00000000..ed24c98a --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/timeout.go @@ -0,0 +1,51 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zaptest + +import ( + "log" + "os" + "strconv" + "time" +) + +var _timeoutScale = 1.0 + +// Timeout scales the provided duration by $TEST_TIMEOUT_SCALE. +func Timeout(base time.Duration) time.Duration { + return time.Duration(float64(base) * _timeoutScale) +} + +// Sleep scales the sleep duration by $TEST_TIMEOUT_SCALE. +func Sleep(base time.Duration) { + time.Sleep(Timeout(base)) +} + +func init() { + if v := os.Getenv("TEST_TIMEOUT_SCALE"); v != "" { + fv, err := strconv.ParseFloat(v, 64) + if err != nil { + panic(err) + } + _timeoutScale = fv + log.Printf("Scaling timeouts by %vx.\n", _timeoutScale) + } +} diff --git a/vendor/src/go.uber.org/zap/zaptest/writer.go b/vendor/src/go.uber.org/zap/zaptest/writer.go new file mode 100644 index 00000000..0b8b254d --- /dev/null +++ b/vendor/src/go.uber.org/zap/zaptest/writer.go @@ -0,0 +1,96 @@ +// Copyright (c) 2016 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package zaptest + +import ( + "bytes" + "errors" + "io/ioutil" + "strings" +) + +// A Syncer is a spy for the Sync portion of zapcore.WriteSyncer. +type Syncer struct { + err error + called bool +} + +// SetError sets the error that the Sync method will return. +func (s *Syncer) SetError(err error) { + s.err = err +} + +// Sync records that it was called, then returns the user-supplied error (if +// any). +func (s *Syncer) Sync() error { + s.called = true + return s.err +} + +// Called reports whether the Sync method was called. +func (s *Syncer) Called() bool { + return s.called +} + +// A Discarder sends all writes to ioutil.Discard. +type Discarder struct{ Syncer } + +// Write implements io.Writer. +func (d *Discarder) Write(b []byte) (int, error) { + return ioutil.Discard.Write(b) +} + +// FailWriter is a WriteSyncer that always returns an error on writes. +type FailWriter struct{ Syncer } + +// Write implements io.Writer. +func (w FailWriter) Write(b []byte) (int, error) { + return len(b), errors.New("failed") +} + +// ShortWriter is a WriteSyncer whose write method never fails, but +// nevertheless fails to the last byte of the input. +type ShortWriter struct{ Syncer } + +// Write implements io.Writer. +func (w ShortWriter) Write(b []byte) (int, error) { + return len(b) - 1, nil +} + +// Buffer is an implementation of zapcore.WriteSyncer that sends all writes to +// a bytes.Buffer. It has convenience methods to split the accumulated buffer +// on newlines. +type Buffer struct { + bytes.Buffer + Syncer +} + +// Lines returns the current buffer contents, split on newlines. +func (b *Buffer) Lines() []string { + output := strings.Split(b.String(), "\n") + return output[:len(output)-1] +} + +// Stripped returns the current buffer contents with the last trailing newline +// stripped. +func (b *Buffer) Stripped() string { + return strings.TrimRight(b.String(), "\n") +} diff --git a/vendor/src/golang.org/x/net/bpf/asm.go b/vendor/src/golang.org/x/net/bpf/asm.go new file mode 100644 index 00000000..15e21b18 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/asm.go @@ -0,0 +1,41 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +import "fmt" + +// Assemble converts insts into raw instructions suitable for loading +// into a BPF virtual machine. +// +// Currently, no optimization is attempted, the assembled program flow +// is exactly as provided. +func Assemble(insts []Instruction) ([]RawInstruction, error) { + ret := make([]RawInstruction, len(insts)) + var err error + for i, inst := range insts { + ret[i], err = inst.Assemble() + if err != nil { + return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err) + } + } + return ret, nil +} + +// Disassemble attempts to parse raw back into +// Instructions. Unrecognized RawInstructions are assumed to be an +// extension not implemented by this package, and are passed through +// unchanged to the output. The allDecoded value reports whether insts +// contains no RawInstructions. +func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) { + insts = make([]Instruction, len(raw)) + allDecoded = true + for i, r := range raw { + insts[i] = r.Disassemble() + if _, ok := insts[i].(RawInstruction); ok { + allDecoded = false + } + } + return insts, allDecoded +} diff --git a/vendor/src/golang.org/x/net/bpf/constants.go b/vendor/src/golang.org/x/net/bpf/constants.go new file mode 100644 index 00000000..b89ca352 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/constants.go @@ -0,0 +1,218 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +// A Register is a register of the BPF virtual machine. +type Register uint16 + +const ( + // RegA is the accumulator register. RegA is always the + // destination register of ALU operations. + RegA Register = iota + // RegX is the indirection register, used by LoadIndirect + // operations. + RegX +) + +// An ALUOp is an arithmetic or logic operation. +type ALUOp uint16 + +// ALU binary operation types. +const ( + ALUOpAdd ALUOp = iota << 4 + ALUOpSub + ALUOpMul + ALUOpDiv + ALUOpOr + ALUOpAnd + ALUOpShiftLeft + ALUOpShiftRight + aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type. + ALUOpMod + ALUOpXor +) + +// A JumpTest is a comparison operator used in conditional jumps. +type JumpTest uint16 + +// Supported operators for conditional jumps. +const ( + // K == A + JumpEqual JumpTest = iota + // K != A + JumpNotEqual + // K > A + JumpGreaterThan + // K < A + JumpLessThan + // K >= A + JumpGreaterOrEqual + // K <= A + JumpLessOrEqual + // K & A != 0 + JumpBitsSet + // K & A == 0 + JumpBitsNotSet +) + +// An Extension is a function call provided by the kernel that +// performs advanced operations that are expensive or impossible +// within the BPF virtual machine. +// +// Extensions are only implemented by the Linux kernel. +// +// TODO: should we prune this list? Some of these extensions seem +// either broken or near-impossible to use correctly, whereas other +// (len, random, ifindex) are quite useful. +type Extension int + +// Extension functions available in the Linux kernel. +const ( + // extOffset is the negative maximum number of instructions used + // to load instructions by overloading the K argument. + extOffset = -0x1000 + // ExtLen returns the length of the packet. + ExtLen Extension = 1 + // ExtProto returns the packet's L3 protocol type. + ExtProto Extension = 0 + // ExtType returns the packet's type (skb->pkt_type in the kernel) + // + // TODO: better documentation. How nice an API do we want to + // provide for these esoteric extensions? + ExtType Extension = 4 + // ExtPayloadOffset returns the offset of the packet payload, or + // the first protocol header that the kernel does not know how to + // parse. + ExtPayloadOffset Extension = 52 + // ExtInterfaceIndex returns the index of the interface on which + // the packet was received. + ExtInterfaceIndex Extension = 8 + // ExtNetlinkAttr returns the netlink attribute of type X at + // offset A. + ExtNetlinkAttr Extension = 12 + // ExtNetlinkAttrNested returns the nested netlink attribute of + // type X at offset A. + ExtNetlinkAttrNested Extension = 16 + // ExtMark returns the packet's mark value. + ExtMark Extension = 20 + // ExtQueue returns the packet's assigned hardware queue. + ExtQueue Extension = 24 + // ExtLinkLayerType returns the packet's hardware address type + // (e.g. Ethernet, Infiniband). + ExtLinkLayerType Extension = 28 + // ExtRXHash returns the packets receive hash. + // + // TODO: figure out what this rxhash actually is. + ExtRXHash Extension = 32 + // ExtCPUID returns the ID of the CPU processing the current + // packet. + ExtCPUID Extension = 36 + // ExtVLANTag returns the packet's VLAN tag. + ExtVLANTag Extension = 44 + // ExtVLANTagPresent returns non-zero if the packet has a VLAN + // tag. + // + // TODO: I think this might be a lie: it reads bit 0x1000 of the + // VLAN header, which changed meaning in recent revisions of the + // spec - this extension may now return meaningless information. + ExtVLANTagPresent Extension = 48 + // ExtVLANProto returns 0x8100 if the frame has a VLAN header, + // 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some + // other value if no VLAN information is present. + ExtVLANProto Extension = 60 + // ExtRand returns a uniformly random uint32. + ExtRand Extension = 56 +) + +// The following gives names to various bit patterns used in opcode construction. + +const ( + opMaskCls uint16 = 0x7 + // opClsLoad masks + opMaskLoadDest = 0x01 + opMaskLoadWidth = 0x18 + opMaskLoadMode = 0xe0 + // opClsALU + opMaskOperandSrc = 0x08 + opMaskOperator = 0xf0 + // opClsJump + opMaskJumpConst = 0x0f + opMaskJumpCond = 0xf0 +) + +const ( + // +---------------+-----------------+---+---+---+ + // | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 | + // +---------------+-----------------+---+---+---+ + opClsLoadA uint16 = iota + // +---------------+-----------------+---+---+---+ + // | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 | + // +---------------+-----------------+---+---+---+ + opClsLoadX + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | + // +---+---+---+---+---+---+---+---+ + opClsStoreA + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | + // +---+---+---+---+---+---+---+---+ + opClsStoreX + // +---------------+-----------------+---+---+---+ + // | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 | + // +---------------+-----------------+---+---+---+ + opClsALU + // +-----------------------------+---+---+---+---+ + // | TestOperator (4b) | 0 | 1 | 0 | 1 | + // +-----------------------------+---+---+---+---+ + opClsJump + // +---+-------------------------+---+---+---+---+ + // | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 | + // +---+-------------------------+---+---+---+---+ + opClsReturn + // +---+-------------------------+---+---+---+---+ + // | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 | + // +---+-------------------------+---+---+---+---+ + opClsMisc +) + +const ( + opAddrModeImmediate uint16 = iota << 5 + opAddrModeAbsolute + opAddrModeIndirect + opAddrModeScratch + opAddrModePacketLen // actually an extension, not an addressing mode. + opAddrModeMemShift +) + +const ( + opLoadWidth4 uint16 = iota << 3 + opLoadWidth2 + opLoadWidth1 +) + +// Operator defined by ALUOp* + +const ( + opALUSrcConstant uint16 = iota << 3 + opALUSrcX +) + +const ( + opJumpAlways = iota << 4 + opJumpEqual + opJumpGT + opJumpGE + opJumpSet +) + +const ( + opRetSrcConstant uint16 = iota << 4 + opRetSrcA +) + +const ( + opMiscTAX = 0x00 + opMiscTXA = 0x80 +) diff --git a/vendor/src/golang.org/x/net/bpf/doc.go b/vendor/src/golang.org/x/net/bpf/doc.go new file mode 100644 index 00000000..ae62feb5 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/doc.go @@ -0,0 +1,82 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Package bpf implements marshaling and unmarshaling of programs for the +Berkeley Packet Filter virtual machine, and provides a Go implementation +of the virtual machine. + +BPF's main use is to specify a packet filter for network taps, so that +the kernel doesn't have to expensively copy every packet it sees to +userspace. However, it's been repurposed to other areas where running +user code in-kernel is needed. For example, Linux's seccomp uses BPF +to apply security policies to system calls. For simplicity, this +documentation refers only to packets, but other uses of BPF have their +own data payloads. + +BPF programs run in a restricted virtual machine. It has almost no +access to kernel functions, and while conditional branches are +allowed, they can only jump forwards, to guarantee that there are no +infinite loops. + +The virtual machine + +The BPF VM is an accumulator machine. Its main register, called +register A, is an implicit source and destination in all arithmetic +and logic operations. The machine also has 16 scratch registers for +temporary storage, and an indirection register (register X) for +indirect memory access. All registers are 32 bits wide. + +Each run of a BPF program is given one packet, which is placed in the +VM's read-only "main memory". LoadAbsolute and LoadIndirect +instructions can fetch up to 32 bits at a time into register A for +examination. + +The goal of a BPF program is to produce and return a verdict (uint32), +which tells the kernel what to do with the packet. In the context of +packet filtering, the returned value is the number of bytes of the +packet to forward to userspace, or 0 to ignore the packet. Other +contexts like seccomp define their own return values. + +In order to simplify programs, attempts to read past the end of the +packet terminate the program execution with a verdict of 0 (ignore +packet). This means that the vast majority of BPF programs don't need +to do any explicit bounds checking. + +In addition to the bytes of the packet, some BPF programs have access +to extensions, which are essentially calls to kernel utility +functions. Currently, the only extensions supported by this package +are the Linux packet filter extensions. + +Examples + +This packet filter selects all ARP packets. + + bpf.Assemble([]bpf.Instruction{ + // Load "EtherType" field from the ethernet header. + bpf.LoadAbsolute{Off: 12, Size: 2}, + // Skip over the next instruction if EtherType is not ARP. + bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1}, + // Verdict is "send up to 4k of the packet to userspace." + bpf.RetConstant{Val: 4096}, + // Verdict is "ignore packet." + bpf.RetConstant{Val: 0}, + }) + +This packet filter captures a random 1% sample of traffic. + + bpf.Assemble([]bpf.Instruction{ + // Get a 32-bit random number from the Linux kernel. + bpf.LoadExtension{Num: bpf.ExtRand}, + // 1% dice roll? + bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1}, + // Capture. + bpf.RetConstant{Val: 4096}, + // Ignore. + bpf.RetConstant{Val: 0}, + }) + +*/ +package bpf // import "golang.org/x/net/bpf" diff --git a/vendor/src/golang.org/x/net/bpf/instructions.go b/vendor/src/golang.org/x/net/bpf/instructions.go new file mode 100644 index 00000000..3b4fd089 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/instructions.go @@ -0,0 +1,704 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +import "fmt" + +// An Instruction is one instruction executed by the BPF virtual +// machine. +type Instruction interface { + // Assemble assembles the Instruction into a RawInstruction. + Assemble() (RawInstruction, error) +} + +// A RawInstruction is a raw BPF virtual machine instruction. +type RawInstruction struct { + // Operation to execute. + Op uint16 + // For conditional jump instructions, the number of instructions + // to skip if the condition is true/false. + Jt uint8 + Jf uint8 + // Constant parameter. The meaning depends on the Op. + K uint32 +} + +// Assemble implements the Instruction Assemble method. +func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil } + +// Disassemble parses ri into an Instruction and returns it. If ri is +// not recognized by this package, ri itself is returned. +func (ri RawInstruction) Disassemble() Instruction { + switch ri.Op & opMaskCls { + case opClsLoadA, opClsLoadX: + reg := Register(ri.Op & opMaskLoadDest) + sz := 0 + switch ri.Op & opMaskLoadWidth { + case opLoadWidth4: + sz = 4 + case opLoadWidth2: + sz = 2 + case opLoadWidth1: + sz = 1 + default: + return ri + } + switch ri.Op & opMaskLoadMode { + case opAddrModeImmediate: + if sz != 4 { + return ri + } + return LoadConstant{Dst: reg, Val: ri.K} + case opAddrModeScratch: + if sz != 4 || ri.K > 15 { + return ri + } + return LoadScratch{Dst: reg, N: int(ri.K)} + case opAddrModeAbsolute: + if ri.K > extOffset+0xffffffff { + return LoadExtension{Num: Extension(-extOffset + ri.K)} + } + return LoadAbsolute{Size: sz, Off: ri.K} + case opAddrModeIndirect: + return LoadIndirect{Size: sz, Off: ri.K} + case opAddrModePacketLen: + if sz != 4 { + return ri + } + return LoadExtension{Num: ExtLen} + case opAddrModeMemShift: + return LoadMemShift{Off: ri.K} + default: + return ri + } + + case opClsStoreA: + if ri.Op != opClsStoreA || ri.K > 15 { + return ri + } + return StoreScratch{Src: RegA, N: int(ri.K)} + + case opClsStoreX: + if ri.Op != opClsStoreX || ri.K > 15 { + return ri + } + return StoreScratch{Src: RegX, N: int(ri.K)} + + case opClsALU: + switch op := ALUOp(ri.Op & opMaskOperator); op { + case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor: + if ri.Op&opMaskOperandSrc != 0 { + return ALUOpX{Op: op} + } + return ALUOpConstant{Op: op, Val: ri.K} + case aluOpNeg: + return NegateA{} + default: + return ri + } + + case opClsJump: + if ri.Op&opMaskJumpConst != opClsJump { + return ri + } + switch ri.Op & opMaskJumpCond { + case opJumpAlways: + return Jump{Skip: ri.K} + case opJumpEqual: + if ri.Jt == 0 { + return JumpIf{ + Cond: JumpNotEqual, + Val: ri.K, + SkipTrue: ri.Jf, + SkipFalse: 0, + } + } + return JumpIf{ + Cond: JumpEqual, + Val: ri.K, + SkipTrue: ri.Jt, + SkipFalse: ri.Jf, + } + case opJumpGT: + if ri.Jt == 0 { + return JumpIf{ + Cond: JumpLessOrEqual, + Val: ri.K, + SkipTrue: ri.Jf, + SkipFalse: 0, + } + } + return JumpIf{ + Cond: JumpGreaterThan, + Val: ri.K, + SkipTrue: ri.Jt, + SkipFalse: ri.Jf, + } + case opJumpGE: + if ri.Jt == 0 { + return JumpIf{ + Cond: JumpLessThan, + Val: ri.K, + SkipTrue: ri.Jf, + SkipFalse: 0, + } + } + return JumpIf{ + Cond: JumpGreaterOrEqual, + Val: ri.K, + SkipTrue: ri.Jt, + SkipFalse: ri.Jf, + } + case opJumpSet: + return JumpIf{ + Cond: JumpBitsSet, + Val: ri.K, + SkipTrue: ri.Jt, + SkipFalse: ri.Jf, + } + default: + return ri + } + + case opClsReturn: + switch ri.Op { + case opClsReturn | opRetSrcA: + return RetA{} + case opClsReturn | opRetSrcConstant: + return RetConstant{Val: ri.K} + default: + return ri + } + + case opClsMisc: + switch ri.Op { + case opClsMisc | opMiscTAX: + return TAX{} + case opClsMisc | opMiscTXA: + return TXA{} + default: + return ri + } + + default: + panic("unreachable") // switch is exhaustive on the bit pattern + } +} + +// LoadConstant loads Val into register Dst. +type LoadConstant struct { + Dst Register + Val uint32 +} + +// Assemble implements the Instruction Assemble method. +func (a LoadConstant) Assemble() (RawInstruction, error) { + return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val) +} + +// String returns the the instruction in assembler notation. +func (a LoadConstant) String() string { + switch a.Dst { + case RegA: + return fmt.Sprintf("ld #%d", a.Val) + case RegX: + return fmt.Sprintf("ldx #%d", a.Val) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// LoadScratch loads scratch[N] into register Dst. +type LoadScratch struct { + Dst Register + N int // 0-15 +} + +// Assemble implements the Instruction Assemble method. +func (a LoadScratch) Assemble() (RawInstruction, error) { + if a.N < 0 || a.N > 15 { + return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N) + } + return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N)) +} + +// String returns the the instruction in assembler notation. +func (a LoadScratch) String() string { + switch a.Dst { + case RegA: + return fmt.Sprintf("ld M[%d]", a.N) + case RegX: + return fmt.Sprintf("ldx M[%d]", a.N) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// LoadAbsolute loads packet[Off:Off+Size] as an integer value into +// register A. +type LoadAbsolute struct { + Off uint32 + Size int // 1, 2 or 4 +} + +// Assemble implements the Instruction Assemble method. +func (a LoadAbsolute) Assemble() (RawInstruction, error) { + return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off) +} + +// String returns the the instruction in assembler notation. +func (a LoadAbsolute) String() string { + switch a.Size { + case 1: // byte + return fmt.Sprintf("ldb [%d]", a.Off) + case 2: // half word + return fmt.Sprintf("ldh [%d]", a.Off) + case 4: // word + if a.Off > extOffset+0xffffffff { + return LoadExtension{Num: Extension(a.Off + 0x1000)}.String() + } + return fmt.Sprintf("ld [%d]", a.Off) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value +// into register A. +type LoadIndirect struct { + Off uint32 + Size int // 1, 2 or 4 +} + +// Assemble implements the Instruction Assemble method. +func (a LoadIndirect) Assemble() (RawInstruction, error) { + return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off) +} + +// String returns the the instruction in assembler notation. +func (a LoadIndirect) String() string { + switch a.Size { + case 1: // byte + return fmt.Sprintf("ldb [x + %d]", a.Off) + case 2: // half word + return fmt.Sprintf("ldh [x + %d]", a.Off) + case 4: // word + return fmt.Sprintf("ld [x + %d]", a.Off) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// LoadMemShift multiplies the first 4 bits of the byte at packet[Off] +// by 4 and stores the result in register X. +// +// This instruction is mainly useful to load into X the length of an +// IPv4 packet header in a single instruction, rather than have to do +// the arithmetic on the header's first byte by hand. +type LoadMemShift struct { + Off uint32 +} + +// Assemble implements the Instruction Assemble method. +func (a LoadMemShift) Assemble() (RawInstruction, error) { + return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off) +} + +// String returns the the instruction in assembler notation. +func (a LoadMemShift) String() string { + return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off) +} + +// LoadExtension invokes a linux-specific extension and stores the +// result in register A. +type LoadExtension struct { + Num Extension +} + +// Assemble implements the Instruction Assemble method. +func (a LoadExtension) Assemble() (RawInstruction, error) { + if a.Num == ExtLen { + return assembleLoad(RegA, 4, opAddrModePacketLen, 0) + } + return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num)) +} + +// String returns the the instruction in assembler notation. +func (a LoadExtension) String() string { + switch a.Num { + case ExtLen: + return "ld #len" + case ExtProto: + return "ld #proto" + case ExtType: + return "ld #type" + case ExtPayloadOffset: + return "ld #poff" + case ExtInterfaceIndex: + return "ld #ifidx" + case ExtNetlinkAttr: + return "ld #nla" + case ExtNetlinkAttrNested: + return "ld #nlan" + case ExtMark: + return "ld #mark" + case ExtQueue: + return "ld #queue" + case ExtLinkLayerType: + return "ld #hatype" + case ExtRXHash: + return "ld #rxhash" + case ExtCPUID: + return "ld #cpu" + case ExtVLANTag: + return "ld #vlan_tci" + case ExtVLANTagPresent: + return "ld #vlan_avail" + case ExtVLANProto: + return "ld #vlan_tpid" + case ExtRand: + return "ld #rand" + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// StoreScratch stores register Src into scratch[N]. +type StoreScratch struct { + Src Register + N int // 0-15 +} + +// Assemble implements the Instruction Assemble method. +func (a StoreScratch) Assemble() (RawInstruction, error) { + if a.N < 0 || a.N > 15 { + return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N) + } + var op uint16 + switch a.Src { + case RegA: + op = opClsStoreA + case RegX: + op = opClsStoreX + default: + return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src) + } + + return RawInstruction{ + Op: op, + K: uint32(a.N), + }, nil +} + +// String returns the the instruction in assembler notation. +func (a StoreScratch) String() string { + switch a.Src { + case RegA: + return fmt.Sprintf("st M[%d]", a.N) + case RegX: + return fmt.Sprintf("stx M[%d]", a.N) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// ALUOpConstant executes A = A Val. +type ALUOpConstant struct { + Op ALUOp + Val uint32 +} + +// Assemble implements the Instruction Assemble method. +func (a ALUOpConstant) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsALU | opALUSrcConstant | uint16(a.Op), + K: a.Val, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a ALUOpConstant) String() string { + switch a.Op { + case ALUOpAdd: + return fmt.Sprintf("add #%d", a.Val) + case ALUOpSub: + return fmt.Sprintf("sub #%d", a.Val) + case ALUOpMul: + return fmt.Sprintf("mul #%d", a.Val) + case ALUOpDiv: + return fmt.Sprintf("div #%d", a.Val) + case ALUOpMod: + return fmt.Sprintf("mod #%d", a.Val) + case ALUOpAnd: + return fmt.Sprintf("and #%d", a.Val) + case ALUOpOr: + return fmt.Sprintf("or #%d", a.Val) + case ALUOpXor: + return fmt.Sprintf("xor #%d", a.Val) + case ALUOpShiftLeft: + return fmt.Sprintf("lsh #%d", a.Val) + case ALUOpShiftRight: + return fmt.Sprintf("rsh #%d", a.Val) + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// ALUOpX executes A = A X +type ALUOpX struct { + Op ALUOp +} + +// Assemble implements the Instruction Assemble method. +func (a ALUOpX) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsALU | opALUSrcX | uint16(a.Op), + }, nil +} + +// String returns the the instruction in assembler notation. +func (a ALUOpX) String() string { + switch a.Op { + case ALUOpAdd: + return "add x" + case ALUOpSub: + return "sub x" + case ALUOpMul: + return "mul x" + case ALUOpDiv: + return "div x" + case ALUOpMod: + return "mod x" + case ALUOpAnd: + return "and x" + case ALUOpOr: + return "or x" + case ALUOpXor: + return "xor x" + case ALUOpShiftLeft: + return "lsh x" + case ALUOpShiftRight: + return "rsh x" + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +// NegateA executes A = -A. +type NegateA struct{} + +// Assemble implements the Instruction Assemble method. +func (a NegateA) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsALU | uint16(aluOpNeg), + }, nil +} + +// String returns the the instruction in assembler notation. +func (a NegateA) String() string { + return fmt.Sprintf("neg") +} + +// Jump skips the following Skip instructions in the program. +type Jump struct { + Skip uint32 +} + +// Assemble implements the Instruction Assemble method. +func (a Jump) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsJump | opJumpAlways, + K: a.Skip, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a Jump) String() string { + return fmt.Sprintf("ja %d", a.Skip) +} + +// JumpIf skips the following Skip instructions in the program if A +// Val is true. +type JumpIf struct { + Cond JumpTest + Val uint32 + SkipTrue uint8 + SkipFalse uint8 +} + +// Assemble implements the Instruction Assemble method. +func (a JumpIf) Assemble() (RawInstruction, error) { + var ( + cond uint16 + flip bool + ) + switch a.Cond { + case JumpEqual: + cond = opJumpEqual + case JumpNotEqual: + cond, flip = opJumpEqual, true + case JumpGreaterThan: + cond = opJumpGT + case JumpLessThan: + cond, flip = opJumpGE, true + case JumpGreaterOrEqual: + cond = opJumpGE + case JumpLessOrEqual: + cond, flip = opJumpGT, true + case JumpBitsSet: + cond = opJumpSet + case JumpBitsNotSet: + cond, flip = opJumpSet, true + default: + return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", a.Cond) + } + jt, jf := a.SkipTrue, a.SkipFalse + if flip { + jt, jf = jf, jt + } + return RawInstruction{ + Op: opClsJump | cond, + Jt: jt, + Jf: jf, + K: a.Val, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a JumpIf) String() string { + switch a.Cond { + // K == A + case JumpEqual: + return conditionalJump(a, "jeq", "jneq") + // K != A + case JumpNotEqual: + return fmt.Sprintf("jneq #%d,%d", a.Val, a.SkipTrue) + // K > A + case JumpGreaterThan: + return conditionalJump(a, "jgt", "jle") + // K < A + case JumpLessThan: + return fmt.Sprintf("jlt #%d,%d", a.Val, a.SkipTrue) + // K >= A + case JumpGreaterOrEqual: + return conditionalJump(a, "jge", "jlt") + // K <= A + case JumpLessOrEqual: + return fmt.Sprintf("jle #%d,%d", a.Val, a.SkipTrue) + // K & A != 0 + case JumpBitsSet: + if a.SkipFalse > 0 { + return fmt.Sprintf("jset #%d,%d,%d", a.Val, a.SkipTrue, a.SkipFalse) + } + return fmt.Sprintf("jset #%d,%d", a.Val, a.SkipTrue) + // K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips + case JumpBitsNotSet: + return JumpIf{Cond: JumpBitsSet, SkipTrue: a.SkipFalse, SkipFalse: a.SkipTrue, Val: a.Val}.String() + default: + return fmt.Sprintf("unknown instruction: %#v", a) + } +} + +func conditionalJump(inst JumpIf, positiveJump, negativeJump string) string { + if inst.SkipTrue > 0 { + if inst.SkipFalse > 0 { + return fmt.Sprintf("%s #%d,%d,%d", positiveJump, inst.Val, inst.SkipTrue, inst.SkipFalse) + } + return fmt.Sprintf("%s #%d,%d", positiveJump, inst.Val, inst.SkipTrue) + } + return fmt.Sprintf("%s #%d,%d", negativeJump, inst.Val, inst.SkipFalse) +} + +// RetA exits the BPF program, returning the value of register A. +type RetA struct{} + +// Assemble implements the Instruction Assemble method. +func (a RetA) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsReturn | opRetSrcA, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a RetA) String() string { + return fmt.Sprintf("ret a") +} + +// RetConstant exits the BPF program, returning a constant value. +type RetConstant struct { + Val uint32 +} + +// Assemble implements the Instruction Assemble method. +func (a RetConstant) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsReturn | opRetSrcConstant, + K: a.Val, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a RetConstant) String() string { + return fmt.Sprintf("ret #%d", a.Val) +} + +// TXA copies the value of register X to register A. +type TXA struct{} + +// Assemble implements the Instruction Assemble method. +func (a TXA) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsMisc | opMiscTXA, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a TXA) String() string { + return fmt.Sprintf("txa") +} + +// TAX copies the value of register A to register X. +type TAX struct{} + +// Assemble implements the Instruction Assemble method. +func (a TAX) Assemble() (RawInstruction, error) { + return RawInstruction{ + Op: opClsMisc | opMiscTAX, + }, nil +} + +// String returns the the instruction in assembler notation. +func (a TAX) String() string { + return fmt.Sprintf("tax") +} + +func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) { + var ( + cls uint16 + sz uint16 + ) + switch dst { + case RegA: + cls = opClsLoadA + case RegX: + cls = opClsLoadX + default: + return RawInstruction{}, fmt.Errorf("invalid target register %v", dst) + } + switch loadSize { + case 1: + sz = opLoadWidth1 + case 2: + sz = opLoadWidth2 + case 4: + sz = opLoadWidth4 + default: + return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz) + } + return RawInstruction{ + Op: cls | sz | mode, + K: k, + }, nil +} diff --git a/vendor/src/golang.org/x/net/bpf/instructions_test.go b/vendor/src/golang.org/x/net/bpf/instructions_test.go new file mode 100644 index 00000000..dde474ab --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/instructions_test.go @@ -0,0 +1,525 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +import ( + "fmt" + "io/ioutil" + "reflect" + "strconv" + "strings" + "testing" +) + +// This is a direct translation of the program in +// testdata/all_instructions.txt. +var allInstructions = []Instruction{ + LoadConstant{Dst: RegA, Val: 42}, + LoadConstant{Dst: RegX, Val: 42}, + + LoadScratch{Dst: RegA, N: 3}, + LoadScratch{Dst: RegX, N: 3}, + + LoadAbsolute{Off: 42, Size: 1}, + LoadAbsolute{Off: 42, Size: 2}, + LoadAbsolute{Off: 42, Size: 4}, + + LoadIndirect{Off: 42, Size: 1}, + LoadIndirect{Off: 42, Size: 2}, + LoadIndirect{Off: 42, Size: 4}, + + LoadMemShift{Off: 42}, + + LoadExtension{Num: ExtLen}, + LoadExtension{Num: ExtProto}, + LoadExtension{Num: ExtType}, + LoadExtension{Num: ExtRand}, + + StoreScratch{Src: RegA, N: 3}, + StoreScratch{Src: RegX, N: 3}, + + ALUOpConstant{Op: ALUOpAdd, Val: 42}, + ALUOpConstant{Op: ALUOpSub, Val: 42}, + ALUOpConstant{Op: ALUOpMul, Val: 42}, + ALUOpConstant{Op: ALUOpDiv, Val: 42}, + ALUOpConstant{Op: ALUOpOr, Val: 42}, + ALUOpConstant{Op: ALUOpAnd, Val: 42}, + ALUOpConstant{Op: ALUOpShiftLeft, Val: 42}, + ALUOpConstant{Op: ALUOpShiftRight, Val: 42}, + ALUOpConstant{Op: ALUOpMod, Val: 42}, + ALUOpConstant{Op: ALUOpXor, Val: 42}, + + ALUOpX{Op: ALUOpAdd}, + ALUOpX{Op: ALUOpSub}, + ALUOpX{Op: ALUOpMul}, + ALUOpX{Op: ALUOpDiv}, + ALUOpX{Op: ALUOpOr}, + ALUOpX{Op: ALUOpAnd}, + ALUOpX{Op: ALUOpShiftLeft}, + ALUOpX{Op: ALUOpShiftRight}, + ALUOpX{Op: ALUOpMod}, + ALUOpX{Op: ALUOpXor}, + + NegateA{}, + + Jump{Skip: 10}, + JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9}, + JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8}, + JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7}, + JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6}, + JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5}, + JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4}, + JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3}, + + TAX{}, + TXA{}, + + RetA{}, + RetConstant{Val: 42}, +} +var allInstructionsExpected = "testdata/all_instructions.bpf" + +// Check that we produce the same output as the canonical bpf_asm +// linux kernel tool. +func TestInterop(t *testing.T) { + out, err := Assemble(allInstructions) + if err != nil { + t.Fatalf("assembly of allInstructions program failed: %s", err) + } + t.Logf("Assembled program is %d instructions long", len(out)) + + bs, err := ioutil.ReadFile(allInstructionsExpected) + if err != nil { + t.Fatalf("reading %s: %s", allInstructionsExpected, err) + } + // First statement is the number of statements, last statement is + // empty. We just ignore both and rely on slice length. + stmts := strings.Split(string(bs), ",") + if len(stmts)-2 != len(out) { + t.Fatalf("test program lengths don't match: %s has %d, Go implementation has %d", allInstructionsExpected, len(stmts)-2, len(allInstructions)) + } + + for i, stmt := range stmts[1 : len(stmts)-2] { + nums := strings.Split(stmt, " ") + if len(nums) != 4 { + t.Fatalf("malformed instruction %d in %s: %s", i+1, allInstructionsExpected, stmt) + } + + actual := out[i] + + op, err := strconv.ParseUint(nums[0], 10, 16) + if err != nil { + t.Fatalf("malformed opcode %s in instruction %d of %s", nums[0], i+1, allInstructionsExpected) + } + if actual.Op != uint16(op) { + t.Errorf("opcode mismatch on instruction %d (%#v): got 0x%02x, want 0x%02x", i+1, allInstructions[i], actual.Op, op) + } + + jt, err := strconv.ParseUint(nums[1], 10, 8) + if err != nil { + t.Fatalf("malformed jt offset %s in instruction %d of %s", nums[1], i+1, allInstructionsExpected) + } + if actual.Jt != uint8(jt) { + t.Errorf("jt mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jt, jt) + } + + jf, err := strconv.ParseUint(nums[2], 10, 8) + if err != nil { + t.Fatalf("malformed jf offset %s in instruction %d of %s", nums[2], i+1, allInstructionsExpected) + } + if actual.Jf != uint8(jf) { + t.Errorf("jf mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.Jf, jf) + } + + k, err := strconv.ParseUint(nums[3], 10, 32) + if err != nil { + t.Fatalf("malformed constant %s in instruction %d of %s", nums[3], i+1, allInstructionsExpected) + } + if actual.K != uint32(k) { + t.Errorf("constant mismatch on instruction %d (%#v): got %d, want %d", i+1, allInstructions[i], actual.K, k) + } + } +} + +// Check that assembly and disassembly match each other. +func TestAsmDisasm(t *testing.T) { + prog1, err := Assemble(allInstructions) + if err != nil { + t.Fatalf("assembly of allInstructions program failed: %s", err) + } + t.Logf("Assembled program is %d instructions long", len(prog1)) + + got, allDecoded := Disassemble(prog1) + if !allDecoded { + t.Errorf("Disassemble(Assemble(allInstructions)) produced unrecognized instructions:") + for i, inst := range got { + if r, ok := inst.(RawInstruction); ok { + t.Logf(" insn %d, %#v --> %#v", i+1, allInstructions[i], r) + } + } + } + + if len(allInstructions) != len(got) { + t.Fatalf("disassembly changed program size: %d insns before, %d insns after", len(allInstructions), len(got)) + } + if !reflect.DeepEqual(allInstructions, got) { + t.Errorf("program mutated by disassembly:") + for i := range got { + if !reflect.DeepEqual(allInstructions[i], got[i]) { + t.Logf(" insn %d, s: %#v, p1: %#v, got: %#v", i+1, allInstructions[i], prog1[i], got[i]) + } + } + } +} + +type InvalidInstruction struct{} + +func (a InvalidInstruction) Assemble() (RawInstruction, error) { + return RawInstruction{}, fmt.Errorf("Invalid Instruction") +} + +func (a InvalidInstruction) String() string { + return fmt.Sprintf("unknown instruction: %#v", a) +} + +func TestString(t *testing.T) { + testCases := []struct { + instruction Instruction + assembler string + }{ + { + instruction: LoadConstant{Dst: RegA, Val: 42}, + assembler: "ld #42", + }, + { + instruction: LoadConstant{Dst: RegX, Val: 42}, + assembler: "ldx #42", + }, + { + instruction: LoadConstant{Dst: 0xffff, Val: 42}, + assembler: "unknown instruction: bpf.LoadConstant{Dst:0xffff, Val:0x2a}", + }, + { + instruction: LoadScratch{Dst: RegA, N: 3}, + assembler: "ld M[3]", + }, + { + instruction: LoadScratch{Dst: RegX, N: 3}, + assembler: "ldx M[3]", + }, + { + instruction: LoadScratch{Dst: 0xffff, N: 3}, + assembler: "unknown instruction: bpf.LoadScratch{Dst:0xffff, N:3}", + }, + { + instruction: LoadAbsolute{Off: 42, Size: 1}, + assembler: "ldb [42]", + }, + { + instruction: LoadAbsolute{Off: 42, Size: 2}, + assembler: "ldh [42]", + }, + { + instruction: LoadAbsolute{Off: 42, Size: 4}, + assembler: "ld [42]", + }, + { + instruction: LoadAbsolute{Off: 42, Size: -1}, + assembler: "unknown instruction: bpf.LoadAbsolute{Off:0x2a, Size:-1}", + }, + { + instruction: LoadIndirect{Off: 42, Size: 1}, + assembler: "ldb [x + 42]", + }, + { + instruction: LoadIndirect{Off: 42, Size: 2}, + assembler: "ldh [x + 42]", + }, + { + instruction: LoadIndirect{Off: 42, Size: 4}, + assembler: "ld [x + 42]", + }, + { + instruction: LoadIndirect{Off: 42, Size: -1}, + assembler: "unknown instruction: bpf.LoadIndirect{Off:0x2a, Size:-1}", + }, + { + instruction: LoadMemShift{Off: 42}, + assembler: "ldx 4*([42]&0xf)", + }, + { + instruction: LoadExtension{Num: ExtLen}, + assembler: "ld #len", + }, + { + instruction: LoadExtension{Num: ExtProto}, + assembler: "ld #proto", + }, + { + instruction: LoadExtension{Num: ExtType}, + assembler: "ld #type", + }, + { + instruction: LoadExtension{Num: ExtPayloadOffset}, + assembler: "ld #poff", + }, + { + instruction: LoadExtension{Num: ExtInterfaceIndex}, + assembler: "ld #ifidx", + }, + { + instruction: LoadExtension{Num: ExtNetlinkAttr}, + assembler: "ld #nla", + }, + { + instruction: LoadExtension{Num: ExtNetlinkAttrNested}, + assembler: "ld #nlan", + }, + { + instruction: LoadExtension{Num: ExtMark}, + assembler: "ld #mark", + }, + { + instruction: LoadExtension{Num: ExtQueue}, + assembler: "ld #queue", + }, + { + instruction: LoadExtension{Num: ExtLinkLayerType}, + assembler: "ld #hatype", + }, + { + instruction: LoadExtension{Num: ExtRXHash}, + assembler: "ld #rxhash", + }, + { + instruction: LoadExtension{Num: ExtCPUID}, + assembler: "ld #cpu", + }, + { + instruction: LoadExtension{Num: ExtVLANTag}, + assembler: "ld #vlan_tci", + }, + { + instruction: LoadExtension{Num: ExtVLANTagPresent}, + assembler: "ld #vlan_avail", + }, + { + instruction: LoadExtension{Num: ExtVLANProto}, + assembler: "ld #vlan_tpid", + }, + { + instruction: LoadExtension{Num: ExtRand}, + assembler: "ld #rand", + }, + { + instruction: LoadAbsolute{Off: 0xfffff038, Size: 4}, + assembler: "ld #rand", + }, + { + instruction: LoadExtension{Num: 0xfff}, + assembler: "unknown instruction: bpf.LoadExtension{Num:4095}", + }, + { + instruction: StoreScratch{Src: RegA, N: 3}, + assembler: "st M[3]", + }, + { + instruction: StoreScratch{Src: RegX, N: 3}, + assembler: "stx M[3]", + }, + { + instruction: StoreScratch{Src: 0xffff, N: 3}, + assembler: "unknown instruction: bpf.StoreScratch{Src:0xffff, N:3}", + }, + { + instruction: ALUOpConstant{Op: ALUOpAdd, Val: 42}, + assembler: "add #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpSub, Val: 42}, + assembler: "sub #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpMul, Val: 42}, + assembler: "mul #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpDiv, Val: 42}, + assembler: "div #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpOr, Val: 42}, + assembler: "or #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpAnd, Val: 42}, + assembler: "and #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpShiftLeft, Val: 42}, + assembler: "lsh #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpShiftRight, Val: 42}, + assembler: "rsh #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpMod, Val: 42}, + assembler: "mod #42", + }, + { + instruction: ALUOpConstant{Op: ALUOpXor, Val: 42}, + assembler: "xor #42", + }, + { + instruction: ALUOpConstant{Op: 0xffff, Val: 42}, + assembler: "unknown instruction: bpf.ALUOpConstant{Op:0xffff, Val:0x2a}", + }, + { + instruction: ALUOpX{Op: ALUOpAdd}, + assembler: "add x", + }, + { + instruction: ALUOpX{Op: ALUOpSub}, + assembler: "sub x", + }, + { + instruction: ALUOpX{Op: ALUOpMul}, + assembler: "mul x", + }, + { + instruction: ALUOpX{Op: ALUOpDiv}, + assembler: "div x", + }, + { + instruction: ALUOpX{Op: ALUOpOr}, + assembler: "or x", + }, + { + instruction: ALUOpX{Op: ALUOpAnd}, + assembler: "and x", + }, + { + instruction: ALUOpX{Op: ALUOpShiftLeft}, + assembler: "lsh x", + }, + { + instruction: ALUOpX{Op: ALUOpShiftRight}, + assembler: "rsh x", + }, + { + instruction: ALUOpX{Op: ALUOpMod}, + assembler: "mod x", + }, + { + instruction: ALUOpX{Op: ALUOpXor}, + assembler: "xor x", + }, + { + instruction: ALUOpX{Op: 0xffff}, + assembler: "unknown instruction: bpf.ALUOpX{Op:0xffff}", + }, + { + instruction: NegateA{}, + assembler: "neg", + }, + { + instruction: Jump{Skip: 10}, + assembler: "ja 10", + }, + { + instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9}, + assembler: "jeq #42,8,9", + }, + { + instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8}, + assembler: "jeq #42,8", + }, + { + instruction: JumpIf{Cond: JumpEqual, Val: 42, SkipFalse: 8}, + assembler: "jneq #42,8", + }, + { + instruction: JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8}, + assembler: "jneq #42,8", + }, + { + instruction: JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7}, + assembler: "jlt #42,7", + }, + { + instruction: JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6}, + assembler: "jle #42,6", + }, + { + instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5}, + assembler: "jgt #42,4,5", + }, + { + instruction: JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4}, + assembler: "jgt #42,4", + }, + { + instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4}, + assembler: "jge #42,3,4", + }, + { + instruction: JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3}, + assembler: "jge #42,3", + }, + { + instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3}, + assembler: "jset #42,2,3", + }, + { + instruction: JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2}, + assembler: "jset #42,2", + }, + { + instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2, SkipFalse: 3}, + assembler: "jset #42,3,2", + }, + { + instruction: JumpIf{Cond: JumpBitsNotSet, Val: 42, SkipTrue: 2}, + assembler: "jset #42,0,2", + }, + { + instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2}, + assembler: "unknown instruction: bpf.JumpIf{Cond:0xffff, Val:0x2a, SkipTrue:0x1, SkipFalse:0x2}", + }, + { + instruction: TAX{}, + assembler: "tax", + }, + { + instruction: TXA{}, + assembler: "txa", + }, + { + instruction: RetA{}, + assembler: "ret a", + }, + { + instruction: RetConstant{Val: 42}, + assembler: "ret #42", + }, + // Invalid instruction + { + instruction: InvalidInstruction{}, + assembler: "unknown instruction: bpf.InvalidInstruction{}", + }, + } + + for _, testCase := range testCases { + if input, ok := testCase.instruction.(fmt.Stringer); ok { + got := input.String() + if got != testCase.assembler { + t.Errorf("String did not return expected assembler notation, expected: %s, got: %s", testCase.assembler, got) + } + } else { + t.Errorf("Instruction %#v is not a fmt.Stringer", testCase.instruction) + } + } +} diff --git a/vendor/src/golang.org/x/net/bpf/setter.go b/vendor/src/golang.org/x/net/bpf/setter.go new file mode 100644 index 00000000..43e35f0a --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/setter.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +// A Setter is a type which can attach a compiled BPF filter to itself. +type Setter interface { + SetBPF(filter []RawInstruction) error +} diff --git a/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.bpf b/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.bpf new file mode 100644 index 00000000..f8714406 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.bpf @@ -0,0 +1 @@ +50,0 0 0 42,1 0 0 42,96 0 0 3,97 0 0 3,48 0 0 42,40 0 0 42,32 0 0 42,80 0 0 42,72 0 0 42,64 0 0 42,177 0 0 42,128 0 0 0,32 0 0 4294963200,32 0 0 4294963204,32 0 0 4294963256,2 0 0 3,3 0 0 3,4 0 0 42,20 0 0 42,36 0 0 42,52 0 0 42,68 0 0 42,84 0 0 42,100 0 0 42,116 0 0 42,148 0 0 42,164 0 0 42,12 0 0 0,28 0 0 0,44 0 0 0,60 0 0 0,76 0 0 0,92 0 0 0,108 0 0 0,124 0 0 0,156 0 0 0,172 0 0 0,132 0 0 0,5 0 0 10,21 8 9 42,21 0 8 42,53 0 7 42,37 0 6 42,37 4 5 42,53 3 4 42,69 2 3 42,7 0 0 0,135 0 0 0,22 0 0 0,6 0 0 0, diff --git a/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.txt b/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.txt new file mode 100644 index 00000000..30455015 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/testdata/all_instructions.txt @@ -0,0 +1,79 @@ +# This filter is compiled to all_instructions.bpf by the `bpf_asm` +# tool, which can be found in the linux kernel source tree under +# tools/net. + +# Load immediate +ld #42 +ldx #42 + +# Load scratch +ld M[3] +ldx M[3] + +# Load absolute +ldb [42] +ldh [42] +ld [42] + +# Load indirect +ldb [x + 42] +ldh [x + 42] +ld [x + 42] + +# Load IPv4 header length +ldx 4*([42]&0xf) + +# Run extension function +ld #len +ld #proto +ld #type +ld #rand + +# Store scratch +st M[3] +stx M[3] + +# A constant +add #42 +sub #42 +mul #42 +div #42 +or #42 +and #42 +lsh #42 +rsh #42 +mod #42 +xor #42 + +# A X +add x +sub x +mul x +div x +or x +and x +lsh x +rsh x +mod x +xor x + +# !A +neg + +# Jumps +ja end +jeq #42,prev,end +jne #42,end +jlt #42,end +jle #42,end +jgt #42,prev,end +jge #42,prev,end +jset #42,prev,end + +# Register transfers +tax +txa + +# Returns +prev: ret a +end: ret #42 diff --git a/vendor/src/golang.org/x/net/bpf/vm.go b/vendor/src/golang.org/x/net/bpf/vm.go new file mode 100644 index 00000000..4c656f1e --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm.go @@ -0,0 +1,140 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +import ( + "errors" + "fmt" +) + +// A VM is an emulated BPF virtual machine. +type VM struct { + filter []Instruction +} + +// NewVM returns a new VM using the input BPF program. +func NewVM(filter []Instruction) (*VM, error) { + if len(filter) == 0 { + return nil, errors.New("one or more Instructions must be specified") + } + + for i, ins := range filter { + check := len(filter) - (i + 1) + switch ins := ins.(type) { + // Check for out-of-bounds jumps in instructions + case Jump: + if check <= int(ins.Skip) { + return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip) + } + case JumpIf: + if check <= int(ins.SkipTrue) { + return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue) + } + if check <= int(ins.SkipFalse) { + return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse) + } + // Check for division or modulus by zero + case ALUOpConstant: + if ins.Val != 0 { + break + } + + switch ins.Op { + case ALUOpDiv, ALUOpMod: + return nil, errors.New("cannot divide by zero using ALUOpConstant") + } + // Check for unknown extensions + case LoadExtension: + switch ins.Num { + case ExtLen: + default: + return nil, fmt.Errorf("extension %d not implemented", ins.Num) + } + } + } + + // Make sure last instruction is a return instruction + switch filter[len(filter)-1].(type) { + case RetA, RetConstant: + default: + return nil, errors.New("BPF program must end with RetA or RetConstant") + } + + // Though our VM works using disassembled instructions, we + // attempt to assemble the input filter anyway to ensure it is compatible + // with an operating system VM. + _, err := Assemble(filter) + + return &VM{ + filter: filter, + }, err +} + +// Run runs the VM's BPF program against the input bytes. +// Run returns the number of bytes accepted by the BPF program, and any errors +// which occurred while processing the program. +func (v *VM) Run(in []byte) (int, error) { + var ( + // Registers of the virtual machine + regA uint32 + regX uint32 + regScratch [16]uint32 + + // OK is true if the program should continue processing the next + // instruction, or false if not, causing the loop to break + ok = true + ) + + // TODO(mdlayher): implement: + // - NegateA: + // - would require a change from uint32 registers to int32 + // registers + + // TODO(mdlayher): add interop tests that check signedness of ALU + // operations against kernel implementation, and make sure Go + // implementation matches behavior + + for i := 0; i < len(v.filter) && ok; i++ { + ins := v.filter[i] + + switch ins := ins.(type) { + case ALUOpConstant: + regA = aluOpConstant(ins, regA) + case ALUOpX: + regA, ok = aluOpX(ins, regA, regX) + case Jump: + i += int(ins.Skip) + case JumpIf: + jump := jumpIf(ins, regA) + i += jump + case LoadAbsolute: + regA, ok = loadAbsolute(ins, in) + case LoadConstant: + regA, regX = loadConstant(ins, regA, regX) + case LoadExtension: + regA = loadExtension(ins, in) + case LoadIndirect: + regA, ok = loadIndirect(ins, in, regX) + case LoadMemShift: + regX, ok = loadMemShift(ins, in) + case LoadScratch: + regA, regX = loadScratch(ins, regScratch, regA, regX) + case RetA: + return int(regA), nil + case RetConstant: + return int(ins.Val), nil + case StoreScratch: + regScratch = storeScratch(ins, regScratch, regA, regX) + case TAX: + regX = regA + case TXA: + regA = regX + default: + return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins) + } + } + + return 0, nil +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_aluop_test.go b/vendor/src/golang.org/x/net/bpf/vm_aluop_test.go new file mode 100644 index 00000000..16678244 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_aluop_test.go @@ -0,0 +1,512 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "testing" + + "golang.org/x/net/bpf" +) + +func TestVMALUOpAdd(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpAdd, + Val: 3, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 8, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 3, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpSub(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.TAX{}, + bpf.ALUOpX{ + Op: bpf.ALUOpSub, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpMul(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpMul, + Val: 2, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 6, 2, 3, 4, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpDiv(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpDiv, + Val: 2, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 20, 2, 3, 4, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpDivByZeroALUOpConstant(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.ALUOpConstant{ + Op: bpf.ALUOpDiv, + Val: 0, + }, + bpf.RetA{}, + }) + if errStr(err) != "cannot divide by zero using ALUOpConstant" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMALUOpDivByZeroALUOpX(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + // Load byte 0 into X + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.TAX{}, + // Load byte 1 into A + bpf.LoadAbsolute{ + Off: 9, + Size: 1, + }, + // Attempt to perform 1/0 + bpf.ALUOpX{ + Op: bpf.ALUOpDiv, + }, + // Return 4 bytes if program does not terminate + bpf.LoadConstant{ + Val: 12, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 3, 4, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpOr(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpOr, + Val: 0x01, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x10, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0xff, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 9, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpAnd(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpAnd, + Val: 0x0019, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xaa, 0x09, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpShiftLeft(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpShiftLeft, + Val: 0x01, + }, + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 0x02, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x01, 0xaa, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpShiftRight(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpShiftRight, + Val: 0x01, + }, + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 0x04, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x08, 0xff, 0xff, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpMod(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpMod, + Val: 20, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 30, 0, 0, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpModByZeroALUOpConstant(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpMod, + Val: 0, + }, + bpf.RetA{}, + }) + if errStr(err) != "cannot divide by zero using ALUOpConstant" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMALUOpModByZeroALUOpX(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + // Load byte 0 into X + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.TAX{}, + // Load byte 1 into A + bpf.LoadAbsolute{ + Off: 9, + Size: 1, + }, + // Attempt to perform 1%0 + bpf.ALUOpX{ + Op: bpf.ALUOpMod, + }, + // Return 4 bytes if program does not terminate + bpf.LoadConstant{ + Val: 12, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 3, 4, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpXor(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpXor, + Val: 0x0a, + }, + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 0x01, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x0b, 0x00, 0x00, 0x00, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMALUOpUnknown(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.ALUOpConstant{ + Op: bpf.ALUOpAdd, + Val: 1, + }, + // Verify that an unknown operation is a no-op + bpf.ALUOpConstant{ + Op: 100, + }, + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 0x02, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_bpf_test.go b/vendor/src/golang.org/x/net/bpf/vm_bpf_test.go new file mode 100644 index 00000000..77fa8fe4 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_bpf_test.go @@ -0,0 +1,192 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "net" + "runtime" + "testing" + "time" + + "golang.org/x/net/bpf" + "golang.org/x/net/ipv4" +) + +// A virtualMachine is a BPF virtual machine which can process an +// input packet against a BPF program and render a verdict. +type virtualMachine interface { + Run(in []byte) (int, error) +} + +// canUseOSVM indicates if the OS BPF VM is available on this platform. +func canUseOSVM() bool { + // OS BPF VM can only be used on platforms where x/net/ipv4 supports + // attaching a BPF program to a socket. + switch runtime.GOOS { + case "linux": + return true + } + + return false +} + +// All BPF tests against both the Go VM and OS VM are assumed to +// be used with a UDP socket. As a result, the entire contents +// of a UDP datagram is sent through the BPF program, but only +// the body after the UDP header will ever be returned in output. + +// testVM sets up a Go BPF VM, and if available, a native OS BPF VM +// for integration testing. +func testVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func(), error) { + goVM, err := bpf.NewVM(filter) + if err != nil { + // Some tests expect an error, so this error must be returned + // instead of fatally exiting the test + return nil, nil, err + } + + mvm := &multiVirtualMachine{ + goVM: goVM, + + t: t, + } + + // If available, add the OS VM for tests which verify that both the Go + // VM and OS VM have exactly the same output for the same input program + // and packet. + done := func() {} + if canUseOSVM() { + osVM, osVMDone := testOSVM(t, filter) + done = func() { osVMDone() } + mvm.osVM = osVM + } + + return mvm, done, nil +} + +// udpHeaderLen is the length of a UDP header. +const udpHeaderLen = 8 + +// A multiVirtualMachine is a virtualMachine which can call out to both the Go VM +// and the native OS VM, if the OS VM is available. +type multiVirtualMachine struct { + goVM virtualMachine + osVM virtualMachine + + t *testing.T +} + +func (mvm *multiVirtualMachine) Run(in []byte) (int, error) { + if len(in) < udpHeaderLen { + mvm.t.Fatalf("input must be at least length of UDP header (%d), got: %d", + udpHeaderLen, len(in)) + } + + // All tests have a UDP header as part of input, because the OS VM + // packets always will. For the Go VM, this output is trimmed before + // being sent back to tests. + goOut, goErr := mvm.goVM.Run(in) + if goOut >= udpHeaderLen { + goOut -= udpHeaderLen + } + + // If Go output is larger than the size of the packet, packet filtering + // interop tests must trim the output bytes to the length of the packet. + // The BPF VM should not do this on its own, as other uses of it do + // not trim the output byte count. + trim := len(in) - udpHeaderLen + if goOut > trim { + goOut = trim + } + + // When the OS VM is not available, process using the Go VM alone + if mvm.osVM == nil { + return goOut, goErr + } + + // The OS VM will apply its own UDP header, so remove the pseudo header + // that the Go VM needs. + osOut, err := mvm.osVM.Run(in[udpHeaderLen:]) + if err != nil { + mvm.t.Fatalf("error while running OS VM: %v", err) + } + + // Verify both VMs return same number of bytes + var mismatch bool + if goOut != osOut { + mismatch = true + mvm.t.Logf("output byte count does not match:\n- go: %v\n- os: %v", goOut, osOut) + } + + if mismatch { + mvm.t.Fatal("Go BPF and OS BPF packet outputs do not match") + } + + return goOut, goErr +} + +// An osVirtualMachine is a virtualMachine which uses the OS's BPF VM for +// processing BPF programs. +type osVirtualMachine struct { + l net.PacketConn + s net.Conn +} + +// testOSVM creates a virtualMachine which uses the OS's BPF VM by injecting +// packets into a UDP listener with a BPF program attached to it. +func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) { + l, err := net.ListenPacket("udp4", "127.0.0.1:0") + if err != nil { + t.Fatalf("failed to open OS VM UDP listener: %v", err) + } + + prog, err := bpf.Assemble(filter) + if err != nil { + t.Fatalf("failed to compile BPF program: %v", err) + } + + p := ipv4.NewPacketConn(l) + if err = p.SetBPF(prog); err != nil { + t.Fatalf("failed to attach BPF program to listener: %v", err) + } + + s, err := net.Dial("udp4", l.LocalAddr().String()) + if err != nil { + t.Fatalf("failed to dial connection to listener: %v", err) + } + + done := func() { + _ = s.Close() + _ = l.Close() + } + + return &osVirtualMachine{ + l: l, + s: s, + }, done +} + +// Run sends the input bytes into the OS's BPF VM and returns its verdict. +func (vm *osVirtualMachine) Run(in []byte) (int, error) { + go func() { + _, _ = vm.s.Write(in) + }() + + vm.l.SetDeadline(time.Now().Add(50 * time.Millisecond)) + + var b [512]byte + n, _, err := vm.l.ReadFrom(b[:]) + if err != nil { + // A timeout indicates that BPF filtered out the packet, and thus, + // no input should be returned. + if nerr, ok := err.(net.Error); ok && nerr.Timeout() { + return n, nil + } + + return n, err + } + + return n, nil +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_extension_test.go b/vendor/src/golang.org/x/net/bpf/vm_extension_test.go new file mode 100644 index 00000000..7a48c82f --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_extension_test.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "testing" + + "golang.org/x/net/bpf" +) + +func TestVMLoadExtensionNotImplemented(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadExtension{ + Num: 100, + }, + bpf.RetA{}, + }) + if errStr(err) != "extension 100 not implemented" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMLoadExtensionExtLen(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadExtension{ + Num: bpf.ExtLen, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_instructions.go b/vendor/src/golang.org/x/net/bpf/vm_instructions.go new file mode 100644 index 00000000..516f9462 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_instructions.go @@ -0,0 +1,174 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf + +import ( + "encoding/binary" + "fmt" +) + +func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 { + return aluOpCommon(ins.Op, regA, ins.Val) +} + +func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) { + // Guard against division or modulus by zero by terminating + // the program, as the OS BPF VM does + if regX == 0 { + switch ins.Op { + case ALUOpDiv, ALUOpMod: + return 0, false + } + } + + return aluOpCommon(ins.Op, regA, regX), true +} + +func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 { + switch op { + case ALUOpAdd: + return regA + value + case ALUOpSub: + return regA - value + case ALUOpMul: + return regA * value + case ALUOpDiv: + // Division by zero not permitted by NewVM and aluOpX checks + return regA / value + case ALUOpOr: + return regA | value + case ALUOpAnd: + return regA & value + case ALUOpShiftLeft: + return regA << value + case ALUOpShiftRight: + return regA >> value + case ALUOpMod: + // Modulus by zero not permitted by NewVM and aluOpX checks + return regA % value + case ALUOpXor: + return regA ^ value + default: + return regA + } +} + +func jumpIf(ins JumpIf, value uint32) int { + var ok bool + inV := uint32(ins.Val) + + switch ins.Cond { + case JumpEqual: + ok = value == inV + case JumpNotEqual: + ok = value != inV + case JumpGreaterThan: + ok = value > inV + case JumpLessThan: + ok = value < inV + case JumpGreaterOrEqual: + ok = value >= inV + case JumpLessOrEqual: + ok = value <= inV + case JumpBitsSet: + ok = (value & inV) != 0 + case JumpBitsNotSet: + ok = (value & inV) == 0 + } + + if ok { + return int(ins.SkipTrue) + } + + return int(ins.SkipFalse) +} + +func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) { + offset := int(ins.Off) + size := int(ins.Size) + + return loadCommon(in, offset, size) +} + +func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) { + switch ins.Dst { + case RegA: + regA = ins.Val + case RegX: + regX = ins.Val + } + + return regA, regX +} + +func loadExtension(ins LoadExtension, in []byte) uint32 { + switch ins.Num { + case ExtLen: + return uint32(len(in)) + default: + panic(fmt.Sprintf("unimplemented extension: %d", ins.Num)) + } +} + +func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) { + offset := int(ins.Off) + int(regX) + size := int(ins.Size) + + return loadCommon(in, offset, size) +} + +func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) { + offset := int(ins.Off) + + if !inBounds(len(in), offset, 0) { + return 0, false + } + + // Mask off high 4 bits and multiply low 4 bits by 4 + return uint32(in[offset]&0x0f) * 4, true +} + +func inBounds(inLen int, offset int, size int) bool { + return offset+size <= inLen +} + +func loadCommon(in []byte, offset int, size int) (uint32, bool) { + if !inBounds(len(in), offset, size) { + return 0, false + } + + switch size { + case 1: + return uint32(in[offset]), true + case 2: + return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true + case 4: + return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true + default: + panic(fmt.Sprintf("invalid load size: %d", size)) + } +} + +func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) { + switch ins.Dst { + case RegA: + regA = regScratch[ins.N] + case RegX: + regX = regScratch[ins.N] + } + + return regA, regX +} + +func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 { + switch ins.Src { + case RegA: + regScratch[ins.N] = regA + case RegX: + regScratch[ins.N] = regX + } + + return regScratch +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_jump_test.go b/vendor/src/golang.org/x/net/bpf/vm_jump_test.go new file mode 100644 index 00000000..e0a3a988 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_jump_test.go @@ -0,0 +1,380 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "testing" + + "golang.org/x/net/bpf" +) + +func TestVMJumpOne(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.Jump{ + Skip: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpOutOfProgram(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.Jump{ + Skip: 1, + }, + bpf.RetA{}, + }) + if errStr(err) != "cannot jump 1 instructions; jumping past program bounds" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMJumpIfTrueOutOfProgram(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.JumpIf{ + Cond: bpf.JumpEqual, + SkipTrue: 2, + }, + bpf.RetA{}, + }) + if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMJumpIfFalseOutOfProgram(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.JumpIf{ + Cond: bpf.JumpEqual, + SkipFalse: 3, + }, + bpf.RetA{}, + }) + if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMJumpIfEqual(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 1, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfNotEqual(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.JumpIf{ + Cond: bpf.JumpNotEqual, + Val: 1, + SkipFalse: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfGreaterThan(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 4, + }, + bpf.JumpIf{ + Cond: bpf.JumpGreaterThan, + Val: 0x00010202, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 12, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfLessThan(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 4, + }, + bpf.JumpIf{ + Cond: bpf.JumpLessThan, + Val: 0xff010203, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 12, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfGreaterOrEqual(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 4, + }, + bpf.JumpIf{ + Cond: bpf.JumpGreaterOrEqual, + Val: 0x00010203, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 12, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfLessOrEqual(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 4, + }, + bpf.JumpIf{ + Cond: bpf.JumpLessOrEqual, + Val: 0xff010203, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 12, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 4, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfBitsSet(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.JumpIf{ + Cond: bpf.JumpBitsSet, + Val: 0x1122, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 10, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x01, 0x02, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMJumpIfBitsNotSet(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.JumpIf{ + Cond: bpf.JumpBitsNotSet, + Val: 0x1221, + SkipTrue: 1, + }, + bpf.RetConstant{ + Val: 0, + }, + bpf.RetConstant{ + Val: 10, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x01, 0x02, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_load_test.go b/vendor/src/golang.org/x/net/bpf/vm_load_test.go new file mode 100644 index 00000000..04578b66 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_load_test.go @@ -0,0 +1,246 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "net" + "testing" + + "golang.org/x/net/bpf" + "golang.org/x/net/ipv4" +) + +func TestVMLoadAbsoluteOffsetOutOfBounds(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 100, + Size: 2, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMLoadAbsoluteOffsetPlusSizeOutOfBounds(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMLoadAbsoluteBadInstructionSize(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Size: 5, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid load byte length 0" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMLoadConstantOK(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadConstant{ + Dst: bpf.RegX, + Val: 9, + }, + bpf.TXA{}, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMLoadIndirectOutOfBounds(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadIndirect{ + Off: 100, + Size: 1, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMLoadMemShiftOutOfBounds(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadMemShift{ + Off: 100, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +const ( + dhcp4Port = 53 +) + +func TestVMLoadMemShiftLoadIndirectNoResult(t *testing.T) { + vm, in, done := testDHCPv4(t) + defer done() + + // Append mostly empty UDP header with incorrect DHCPv4 port + in = append(in, []byte{ + 0, 0, + 0, dhcp4Port + 1, + 0, 0, + 0, 0, + }...) + + out, err := vm.Run(in) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 0, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMLoadMemShiftLoadIndirectOK(t *testing.T) { + vm, in, done := testDHCPv4(t) + defer done() + + // Append mostly empty UDP header with correct DHCPv4 port + in = append(in, []byte{ + 0, 0, + 0, dhcp4Port, + 0, 0, + 0, 0, + }...) + + out, err := vm.Run(in) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := len(in)-8, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func testDHCPv4(t *testing.T) (virtualMachine, []byte, func()) { + // DHCPv4 test data courtesy of David Anderson: + // https://github.com/google/netboot/blob/master/dhcp4/conn_linux.go#L59-L70 + vm, done, err := testVM(t, []bpf.Instruction{ + // Load IPv4 packet length + bpf.LoadMemShift{Off: 8}, + // Get UDP dport + bpf.LoadIndirect{Off: 8 + 2, Size: 2}, + // Correct dport? + bpf.JumpIf{Cond: bpf.JumpEqual, Val: dhcp4Port, SkipFalse: 1}, + // Accept + bpf.RetConstant{Val: 1500}, + // Ignore + bpf.RetConstant{Val: 0}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + + // Minimal requirements to make a valid IPv4 header + h := &ipv4.Header{ + Len: ipv4.HeaderLen, + Src: net.IPv4(192, 168, 1, 1), + Dst: net.IPv4(192, 168, 1, 2), + } + hb, err := h.Marshal() + if err != nil { + t.Fatalf("failed to marshal IPv4 header: %v", err) + } + + hb = append([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + }, hb...) + + return vm, hb, done +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_ret_test.go b/vendor/src/golang.org/x/net/bpf/vm_ret_test.go new file mode 100644 index 00000000..2d86eae3 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_ret_test.go @@ -0,0 +1,115 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "testing" + + "golang.org/x/net/bpf" +) + +func TestVMRetA(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 9, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMRetALargerThanInput(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadAbsolute{ + Off: 8, + Size: 2, + }, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 255, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMRetConstant(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.RetConstant{ + Val: 9, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 1, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMRetConstantLargerThanInput(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.RetConstant{ + Val: 16, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_scratch_test.go b/vendor/src/golang.org/x/net/bpf/vm_scratch_test.go new file mode 100644 index 00000000..e600e3c2 --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_scratch_test.go @@ -0,0 +1,247 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "testing" + + "golang.org/x/net/bpf" +) + +func TestVMStoreScratchInvalidScratchRegisterTooSmall(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.StoreScratch{ + Src: bpf.RegA, + N: -1, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid scratch slot -1" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMStoreScratchInvalidScratchRegisterTooLarge(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.StoreScratch{ + Src: bpf.RegA, + N: 16, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid scratch slot 16" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMStoreScratchUnknownSourceRegister(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.StoreScratch{ + Src: 100, + N: 0, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid source register 100" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMLoadScratchInvalidScratchRegisterTooSmall(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadScratch{ + Dst: bpf.RegX, + N: -1, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid scratch slot -1" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMLoadScratchInvalidScratchRegisterTooLarge(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadScratch{ + Dst: bpf.RegX, + N: 16, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid scratch slot 16" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMLoadScratchUnknownDestinationRegister(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadScratch{ + Dst: 100, + N: 0, + }, + bpf.RetA{}, + }) + if errStr(err) != "assembling instruction 1: invalid target register 100" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMStoreScratchLoadScratchOneValue(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + // Load byte 255 + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + // Copy to X and store in scratch[0] + bpf.TAX{}, + bpf.StoreScratch{ + Src: bpf.RegX, + N: 0, + }, + // Load byte 1 + bpf.LoadAbsolute{ + Off: 9, + Size: 1, + }, + // Overwrite 1 with 255 from scratch[0] + bpf.LoadScratch{ + Dst: bpf.RegA, + N: 0, + }, + // Return 255 + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 255, 1, 2, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 3, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} + +func TestVMStoreScratchLoadScratchMultipleValues(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + // Load byte 10 + bpf.LoadAbsolute{ + Off: 8, + Size: 1, + }, + // Store in scratch[0] + bpf.StoreScratch{ + Src: bpf.RegA, + N: 0, + }, + // Load byte 20 + bpf.LoadAbsolute{ + Off: 9, + Size: 1, + }, + // Store in scratch[1] + bpf.StoreScratch{ + Src: bpf.RegA, + N: 1, + }, + // Load byte 30 + bpf.LoadAbsolute{ + Off: 10, + Size: 1, + }, + // Store in scratch[2] + bpf.StoreScratch{ + Src: bpf.RegA, + N: 2, + }, + // Load byte 1 + bpf.LoadAbsolute{ + Off: 11, + Size: 1, + }, + // Store in scratch[3] + bpf.StoreScratch{ + Src: bpf.RegA, + N: 3, + }, + // Load in byte 10 to X + bpf.LoadScratch{ + Dst: bpf.RegX, + N: 0, + }, + // Copy X -> A + bpf.TXA{}, + // Verify value is 10 + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 10, + SkipTrue: 1, + }, + // Fail test if incorrect + bpf.RetConstant{ + Val: 0, + }, + // Load in byte 20 to A + bpf.LoadScratch{ + Dst: bpf.RegA, + N: 1, + }, + // Verify value is 20 + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 20, + SkipTrue: 1, + }, + // Fail test if incorrect + bpf.RetConstant{ + Val: 0, + }, + // Load in byte 30 to A + bpf.LoadScratch{ + Dst: bpf.RegA, + N: 2, + }, + // Verify value is 30 + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: 30, + SkipTrue: 1, + }, + // Fail test if incorrect + bpf.RetConstant{ + Val: 0, + }, + // Return first two bytes on success + bpf.RetConstant{ + Val: 10, + }, + }) + if err != nil { + t.Fatalf("failed to load BPF program: %v", err) + } + defer done() + + out, err := vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 10, 20, 30, 1, + }) + if err != nil { + t.Fatalf("unexpected error while running program: %v", err) + } + if want, got := 2, out; want != got { + t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d", + want, got) + } +} diff --git a/vendor/src/golang.org/x/net/bpf/vm_test.go b/vendor/src/golang.org/x/net/bpf/vm_test.go new file mode 100644 index 00000000..6bd4dd5c --- /dev/null +++ b/vendor/src/golang.org/x/net/bpf/vm_test.go @@ -0,0 +1,144 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bpf_test + +import ( + "fmt" + "testing" + + "golang.org/x/net/bpf" +) + +var _ bpf.Instruction = unknown{} + +type unknown struct{} + +func (unknown) Assemble() (bpf.RawInstruction, error) { + return bpf.RawInstruction{}, nil +} + +func TestVMUnknownInstruction(t *testing.T) { + vm, done, err := testVM(t, []bpf.Instruction{ + bpf.LoadConstant{ + Dst: bpf.RegA, + Val: 100, + }, + // Should terminate the program with an error immediately + unknown{}, + bpf.RetA{}, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer done() + + _, err = vm.Run([]byte{ + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, + }) + if errStr(err) != "unknown Instruction at index 1: bpf_test.unknown" { + t.Fatalf("unexpected error while running program: %v", err) + } +} + +func TestVMNoReturnInstruction(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{ + bpf.LoadConstant{ + Dst: bpf.RegA, + Val: 1, + }, + }) + if errStr(err) != "BPF program must end with RetA or RetConstant" { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestVMNoInputInstructions(t *testing.T) { + _, _, err := testVM(t, []bpf.Instruction{}) + if errStr(err) != "one or more Instructions must be specified" { + t.Fatalf("unexpected error: %v", err) + } +} + +// ExampleNewVM demonstrates usage of a VM, using an Ethernet frame +// as input and checking its EtherType to determine if it should be accepted. +func ExampleNewVM() { + // Offset | Length | Comment + // ------------------------- + // 00 | 06 | Ethernet destination MAC address + // 06 | 06 | Ethernet source MAC address + // 12 | 02 | Ethernet EtherType + const ( + etOff = 12 + etLen = 2 + + etARP = 0x0806 + ) + + // Set up a VM to filter traffic based on if its EtherType + // matches the ARP EtherType. + vm, err := bpf.NewVM([]bpf.Instruction{ + // Load EtherType value from Ethernet header + bpf.LoadAbsolute{ + Off: etOff, + Size: etLen, + }, + // If EtherType is equal to the ARP EtherType, jump to allow + // packet to be accepted + bpf.JumpIf{ + Cond: bpf.JumpEqual, + Val: etARP, + SkipTrue: 1, + }, + // EtherType does not match the ARP EtherType + bpf.RetConstant{ + Val: 0, + }, + // EtherType matches the ARP EtherType, accept up to 1500 + // bytes of packet + bpf.RetConstant{ + Val: 1500, + }, + }) + if err != nil { + panic(fmt.Sprintf("failed to load BPF program: %v", err)) + } + + // Create an Ethernet frame with the ARP EtherType for testing + frame := []byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, + 0x08, 0x06, + // Payload omitted for brevity + } + + // Run our VM's BPF program using the Ethernet frame as input + out, err := vm.Run(frame) + if err != nil { + panic(fmt.Sprintf("failed to accept Ethernet frame: %v", err)) + } + + // BPF VM can return a byte count greater than the number of input + // bytes, so trim the output to match the input byte length + if out > len(frame) { + out = len(frame) + } + + fmt.Printf("out: %d bytes", out) + + // Output: + // out: 14 bytes +} + +// errStr returns the string representation of an error, or +// "" if it is nil. +func errStr(err error) string { + if err == nil { + return "" + } + + return err.Error() +} diff --git a/vendor/src/golang.org/x/net/internal/iana/const.go b/vendor/src/golang.org/x/net/internal/iana/const.go new file mode 100644 index 00000000..c9df24d9 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/iana/const.go @@ -0,0 +1,180 @@ +// go generate gen.go +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA). +package iana // import "golang.org/x/net/internal/iana" + +// Differentiated Services Field Codepoints (DSCP), Updated: 2017-05-12 +const ( + DiffServCS0 = 0x0 // CS0 + DiffServCS1 = 0x20 // CS1 + DiffServCS2 = 0x40 // CS2 + DiffServCS3 = 0x60 // CS3 + DiffServCS4 = 0x80 // CS4 + DiffServCS5 = 0xa0 // CS5 + DiffServCS6 = 0xc0 // CS6 + DiffServCS7 = 0xe0 // CS7 + DiffServAF11 = 0x28 // AF11 + DiffServAF12 = 0x30 // AF12 + DiffServAF13 = 0x38 // AF13 + DiffServAF21 = 0x48 // AF21 + DiffServAF22 = 0x50 // AF22 + DiffServAF23 = 0x58 // AF23 + DiffServAF31 = 0x68 // AF31 + DiffServAF32 = 0x70 // AF32 + DiffServAF33 = 0x78 // AF33 + DiffServAF41 = 0x88 // AF41 + DiffServAF42 = 0x90 // AF42 + DiffServAF43 = 0x98 // AF43 + DiffServEF = 0xb8 // EF + DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT +) + +// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06 +const ( + NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport) + ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1)) + ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0)) + CongestionExperienced = 0x3 // CE (Congestion Experienced) +) + +// Protocol Numbers, Updated: 2016-06-22 +const ( + ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number + ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option + ProtocolICMP = 1 // Internet Control Message + ProtocolIGMP = 2 // Internet Group Management + ProtocolGGP = 3 // Gateway-to-Gateway + ProtocolIPv4 = 4 // IPv4 encapsulation + ProtocolST = 5 // Stream + ProtocolTCP = 6 // Transmission Control + ProtocolCBT = 7 // CBT + ProtocolEGP = 8 // Exterior Gateway Protocol + ProtocolIGP = 9 // any private interior gateway (used by Cisco for their IGRP) + ProtocolBBNRCCMON = 10 // BBN RCC Monitoring + ProtocolNVPII = 11 // Network Voice Protocol + ProtocolPUP = 12 // PUP + ProtocolEMCON = 14 // EMCON + ProtocolXNET = 15 // Cross Net Debugger + ProtocolCHAOS = 16 // Chaos + ProtocolUDP = 17 // User Datagram + ProtocolMUX = 18 // Multiplexing + ProtocolDCNMEAS = 19 // DCN Measurement Subsystems + ProtocolHMP = 20 // Host Monitoring + ProtocolPRM = 21 // Packet Radio Measurement + ProtocolXNSIDP = 22 // XEROX NS IDP + ProtocolTRUNK1 = 23 // Trunk-1 + ProtocolTRUNK2 = 24 // Trunk-2 + ProtocolLEAF1 = 25 // Leaf-1 + ProtocolLEAF2 = 26 // Leaf-2 + ProtocolRDP = 27 // Reliable Data Protocol + ProtocolIRTP = 28 // Internet Reliable Transaction + ProtocolISOTP4 = 29 // ISO Transport Protocol Class 4 + ProtocolNETBLT = 30 // Bulk Data Transfer Protocol + ProtocolMFENSP = 31 // MFE Network Services Protocol + ProtocolMERITINP = 32 // MERIT Internodal Protocol + ProtocolDCCP = 33 // Datagram Congestion Control Protocol + Protocol3PC = 34 // Third Party Connect Protocol + ProtocolIDPR = 35 // Inter-Domain Policy Routing Protocol + ProtocolXTP = 36 // XTP + ProtocolDDP = 37 // Datagram Delivery Protocol + ProtocolIDPRCMTP = 38 // IDPR Control Message Transport Proto + ProtocolTPPP = 39 // TP++ Transport Protocol + ProtocolIL = 40 // IL Transport Protocol + ProtocolIPv6 = 41 // IPv6 encapsulation + ProtocolSDRP = 42 // Source Demand Routing Protocol + ProtocolIPv6Route = 43 // Routing Header for IPv6 + ProtocolIPv6Frag = 44 // Fragment Header for IPv6 + ProtocolIDRP = 45 // Inter-Domain Routing Protocol + ProtocolRSVP = 46 // Reservation Protocol + ProtocolGRE = 47 // Generic Routing Encapsulation + ProtocolDSR = 48 // Dynamic Source Routing Protocol + ProtocolBNA = 49 // BNA + ProtocolESP = 50 // Encap Security Payload + ProtocolAH = 51 // Authentication Header + ProtocolINLSP = 52 // Integrated Net Layer Security TUBA + ProtocolNARP = 54 // NBMA Address Resolution Protocol + ProtocolMOBILE = 55 // IP Mobility + ProtocolTLSP = 56 // Transport Layer Security Protocol using Kryptonet key management + ProtocolSKIP = 57 // SKIP + ProtocolIPv6ICMP = 58 // ICMP for IPv6 + ProtocolIPv6NoNxt = 59 // No Next Header for IPv6 + ProtocolIPv6Opts = 60 // Destination Options for IPv6 + ProtocolCFTP = 62 // CFTP + ProtocolSATEXPAK = 64 // SATNET and Backroom EXPAK + ProtocolKRYPTOLAN = 65 // Kryptolan + ProtocolRVD = 66 // MIT Remote Virtual Disk Protocol + ProtocolIPPC = 67 // Internet Pluribus Packet Core + ProtocolSATMON = 69 // SATNET Monitoring + ProtocolVISA = 70 // VISA Protocol + ProtocolIPCV = 71 // Internet Packet Core Utility + ProtocolCPNX = 72 // Computer Protocol Network Executive + ProtocolCPHB = 73 // Computer Protocol Heart Beat + ProtocolWSN = 74 // Wang Span Network + ProtocolPVP = 75 // Packet Video Protocol + ProtocolBRSATMON = 76 // Backroom SATNET Monitoring + ProtocolSUNND = 77 // SUN ND PROTOCOL-Temporary + ProtocolWBMON = 78 // WIDEBAND Monitoring + ProtocolWBEXPAK = 79 // WIDEBAND EXPAK + ProtocolISOIP = 80 // ISO Internet Protocol + ProtocolVMTP = 81 // VMTP + ProtocolSECUREVMTP = 82 // SECURE-VMTP + ProtocolVINES = 83 // VINES + ProtocolTTP = 84 // Transaction Transport Protocol + ProtocolIPTM = 84 // Internet Protocol Traffic Manager + ProtocolNSFNETIGP = 85 // NSFNET-IGP + ProtocolDGP = 86 // Dissimilar Gateway Protocol + ProtocolTCF = 87 // TCF + ProtocolEIGRP = 88 // EIGRP + ProtocolOSPFIGP = 89 // OSPFIGP + ProtocolSpriteRPC = 90 // Sprite RPC Protocol + ProtocolLARP = 91 // Locus Address Resolution Protocol + ProtocolMTP = 92 // Multicast Transport Protocol + ProtocolAX25 = 93 // AX.25 Frames + ProtocolIPIP = 94 // IP-within-IP Encapsulation Protocol + ProtocolSCCSP = 96 // Semaphore Communications Sec. Pro. + ProtocolETHERIP = 97 // Ethernet-within-IP Encapsulation + ProtocolENCAP = 98 // Encapsulation Header + ProtocolGMTP = 100 // GMTP + ProtocolIFMP = 101 // Ipsilon Flow Management Protocol + ProtocolPNNI = 102 // PNNI over IP + ProtocolPIM = 103 // Protocol Independent Multicast + ProtocolARIS = 104 // ARIS + ProtocolSCPS = 105 // SCPS + ProtocolQNX = 106 // QNX + ProtocolAN = 107 // Active Networks + ProtocolIPComp = 108 // IP Payload Compression Protocol + ProtocolSNP = 109 // Sitara Networks Protocol + ProtocolCompaqPeer = 110 // Compaq Peer Protocol + ProtocolIPXinIP = 111 // IPX in IP + ProtocolVRRP = 112 // Virtual Router Redundancy Protocol + ProtocolPGM = 113 // PGM Reliable Transport Protocol + ProtocolL2TP = 115 // Layer Two Tunneling Protocol + ProtocolDDX = 116 // D-II Data Exchange (DDX) + ProtocolIATP = 117 // Interactive Agent Transfer Protocol + ProtocolSTP = 118 // Schedule Transfer Protocol + ProtocolSRP = 119 // SpectraLink Radio Protocol + ProtocolUTI = 120 // UTI + ProtocolSMP = 121 // Simple Message Protocol + ProtocolPTP = 123 // Performance Transparency Protocol + ProtocolISIS = 124 // ISIS over IPv4 + ProtocolFIRE = 125 // FIRE + ProtocolCRTP = 126 // Combat Radio Transport Protocol + ProtocolCRUDP = 127 // Combat Radio User Datagram + ProtocolSSCOPMCE = 128 // SSCOPMCE + ProtocolIPLT = 129 // IPLT + ProtocolSPS = 130 // Secure Packet Shield + ProtocolPIPE = 131 // Private IP Encapsulation within IP + ProtocolSCTP = 132 // Stream Control Transmission Protocol + ProtocolFC = 133 // Fibre Channel + ProtocolRSVPE2EIGNORE = 134 // RSVP-E2E-IGNORE + ProtocolMobilityHeader = 135 // Mobility Header + ProtocolUDPLite = 136 // UDPLite + ProtocolMPLSinIP = 137 // MPLS-in-IP + ProtocolMANET = 138 // MANET Protocols + ProtocolHIP = 139 // Host Identity Protocol + ProtocolShim6 = 140 // Shim6 Protocol + ProtocolWESP = 141 // Wrapped Encapsulating Security Payload + ProtocolROHC = 142 // Robust Header Compression + ProtocolReserved = 255 // Reserved +) diff --git a/vendor/src/golang.org/x/net/internal/iana/gen.go b/vendor/src/golang.org/x/net/internal/iana/gen.go new file mode 100644 index 00000000..86c78b3b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/iana/gen.go @@ -0,0 +1,293 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +//go:generate go run gen.go + +// This program generates internet protocol constants and tables by +// reading IANA protocol registries. +package main + +import ( + "bytes" + "encoding/xml" + "fmt" + "go/format" + "io" + "io/ioutil" + "net/http" + "os" + "strconv" + "strings" +) + +var registries = []struct { + url string + parse func(io.Writer, io.Reader) error +}{ + { + "http://www.iana.org/assignments/dscp-registry/dscp-registry.xml", + parseDSCPRegistry, + }, + { + "http://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml", + parseTOSTCByte, + }, + { + "http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml", + parseProtocolNumbers, + }, +} + +func main() { + var bb bytes.Buffer + fmt.Fprintf(&bb, "// go generate gen.go\n") + fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") + fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n") + fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n") + for _, r := range registries { + resp, err := http.Get(r.url) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url) + os.Exit(1) + } + if err := r.parse(&bb, resp.Body); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Fprintf(&bb, "\n") + } + b, err := format.Source(bb.Bytes()) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if err := ioutil.WriteFile("const.go", b, 0644); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func parseDSCPRegistry(w io.Writer, r io.Reader) error { + dec := xml.NewDecoder(r) + var dr dscpRegistry + if err := dec.Decode(&dr); err != nil { + return err + } + drs := dr.escape() + fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated) + fmt.Fprintf(w, "const (\n") + for _, dr := range drs { + fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value) + fmt.Fprintf(w, "// %s\n", dr.OrigName) + } + fmt.Fprintf(w, ")\n") + return nil +} + +type dscpRegistry struct { + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + PoolRecords []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>record"` + Records []struct { + Name string `xml:"name"` + Space string `xml:"space"` + } `xml:"registry>registry>record"` +} + +type canonDSCPRecord struct { + OrigName string + Name string + Value int +} + +func (drr *dscpRegistry) escape() []canonDSCPRecord { + drs := make([]canonDSCPRecord, len(drr.Records)) + sr := strings.NewReplacer( + "+", "", + "-", "", + "/", "", + ".", "", + " ", "", + ) + for i, dr := range drr.Records { + s := strings.TrimSpace(dr.Name) + drs[i].OrigName = s + drs[i].Name = sr.Replace(s) + n, err := strconv.ParseUint(dr.Space, 2, 8) + if err != nil { + continue + } + drs[i].Value = int(n) << 2 + } + return drs +} + +func parseTOSTCByte(w io.Writer, r io.Reader) error { + dec := xml.NewDecoder(r) + var ttb tosTCByte + if err := dec.Decode(&ttb); err != nil { + return err + } + trs := ttb.escape() + fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated) + fmt.Fprintf(w, "const (\n") + for _, tr := range trs { + fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value) + fmt.Fprintf(w, "// %s\n", tr.OrigKeyword) + } + fmt.Fprintf(w, ")\n") + return nil +} + +type tosTCByte struct { + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Note string `xml:"note"` + RegTitle string `xml:"registry>title"` + Records []struct { + Binary string `xml:"binary"` + Keyword string `xml:"keyword"` + } `xml:"registry>record"` +} + +type canonTOSTCByteRecord struct { + OrigKeyword string + Keyword string + Value int +} + +func (ttb *tosTCByte) escape() []canonTOSTCByteRecord { + trs := make([]canonTOSTCByteRecord, len(ttb.Records)) + sr := strings.NewReplacer( + "Capable", "", + "(", "", + ")", "", + "+", "", + "-", "", + "/", "", + ".", "", + " ", "", + ) + for i, tr := range ttb.Records { + s := strings.TrimSpace(tr.Keyword) + trs[i].OrigKeyword = s + ss := strings.Split(s, " ") + if len(ss) > 1 { + trs[i].Keyword = strings.Join(ss[1:], " ") + } else { + trs[i].Keyword = ss[0] + } + trs[i].Keyword = sr.Replace(trs[i].Keyword) + n, err := strconv.ParseUint(tr.Binary, 2, 8) + if err != nil { + continue + } + trs[i].Value = int(n) + } + return trs +} + +func parseProtocolNumbers(w io.Writer, r io.Reader) error { + dec := xml.NewDecoder(r) + var pn protocolNumbers + if err := dec.Decode(&pn); err != nil { + return err + } + prs := pn.escape() + prs = append([]canonProtocolRecord{{ + Name: "IP", + Descr: "IPv4 encapsulation, pseudo protocol number", + Value: 0, + }}, prs...) + fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated) + fmt.Fprintf(w, "const (\n") + for _, pr := range prs { + if pr.Name == "" { + continue + } + fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value) + s := pr.Descr + if s == "" { + s = pr.OrigName + } + fmt.Fprintf(w, "// %s\n", s) + } + fmt.Fprintf(w, ")\n") + return nil +} + +type protocolNumbers struct { + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + RegTitle string `xml:"registry>title"` + Note string `xml:"registry>note"` + Records []struct { + Value string `xml:"value"` + Name string `xml:"name"` + Descr string `xml:"description"` + } `xml:"registry>record"` +} + +type canonProtocolRecord struct { + OrigName string + Name string + Descr string + Value int +} + +func (pn *protocolNumbers) escape() []canonProtocolRecord { + prs := make([]canonProtocolRecord, len(pn.Records)) + sr := strings.NewReplacer( + "-in-", "in", + "-within-", "within", + "-over-", "over", + "+", "P", + "-", "", + "/", "", + ".", "", + " ", "", + ) + for i, pr := range pn.Records { + if strings.Contains(pr.Name, "Deprecated") || + strings.Contains(pr.Name, "deprecated") { + continue + } + prs[i].OrigName = pr.Name + s := strings.TrimSpace(pr.Name) + switch pr.Name { + case "ISIS over IPv4": + prs[i].Name = "ISIS" + case "manet": + prs[i].Name = "MANET" + default: + prs[i].Name = sr.Replace(s) + } + ss := strings.Split(pr.Descr, "\n") + for i := range ss { + ss[i] = strings.TrimSpace(ss[i]) + } + if len(ss) > 1 { + prs[i].Descr = strings.Join(ss, " ") + } else { + prs[i].Descr = ss[0] + } + prs[i].Value, _ = strconv.Atoi(pr.Value) + } + return prs +} diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr.go new file mode 100644 index 00000000..1eb07d26 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr.go @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package socket + +func (h *cmsghdr) len() int { return int(h.Len) } +func (h *cmsghdr) lvl() int { return int(h.Level) } +func (h *cmsghdr) typ() int { return int(h.Type) } diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr_bsd.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_bsd.go new file mode 100644 index 00000000..d1d0c2de --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_bsd.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package socket + +func (h *cmsghdr) set(l, lvl, typ int) { + h.Len = uint32(l) + h.Level = int32(lvl) + h.Type = int32(typ) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go new file mode 100644 index 00000000..bac66811 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm mips mipsle 386 +// +build linux + +package socket + +func (h *cmsghdr) set(l, lvl, typ int) { + h.Len = uint32(l) + h.Level = int32(lvl) + h.Type = int32(typ) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go new file mode 100644 index 00000000..63f0534f --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build linux + +package socket + +func (h *cmsghdr) set(l, lvl, typ int) { + h.Len = uint64(l) + h.Level = int32(lvl) + h.Type = int32(typ) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go new file mode 100644 index 00000000..7dedd430 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 +// +build solaris + +package socket + +func (h *cmsghdr) set(l, lvl, typ int) { + h.Len = uint32(l) + h.Level = int32(lvl) + h.Type = int32(typ) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/cmsghdr_stub.go b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_stub.go new file mode 100644 index 00000000..a4e71226 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/cmsghdr_stub.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package socket + +type cmsghdr struct{} + +const sizeofCmsghdr = 0 + +func (h *cmsghdr) len() int { return 0 } +func (h *cmsghdr) lvl() int { return 0 } +func (h *cmsghdr) typ() int { return 0 } + +func (h *cmsghdr) set(l, lvl, typ int) {} diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_darwin.go b/vendor/src/golang.org/x/net/internal/socket/defs_darwin.go new file mode 100644 index 00000000..14e28c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_darwin.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_dragonfly.go b/vendor/src/golang.org/x/net/internal/socket/defs_dragonfly.go new file mode 100644 index 00000000..14e28c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_dragonfly.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_freebsd.go b/vendor/src/golang.org/x/net/internal/socket/defs_freebsd.go new file mode 100644 index 00000000..14e28c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_freebsd.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_linux.go b/vendor/src/golang.org/x/net/internal/socket/defs_linux.go new file mode 100644 index 00000000..ce9ec2f6 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_linux.go @@ -0,0 +1,49 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include +#include + +#define _GNU_SOURCE +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type mmsghdr C.struct_mmsghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofMmsghdr = C.sizeof_struct_mmsghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_netbsd.go b/vendor/src/golang.org/x/net/internal/socket/defs_netbsd.go new file mode 100644 index 00000000..3f843356 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_netbsd.go @@ -0,0 +1,47 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type mmsghdr C.struct_mmsghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofMmsghdr = C.sizeof_struct_mmsghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_openbsd.go b/vendor/src/golang.org/x/net/internal/socket/defs_openbsd.go new file mode 100644 index 00000000..14e28c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_openbsd.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/defs_solaris.go b/vendor/src/golang.org/x/net/internal/socket/defs_solaris.go new file mode 100644 index 00000000..14e28c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/defs_solaris.go @@ -0,0 +1,44 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package socket + +/* +#include + +#include +*/ +import "C" + +const ( + sysAF_UNSPEC = C.AF_UNSPEC + sysAF_INET = C.AF_INET + sysAF_INET6 = C.AF_INET6 + + sysSOCK_RAW = C.SOCK_RAW +) + +type iovec C.struct_iovec + +type msghdr C.struct_msghdr + +type cmsghdr C.struct_cmsghdr + +type sockaddrInet C.struct_sockaddr_in + +type sockaddrInet6 C.struct_sockaddr_in6 + +const ( + sizeofIovec = C.sizeof_struct_iovec + sizeofMsghdr = C.sizeof_struct_msghdr + sizeofCmsghdr = C.sizeof_struct_cmsghdr + + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/error_unix.go b/vendor/src/golang.org/x/net/internal/socket/error_unix.go new file mode 100644 index 00000000..93dff918 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/error_unix.go @@ -0,0 +1,31 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package socket + +import "syscall" + +var ( + errEAGAIN error = syscall.EAGAIN + errEINVAL error = syscall.EINVAL + errENOENT error = syscall.ENOENT +) + +// errnoErr returns common boxed Errno values, to prevent allocations +// at runtime. +func errnoErr(errno syscall.Errno) error { + switch errno { + case 0: + return nil + case syscall.EAGAIN: + return errEAGAIN + case syscall.EINVAL: + return errEINVAL + case syscall.ENOENT: + return errENOENT + } + return errno +} diff --git a/vendor/src/golang.org/x/net/internal/socket/error_windows.go b/vendor/src/golang.org/x/net/internal/socket/error_windows.go new file mode 100644 index 00000000..6a6379a8 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/error_windows.go @@ -0,0 +1,26 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import "syscall" + +var ( + errERROR_IO_PENDING error = syscall.ERROR_IO_PENDING + errEINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent allocations +// at runtime. +func errnoErr(errno syscall.Errno) error { + switch errno { + case 0: + return nil + case syscall.ERROR_IO_PENDING: + return errERROR_IO_PENDING + case syscall.EINVAL: + return errEINVAL + } + return errno +} diff --git a/vendor/src/golang.org/x/net/internal/socket/iovec_32bit.go b/vendor/src/golang.org/x/net/internal/socket/iovec_32bit.go new file mode 100644 index 00000000..d6a570c9 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/iovec_32bit.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm mips mipsle 386 +// +build darwin dragonfly freebsd linux netbsd openbsd + +package socket + +import "unsafe" + +func (v *iovec) set(b []byte) { + v.Base = (*byte)(unsafe.Pointer(&b[0])) + v.Len = uint32(len(b)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/iovec_64bit.go b/vendor/src/golang.org/x/net/internal/socket/iovec_64bit.go new file mode 100644 index 00000000..2ae435e6 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/iovec_64bit.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build darwin dragonfly freebsd linux netbsd openbsd + +package socket + +import "unsafe" + +func (v *iovec) set(b []byte) { + v.Base = (*byte)(unsafe.Pointer(&b[0])) + v.Len = uint64(len(b)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/iovec_solaris_64bit.go b/vendor/src/golang.org/x/net/internal/socket/iovec_solaris_64bit.go new file mode 100644 index 00000000..100a6282 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/iovec_solaris_64bit.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 +// +build solaris + +package socket + +import "unsafe" + +func (v *iovec) set(b []byte) { + v.Base = (*int8)(unsafe.Pointer(&b[0])) + v.Len = uint64(len(b)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/iovec_stub.go b/vendor/src/golang.org/x/net/internal/socket/iovec_stub.go new file mode 100644 index 00000000..c87d2a93 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/iovec_stub.go @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package socket + +type iovec struct{} + +func (v *iovec) set(b []byte) {} diff --git a/vendor/src/golang.org/x/net/internal/socket/mmsghdr_stub.go b/vendor/src/golang.org/x/net/internal/socket/mmsghdr_stub.go new file mode 100644 index 00000000..2e80a9cb --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/mmsghdr_stub.go @@ -0,0 +1,21 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !linux,!netbsd + +package socket + +import "net" + +type mmsghdr struct{} + +type mmsghdrs []mmsghdr + +func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error { + return nil +} + +func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error { + return nil +} diff --git a/vendor/src/golang.org/x/net/internal/socket/mmsghdr_unix.go b/vendor/src/golang.org/x/net/internal/socket/mmsghdr_unix.go new file mode 100644 index 00000000..3c42ea7a --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/mmsghdr_unix.go @@ -0,0 +1,42 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux netbsd + +package socket + +import "net" + +type mmsghdrs []mmsghdr + +func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error { + for i := range hs { + vs := make([]iovec, len(ms[i].Buffers)) + var sa []byte + if parseFn != nil { + sa = make([]byte, sizeofSockaddrInet6) + } + if marshalFn != nil { + sa = marshalFn(ms[i].Addr) + } + hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa) + } + return nil +} + +func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error { + for i := range hs { + ms[i].N = int(hs[i].Len) + ms[i].NN = hs[i].Hdr.controllen() + ms[i].Flags = hs[i].Hdr.flags() + if parseFn != nil { + var err error + ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint) + if err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_bsd.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_bsd.go new file mode 100644 index 00000000..5567afc8 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_bsd.go @@ -0,0 +1,39 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package socket + +import "unsafe" + +func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { + for i := range vs { + vs[i].set(bs[i]) + } + h.setIov(vs) + if len(oob) > 0 { + h.Control = (*byte)(unsafe.Pointer(&oob[0])) + h.Controllen = uint32(len(oob)) + } + if sa != nil { + h.Name = (*byte)(unsafe.Pointer(&sa[0])) + h.Namelen = uint32(len(sa)) + } +} + +func (h *msghdr) name() []byte { + if h.Name != nil && h.Namelen > 0 { + return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen] + } + return nil +} + +func (h *msghdr) controllen() int { + return int(h.Controllen) +} + +func (h *msghdr) flags() int { + return int(h.Flags) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_bsdvar.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_bsdvar.go new file mode 100644 index 00000000..3fcb0428 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_bsdvar.go @@ -0,0 +1,12 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd + +package socket + +func (h *msghdr) setIov(vs []iovec) { + h.Iov = &vs[0] + h.Iovlen = int32(len(vs)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_linux.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux.go new file mode 100644 index 00000000..5a38798c --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux.go @@ -0,0 +1,36 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import "unsafe" + +func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { + for i := range vs { + vs[i].set(bs[i]) + } + h.setIov(vs) + if len(oob) > 0 { + h.setControl(oob) + } + if sa != nil { + h.Name = (*byte)(unsafe.Pointer(&sa[0])) + h.Namelen = uint32(len(sa)) + } +} + +func (h *msghdr) name() []byte { + if h.Name != nil && h.Namelen > 0 { + return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen] + } + return nil +} + +func (h *msghdr) controllen() int { + return int(h.Controllen) +} + +func (h *msghdr) flags() int { + return int(h.Flags) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_32bit.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_32bit.go new file mode 100644 index 00000000..9f671aec --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_32bit.go @@ -0,0 +1,20 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm mips mipsle 386 +// +build linux + +package socket + +import "unsafe" + +func (h *msghdr) setIov(vs []iovec) { + h.Iov = &vs[0] + h.Iovlen = uint32(len(vs)) +} + +func (h *msghdr) setControl(b []byte) { + h.Control = (*byte)(unsafe.Pointer(&b[0])) + h.Controllen = uint32(len(b)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_64bit.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_64bit.go new file mode 100644 index 00000000..9f787062 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_linux_64bit.go @@ -0,0 +1,20 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm64 amd64 ppc64 ppc64le mips64 mips64le s390x +// +build linux + +package socket + +import "unsafe" + +func (h *msghdr) setIov(vs []iovec) { + h.Iov = &vs[0] + h.Iovlen = uint64(len(vs)) +} + +func (h *msghdr) setControl(b []byte) { + h.Control = (*byte)(unsafe.Pointer(&b[0])) + h.Controllen = uint64(len(b)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_openbsd.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_openbsd.go new file mode 100644 index 00000000..be354ff8 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_openbsd.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +func (h *msghdr) setIov(vs []iovec) { + h.Iov = &vs[0] + h.Iovlen = uint32(len(vs)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go new file mode 100644 index 00000000..d1b05939 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go @@ -0,0 +1,34 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 +// +build solaris + +package socket + +import "unsafe" + +func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) { + for i := range vs { + vs[i].set(bs[i]) + } + h.Iov = &vs[0] + h.Iovlen = int32(len(vs)) + if len(oob) > 0 { + h.Accrights = (*int8)(unsafe.Pointer(&oob[0])) + h.Accrightslen = int32(len(oob)) + } + if sa != nil { + h.Name = (*byte)(unsafe.Pointer(&sa[0])) + h.Namelen = uint32(len(sa)) + } +} + +func (h *msghdr) controllen() int { + return int(h.Accrightslen) +} + +func (h *msghdr) flags() int { + return int(NativeEndian.Uint32(h.Pad_cgo_2[:])) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/msghdr_stub.go b/vendor/src/golang.org/x/net/internal/socket/msghdr_stub.go new file mode 100644 index 00000000..64e81733 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/msghdr_stub.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package socket + +type msghdr struct{} + +func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {} +func (h *msghdr) name() []byte { return nil } +func (h *msghdr) controllen() int { return 0 } +func (h *msghdr) flags() int { return 0 } diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn.go b/vendor/src/golang.org/x/net/internal/socket/rawconn.go new file mode 100644 index 00000000..d6871d55 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn.go @@ -0,0 +1,66 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package socket + +import ( + "errors" + "net" + "os" + "syscall" +) + +// A Conn represents a raw connection. +type Conn struct { + network string + c syscall.RawConn +} + +// NewConn returns a new raw connection. +func NewConn(c net.Conn) (*Conn, error) { + var err error + var cc Conn + switch c := c.(type) { + case *net.TCPConn: + cc.network = "tcp" + cc.c, err = c.SyscallConn() + case *net.UDPConn: + cc.network = "udp" + cc.c, err = c.SyscallConn() + case *net.IPConn: + cc.network = "ip" + cc.c, err = c.SyscallConn() + default: + return nil, errors.New("unknown connection type") + } + if err != nil { + return nil, err + } + return &cc, nil +} + +func (o *Option) get(c *Conn, b []byte) (int, error) { + var operr error + var n int + fn := func(s uintptr) { + n, operr = getsockopt(s, o.Level, o.Name, b) + } + if err := c.c.Control(fn); err != nil { + return 0, err + } + return n, os.NewSyscallError("getsockopt", operr) +} + +func (o *Option) set(c *Conn, b []byte) error { + var operr error + fn := func(s uintptr) { + operr = setsockopt(s, o.Level, o.Name, b) + } + if err := c.c.Control(fn); err != nil { + return err + } + return os.NewSyscallError("setsockopt", operr) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn_mmsg.go b/vendor/src/golang.org/x/net/internal/socket/rawconn_mmsg.go new file mode 100644 index 00000000..499164a3 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn_mmsg.go @@ -0,0 +1,74 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build linux + +package socket + +import ( + "net" + "os" + "syscall" +) + +func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { + hs := make(mmsghdrs, len(ms)) + var parseFn func([]byte, string) (net.Addr, error) + if c.network != "tcp" { + parseFn = parseInetAddr + } + if err := hs.pack(ms, parseFn, nil); err != nil { + return 0, err + } + var operr error + var n int + fn := func(s uintptr) bool { + n, operr = recvmmsg(s, hs, flags) + if operr == syscall.EAGAIN { + return false + } + return true + } + if err := c.c.Read(fn); err != nil { + return n, err + } + if operr != nil { + return n, os.NewSyscallError("recvmmsg", operr) + } + if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil { + return n, err + } + return n, nil +} + +func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { + hs := make(mmsghdrs, len(ms)) + var marshalFn func(net.Addr) []byte + if c.network != "tcp" { + marshalFn = marshalInetAddr + } + if err := hs.pack(ms, nil, marshalFn); err != nil { + return 0, err + } + var operr error + var n int + fn := func(s uintptr) bool { + n, operr = sendmmsg(s, hs, flags) + if operr == syscall.EAGAIN { + return false + } + return true + } + if err := c.c.Write(fn); err != nil { + return n, err + } + if operr != nil { + return n, os.NewSyscallError("sendmmsg", operr) + } + if err := hs[:n].unpack(ms[:n], nil, ""); err != nil { + return n, err + } + return n, nil +} diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn_msg.go b/vendor/src/golang.org/x/net/internal/socket/rawconn_msg.go new file mode 100644 index 00000000..b21d2e64 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn_msg.go @@ -0,0 +1,77 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package socket + +import ( + "os" + "syscall" +) + +func (c *Conn) recvMsg(m *Message, flags int) error { + var h msghdr + vs := make([]iovec, len(m.Buffers)) + var sa []byte + if c.network != "tcp" { + sa = make([]byte, sizeofSockaddrInet6) + } + h.pack(vs, m.Buffers, m.OOB, sa) + var operr error + var n int + fn := func(s uintptr) bool { + n, operr = recvmsg(s, &h, flags) + if operr == syscall.EAGAIN { + return false + } + return true + } + if err := c.c.Read(fn); err != nil { + return err + } + if operr != nil { + return os.NewSyscallError("recvmsg", operr) + } + if c.network != "tcp" { + var err error + m.Addr, err = parseInetAddr(sa[:], c.network) + if err != nil { + return err + } + } + m.N = n + m.NN = h.controllen() + m.Flags = h.flags() + return nil +} + +func (c *Conn) sendMsg(m *Message, flags int) error { + var h msghdr + vs := make([]iovec, len(m.Buffers)) + var sa []byte + if m.Addr != nil { + sa = marshalInetAddr(m.Addr) + } + h.pack(vs, m.Buffers, m.OOB, sa) + var operr error + var n int + fn := func(s uintptr) bool { + n, operr = sendmsg(s, &h, flags) + if operr == syscall.EAGAIN { + return false + } + return true + } + if err := c.c.Write(fn); err != nil { + return err + } + if operr != nil { + return os.NewSyscallError("sendmsg", operr) + } + m.N = n + m.NN = len(m.OOB) + return nil +} diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn_nommsg.go b/vendor/src/golang.org/x/net/internal/socket/rawconn_nommsg.go new file mode 100644 index 00000000..f78832aa --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn_nommsg.go @@ -0,0 +1,18 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build !linux + +package socket + +import "errors" + +func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn_nomsg.go b/vendor/src/golang.org/x/net/internal/socket/rawconn_nomsg.go new file mode 100644 index 00000000..96733cbe --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn_nomsg.go @@ -0,0 +1,18 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package socket + +import "errors" + +func (c *Conn) recvMsg(m *Message, flags int) error { + return errors.New("not implemented") +} + +func (c *Conn) sendMsg(m *Message, flags int) error { + return errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/rawconn_stub.go b/vendor/src/golang.org/x/net/internal/socket/rawconn_stub.go new file mode 100644 index 00000000..d2add1a0 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/rawconn_stub.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package socket + +import "errors" + +func (c *Conn) recvMsg(m *Message, flags int) error { + return errors.New("not implemented") +} + +func (c *Conn) sendMsg(m *Message, flags int) error { + return errors.New("not implemented") +} + +func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/reflect.go b/vendor/src/golang.org/x/net/internal/socket/reflect.go new file mode 100644 index 00000000..bb179f11 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/reflect.go @@ -0,0 +1,62 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package socket + +import ( + "errors" + "net" + "os" + "reflect" + "runtime" +) + +// A Conn represents a raw connection. +type Conn struct { + c net.Conn +} + +// NewConn returns a new raw connection. +func NewConn(c net.Conn) (*Conn, error) { + return &Conn{c: c}, nil +} + +func (o *Option) get(c *Conn, b []byte) (int, error) { + s, err := socketOf(c.c) + if err != nil { + return 0, err + } + n, err := getsockopt(s, o.Level, o.Name, b) + return n, os.NewSyscallError("getsockopt", err) +} + +func (o *Option) set(c *Conn, b []byte) error { + s, err := socketOf(c.c) + if err != nil { + return err + } + return os.NewSyscallError("setsockopt", setsockopt(s, o.Level, o.Name, b)) +} + +func socketOf(c net.Conn) (uintptr, error) { + switch c.(type) { + case *net.TCPConn, *net.UDPConn, *net.IPConn: + v := reflect.ValueOf(c) + switch e := v.Elem(); e.Kind() { + case reflect.Struct: + fd := e.FieldByName("conn").FieldByName("fd") + switch e := fd.Elem(); e.Kind() { + case reflect.Struct: + sysfd := e.FieldByName("sysfd") + if runtime.GOOS == "windows" { + return uintptr(sysfd.Uint()), nil + } + return uintptr(sysfd.Int()), nil + } + } + } + return 0, errors.New("invalid type") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/socket.go b/vendor/src/golang.org/x/net/internal/socket/socket.go new file mode 100644 index 00000000..729dea14 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/socket.go @@ -0,0 +1,285 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package socket provides a portable interface for socket system +// calls. +package socket // import "golang.org/x/net/internal/socket" + +import ( + "errors" + "net" + "unsafe" +) + +// An Option represents a sticky socket option. +type Option struct { + Level int // level + Name int // name; must be equal or greater than 1 + Len int // length of value in bytes; must be equal or greater than 1 +} + +// Get reads a value for the option from the kernel. +// It returns the number of bytes written into b. +func (o *Option) Get(c *Conn, b []byte) (int, error) { + if o.Name < 1 || o.Len < 1 { + return 0, errors.New("invalid option") + } + if len(b) < o.Len { + return 0, errors.New("short buffer") + } + return o.get(c, b) +} + +// GetInt returns an integer value for the option. +// +// The Len field of Option must be either 1 or 4. +func (o *Option) GetInt(c *Conn) (int, error) { + if o.Len != 1 && o.Len != 4 { + return 0, errors.New("invalid option") + } + var b []byte + var bb [4]byte + if o.Len == 1 { + b = bb[:1] + } else { + b = bb[:4] + } + n, err := o.get(c, b) + if err != nil { + return 0, err + } + if n != o.Len { + return 0, errors.New("invalid option length") + } + if o.Len == 1 { + return int(b[0]), nil + } + return int(NativeEndian.Uint32(b[:4])), nil +} + +// Set writes the option and value to the kernel. +func (o *Option) Set(c *Conn, b []byte) error { + if o.Name < 1 || o.Len < 1 { + return errors.New("invalid option") + } + if len(b) < o.Len { + return errors.New("short buffer") + } + return o.set(c, b) +} + +// SetInt writes the option and value to the kernel. +// +// The Len field of Option must be either 1 or 4. +func (o *Option) SetInt(c *Conn, v int) error { + if o.Len != 1 && o.Len != 4 { + return errors.New("invalid option") + } + var b []byte + if o.Len == 1 { + b = []byte{byte(v)} + } else { + var bb [4]byte + NativeEndian.PutUint32(bb[:o.Len], uint32(v)) + b = bb[:4] + } + return o.set(c, b) +} + +func controlHeaderLen() int { + return roundup(sizeofCmsghdr) +} + +func controlMessageLen(dataLen int) int { + return roundup(sizeofCmsghdr) + dataLen +} + +// ControlMessageSpace returns the whole length of control message. +func ControlMessageSpace(dataLen int) int { + return roundup(sizeofCmsghdr) + roundup(dataLen) +} + +// A ControlMessage represents the head message in a stream of control +// messages. +// +// A control message comprises of a header, data and a few padding +// fields to conform to the interface to the kernel. +// +// See RFC 3542 for further information. +type ControlMessage []byte + +// Data returns the data field of the control message at the head on +// w. +func (m ControlMessage) Data(dataLen int) []byte { + l := controlHeaderLen() + if len(m) < l || len(m) < l+dataLen { + return nil + } + return m[l : l+dataLen] +} + +// Next returns the control message at the next on w. +// +// Next works only for standard control messages. +func (m ControlMessage) Next(dataLen int) ControlMessage { + l := ControlMessageSpace(dataLen) + if len(m) < l { + return nil + } + return m[l:] +} + +// MarshalHeader marshals the header fields of the control message at +// the head on w. +func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error { + if len(m) < controlHeaderLen() { + return errors.New("short message") + } + h := (*cmsghdr)(unsafe.Pointer(&m[0])) + h.set(controlMessageLen(dataLen), lvl, typ) + return nil +} + +// ParseHeader parses and returns the header fields of the control +// message at the head on w. +func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) { + l := controlHeaderLen() + if len(m) < l { + return 0, 0, 0, errors.New("short message") + } + h := (*cmsghdr)(unsafe.Pointer(&m[0])) + return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil +} + +// Marshal marshals the control message at the head on w, and returns +// the next control message. +func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) { + l := len(data) + if len(m) < ControlMessageSpace(l) { + return nil, errors.New("short message") + } + h := (*cmsghdr)(unsafe.Pointer(&m[0])) + h.set(controlMessageLen(l), lvl, typ) + if l > 0 { + copy(m.Data(l), data) + } + return m.Next(l), nil +} + +// Parse parses w as a single or multiple control messages. +// +// Parse works for both standard and compatible messages. +func (m ControlMessage) Parse() ([]ControlMessage, error) { + var ms []ControlMessage + for len(m) >= controlHeaderLen() { + h := (*cmsghdr)(unsafe.Pointer(&m[0])) + l := h.len() + if l <= 0 { + return nil, errors.New("invalid header length") + } + if uint64(l) < uint64(controlHeaderLen()) { + return nil, errors.New("invalid message length") + } + if uint64(l) > uint64(len(m)) { + return nil, errors.New("short buffer") + } + // On message reception: + // + // |<- ControlMessageSpace --------------->| + // |<- controlMessageLen ---------->| | + // |<- controlHeaderLen ->| | | + // +---------------+------+---------+------+ + // | Header | PadH | Data | PadD | + // +---------------+------+---------+------+ + // + // On compatible message reception: + // + // | ... |<- controlMessageLen ----------->| + // | ... |<- controlHeaderLen ->| | + // +-----+---------------+------+----------+ + // | ... | Header | PadH | Data | + // +-----+---------------+------+----------+ + ms = append(ms, ControlMessage(m[:l])) + ll := l - controlHeaderLen() + if len(m) >= ControlMessageSpace(ll) { + m = m[ControlMessageSpace(ll):] + } else { + m = m[controlMessageLen(ll):] + } + } + return ms, nil +} + +// NewControlMessage returns a new stream of control messages. +func NewControlMessage(dataLen []int) ControlMessage { + var l int + for i := range dataLen { + l += ControlMessageSpace(dataLen[i]) + } + return make([]byte, l) +} + +// A Message represents an IO message. +type Message struct { + // When writing, the Buffers field must contain at least one + // byte to write. + // When reading, the Buffers field will always contain a byte + // to read. + Buffers [][]byte + + // OOB contains protocol-specific control or miscellaneous + // ancillary data known as out-of-band data. + OOB []byte + + // Addr specifies a destination address when writing. + // It can be nil when the underlying protocol of the raw + // connection uses connection-oriented communication. + // After a successful read, it may contain the source address + // on the received packet. + Addr net.Addr + + N int // # of bytes read or written from/to Buffers + NN int // # of bytes read or written from/to OOB + Flags int // protocol-specific information on the received message +} + +// RecvMsg wraps recvmsg system call. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_PEEK. +func (c *Conn) RecvMsg(m *Message, flags int) error { + return c.recvMsg(m, flags) +} + +// SendMsg wraps sendmsg system call. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_DONTROUTE. +func (c *Conn) SendMsg(m *Message, flags int) error { + return c.sendMsg(m, flags) +} + +// RecvMsgs wraps recvmmsg system call. +// +// It returns the number of processed messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_PEEK. +// +// Only Linux supports this. +func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) { + return c.recvMsgs(ms, flags) +} + +// SendMsgs wraps sendmmsg system call. +// +// It returns the number of processed messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_DONTROUTE. +// +// Only Linux supports this. +func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) { + return c.sendMsgs(ms, flags) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/socket_go1_9_test.go b/vendor/src/golang.org/x/net/internal/socket/socket_go1_9_test.go new file mode 100644 index 00000000..109fed76 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/socket_go1_9_test.go @@ -0,0 +1,256 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package socket_test + +import ( + "bytes" + "fmt" + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/internal/socket" +) + +type mockControl struct { + Level int + Type int + Data []byte +} + +func TestControlMessage(t *testing.T) { + for _, tt := range []struct { + cs []mockControl + }{ + { + []mockControl{ + {Level: 1, Type: 1}, + }, + }, + { + []mockControl{ + {Level: 2, Type: 2, Data: []byte{0xfe}}, + }, + }, + { + []mockControl{ + {Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}}, + }, + }, + { + []mockControl{ + {Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}}, + }, + }, + { + []mockControl{ + {Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}}, + {Level: 2, Type: 2, Data: []byte{0xfe}}, + }, + }, + } { + var w []byte + var tailPadLen int + mm := socket.NewControlMessage([]int{0}) + for i, c := range tt.cs { + m := socket.NewControlMessage([]int{len(c.Data)}) + l := len(m) - len(mm) + if i == len(tt.cs)-1 && l > len(c.Data) { + tailPadLen = l - len(c.Data) + } + w = append(w, m...) + } + + var err error + ww := make([]byte, len(w)) + copy(ww, w) + m := socket.ControlMessage(ww) + for _, c := range tt.cs { + if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil { + t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err) + } + copy(m.Data(len(c.Data)), c.Data) + m = m.Next(len(c.Data)) + } + m = socket.ControlMessage(w) + for _, c := range tt.cs { + m, err = m.Marshal(c.Level, c.Type, c.Data) + if err != nil { + t.Fatalf("(%v).Marshal() = %v", tt.cs, err) + } + } + if !bytes.Equal(ww, w) { + t.Fatalf("got %#v; want %#v", ww, w) + } + + ws := [][]byte{w} + if tailPadLen > 0 { + // Test a message with no tail padding. + nopad := w[:len(w)-tailPadLen] + ws = append(ws, [][]byte{nopad}...) + } + for _, w := range ws { + ms, err := socket.ControlMessage(w).Parse() + if err != nil { + t.Fatalf("(%v).Parse() = %v", tt.cs, err) + } + for i, m := range ms { + lvl, typ, dataLen, err := m.ParseHeader() + if err != nil { + t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err) + } + if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) { + t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data)) + } + } + } + } +} + +func TestUDP(t *testing.T) { + c, err := nettest.NewLocalPacketListener("udp") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + + t.Run("Message", func(t *testing.T) { + testUDPMessage(t, c.(net.Conn)) + }) + switch runtime.GOOS { + case "linux": + t.Run("Messages", func(t *testing.T) { + testUDPMessages(t, c.(net.Conn)) + }) + } +} + +func testUDPMessage(t *testing.T, c net.Conn) { + cc, err := socket.NewConn(c) + if err != nil { + t.Fatal(err) + } + data := []byte("HELLO-R-U-THERE") + wm := socket.Message{ + Buffers: bytes.SplitAfter(data, []byte("-")), + Addr: c.LocalAddr(), + } + if err := cc.SendMsg(&wm, 0); err != nil { + t.Fatal(err) + } + b := make([]byte, 32) + rm := socket.Message{ + Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]}, + } + if err := cc.RecvMsg(&rm, 0); err != nil { + t.Fatal(err) + } + if !bytes.Equal(b[:rm.N], data) { + t.Fatalf("got %#v; want %#v", b[:rm.N], data) + } +} + +func testUDPMessages(t *testing.T, c net.Conn) { + cc, err := socket.NewConn(c) + if err != nil { + t.Fatal(err) + } + data := []byte("HELLO-R-U-THERE") + wmbs := bytes.SplitAfter(data, []byte("-")) + wms := []socket.Message{ + {Buffers: wmbs[:1], Addr: c.LocalAddr()}, + {Buffers: wmbs[1:], Addr: c.LocalAddr()}, + } + n, err := cc.SendMsgs(wms, 0) + if err != nil { + t.Fatal(err) + } + if n != len(wms) { + t.Fatalf("got %d; want %d", n, len(wms)) + } + b := make([]byte, 32) + rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}} + rms := []socket.Message{ + {Buffers: rmbs[0]}, + {Buffers: rmbs[1]}, + } + n, err = cc.RecvMsgs(rms, 0) + if err != nil { + t.Fatal(err) + } + if n != len(rms) { + t.Fatalf("got %d; want %d", n, len(rms)) + } + nn := 0 + for i := 0; i < n; i++ { + nn += rms[i].N + } + if !bytes.Equal(b[:nn], data) { + t.Fatalf("got %#v; want %#v", b[:nn], data) + } +} + +func BenchmarkUDP(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + cc, err := socket.NewConn(c.(net.Conn)) + if err != nil { + b.Fatal(err) + } + data := []byte("HELLO-R-U-THERE") + wm := socket.Message{ + Buffers: [][]byte{data}, + Addr: c.LocalAddr(), + } + rm := socket.Message{ + Buffers: [][]byte{make([]byte, 128)}, + OOB: make([]byte, 128), + } + + for M := 1; M <= 1<<9; M = M << 1 { + b.Run(fmt.Sprintf("Iter-%d", M), func(b *testing.B) { + for i := 0; i < b.N; i++ { + for j := 0; j < M; j++ { + if err := cc.SendMsg(&wm, 0); err != nil { + b.Fatal(err) + } + if err := cc.RecvMsg(&rm, 0); err != nil { + b.Fatal(err) + } + } + } + }) + switch runtime.GOOS { + case "linux": + wms := make([]socket.Message, M) + for i := range wms { + wms[i].Buffers = [][]byte{data} + wms[i].Addr = c.LocalAddr() + } + rms := make([]socket.Message, M) + for i := range rms { + rms[i].Buffers = [][]byte{make([]byte, 128)} + rms[i].OOB = make([]byte, 128) + } + b.Run(fmt.Sprintf("Batch-%d", M), func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := cc.SendMsgs(wms, 0); err != nil { + b.Fatal(err) + } + if _, err := cc.RecvMsgs(rms, 0); err != nil { + b.Fatal(err) + } + } + }) + } + } +} diff --git a/vendor/src/golang.org/x/net/internal/socket/socket_test.go b/vendor/src/golang.org/x/net/internal/socket/socket_test.go new file mode 100644 index 00000000..bf3751b5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/socket_test.go @@ -0,0 +1,46 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package socket_test + +import ( + "net" + "runtime" + "syscall" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/internal/socket" +) + +func TestSocket(t *testing.T) { + t.Run("Option", func(t *testing.T) { + testSocketOption(t, &socket.Option{Level: syscall.SOL_SOCKET, Name: syscall.SO_RCVBUF, Len: 4}) + }) +} + +func testSocketOption(t *testing.T, so *socket.Option) { + c, err := nettest.NewLocalPacketListener("udp") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + cc, err := socket.NewConn(c.(net.Conn)) + if err != nil { + t.Fatal(err) + } + const N = 2048 + if err := so.SetInt(cc, N); err != nil { + t.Fatal(err) + } + n, err := so.GetInt(cc) + if err != nil { + t.Fatal(err) + } + if n < N { + t.Fatalf("got %d; want greater than or equal to %d", n, N) + } +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys.go b/vendor/src/golang.org/x/net/internal/socket/sys.go new file mode 100644 index 00000000..4f0eead1 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys.go @@ -0,0 +1,33 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "encoding/binary" + "unsafe" +) + +var ( + // NativeEndian is the machine native endian implementation of + // ByteOrder. + NativeEndian binary.ByteOrder + + kernelAlign int +) + +func init() { + i := uint32(1) + b := (*[4]byte)(unsafe.Pointer(&i)) + if b[0] == 1 { + NativeEndian = binary.LittleEndian + } else { + NativeEndian = binary.BigEndian + } + kernelAlign = probeProtocolStack() +} + +func roundup(l int) int { + return (l + kernelAlign - 1) & ^(kernelAlign - 1) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_bsd.go b/vendor/src/golang.org/x/net/internal/socket/sys_bsd.go new file mode 100644 index 00000000..f13e14ff --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_bsd.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd openbsd + +package socket + +import "errors" + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_bsdvar.go b/vendor/src/golang.org/x/net/internal/socket/sys_bsdvar.go new file mode 100644 index 00000000..f723fa36 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_bsdvar.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build freebsd netbsd openbsd + +package socket + +import "unsafe" + +func probeProtocolStack() int { + var p uintptr + return int(unsafe.Sizeof(p)) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_darwin.go b/vendor/src/golang.org/x/net/internal/socket/sys_darwin.go new file mode 100644 index 00000000..b17d223b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_darwin.go @@ -0,0 +1,7 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +func probeProtocolStack() int { return 4 } diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_dragonfly.go b/vendor/src/golang.org/x/net/internal/socket/sys_dragonfly.go new file mode 100644 index 00000000..b17d223b --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_dragonfly.go @@ -0,0 +1,7 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +func probeProtocolStack() int { return 4 } diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux.go new file mode 100644 index 00000000..1559521e --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux.go @@ -0,0 +1,27 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,!s390x,!386 + +package socket + +import ( + "syscall" + "unsafe" +) + +func probeProtocolStack() int { + var p uintptr + return int(unsafe.Sizeof(p)) +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.go new file mode 100644 index 00000000..235b2cc0 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.go @@ -0,0 +1,55 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "syscall" + "unsafe" +) + +func probeProtocolStack() int { return 4 } + +const ( + sysSETSOCKOPT = 0xe + sysGETSOCKOPT = 0xf + sysSENDMSG = 0x10 + sysRECVMSG = 0x11 + sysRECVMMSG = 0x13 + sysSENDMMSG = 0x14 +) + +func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) +func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + _, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) + return int(l), errnoErr(errno) +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + _, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) + return errnoErr(errno) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.s b/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.s new file mode 100644 index 00000000..93e7d75e --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_386.s @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·socketcall(SB),NOSPLIT,$0-36 + JMP syscall·socketcall(SB) + +TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 + JMP syscall·rawsocketcall(SB) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_amd64.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_amd64.go new file mode 100644 index 00000000..9decee2e --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_amd64.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x12b + sysSENDMMSG = 0x133 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm.go new file mode 100644 index 00000000..d753b436 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x16d + sysSENDMMSG = 0x176 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm64.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm64.go new file mode 100644 index 00000000..b6708943 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_arm64.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0xf3 + sysSENDMMSG = 0x10d +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips.go new file mode 100644 index 00000000..9c0d7401 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x10ef + sysSENDMMSG = 0x10f7 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64.go new file mode 100644 index 00000000..071a4aba --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x14ae + sysSENDMMSG = 0x14b6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64le.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64le.go new file mode 100644 index 00000000..071a4aba --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mips64le.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x14ae + sysSENDMMSG = 0x14b6 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_mipsle.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mipsle.go new file mode 100644 index 00000000..9c0d7401 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_mipsle.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x10ef + sysSENDMMSG = 0x10f7 +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64.go new file mode 100644 index 00000000..21c1e3f0 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x157 + sysSENDMMSG = 0x15d +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64le.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64le.go new file mode 100644 index 00000000..21c1e3f0 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_ppc64le.go @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +const ( + sysRECVMMSG = 0x157 + sysSENDMMSG = 0x15d +) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.go b/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.go new file mode 100644 index 00000000..327979ef --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.go @@ -0,0 +1,55 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "syscall" + "unsafe" +) + +func probeProtocolStack() int { return 8 } + +const ( + sysSETSOCKOPT = 0xe + sysGETSOCKOPT = 0xf + sysSENDMSG = 0x10 + sysRECVMSG = 0x11 + sysRECVMMSG = 0x13 + sysSENDMMSG = 0x14 +) + +func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) +func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + _, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) + return int(l), errnoErr(errno) +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + _, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) + return errnoErr(errno) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.s b/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.s new file mode 100644 index 00000000..06d75628 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_linux_s390x.s @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·socketcall(SB),NOSPLIT,$0-72 + JMP syscall·socketcall(SB) + +TEXT ·rawsocketcall(SB),NOSPLIT,$0-72 + JMP syscall·rawsocketcall(SB) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_netbsd.go b/vendor/src/golang.org/x/net/internal/socket/sys_netbsd.go new file mode 100644 index 00000000..431851c1 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_netbsd.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "syscall" + "unsafe" +) + +const ( + sysRECVMMSG = 0x1db + sysSENDMMSG = 0x1dc +) + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0) + return int(n), errnoErr(errno) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_posix.go b/vendor/src/golang.org/x/net/internal/socket/sys_posix.go new file mode 100644 index 00000000..9a0dbcfb --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_posix.go @@ -0,0 +1,168 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package socket + +import ( + "encoding/binary" + "errors" + "net" + "runtime" + "strconv" + "sync" + "time" +) + +func marshalInetAddr(a net.Addr) []byte { + switch a := a.(type) { + case *net.TCPAddr: + return marshalSockaddr(a.IP, a.Port, a.Zone) + case *net.UDPAddr: + return marshalSockaddr(a.IP, a.Port, a.Zone) + case *net.IPAddr: + return marshalSockaddr(a.IP, 0, a.Zone) + default: + return nil + } +} + +func marshalSockaddr(ip net.IP, port int, zone string) []byte { + if ip4 := ip.To4(); ip4 != nil { + b := make([]byte, sizeofSockaddrInet) + switch runtime.GOOS { + case "linux", "solaris", "windows": + NativeEndian.PutUint16(b[:2], uint16(sysAF_INET)) + default: + b[0] = sizeofSockaddrInet + b[1] = sysAF_INET + } + binary.BigEndian.PutUint16(b[2:4], uint16(port)) + copy(b[4:8], ip4) + return b + } + if ip6 := ip.To16(); ip6 != nil && ip.To4() == nil { + b := make([]byte, sizeofSockaddrInet6) + switch runtime.GOOS { + case "linux", "solaris", "windows": + NativeEndian.PutUint16(b[:2], uint16(sysAF_INET6)) + default: + b[0] = sizeofSockaddrInet6 + b[1] = sysAF_INET6 + } + binary.BigEndian.PutUint16(b[2:4], uint16(port)) + copy(b[8:24], ip6) + if zone != "" { + NativeEndian.PutUint32(b[24:28], uint32(zoneCache.index(zone))) + } + return b + } + return nil +} + +func parseInetAddr(b []byte, network string) (net.Addr, error) { + if len(b) < 2 { + return nil, errors.New("invalid address") + } + var af int + switch runtime.GOOS { + case "linux", "solaris", "windows": + af = int(NativeEndian.Uint16(b[:2])) + default: + af = int(b[1]) + } + var ip net.IP + var zone string + if af == sysAF_INET { + if len(b) < sizeofSockaddrInet { + return nil, errors.New("short address") + } + ip = make(net.IP, net.IPv4len) + copy(ip, b[4:8]) + } + if af == sysAF_INET6 { + if len(b) < sizeofSockaddrInet6 { + return nil, errors.New("short address") + } + ip = make(net.IP, net.IPv6len) + copy(ip, b[8:24]) + if id := int(NativeEndian.Uint32(b[24:28])); id > 0 { + zone = zoneCache.name(id) + } + } + switch network { + case "tcp", "tcp4", "tcp6": + return &net.TCPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil + case "udp", "udp4", "udp6": + return &net.UDPAddr{IP: ip, Port: int(binary.BigEndian.Uint16(b[2:4])), Zone: zone}, nil + default: + return &net.IPAddr{IP: ip, Zone: zone}, nil + } +} + +// An ipv6ZoneCache represents a cache holding partial network +// interface information. It is used for reducing the cost of IPv6 +// addressing scope zone resolution. +// +// Multiple names sharing the index are managed by first-come +// first-served basis for consistency. +type ipv6ZoneCache struct { + sync.RWMutex // guard the following + lastFetched time.Time // last time routing information was fetched + toIndex map[string]int // interface name to its index + toName map[int]string // interface index to its name +} + +var zoneCache = ipv6ZoneCache{ + toIndex: make(map[string]int), + toName: make(map[int]string), +} + +func (zc *ipv6ZoneCache) update(ift []net.Interface) { + zc.Lock() + defer zc.Unlock() + now := time.Now() + if zc.lastFetched.After(now.Add(-60 * time.Second)) { + return + } + zc.lastFetched = now + if len(ift) == 0 { + var err error + if ift, err = net.Interfaces(); err != nil { + return + } + } + zc.toIndex = make(map[string]int, len(ift)) + zc.toName = make(map[int]string, len(ift)) + for _, ifi := range ift { + zc.toIndex[ifi.Name] = ifi.Index + if _, ok := zc.toName[ifi.Index]; !ok { + zc.toName[ifi.Index] = ifi.Name + } + } +} + +func (zc *ipv6ZoneCache) name(zone int) string { + zoneCache.update(nil) + zoneCache.RLock() + defer zoneCache.RUnlock() + name, ok := zoneCache.toName[zone] + if !ok { + name = strconv.Itoa(zone) + } + return name +} + +func (zc *ipv6ZoneCache) index(zone string) int { + zoneCache.update(nil) + zoneCache.RLock() + defer zoneCache.RUnlock() + index, ok := zoneCache.toIndex[zone] + if !ok { + index, _ = strconv.Atoi(zone) + } + return index +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_solaris.go b/vendor/src/golang.org/x/net/internal/socket/sys_solaris.go new file mode 100644 index 00000000..cced74e6 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_solaris.go @@ -0,0 +1,71 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "errors" + "runtime" + "syscall" + "unsafe" +) + +func probeProtocolStack() int { + switch runtime.GOARCH { + case "amd64": + return 4 + default: + var p uintptr + return int(unsafe.Sizeof(p)) + } +} + +//go:cgo_import_dynamic libc___xnet_getsockopt __xnet_getsockopt "libsocket.so" +//go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so" +//go:cgo_import_dynamic libc___xnet_recvmsg __xnet_recvmsg "libsocket.so" +//go:cgo_import_dynamic libc___xnet_sendmsg __xnet_sendmsg "libsocket.so" + +//go:linkname procGetsockopt libc___xnet_getsockopt +//go:linkname procSetsockopt libc_setsockopt +//go:linkname procRecvmsg libc___xnet_recvmsg +//go:linkname procSendmsg libc___xnet_sendmsg + +var ( + procGetsockopt uintptr + procSetsockopt uintptr + procRecvmsg uintptr + procSendmsg uintptr +) + +func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno) +func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procGetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) + return int(l), errnoErr(errno) +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procSetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) + return errnoErr(errno) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procRecvmsg)), 3, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procSendmsg)), 3, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0) + return int(n), errnoErr(errno) +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_solaris_amd64.s b/vendor/src/golang.org/x/net/internal/socket/sys_solaris_amd64.s new file mode 100644 index 00000000..a18ac5ed --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_solaris_amd64.s @@ -0,0 +1,11 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·sysvicall6(SB),NOSPLIT,$0-88 + JMP syscall·sysvicall6(SB) + +TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88 + JMP syscall·rawSysvicall6(SB) diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_stub.go b/vendor/src/golang.org/x/net/internal/socket/sys_stub.go new file mode 100644 index 00000000..d9f06d00 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_stub.go @@ -0,0 +1,64 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package socket + +import ( + "errors" + "net" + "runtime" + "unsafe" +) + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +func probeProtocolStack() int { + switch runtime.GOARCH { + case "amd64p32", "mips64p32": + return 4 + default: + var p uintptr + return int(unsafe.Sizeof(p)) + } +} + +func marshalInetAddr(ip net.IP, port int, zone string) []byte { + return nil +} + +func parseInetAddr(b []byte, network string) (net.Addr, error) { + return nil, errors.New("not implemented") +} + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + return 0, errors.New("not implemented") +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + return errors.New("not implemented") +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_unix.go b/vendor/src/golang.org/x/net/internal/socket/sys_unix.go new file mode 100644 index 00000000..18eba308 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_unix.go @@ -0,0 +1,33 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!s390x,!386 netbsd openbsd + +package socket + +import ( + "syscall" + "unsafe" +) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) + return int(l), errnoErr(errno) +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + _, _, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) + return errnoErr(errno) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall(syscall.SYS_RECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) + return int(n), errnoErr(errno) +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + n, _, errno := syscall.Syscall(syscall.SYS_SENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags)) + return int(n), errnoErr(errno) +} diff --git a/vendor/src/golang.org/x/net/internal/socket/sys_windows.go b/vendor/src/golang.org/x/net/internal/socket/sys_windows.go new file mode 100644 index 00000000..54a470eb --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/sys_windows.go @@ -0,0 +1,70 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package socket + +import ( + "errors" + "syscall" + "unsafe" +) + +func probeProtocolStack() int { + var p uintptr + return int(unsafe.Sizeof(p)) +} + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x17 + + sysSOCK_RAW = 0x3 +) + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) + +func getsockopt(s uintptr, level, name int, b []byte) (int, error) { + l := uint32(len(b)) + err := syscall.Getsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), (*int32)(unsafe.Pointer(&l))) + return int(l), err +} + +func setsockopt(s uintptr, level, name int, b []byte) error { + return syscall.Setsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), int32(len(b))) +} + +func recvmsg(s uintptr, h *msghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmsg(s uintptr, h *msghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} + +func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) { + return 0, errors.New("not implemented") +} diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_386.go b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_386.go new file mode 100644 index 00000000..26f8feff --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_386.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1e + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_amd64.go new file mode 100644 index 00000000..e2987f7d --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_amd64.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1e + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_arm.go b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_arm.go new file mode 100644 index 00000000..26f8feff --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_darwin_arm.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1e + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go new file mode 100644 index 00000000..c582abd5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_dragonfly.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1c + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_386.go b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_386.go new file mode 100644 index 00000000..04a24886 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_386.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1c + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go new file mode 100644 index 00000000..35c7cb9c --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1c + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_arm.go b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_arm.go new file mode 100644 index 00000000..04a24886 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_freebsd_arm.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1c + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_386.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_386.go new file mode 100644 index 00000000..43020693 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_386.go @@ -0,0 +1,63 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_amd64.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_amd64.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm.go new file mode 100644 index 00000000..43020693 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm.go @@ -0,0 +1,63 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm64.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_arm64.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips.go new file mode 100644 index 00000000..43020693 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips.go @@ -0,0 +1,63 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64le.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64le.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mips64le.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mipsle.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mipsle.go new file mode 100644 index 00000000..43020693 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_mipsle.go @@ -0,0 +1,63 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_linux_s390x.go b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_s390x.go new file mode 100644 index 00000000..1502f6c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_linux_s390x.go @@ -0,0 +1,66 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0xa + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint64 + Control *byte + Controllen uint64 + Flags int32 + Pad_cgo_1 [4]byte +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint64 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x38 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0x10 + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_386.go b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_386.go new file mode 100644 index 00000000..db60491f --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_386.go @@ -0,0 +1,65 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofMmsghdr = 0x20 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go new file mode 100644 index 00000000..2a1a7998 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go @@ -0,0 +1,68 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type mmsghdr struct { + Hdr msghdr + Len uint32 + Pad_cgo_0 [4]byte +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofMmsghdr = 0x40 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_arm.go b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_arm.go new file mode 100644 index 00000000..206ea2d1 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_netbsd_arm.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_386.go b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_386.go new file mode 100644 index 00000000..1c836361 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_386.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go new file mode 100644 index 00000000..a6c0bf46 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go @@ -0,0 +1,61 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen uint32 + Pad_cgo_1 [4]byte + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_arm.go b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_arm.go new file mode 100644 index 00000000..1c836361 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_openbsd_arm.go @@ -0,0 +1,59 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x18 + + sysSOCK_RAW = 0x3 +) + +type iovec struct { + Base *byte + Len uint32 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Iov *iovec + Iovlen uint32 + Control *byte + Controllen uint32 + Flags int32 +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +const ( + sizeofIovec = 0x8 + sizeofMsghdr = 0x1c + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x1c +) diff --git a/vendor/src/golang.org/x/net/internal/socket/zsys_solaris_amd64.go b/vendor/src/golang.org/x/net/internal/socket/zsys_solaris_amd64.go new file mode 100644 index 00000000..327c6329 --- /dev/null +++ b/vendor/src/golang.org/x/net/internal/socket/zsys_solaris_amd64.go @@ -0,0 +1,60 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_solaris.go + +package socket + +const ( + sysAF_UNSPEC = 0x0 + sysAF_INET = 0x2 + sysAF_INET6 = 0x1a + + sysSOCK_RAW = 0x4 +) + +type iovec struct { + Base *int8 + Len uint64 +} + +type msghdr struct { + Name *byte + Namelen uint32 + Pad_cgo_0 [4]byte + Iov *iovec + Iovlen int32 + Pad_cgo_1 [4]byte + Accrights *int8 + Accrightslen int32 + Pad_cgo_2 [4]byte +} + +type cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 + X__sin6_src_id uint32 +} + +const ( + sizeofIovec = 0x10 + sizeofMsghdr = 0x30 + sizeofCmsghdr = 0xc + + sizeofSockaddrInet = 0x10 + sizeofSockaddrInet6 = 0x20 +) diff --git a/vendor/src/golang.org/x/net/ipv4/batch.go b/vendor/src/golang.org/x/net/ipv4/batch.go new file mode 100644 index 00000000..b4454992 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/batch.go @@ -0,0 +1,191 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package ipv4 + +import ( + "net" + "runtime" + "syscall" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the ReadBatch and WriteBatch methods of +// PacketConn are not implemented. + +// BUG(mikio): On Windows, the ReadBatch and WriteBatch methods of +// RawConn are not implemented. + +// A Message represents an IO message. +// +// type Message struct { +// Buffers [][]byte +// OOB []byte +// Addr net.Addr +// N int +// NN int +// Flags int +// } +// +// The Buffers fields represents a list of contiguous buffers, which +// can be used for vectored IO, for example, putting a header and a +// payload in each slice. +// When writing, the Buffers field must contain at least one byte to +// write. +// When reading, the Buffers field will always contain a byte to read. +// +// The OOB field contains protocol-specific control or miscellaneous +// ancillary data known as out-of-band data. +// It can be nil when not required. +// +// The Addr field specifies a destination address when writing. +// It can be nil when the underlying protocol of the endpoint uses +// connection-oriented communication. +// After a successful read, it may contain the source address on the +// received packet. +// +// The N field indicates the number of bytes read or written from/to +// Buffers. +// +// The NN field indicates the number of bytes read or written from/to +// OOB. +// +// The Flags field contains protocol-specific information on the +// received message. +type Message = socket.Message + +// ReadBatch reads a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_PEEK. +// +// On a successful read it returns the number of messages received, up +// to len(ms). +// +// On Linux, a batch read will be optimized. +// On other platforms, this method will read only a single message. +// +// Unlike the ReadFrom method, it doesn't strip the IPv4 header +// followed by option headers from the received IPv4 datagram when the +// underlying transport is net.IPConn. Each Buffers field of Message +// must be large enough to accommodate an IPv4 header and option +// headers. +func (c *payloadHandler) ReadBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.RecvMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.RecvMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + } +} + +// WriteBatch writes a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_DONTROUTE. +// +// It returns the number of messages written on a successful write. +// +// On Linux, a batch write will be optimized. +// On other platforms, this method will write only a single message. +func (c *payloadHandler) WriteBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.SendMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.SendMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + } +} + +// ReadBatch reads a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_PEEK. +// +// On a successful read it returns the number of messages received, up +// to len(ms). +// +// On Linux, a batch read will be optimized. +// On other platforms, this method will read only a single message. +func (c *packetHandler) ReadBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.RecvMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.RecvMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + return n, err + } +} + +// WriteBatch writes a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_DONTROUTE. +// +// It returns the number of messages written on a successful write. +// +// On Linux, a batch write will be optimized. +// On other platforms, this method will write only a single message. +func (c *packetHandler) WriteBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.SendMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.SendMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + return n, err + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/bpf_test.go b/vendor/src/golang.org/x/net/ipv4/bpf_test.go new file mode 100644 index 00000000..b44da905 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/bpf_test.go @@ -0,0 +1,93 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "net" + "runtime" + "testing" + "time" + + "golang.org/x/net/bpf" + "golang.org/x/net/ipv4" +) + +func TestBPF(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skipf("not supported on %s", runtime.GOOS) + } + + l, err := net.ListenPacket("udp4", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + + p := ipv4.NewPacketConn(l) + + // This filter accepts UDP packets whose first payload byte is + // even. + prog, err := bpf.Assemble([]bpf.Instruction{ + // Load the first byte of the payload (skipping UDP header). + bpf.LoadAbsolute{Off: 8, Size: 1}, + // Select LSB of the byte. + bpf.ALUOpConstant{Op: bpf.ALUOpAnd, Val: 1}, + // Byte is even? + bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0, SkipFalse: 1}, + // Accept. + bpf.RetConstant{Val: 4096}, + // Ignore. + bpf.RetConstant{Val: 0}, + }) + if err != nil { + t.Fatalf("compiling BPF: %s", err) + } + + if err = p.SetBPF(prog); err != nil { + t.Fatalf("attaching filter to Conn: %s", err) + } + + s, err := net.Dial("udp4", l.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + defer s.Close() + go func() { + for i := byte(0); i < 10; i++ { + s.Write([]byte{i}) + } + }() + + l.SetDeadline(time.Now().Add(2 * time.Second)) + seen := make([]bool, 5) + for { + var b [512]byte + n, _, err := l.ReadFrom(b[:]) + if err != nil { + t.Fatalf("reading from listener: %s", err) + } + if n != 1 { + t.Fatalf("unexpected packet length, want 1, got %d", n) + } + if b[0] >= 10 { + t.Fatalf("unexpected byte, want 0-9, got %d", b[0]) + } + if b[0]%2 != 0 { + t.Fatalf("got odd byte %d, wanted only even bytes", b[0]) + } + seen[b[0]/2] = true + + seenAll := true + for _, v := range seen { + if !v { + seenAll = false + break + } + } + if seenAll { + break + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/control.go b/vendor/src/golang.org/x/net/ipv4/control.go new file mode 100644 index 00000000..a2b02ca9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control.go @@ -0,0 +1,144 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "fmt" + "net" + "sync" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +type rawOpt struct { + sync.RWMutex + cflags ControlFlags +} + +func (c *rawOpt) set(f ControlFlags) { c.cflags |= f } +func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f } +func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 } + +type ControlFlags uint + +const ( + FlagTTL ControlFlags = 1 << iota // pass the TTL on the received packet + FlagSrc // pass the source address on the received packet + FlagDst // pass the destination address on the received packet + FlagInterface // pass the interface index on the received packet +) + +// A ControlMessage represents per packet basis IP-level socket options. +type ControlMessage struct { + // Receiving socket options: SetControlMessage allows to + // receive the options from the protocol stack using ReadFrom + // method of PacketConn or RawConn. + // + // Specifying socket options: ControlMessage for WriteTo + // method of PacketConn or RawConn allows to send the options + // to the protocol stack. + // + TTL int // time-to-live, receiving only + Src net.IP // source address, specifying only + Dst net.IP // destination address, receiving only + IfIndex int // interface index, must be 1 <= value when specifying +} + +func (cm *ControlMessage) String() string { + if cm == nil { + return "" + } + return fmt.Sprintf("ttl=%d src=%v dst=%v ifindex=%d", cm.TTL, cm.Src, cm.Dst, cm.IfIndex) +} + +// Marshal returns the binary encoding of cm. +func (cm *ControlMessage) Marshal() []byte { + if cm == nil { + return nil + } + var m socket.ControlMessage + if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To4() != nil || cm.IfIndex > 0) { + m = socket.NewControlMessage([]int{ctlOpts[ctlPacketInfo].length}) + } + if len(m) > 0 { + ctlOpts[ctlPacketInfo].marshal(m, cm) + } + return m +} + +// Parse parses b as a control message and stores the result in cm. +func (cm *ControlMessage) Parse(b []byte) error { + ms, err := socket.ControlMessage(b).Parse() + if err != nil { + return err + } + for _, m := range ms { + lvl, typ, l, err := m.ParseHeader() + if err != nil { + return err + } + if lvl != iana.ProtocolIP { + continue + } + switch { + case typ == ctlOpts[ctlTTL].name && l >= ctlOpts[ctlTTL].length: + ctlOpts[ctlTTL].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlDst].name && l >= ctlOpts[ctlDst].length: + ctlOpts[ctlDst].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlInterface].name && l >= ctlOpts[ctlInterface].length: + ctlOpts[ctlInterface].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlPacketInfo].name && l >= ctlOpts[ctlPacketInfo].length: + ctlOpts[ctlPacketInfo].parse(cm, m.Data(l)) + } + } + return nil +} + +// NewControlMessage returns a new control message. +// +// The returned message is large enough for options specified by cf. +func NewControlMessage(cf ControlFlags) []byte { + opt := rawOpt{cflags: cf} + var l int + if opt.isset(FlagTTL) && ctlOpts[ctlTTL].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlTTL].length) + } + if ctlOpts[ctlPacketInfo].name > 0 { + if opt.isset(FlagSrc | FlagDst | FlagInterface) { + l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length) + } + } else { + if opt.isset(FlagDst) && ctlOpts[ctlDst].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlDst].length) + } + if opt.isset(FlagInterface) && ctlOpts[ctlInterface].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlInterface].length) + } + } + var b []byte + if l > 0 { + b = make([]byte, l) + } + return b +} + +// Ancillary data socket options +const ( + ctlTTL = iota // header field + ctlSrc // header field + ctlDst // header field + ctlInterface // inbound or outbound interface + ctlPacketInfo // inbound or outbound packet path + ctlMax +) + +// A ctlOpt represents a binding for ancillary data socket option. +type ctlOpt struct { + name int // option name, must be equal or greater than 1 + length int // option length + marshal func([]byte, *ControlMessage) []byte + parse func(*ControlMessage, []byte) +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_bsd.go b/vendor/src/golang.org/x/net/ipv4/control_bsd.go new file mode 100644 index 00000000..77e7ad5b --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_bsd.go @@ -0,0 +1,40 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package ipv4 + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +func marshalDst(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIP, sysIP_RECVDSTADDR, net.IPv4len) + return m.Next(net.IPv4len) +} + +func parseDst(cm *ControlMessage, b []byte) { + if len(cm.Dst) < net.IPv4len { + cm.Dst = make(net.IP, net.IPv4len) + } + copy(cm.Dst, b[:net.IPv4len]) +} + +func marshalInterface(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIP, sysIP_RECVIF, syscall.SizeofSockaddrDatalink) + return m.Next(syscall.SizeofSockaddrDatalink) +} + +func parseInterface(cm *ControlMessage, b []byte) { + sadl := (*syscall.SockaddrDatalink)(unsafe.Pointer(&b[0])) + cm.IfIndex = int(sadl.Index) +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_pktinfo.go b/vendor/src/golang.org/x/net/ipv4/control_pktinfo.go new file mode 100644 index 00000000..425338f3 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_pktinfo.go @@ -0,0 +1,39 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin linux solaris + +package ipv4 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +func marshalPacketInfo(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIP, sysIP_PKTINFO, sizeofInetPktinfo) + if cm != nil { + pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0])) + if ip := cm.Src.To4(); ip != nil { + copy(pi.Spec_dst[:], ip) + } + if cm.IfIndex > 0 { + pi.setIfindex(cm.IfIndex) + } + } + return m.Next(sizeofInetPktinfo) +} + +func parsePacketInfo(cm *ControlMessage, b []byte) { + pi := (*inetPktinfo)(unsafe.Pointer(&b[0])) + cm.IfIndex = int(pi.Ifindex) + if len(cm.Dst) < net.IPv4len { + cm.Dst = make(net.IP, net.IPv4len) + } + copy(cm.Dst, pi.Addr[:]) +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_stub.go b/vendor/src/golang.org/x/net/ipv4/control_stub.go new file mode 100644 index 00000000..5a2f7d8d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_stub.go @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv4 + +import "golang.org/x/net/internal/socket" + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_test.go b/vendor/src/golang.org/x/net/ipv4/control_test.go new file mode 100644 index 00000000..f87fe124 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_test.go @@ -0,0 +1,21 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "testing" + + "golang.org/x/net/ipv4" +) + +func TestControlMessageParseWithFuzz(t *testing.T) { + var cm ipv4.ControlMessage + for _, fuzz := range []string{ + "\f\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00", + "\f\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00", + } { + cm.Parse([]byte(fuzz)) + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_unix.go b/vendor/src/golang.org/x/net/ipv4/control_unix.go new file mode 100644 index 00000000..e1ae8167 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_unix.go @@ -0,0 +1,73 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package ipv4 + +import ( + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + opt.Lock() + defer opt.Unlock() + if so, ok := sockOpts[ssoReceiveTTL]; ok && cf&FlagTTL != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagTTL) + } else { + opt.clear(FlagTTL) + } + } + if so, ok := sockOpts[ssoPacketInfo]; ok { + if cf&(FlagSrc|FlagDst|FlagInterface) != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(cf & (FlagSrc | FlagDst | FlagInterface)) + } else { + opt.clear(cf & (FlagSrc | FlagDst | FlagInterface)) + } + } + } else { + if so, ok := sockOpts[ssoReceiveDst]; ok && cf&FlagDst != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagDst) + } else { + opt.clear(FlagDst) + } + } + if so, ok := sockOpts[ssoReceiveInterface]; ok && cf&FlagInterface != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagInterface) + } else { + opt.clear(FlagInterface) + } + } + } + return nil +} + +func marshalTTL(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIP, sysIP_RECVTTL, 1) + return m.Next(1) +} + +func parseTTL(cm *ControlMessage, b []byte) { + cm.TTL = int(*(*byte)(unsafe.Pointer(&b[:1][0]))) +} diff --git a/vendor/src/golang.org/x/net/ipv4/control_windows.go b/vendor/src/golang.org/x/net/ipv4/control_windows.go new file mode 100644 index 00000000..ce55c664 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/control_windows.go @@ -0,0 +1,16 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "syscall" + + "golang.org/x/net/internal/socket" +) + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + // TODO(mikio): implement this + return syscall.EWINDOWS +} diff --git a/vendor/src/golang.org/x/net/ipv4/defs_darwin.go b/vendor/src/golang.org/x/net/ipv4/defs_darwin.go new file mode 100644 index 00000000..c8f2e05b --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_darwin.go @@ -0,0 +1,77 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include + +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_STRIPHDR = C.IP_STRIPHDR + sysIP_RECVTTL = C.IP_RECVTTL + sysIP_BOUND_IF = C.IP_BOUND_IF + sysIP_PKTINFO = C.IP_PKTINFO + sysIP_RECVPKTINFO = C.IP_RECVPKTINFO + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF + sysIP_MULTICAST_IFINDEX = C.IP_MULTICAST_IFINDEX + sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP + sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP + sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE + sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofInetPktinfo = C.sizeof_struct_in_pktinfo + + sizeofIPMreq = C.sizeof_struct_ip_mreq + sizeofIPMreqn = C.sizeof_struct_ip_mreqn + sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet C.struct_sockaddr_in + +type inetPktinfo C.struct_in_pktinfo + +type ipMreq C.struct_ip_mreq + +type ipMreqn C.struct_ip_mreqn + +type ipMreqSource C.struct_ip_mreq_source + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req diff --git a/vendor/src/golang.org/x/net/ipv4/defs_dragonfly.go b/vendor/src/golang.org/x/net/ipv4/defs_dragonfly.go new file mode 100644 index 00000000..f30544ea --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_dragonfly.go @@ -0,0 +1,38 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_RECVTTL = C.IP_RECVTTL + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + + sizeofIPMreq = C.sizeof_struct_ip_mreq +) + +type ipMreq C.struct_ip_mreq diff --git a/vendor/src/golang.org/x/net/ipv4/defs_freebsd.go b/vendor/src/golang.org/x/net/ipv4/defs_freebsd.go new file mode 100644 index 00000000..4dd57d86 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_freebsd.go @@ -0,0 +1,75 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include + +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_SENDSRCADDR = C.IP_SENDSRCADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_ONESBCAST = C.IP_ONESBCAST + sysIP_BINDANY = C.IP_BINDANY + sysIP_RECVTTL = C.IP_RECVTTL + sysIP_MINTTL = C.IP_MINTTL + sysIP_DONTFRAG = C.IP_DONTFRAG + sysIP_RECVTOS = C.IP_RECVTOS + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + sysIP_MULTICAST_VIF = C.IP_MULTICAST_VIF + sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP + sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP + sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE + sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + + sizeofIPMreq = C.sizeof_struct_ip_mreq + sizeofIPMreqn = C.sizeof_struct_ip_mreqn + sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet C.struct_sockaddr_in + +type ipMreq C.struct_ip_mreq + +type ipMreqn C.struct_ip_mreqn + +type ipMreqSource C.struct_ip_mreq_source + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req diff --git a/vendor/src/golang.org/x/net/ipv4/defs_linux.go b/vendor/src/golang.org/x/net/ipv4/defs_linux.go new file mode 100644 index 00000000..beb11071 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_linux.go @@ -0,0 +1,122 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include + +#include +#include +#include +#include +#include +*/ +import "C" + +const ( + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_ROUTER_ALERT = C.IP_ROUTER_ALERT + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_PKTINFO = C.IP_PKTINFO + sysIP_PKTOPTIONS = C.IP_PKTOPTIONS + sysIP_MTU_DISCOVER = C.IP_MTU_DISCOVER + sysIP_RECVERR = C.IP_RECVERR + sysIP_RECVTTL = C.IP_RECVTTL + sysIP_RECVTOS = C.IP_RECVTOS + sysIP_MTU = C.IP_MTU + sysIP_FREEBIND = C.IP_FREEBIND + sysIP_TRANSPARENT = C.IP_TRANSPARENT + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_ORIGDSTADDR = C.IP_ORIGDSTADDR + sysIP_RECVORIGDSTADDR = C.IP_RECVORIGDSTADDR + sysIP_MINTTL = C.IP_MINTTL + sysIP_NODEFRAG = C.IP_NODEFRAG + sysIP_UNICAST_IF = C.IP_UNICAST_IF + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE + sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE + sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP + sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP + sysIP_MSFILTER = C.IP_MSFILTER + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + sysMCAST_MSFILTER = C.MCAST_MSFILTER + sysIP_MULTICAST_ALL = C.IP_MULTICAST_ALL + + //sysIP_PMTUDISC_DONT = C.IP_PMTUDISC_DONT + //sysIP_PMTUDISC_WANT = C.IP_PMTUDISC_WANT + //sysIP_PMTUDISC_DO = C.IP_PMTUDISC_DO + //sysIP_PMTUDISC_PROBE = C.IP_PMTUDISC_PROBE + //sysIP_PMTUDISC_INTERFACE = C.IP_PMTUDISC_INTERFACE + //sysIP_PMTUDISC_OMIT = C.IP_PMTUDISC_OMIT + + sysICMP_FILTER = C.ICMP_FILTER + + sysSO_EE_ORIGIN_NONE = C.SO_EE_ORIGIN_NONE + sysSO_EE_ORIGIN_LOCAL = C.SO_EE_ORIGIN_LOCAL + sysSO_EE_ORIGIN_ICMP = C.SO_EE_ORIGIN_ICMP + sysSO_EE_ORIGIN_ICMP6 = C.SO_EE_ORIGIN_ICMP6 + sysSO_EE_ORIGIN_TXSTATUS = C.SO_EE_ORIGIN_TXSTATUS + sysSO_EE_ORIGIN_TIMESTAMPING = C.SO_EE_ORIGIN_TIMESTAMPING + + sysSOL_SOCKET = C.SOL_SOCKET + sysSO_ATTACH_FILTER = C.SO_ATTACH_FILTER + + sizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofInetPktinfo = C.sizeof_struct_in_pktinfo + sizeofSockExtendedErr = C.sizeof_struct_sock_extended_err + + sizeofIPMreq = C.sizeof_struct_ip_mreq + sizeofIPMreqn = C.sizeof_struct_ip_mreqn + sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req + + sizeofICMPFilter = C.sizeof_struct_icmp_filter + + sizeofSockFprog = C.sizeof_struct_sock_fprog +) + +type kernelSockaddrStorage C.struct___kernel_sockaddr_storage + +type sockaddrInet C.struct_sockaddr_in + +type inetPktinfo C.struct_in_pktinfo + +type sockExtendedErr C.struct_sock_extended_err + +type ipMreq C.struct_ip_mreq + +type ipMreqn C.struct_ip_mreqn + +type ipMreqSource C.struct_ip_mreq_source + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req + +type icmpFilter C.struct_icmp_filter + +type sockFProg C.struct_sock_fprog + +type sockFilter C.struct_sock_filter diff --git a/vendor/src/golang.org/x/net/ipv4/defs_netbsd.go b/vendor/src/golang.org/x/net/ipv4/defs_netbsd.go new file mode 100644 index 00000000..8f8af1b8 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_netbsd.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_RECVTTL = C.IP_RECVTTL + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + + sizeofIPMreq = C.sizeof_struct_ip_mreq +) + +type ipMreq C.struct_ip_mreq diff --git a/vendor/src/golang.org/x/net/ipv4/defs_openbsd.go b/vendor/src/golang.org/x/net/ipv4/defs_openbsd.go new file mode 100644 index 00000000..8f8af1b8 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_openbsd.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_RECVTTL = C.IP_RECVTTL + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + + sizeofIPMreq = C.sizeof_struct_ip_mreq +) + +type ipMreq C.struct_ip_mreq diff --git a/vendor/src/golang.org/x/net/ipv4/defs_solaris.go b/vendor/src/golang.org/x/net/ipv4/defs_solaris.go new file mode 100644 index 00000000..aeb33e9c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/defs_solaris.go @@ -0,0 +1,84 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in_addr [4]byte /* in_addr */ + +package ipv4 + +/* +#include + +#include +*/ +import "C" + +const ( + sysIP_OPTIONS = C.IP_OPTIONS + sysIP_HDRINCL = C.IP_HDRINCL + sysIP_TOS = C.IP_TOS + sysIP_TTL = C.IP_TTL + sysIP_RECVOPTS = C.IP_RECVOPTS + sysIP_RECVRETOPTS = C.IP_RECVRETOPTS + sysIP_RECVDSTADDR = C.IP_RECVDSTADDR + sysIP_RETOPTS = C.IP_RETOPTS + sysIP_RECVIF = C.IP_RECVIF + sysIP_RECVSLLA = C.IP_RECVSLLA + sysIP_RECVTTL = C.IP_RECVTTL + + sysIP_MULTICAST_IF = C.IP_MULTICAST_IF + sysIP_MULTICAST_TTL = C.IP_MULTICAST_TTL + sysIP_MULTICAST_LOOP = C.IP_MULTICAST_LOOP + sysIP_ADD_MEMBERSHIP = C.IP_ADD_MEMBERSHIP + sysIP_DROP_MEMBERSHIP = C.IP_DROP_MEMBERSHIP + sysIP_BLOCK_SOURCE = C.IP_BLOCK_SOURCE + sysIP_UNBLOCK_SOURCE = C.IP_UNBLOCK_SOURCE + sysIP_ADD_SOURCE_MEMBERSHIP = C.IP_ADD_SOURCE_MEMBERSHIP + sysIP_DROP_SOURCE_MEMBERSHIP = C.IP_DROP_SOURCE_MEMBERSHIP + sysIP_NEXTHOP = C.IP_NEXTHOP + + sysIP_PKTINFO = C.IP_PKTINFO + sysIP_RECVPKTINFO = C.IP_RECVPKTINFO + sysIP_DONTFRAG = C.IP_DONTFRAG + + sysIP_BOUND_IF = C.IP_BOUND_IF + sysIP_UNSPEC_SRC = C.IP_UNSPEC_SRC + sysIP_BROADCAST_TTL = C.IP_BROADCAST_TTL + sysIP_DHCPINIT_IF = C.IP_DHCPINIT_IF + + sysIP_REUSEADDR = C.IP_REUSEADDR + sysIP_DONTROUTE = C.IP_DONTROUTE + sysIP_BROADCAST = C.IP_BROADCAST + + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet = C.sizeof_struct_sockaddr_in + sizeofInetPktinfo = C.sizeof_struct_in_pktinfo + + sizeofIPMreq = C.sizeof_struct_ip_mreq + sizeofIPMreqSource = C.sizeof_struct_ip_mreq_source + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet C.struct_sockaddr_in + +type inetPktinfo C.struct_in_pktinfo + +type ipMreq C.struct_ip_mreq + +type ipMreqSource C.struct_ip_mreq_source + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req diff --git a/vendor/src/golang.org/x/net/ipv4/dgramopt.go b/vendor/src/golang.org/x/net/ipv4/dgramopt.go new file mode 100644 index 00000000..54d77d5f --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/dgramopt.go @@ -0,0 +1,265 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + + "golang.org/x/net/bpf" +) + +// MulticastTTL returns the time-to-live field value for outgoing +// multicast packets. +func (c *dgramOpt) MulticastTTL() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastTTL] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetMulticastTTL sets the time-to-live field value for future +// outgoing multicast packets. +func (c *dgramOpt) SetMulticastTTL(ttl int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastTTL] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, ttl) +} + +// MulticastInterface returns the default interface for multicast +// packet transmissions. +func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { + if !c.ok() { + return nil, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastInterface] + if !ok { + return nil, errOpNoSupport + } + return so.getMulticastInterface(c.Conn) +} + +// SetMulticastInterface sets the default interface for future +// multicast packet transmissions. +func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastInterface] + if !ok { + return errOpNoSupport + } + return so.setMulticastInterface(c.Conn, ifi) +} + +// MulticastLoopback reports whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) MulticastLoopback() (bool, error) { + if !c.ok() { + return false, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastLoopback] + if !ok { + return false, errOpNoSupport + } + on, err := so.GetInt(c.Conn) + if err != nil { + return false, err + } + return on == 1, nil +} + +// SetMulticastLoopback sets whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) SetMulticastLoopback(on bool) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastLoopback] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, boolint(on)) +} + +// JoinGroup joins the group address group on the interface ifi. +// By default all sources that can cast data to group are accepted. +// It's possible to mute and unmute data transmission from a specific +// source by using ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup. +// JoinGroup uses the system assigned multicast interface when ifi is +// nil, although this is not recommended because the assignment +// depends on platforms and sometimes it might require routing +// configuration. +func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoJoinGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + return so.setGroup(c.Conn, ifi, grp) +} + +// LeaveGroup leaves the group address group on the interface ifi +// regardless of whether the group is any-source group or +// source-specific group. +func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoLeaveGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + return so.setGroup(c.Conn, ifi, grp) +} + +// JoinSourceSpecificGroup joins the source-specific group comprising +// group and source on the interface ifi. +// JoinSourceSpecificGroup uses the system assigned multicast +// interface when ifi is nil, although this is not recommended because +// the assignment depends on platforms and sometimes it might require +// routing configuration. +func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoJoinSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP4(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// LeaveSourceSpecificGroup leaves the source-specific group on the +// interface ifi. +func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoLeaveSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP4(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// ExcludeSourceSpecificGroup excludes the source-specific group from +// the already joined any-source groups by JoinGroup on the interface +// ifi. +func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoBlockSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP4(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// IncludeSourceSpecificGroup includes the excluded source-specific +// group by ExcludeSourceSpecificGroup again on the interface ifi. +func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoUnblockSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP4(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP4(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// ICMPFilter returns an ICMP filter. +// Currently only Linux supports this. +func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) { + if !c.ok() { + return nil, syscall.EINVAL + } + so, ok := sockOpts[ssoICMPFilter] + if !ok { + return nil, errOpNoSupport + } + return so.getICMPFilter(c.Conn) +} + +// SetICMPFilter deploys the ICMP filter. +// Currently only Linux supports this. +func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoICMPFilter] + if !ok { + return errOpNoSupport + } + return so.setICMPFilter(c.Conn, f) +} + +// SetBPF attaches a BPF program to the connection. +// +// Only supported on Linux. +func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoAttachFilter] + if !ok { + return errOpNoSupport + } + return so.setBPF(c.Conn, filter) +} diff --git a/vendor/src/golang.org/x/net/ipv4/doc.go b/vendor/src/golang.org/x/net/ipv4/doc.go new file mode 100644 index 00000000..b43935a5 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/doc.go @@ -0,0 +1,244 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ipv4 implements IP-level socket options for the Internet +// Protocol version 4. +// +// The package provides IP-level socket options that allow +// manipulation of IPv4 facilities. +// +// The IPv4 protocol and basic host requirements for IPv4 are defined +// in RFC 791 and RFC 1122. +// Host extensions for multicasting and socket interface extensions +// for multicast source filters are defined in RFC 1112 and RFC 3678. +// IGMPv1, IGMPv2 and IGMPv3 are defined in RFC 1112, RFC 2236 and RFC +// 3376. +// Source-specific multicast is defined in RFC 4607. +// +// +// Unicasting +// +// The options for unicasting are available for net.TCPConn, +// net.UDPConn and net.IPConn which are created as network connections +// that use the IPv4 transport. When a single TCP connection carrying +// a data flow of multiple packets needs to indicate the flow is +// important, Conn is used to set the type-of-service field on the +// IPv4 header for each packet. +// +// ln, err := net.Listen("tcp4", "0.0.0.0:1024") +// if err != nil { +// // error handling +// } +// defer ln.Close() +// for { +// c, err := ln.Accept() +// if err != nil { +// // error handling +// } +// go func(c net.Conn) { +// defer c.Close() +// +// The outgoing packets will be labeled DiffServ assured forwarding +// class 1 low drop precedence, known as AF11 packets. +// +// if err := ipv4.NewConn(c).SetTOS(0x28); err != nil { +// // error handling +// } +// if _, err := c.Write(data); err != nil { +// // error handling +// } +// }(c) +// } +// +// +// Multicasting +// +// The options for multicasting are available for net.UDPConn and +// net.IPconn which are created as network connections that use the +// IPv4 transport. A few network facilities must be prepared before +// you begin multicasting, at a minimum joining network interfaces and +// multicast groups. +// +// en0, err := net.InterfaceByName("en0") +// if err != nil { +// // error handling +// } +// en1, err := net.InterfaceByIndex(911) +// if err != nil { +// // error handling +// } +// group := net.IPv4(224, 0, 0, 250) +// +// First, an application listens to an appropriate address with an +// appropriate service port. +// +// c, err := net.ListenPacket("udp4", "0.0.0.0:1024") +// if err != nil { +// // error handling +// } +// defer c.Close() +// +// Second, the application joins multicast groups, starts listening to +// the groups on the specified network interfaces. Note that the +// service port for transport layer protocol does not matter with this +// operation as joining groups affects only network and link layer +// protocols, such as IPv4 and Ethernet. +// +// p := ipv4.NewPacketConn(c) +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil { +// // error handling +// } +// +// The application might set per packet control message transmissions +// between the protocol stack within the kernel. When the application +// needs a destination address on an incoming packet, +// SetControlMessage of PacketConn is used to enable control message +// transmissions. +// +// if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil { +// // error handling +// } +// +// The application could identify whether the received packets are +// of interest by using the control message that contains the +// destination address of the received packet. +// +// b := make([]byte, 1500) +// for { +// n, cm, src, err := p.ReadFrom(b) +// if err != nil { +// // error handling +// } +// if cm.Dst.IsMulticast() { +// if cm.Dst.Equal(group) { +// // joined group, do something +// } else { +// // unknown group, discard +// continue +// } +// } +// +// The application can also send both unicast and multicast packets. +// +// p.SetTOS(0x0) +// p.SetTTL(16) +// if _, err := p.WriteTo(data, nil, src); err != nil { +// // error handling +// } +// dst := &net.UDPAddr{IP: group, Port: 1024} +// for _, ifi := range []*net.Interface{en0, en1} { +// if err := p.SetMulticastInterface(ifi); err != nil { +// // error handling +// } +// p.SetMulticastTTL(2) +// if _, err := p.WriteTo(data, nil, dst); err != nil { +// // error handling +// } +// } +// } +// +// +// More multicasting +// +// An application that uses PacketConn or RawConn may join multiple +// multicast groups. For example, a UDP listener with port 1024 might +// join two different groups across over two different network +// interfaces by using: +// +// c, err := net.ListenPacket("udp4", "0.0.0.0:1024") +// if err != nil { +// // error handling +// } +// defer c.Close() +// p := ipv4.NewPacketConn(c) +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}); err != nil { +// // error handling +// } +// +// It is possible for multiple UDP listeners that listen on the same +// UDP port to join the same multicast group. The net package will +// provide a socket that listens to a wildcard address with reusable +// UDP port when an appropriate multicast address prefix is passed to +// the net.ListenPacket or net.ListenUDP. +// +// c1, err := net.ListenPacket("udp4", "224.0.0.0:1024") +// if err != nil { +// // error handling +// } +// defer c1.Close() +// c2, err := net.ListenPacket("udp4", "224.0.0.0:1024") +// if err != nil { +// // error handling +// } +// defer c2.Close() +// p1 := ipv4.NewPacketConn(c1) +// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { +// // error handling +// } +// p2 := ipv4.NewPacketConn(c2) +// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { +// // error handling +// } +// +// Also it is possible for the application to leave or rejoin a +// multicast group on the network interface. +// +// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 248)}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}); err != nil { +// // error handling +// } +// +// +// Source-specific multicasting +// +// An application that uses PacketConn or RawConn on IGMPv3 supported +// platform is able to join source-specific multicast groups. +// The application may use JoinSourceSpecificGroup and +// LeaveSourceSpecificGroup for the operation known as "include" mode, +// +// ssmgroup := net.UDPAddr{IP: net.IPv4(232, 7, 8, 9)} +// ssmsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)}) +// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil { +// // error handling +// } +// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil { +// // error handling +// } +// +// or JoinGroup, ExcludeSourceSpecificGroup, +// IncludeSourceSpecificGroup and LeaveGroup for the operation known +// as "exclude" mode. +// +// exclsource := net.UDPAddr{IP: net.IPv4(192, 168, 0, 254)} +// if err := p.JoinGroup(en0, &ssmgroup); err != nil { +// // error handling +// } +// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil { +// // error handling +// } +// if err := p.LeaveGroup(en0, &ssmgroup); err != nil { +// // error handling +// } +// +// Note that it depends on each platform implementation what happens +// when an application which runs on IGMPv3 unsupported platform uses +// JoinSourceSpecificGroup and LeaveSourceSpecificGroup. +// In general the platform tries to fall back to conversations using +// IGMPv1 or IGMPv2 and starts to listen to multicast traffic. +// In the fallback case, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup may return an error. +package ipv4 // import "golang.org/x/net/ipv4" + +// BUG(mikio): This package is not implemented on NaCl and Plan 9. diff --git a/vendor/src/golang.org/x/net/ipv4/endpoint.go b/vendor/src/golang.org/x/net/ipv4/endpoint.go new file mode 100644 index 00000000..2ab87736 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/endpoint.go @@ -0,0 +1,187 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + "time" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the JoinSourceSpecificGroup, +// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup methods of PacketConn and RawConn are +// not implemented. + +// A Conn represents a network endpoint that uses the IPv4 transport. +// It is used to control basic IP-level socket options such as TOS and +// TTL. +type Conn struct { + genericOpt +} + +type genericOpt struct { + *socket.Conn +} + +func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil } + +// NewConn returns a new Conn. +func NewConn(c net.Conn) *Conn { + cc, _ := socket.NewConn(c) + return &Conn{ + genericOpt: genericOpt{Conn: cc}, + } +} + +// A PacketConn represents a packet network endpoint that uses the +// IPv4 transport. It is used to control several IP-level socket +// options including multicasting. It also provides datagram based +// network I/O methods specific to the IPv4 and higher layer protocols +// such as UDP. +type PacketConn struct { + genericOpt + dgramOpt + payloadHandler +} + +type dgramOpt struct { + *socket.Conn +} + +func (c *dgramOpt) ok() bool { return c != nil && c.Conn != nil } + +// SetControlMessage sets the per packet IP-level socket options. +func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return setControlMessage(c.dgramOpt.Conn, &c.payloadHandler.rawOpt, cf, on) +} + +// SetDeadline sets the read and write deadlines associated with the +// endpoint. +func (c *PacketConn) SetDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.PacketConn.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline associated with the +// endpoint. +func (c *PacketConn) SetReadDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.PacketConn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline associated with the +// endpoint. +func (c *PacketConn) SetWriteDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.PacketConn.SetWriteDeadline(t) +} + +// Close closes the endpoint. +func (c *PacketConn) Close() error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.PacketConn.Close() +} + +// NewPacketConn returns a new PacketConn using c as its underlying +// transport. +func NewPacketConn(c net.PacketConn) *PacketConn { + cc, _ := socket.NewConn(c.(net.Conn)) + p := &PacketConn{ + genericOpt: genericOpt{Conn: cc}, + dgramOpt: dgramOpt{Conn: cc}, + payloadHandler: payloadHandler{PacketConn: c, Conn: cc}, + } + return p +} + +// A RawConn represents a packet network endpoint that uses the IPv4 +// transport. It is used to control several IP-level socket options +// including IPv4 header manipulation. It also provides datagram +// based network I/O methods specific to the IPv4 and higher layer +// protocols that handle IPv4 datagram directly such as OSPF, GRE. +type RawConn struct { + genericOpt + dgramOpt + packetHandler +} + +// SetControlMessage sets the per packet IP-level socket options. +func (c *RawConn) SetControlMessage(cf ControlFlags, on bool) error { + if !c.packetHandler.ok() { + return syscall.EINVAL + } + return setControlMessage(c.dgramOpt.Conn, &c.packetHandler.rawOpt, cf, on) +} + +// SetDeadline sets the read and write deadlines associated with the +// endpoint. +func (c *RawConn) SetDeadline(t time.Time) error { + if !c.packetHandler.ok() { + return syscall.EINVAL + } + return c.packetHandler.IPConn.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline associated with the +// endpoint. +func (c *RawConn) SetReadDeadline(t time.Time) error { + if !c.packetHandler.ok() { + return syscall.EINVAL + } + return c.packetHandler.IPConn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline associated with the +// endpoint. +func (c *RawConn) SetWriteDeadline(t time.Time) error { + if !c.packetHandler.ok() { + return syscall.EINVAL + } + return c.packetHandler.IPConn.SetWriteDeadline(t) +} + +// Close closes the endpoint. +func (c *RawConn) Close() error { + if !c.packetHandler.ok() { + return syscall.EINVAL + } + return c.packetHandler.IPConn.Close() +} + +// NewRawConn returns a new RawConn using c as its underlying +// transport. +func NewRawConn(c net.PacketConn) (*RawConn, error) { + cc, err := socket.NewConn(c.(net.Conn)) + if err != nil { + return nil, err + } + r := &RawConn{ + genericOpt: genericOpt{Conn: cc}, + dgramOpt: dgramOpt{Conn: cc}, + packetHandler: packetHandler{IPConn: c.(*net.IPConn), Conn: cc}, + } + so, ok := sockOpts[ssoHeaderPrepend] + if !ok { + return nil, errOpNoSupport + } + if err := so.SetInt(r.dgramOpt.Conn, boolint(true)); err != nil { + return nil, err + } + return r, nil +} diff --git a/vendor/src/golang.org/x/net/ipv4/example_test.go b/vendor/src/golang.org/x/net/ipv4/example_test.go new file mode 100644 index 00000000..ddc7577e --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/example_test.go @@ -0,0 +1,224 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "fmt" + "log" + "net" + "os" + "runtime" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" +) + +func ExampleConn_markingTCP() { + ln, err := net.Listen("tcp", "0.0.0.0:1024") + if err != nil { + log.Fatal(err) + } + defer ln.Close() + + for { + c, err := ln.Accept() + if err != nil { + log.Fatal(err) + } + go func(c net.Conn) { + defer c.Close() + if c.RemoteAddr().(*net.TCPAddr).IP.To4() != nil { + p := ipv4.NewConn(c) + if err := p.SetTOS(0x28); err != nil { // DSCP AF11 + log.Fatal(err) + } + if err := p.SetTTL(128); err != nil { + log.Fatal(err) + } + } + if _, err := c.Write([]byte("HELLO-R-U-THERE-ACK")); err != nil { + log.Fatal(err) + } + }(c) + } +} + +func ExamplePacketConn_servingOneShotMulticastDNS() { + c, err := net.ListenPacket("udp4", "0.0.0.0:5353") // mDNS over UDP + if err != nil { + log.Fatal(err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + + en0, err := net.InterfaceByName("en0") + if err != nil { + log.Fatal(err) + } + mDNSLinkLocal := net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)} + if err := p.JoinGroup(en0, &mDNSLinkLocal); err != nil { + log.Fatal(err) + } + defer p.LeaveGroup(en0, &mDNSLinkLocal) + if err := p.SetControlMessage(ipv4.FlagDst, true); err != nil { + log.Fatal(err) + } + + b := make([]byte, 1500) + for { + _, cm, peer, err := p.ReadFrom(b) + if err != nil { + log.Fatal(err) + } + if !cm.Dst.IsMulticast() || !cm.Dst.Equal(mDNSLinkLocal.IP) { + continue + } + answers := []byte("FAKE-MDNS-ANSWERS") // fake mDNS answers, you need to implement this + if _, err := p.WriteTo(answers, nil, peer); err != nil { + log.Fatal(err) + } + } +} + +func ExamplePacketConn_tracingIPPacketRoute() { + // Tracing an IP packet route to www.google.com. + + const host = "www.google.com" + ips, err := net.LookupIP(host) + if err != nil { + log.Fatal(err) + } + var dst net.IPAddr + for _, ip := range ips { + if ip.To4() != nil { + dst.IP = ip + fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host) + break + } + } + if dst.IP == nil { + log.Fatal("no A record found") + } + + c, err := net.ListenPacket("ip4:1", "0.0.0.0") // ICMP for IPv4 + if err != nil { + log.Fatal(err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + + if err := p.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil { + log.Fatal(err) + } + wm := icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, + Data: []byte("HELLO-R-U-THERE"), + }, + } + + rb := make([]byte, 1500) + for i := 1; i <= 64; i++ { // up to 64 hops + wm.Body.(*icmp.Echo).Seq = i + wb, err := wm.Marshal(nil) + if err != nil { + log.Fatal(err) + } + if err := p.SetTTL(i); err != nil { + log.Fatal(err) + } + + // In the real world usually there are several + // multiple traffic-engineered paths for each hop. + // You may need to probe a few times to each hop. + begin := time.Now() + if _, err := p.WriteTo(wb, nil, &dst); err != nil { + log.Fatal(err) + } + if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { + log.Fatal(err) + } + n, cm, peer, err := p.ReadFrom(rb) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + fmt.Printf("%v\t*\n", i) + continue + } + log.Fatal(err) + } + rm, err := icmp.ParseMessage(1, rb[:n]) + if err != nil { + log.Fatal(err) + } + rtt := time.Since(begin) + + // In the real world you need to determine whether the + // received message is yours using ControlMessage.Src, + // ControlMessage.Dst, icmp.Echo.ID and icmp.Echo.Seq. + switch rm.Type { + case ipv4.ICMPTypeTimeExceeded: + names, _ := net.LookupAddr(peer.String()) + fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm) + case ipv4.ICMPTypeEchoReply: + names, _ := net.LookupAddr(peer.String()) + fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, cm) + return + default: + log.Printf("unknown ICMP message: %+v\n", rm) + } + } +} + +func ExampleRawConn_advertisingOSPFHello() { + c, err := net.ListenPacket("ip4:89", "0.0.0.0") // OSPF for IPv4 + if err != nil { + log.Fatal(err) + } + defer c.Close() + r, err := ipv4.NewRawConn(c) + if err != nil { + log.Fatal(err) + } + + en0, err := net.InterfaceByName("en0") + if err != nil { + log.Fatal(err) + } + allSPFRouters := net.IPAddr{IP: net.IPv4(224, 0, 0, 5)} + if err := r.JoinGroup(en0, &allSPFRouters); err != nil { + log.Fatal(err) + } + defer r.LeaveGroup(en0, &allSPFRouters) + + hello := make([]byte, 24) // fake hello data, you need to implement this + ospf := make([]byte, 24) // fake ospf header, you need to implement this + ospf[0] = 2 // version 2 + ospf[1] = 1 // hello packet + ospf = append(ospf, hello...) + iph := &ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TOS: 0xc0, // DSCP CS6 + TotalLen: ipv4.HeaderLen + len(ospf), + TTL: 1, + Protocol: 89, + Dst: allSPFRouters.IP.To4(), + } + + var cm *ipv4.ControlMessage + switch runtime.GOOS { + case "darwin", "linux": + cm = &ipv4.ControlMessage{IfIndex: en0.Index} + default: + if err := r.SetMulticastInterface(en0); err != nil { + log.Fatal(err) + } + } + if err := r.WriteTo(iph, ospf, cm); err != nil { + log.Fatal(err) + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/gen.go b/vendor/src/golang.org/x/net/ipv4/gen.go new file mode 100644 index 00000000..ffb44fe6 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/gen.go @@ -0,0 +1,199 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +//go:generate go run gen.go + +// This program generates system adaptation constants and types, +// internet protocol constants and tables by reading template files +// and IANA protocol registries. +package main + +import ( + "bytes" + "encoding/xml" + "fmt" + "go/format" + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "runtime" + "strconv" + "strings" +) + +func main() { + if err := genzsys(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if err := geniana(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func genzsys() error { + defs := "defs_" + runtime.GOOS + ".go" + f, err := os.Open(defs) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + f.Close() + cmd := exec.Command("go", "tool", "cgo", "-godefs", defs) + b, err := cmd.Output() + if err != nil { + return err + } + b, err = format.Source(b) + if err != nil { + return err + } + zsys := "zsys_" + runtime.GOOS + ".go" + switch runtime.GOOS { + case "freebsd", "linux": + zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go" + } + if err := ioutil.WriteFile(zsys, b, 0644); err != nil { + return err + } + return nil +} + +var registries = []struct { + url string + parse func(io.Writer, io.Reader) error +}{ + { + "http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xml", + parseICMPv4Parameters, + }, +} + +func geniana() error { + var bb bytes.Buffer + fmt.Fprintf(&bb, "// go generate gen.go\n") + fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") + fmt.Fprintf(&bb, "package ipv4\n\n") + for _, r := range registries { + resp, err := http.Get(r.url) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url) + } + if err := r.parse(&bb, resp.Body); err != nil { + return err + } + fmt.Fprintf(&bb, "\n") + } + b, err := format.Source(bb.Bytes()) + if err != nil { + return err + } + if err := ioutil.WriteFile("iana.go", b, 0644); err != nil { + return err + } + return nil +} + +func parseICMPv4Parameters(w io.Writer, r io.Reader) error { + dec := xml.NewDecoder(r) + var icp icmpv4Parameters + if err := dec.Decode(&icp); err != nil { + return err + } + prs := icp.escape() + fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) + fmt.Fprintf(w, "const (\n") + for _, pr := range prs { + if pr.Descr == "" { + continue + } + fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Descr, pr.Value) + fmt.Fprintf(w, "// %s\n", pr.OrigDescr) + } + fmt.Fprintf(w, ")\n\n") + fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) + fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n") + for _, pr := range prs { + if pr.Descr == "" { + continue + } + fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigDescr)) + } + fmt.Fprintf(w, "}\n") + return nil +} + +type icmpv4Parameters struct { + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Registries []struct { + Title string `xml:"title"` + Records []struct { + Value string `xml:"value"` + Descr string `xml:"description"` + } `xml:"record"` + } `xml:"registry"` +} + +type canonICMPv4ParamRecord struct { + OrigDescr string + Descr string + Value int +} + +func (icp *icmpv4Parameters) escape() []canonICMPv4ParamRecord { + id := -1 + for i, r := range icp.Registries { + if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") { + id = i + break + } + } + if id < 0 { + return nil + } + prs := make([]canonICMPv4ParamRecord, len(icp.Registries[id].Records)) + sr := strings.NewReplacer( + "Messages", "", + "Message", "", + "ICMP", "", + "+", "P", + "-", "", + "/", "", + ".", "", + " ", "", + ) + for i, pr := range icp.Registries[id].Records { + if strings.Contains(pr.Descr, "Reserved") || + strings.Contains(pr.Descr, "Unassigned") || + strings.Contains(pr.Descr, "Deprecated") || + strings.Contains(pr.Descr, "Experiment") || + strings.Contains(pr.Descr, "experiment") { + continue + } + ss := strings.Split(pr.Descr, "\n") + if len(ss) > 1 { + prs[i].Descr = strings.Join(ss, " ") + } else { + prs[i].Descr = ss[0] + } + s := strings.TrimSpace(prs[i].Descr) + prs[i].OrigDescr = s + prs[i].Descr = sr.Replace(s) + prs[i].Value, _ = strconv.Atoi(pr.Value) + } + return prs +} diff --git a/vendor/src/golang.org/x/net/ipv4/genericopt.go b/vendor/src/golang.org/x/net/ipv4/genericopt.go new file mode 100644 index 00000000..119bf841 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/genericopt.go @@ -0,0 +1,57 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import "syscall" + +// TOS returns the type-of-service field value for outgoing packets. +func (c *genericOpt) TOS() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoTOS] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetTOS sets the type-of-service field value for future outgoing +// packets. +func (c *genericOpt) SetTOS(tos int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoTOS] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, tos) +} + +// TTL returns the time-to-live field value for outgoing packets. +func (c *genericOpt) TTL() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoTTL] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetTTL sets the time-to-live field value for future outgoing +// packets. +func (c *genericOpt) SetTTL(ttl int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoTTL] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, ttl) +} diff --git a/vendor/src/golang.org/x/net/ipv4/header.go b/vendor/src/golang.org/x/net/ipv4/header.go new file mode 100644 index 00000000..8bb0f0f4 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/header.go @@ -0,0 +1,159 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "encoding/binary" + "fmt" + "net" + "runtime" + "syscall" + + "golang.org/x/net/internal/socket" +) + +const ( + Version = 4 // protocol version + HeaderLen = 20 // header length without extension headers + maxHeaderLen = 60 // sensible default, revisit if later RFCs define new usage of version and header length fields +) + +type HeaderFlags int + +const ( + MoreFragments HeaderFlags = 1 << iota // more fragments flag + DontFragment // don't fragment flag +) + +// A Header represents an IPv4 header. +type Header struct { + Version int // protocol version + Len int // header length + TOS int // type-of-service + TotalLen int // packet total length + ID int // identification + Flags HeaderFlags // flags + FragOff int // fragment offset + TTL int // time-to-live + Protocol int // next protocol + Checksum int // checksum + Src net.IP // source address + Dst net.IP // destination address + Options []byte // options, extension headers +} + +func (h *Header) String() string { + if h == nil { + return "" + } + return fmt.Sprintf("ver=%d hdrlen=%d tos=%#x totallen=%d id=%#x flags=%#x fragoff=%#x ttl=%d proto=%d cksum=%#x src=%v dst=%v", h.Version, h.Len, h.TOS, h.TotalLen, h.ID, h.Flags, h.FragOff, h.TTL, h.Protocol, h.Checksum, h.Src, h.Dst) +} + +// Marshal returns the binary encoding of h. +func (h *Header) Marshal() ([]byte, error) { + if h == nil { + return nil, syscall.EINVAL + } + if h.Len < HeaderLen { + return nil, errHeaderTooShort + } + hdrlen := HeaderLen + len(h.Options) + b := make([]byte, hdrlen) + b[0] = byte(Version<<4 | (hdrlen >> 2 & 0x0f)) + b[1] = byte(h.TOS) + flagsAndFragOff := (h.FragOff & 0x1fff) | int(h.Flags<<13) + switch runtime.GOOS { + case "darwin", "dragonfly", "netbsd": + socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen)) + socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff)) + case "freebsd": + if freebsdVersion < 1100000 { + socket.NativeEndian.PutUint16(b[2:4], uint16(h.TotalLen)) + socket.NativeEndian.PutUint16(b[6:8], uint16(flagsAndFragOff)) + } else { + binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen)) + binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff)) + } + default: + binary.BigEndian.PutUint16(b[2:4], uint16(h.TotalLen)) + binary.BigEndian.PutUint16(b[6:8], uint16(flagsAndFragOff)) + } + binary.BigEndian.PutUint16(b[4:6], uint16(h.ID)) + b[8] = byte(h.TTL) + b[9] = byte(h.Protocol) + binary.BigEndian.PutUint16(b[10:12], uint16(h.Checksum)) + if ip := h.Src.To4(); ip != nil { + copy(b[12:16], ip[:net.IPv4len]) + } + if ip := h.Dst.To4(); ip != nil { + copy(b[16:20], ip[:net.IPv4len]) + } else { + return nil, errMissingAddress + } + if len(h.Options) > 0 { + copy(b[HeaderLen:], h.Options) + } + return b, nil +} + +// Parse parses b as an IPv4 header and sotres the result in h. +func (h *Header) Parse(b []byte) error { + if h == nil || len(b) < HeaderLen { + return errHeaderTooShort + } + hdrlen := int(b[0]&0x0f) << 2 + if hdrlen > len(b) { + return errBufferTooShort + } + h.Version = int(b[0] >> 4) + h.Len = hdrlen + h.TOS = int(b[1]) + h.ID = int(binary.BigEndian.Uint16(b[4:6])) + h.TTL = int(b[8]) + h.Protocol = int(b[9]) + h.Checksum = int(binary.BigEndian.Uint16(b[10:12])) + h.Src = net.IPv4(b[12], b[13], b[14], b[15]) + h.Dst = net.IPv4(b[16], b[17], b[18], b[19]) + switch runtime.GOOS { + case "darwin", "dragonfly", "netbsd": + h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + hdrlen + h.FragOff = int(socket.NativeEndian.Uint16(b[6:8])) + case "freebsd": + if freebsdVersion < 1100000 { + h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4])) + if freebsdVersion < 1000000 { + h.TotalLen += hdrlen + } + h.FragOff = int(socket.NativeEndian.Uint16(b[6:8])) + } else { + h.TotalLen = int(binary.BigEndian.Uint16(b[2:4])) + h.FragOff = int(binary.BigEndian.Uint16(b[6:8])) + } + default: + h.TotalLen = int(binary.BigEndian.Uint16(b[2:4])) + h.FragOff = int(binary.BigEndian.Uint16(b[6:8])) + } + h.Flags = HeaderFlags(h.FragOff&0xe000) >> 13 + h.FragOff = h.FragOff & 0x1fff + optlen := hdrlen - HeaderLen + if optlen > 0 && len(b) >= hdrlen { + if cap(h.Options) < optlen { + h.Options = make([]byte, optlen) + } else { + h.Options = h.Options[:optlen] + } + copy(h.Options, b[HeaderLen:hdrlen]) + } + return nil +} + +// ParseHeader parses b as an IPv4 header. +func ParseHeader(b []byte) (*Header, error) { + h := new(Header) + if err := h.Parse(b); err != nil { + return nil, err + } + return h, nil +} diff --git a/vendor/src/golang.org/x/net/ipv4/header_test.go b/vendor/src/golang.org/x/net/ipv4/header_test.go new file mode 100644 index 00000000..a246aeea --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/header_test.go @@ -0,0 +1,228 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "bytes" + "encoding/binary" + "net" + "reflect" + "runtime" + "strings" + "testing" + + "golang.org/x/net/internal/socket" +) + +type headerTest struct { + wireHeaderFromKernel []byte + wireHeaderToKernel []byte + wireHeaderFromTradBSDKernel []byte + wireHeaderToTradBSDKernel []byte + wireHeaderFromFreeBSD10Kernel []byte + wireHeaderToFreeBSD10Kernel []byte + *Header +} + +var headerLittleEndianTests = []headerTest{ + // TODO(mikio): Add platform dependent wire header formats when + // we support new platforms. + { + wireHeaderFromKernel: []byte{ + 0x45, 0x01, 0xbe, 0xef, + 0xca, 0xfe, 0x45, 0xdc, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + wireHeaderToKernel: []byte{ + 0x45, 0x01, 0xbe, 0xef, + 0xca, 0xfe, 0x45, 0xdc, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + wireHeaderFromTradBSDKernel: []byte{ + 0x45, 0x01, 0xdb, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + wireHeaderToTradBSDKernel: []byte{ + 0x45, 0x01, 0xef, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + wireHeaderFromFreeBSD10Kernel: []byte{ + 0x45, 0x01, 0xef, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + wireHeaderToFreeBSD10Kernel: []byte{ + 0x45, 0x01, 0xef, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + }, + Header: &Header{ + Version: Version, + Len: HeaderLen, + TOS: 1, + TotalLen: 0xbeef, + ID: 0xcafe, + Flags: DontFragment, + FragOff: 1500, + TTL: 255, + Protocol: 1, + Checksum: 0xdead, + Src: net.IPv4(172, 16, 254, 254), + Dst: net.IPv4(192, 168, 0, 1), + }, + }, + + // with option headers + { + wireHeaderFromKernel: []byte{ + 0x46, 0x01, 0xbe, 0xf3, + 0xca, 0xfe, 0x45, 0xdc, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + wireHeaderToKernel: []byte{ + 0x46, 0x01, 0xbe, 0xf3, + 0xca, 0xfe, 0x45, 0xdc, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + wireHeaderFromTradBSDKernel: []byte{ + 0x46, 0x01, 0xdb, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + wireHeaderToTradBSDKernel: []byte{ + 0x46, 0x01, 0xf3, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + wireHeaderFromFreeBSD10Kernel: []byte{ + 0x46, 0x01, 0xf3, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + wireHeaderToFreeBSD10Kernel: []byte{ + 0x46, 0x01, 0xf3, 0xbe, + 0xca, 0xfe, 0xdc, 0x45, + 0xff, 0x01, 0xde, 0xad, + 172, 16, 254, 254, + 192, 168, 0, 1, + 0xff, 0xfe, 0xfe, 0xff, + }, + Header: &Header{ + Version: Version, + Len: HeaderLen + 4, + TOS: 1, + TotalLen: 0xbef3, + ID: 0xcafe, + Flags: DontFragment, + FragOff: 1500, + TTL: 255, + Protocol: 1, + Checksum: 0xdead, + Src: net.IPv4(172, 16, 254, 254), + Dst: net.IPv4(192, 168, 0, 1), + Options: []byte{0xff, 0xfe, 0xfe, 0xff}, + }, + }, +} + +func TestMarshalHeader(t *testing.T) { + if socket.NativeEndian != binary.LittleEndian { + t.Skip("no test for non-little endian machine yet") + } + + for _, tt := range headerLittleEndianTests { + b, err := tt.Header.Marshal() + if err != nil { + t.Fatal(err) + } + var wh []byte + switch runtime.GOOS { + case "darwin", "dragonfly", "netbsd": + wh = tt.wireHeaderToTradBSDKernel + case "freebsd": + switch { + case freebsdVersion < 1000000: + wh = tt.wireHeaderToTradBSDKernel + case 1000000 <= freebsdVersion && freebsdVersion < 1100000: + wh = tt.wireHeaderToFreeBSD10Kernel + default: + wh = tt.wireHeaderToKernel + } + default: + wh = tt.wireHeaderToKernel + } + if !bytes.Equal(b, wh) { + t.Fatalf("got %#v; want %#v", b, wh) + } + } +} + +func TestParseHeader(t *testing.T) { + if socket.NativeEndian != binary.LittleEndian { + t.Skip("no test for big endian machine yet") + } + + for _, tt := range headerLittleEndianTests { + var wh []byte + switch runtime.GOOS { + case "darwin", "dragonfly", "netbsd": + wh = tt.wireHeaderFromTradBSDKernel + case "freebsd": + switch { + case freebsdVersion < 1000000: + wh = tt.wireHeaderFromTradBSDKernel + case 1000000 <= freebsdVersion && freebsdVersion < 1100000: + wh = tt.wireHeaderFromFreeBSD10Kernel + default: + wh = tt.wireHeaderFromKernel + } + default: + wh = tt.wireHeaderFromKernel + } + h, err := ParseHeader(wh) + if err != nil { + t.Fatal(err) + } + if err := h.Parse(wh); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(h, tt.Header) { + t.Fatalf("got %#v; want %#v", h, tt.Header) + } + s := h.String() + if strings.Contains(s, ",") { + t.Fatalf("should be space-separated values: %s", s) + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/helper.go b/vendor/src/golang.org/x/net/ipv4/helper.go new file mode 100644 index 00000000..a5052e32 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/helper.go @@ -0,0 +1,63 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "errors" + "net" +) + +var ( + errMissingAddress = errors.New("missing address") + errMissingHeader = errors.New("missing header") + errHeaderTooShort = errors.New("header too short") + errBufferTooShort = errors.New("buffer too short") + errInvalidConnType = errors.New("invalid conn type") + errOpNoSupport = errors.New("operation not supported") + errNoSuchInterface = errors.New("no such interface") + errNoSuchMulticastInterface = errors.New("no such multicast interface") + + // See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html. + freebsdVersion uint32 +) + +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +func netAddrToIP4(a net.Addr) net.IP { + switch v := a.(type) { + case *net.UDPAddr: + if ip := v.IP.To4(); ip != nil { + return ip + } + case *net.IPAddr: + if ip := v.IP.To4(); ip != nil { + return ip + } + } + return nil +} + +func opAddr(a net.Addr) net.Addr { + switch a.(type) { + case *net.TCPAddr: + if a == nil { + return nil + } + case *net.UDPAddr: + if a == nil { + return nil + } + case *net.IPAddr: + if a == nil { + return nil + } + } + return a +} diff --git a/vendor/src/golang.org/x/net/ipv4/iana.go b/vendor/src/golang.org/x/net/ipv4/iana.go new file mode 100644 index 00000000..be10c948 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/iana.go @@ -0,0 +1,34 @@ +// go generate gen.go +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package ipv4 + +// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 +const ( + ICMPTypeEchoReply ICMPType = 0 // Echo Reply + ICMPTypeDestinationUnreachable ICMPType = 3 // Destination Unreachable + ICMPTypeRedirect ICMPType = 5 // Redirect + ICMPTypeEcho ICMPType = 8 // Echo + ICMPTypeRouterAdvertisement ICMPType = 9 // Router Advertisement + ICMPTypeRouterSolicitation ICMPType = 10 // Router Solicitation + ICMPTypeTimeExceeded ICMPType = 11 // Time Exceeded + ICMPTypeParameterProblem ICMPType = 12 // Parameter Problem + ICMPTypeTimestamp ICMPType = 13 // Timestamp + ICMPTypeTimestampReply ICMPType = 14 // Timestamp Reply + ICMPTypePhoturis ICMPType = 40 // Photuris +) + +// Internet Control Message Protocol (ICMP) Parameters, Updated: 2013-04-19 +var icmpTypes = map[ICMPType]string{ + 0: "echo reply", + 3: "destination unreachable", + 5: "redirect", + 8: "echo", + 9: "router advertisement", + 10: "router solicitation", + 11: "time exceeded", + 12: "parameter problem", + 13: "timestamp", + 14: "timestamp reply", + 40: "photuris", +} diff --git a/vendor/src/golang.org/x/net/ipv4/icmp.go b/vendor/src/golang.org/x/net/ipv4/icmp.go new file mode 100644 index 00000000..9902bb3d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/icmp.go @@ -0,0 +1,57 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import "golang.org/x/net/internal/iana" + +// An ICMPType represents a type of ICMP message. +type ICMPType int + +func (typ ICMPType) String() string { + s, ok := icmpTypes[typ] + if !ok { + return "" + } + return s +} + +// Protocol returns the ICMPv4 protocol number. +func (typ ICMPType) Protocol() int { + return iana.ProtocolICMP +} + +// An ICMPFilter represents an ICMP message filter for incoming +// packets. The filter belongs to a packet delivery path on a host and +// it cannot interact with forwarding packets or tunnel-outer packets. +// +// Note: RFC 8200 defines a reasonable role model and it works not +// only for IPv6 but IPv4. A node means a device that implements IP. +// A router means a node that forwards IP packets not explicitly +// addressed to itself, and a host means a node that is not a router. +type ICMPFilter struct { + icmpFilter +} + +// Accept accepts incoming ICMP packets including the type field value +// typ. +func (f *ICMPFilter) Accept(typ ICMPType) { + f.accept(typ) +} + +// Block blocks incoming ICMP packets including the type field value +// typ. +func (f *ICMPFilter) Block(typ ICMPType) { + f.block(typ) +} + +// SetAll sets the filter action to the filter. +func (f *ICMPFilter) SetAll(block bool) { + f.setAll(block) +} + +// WillBlock reports whether the ICMP type will be blocked. +func (f *ICMPFilter) WillBlock(typ ICMPType) bool { + return f.willBlock(typ) +} diff --git a/vendor/src/golang.org/x/net/ipv4/icmp_linux.go b/vendor/src/golang.org/x/net/ipv4/icmp_linux.go new file mode 100644 index 00000000..6e1c5c80 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/icmp_linux.go @@ -0,0 +1,25 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +func (f *icmpFilter) accept(typ ICMPType) { + f.Data &^= 1 << (uint32(typ) & 31) +} + +func (f *icmpFilter) block(typ ICMPType) { + f.Data |= 1 << (uint32(typ) & 31) +} + +func (f *icmpFilter) setAll(block bool) { + if block { + f.Data = 1<<32 - 1 + } else { + f.Data = 0 + } +} + +func (f *icmpFilter) willBlock(typ ICMPType) bool { + return f.Data&(1<<(uint32(typ)&31)) != 0 +} diff --git a/vendor/src/golang.org/x/net/ipv4/icmp_stub.go b/vendor/src/golang.org/x/net/ipv4/icmp_stub.go new file mode 100644 index 00000000..21bb29ab --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/icmp_stub.go @@ -0,0 +1,25 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !linux + +package ipv4 + +const sizeofICMPFilter = 0x0 + +type icmpFilter struct { +} + +func (f *icmpFilter) accept(typ ICMPType) { +} + +func (f *icmpFilter) block(typ ICMPType) { +} + +func (f *icmpFilter) setAll(block bool) { +} + +func (f *icmpFilter) willBlock(typ ICMPType) bool { + return false +} diff --git a/vendor/src/golang.org/x/net/ipv4/icmp_test.go b/vendor/src/golang.org/x/net/ipv4/icmp_test.go new file mode 100644 index 00000000..3324b54d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/icmp_test.go @@ -0,0 +1,95 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "net" + "reflect" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +var icmpStringTests = []struct { + in ipv4.ICMPType + out string +}{ + {ipv4.ICMPTypeDestinationUnreachable, "destination unreachable"}, + + {256, ""}, +} + +func TestICMPString(t *testing.T) { + for _, tt := range icmpStringTests { + s := tt.in.String() + if s != tt.out { + t.Errorf("got %s; want %s", s, tt.out) + } + } +} + +func TestICMPFilter(t *testing.T) { + switch runtime.GOOS { + case "linux": + default: + t.Skipf("not supported on %s", runtime.GOOS) + } + + var f ipv4.ICMPFilter + for _, toggle := range []bool{false, true} { + f.SetAll(toggle) + for _, typ := range []ipv4.ICMPType{ + ipv4.ICMPTypeDestinationUnreachable, + ipv4.ICMPTypeEchoReply, + ipv4.ICMPTypeTimeExceeded, + ipv4.ICMPTypeParameterProblem, + } { + f.Accept(typ) + if f.WillBlock(typ) { + t.Errorf("ipv4.ICMPFilter.Set(%v, false) failed", typ) + } + f.Block(typ) + if !f.WillBlock(typ) { + t.Errorf("ipv4.ICMPFilter.Set(%v, true) failed", typ) + } + } + } +} + +func TestSetICMPFilter(t *testing.T) { + switch runtime.GOOS { + case "linux": + default: + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket("ip4:icmp", "127.0.0.1") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv4.NewPacketConn(c) + + var f ipv4.ICMPFilter + f.SetAll(true) + f.Accept(ipv4.ICMPTypeEcho) + f.Accept(ipv4.ICMPTypeEchoReply) + if err := p.SetICMPFilter(&f); err != nil { + t.Fatal(err) + } + kf, err := p.ICMPFilter() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(kf, &f) { + t.Fatalf("got %#v; want %#v", kf, f) + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/multicast_test.go b/vendor/src/golang.org/x/net/ipv4/multicast_test.go new file mode 100644 index 00000000..bcf49736 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/multicast_test.go @@ -0,0 +1,334 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "bytes" + "net" + "os" + "runtime" + "testing" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +var packetConnReadWriteMulticastUDPTests = []struct { + addr string + grp, src *net.UDPAddr +}{ + {"224.0.0.0:0", &net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727 + + {"232.0.1.0:0", &net.UDPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 +} + +func TestPacketConnReadWriteMulticastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "solaris", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range packetConnReadWriteMulticastUDPTests { + c, err := net.ListenPacket("udp4", tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + grp := *tt.grp + grp.Port = c.LocalAddr().(*net.UDPAddr).Port + p := ipv4.NewPacketConn(c) + defer p.Close() + if tt.src == nil { + if err := p.JoinGroup(ifi, &grp); err != nil { + t.Fatal(err) + } + defer p.LeaveGroup(ifi, &grp) + } else { + if err := p.JoinSourceSpecificGroup(ifi, &grp, tt.src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support IGMPv2/3 fail here + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + defer p.LeaveSourceSpecificGroup(ifi, &grp, tt.src) + } + if err := p.SetMulticastInterface(ifi); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastInterface(); err != nil { + t.Fatal(err) + } + if err := p.SetMulticastLoopback(true); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatal(err) + } + cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface + wb := []byte("HELLO-R-U-THERE") + + for i, toggle := range []bool{true, false, true} { + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatal(err) + } + p.SetMulticastTTL(i + 1) + if n, err := p.WriteTo(wb, nil, &grp); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if n, _, _, err := p.ReadFrom(rb); err != nil { + t.Fatal(err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; want %v", rb[:n], wb) + } + } + } +} + +var packetConnReadWriteMulticastICMPTests = []struct { + grp, src *net.IPAddr +}{ + {&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727 + + {&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 +} + +func TestPacketConnReadWriteMulticastICMP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "solaris", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range packetConnReadWriteMulticastICMPTests { + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv4.NewPacketConn(c) + defer p.Close() + if tt.src == nil { + if err := p.JoinGroup(ifi, tt.grp); err != nil { + t.Fatal(err) + } + defer p.LeaveGroup(ifi, tt.grp) + } else { + if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support IGMPv2/3 fail here + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) + } + if err := p.SetMulticastInterface(ifi); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastInterface(); err != nil { + t.Fatal(err) + } + if err := p.SetMulticastLoopback(true); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatal(err) + } + cf := ipv4.FlagDst | ipv4.FlagInterface + if runtime.GOOS != "solaris" { + // Solaris never allows to modify ICMP properties. + cf |= ipv4.FlagTTL + } + + for i, toggle := range []bool{true, false, true} { + wb, err := (&icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(nil) + if err != nil { + t.Fatal(err) + } + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatal(err) + } + p.SetMulticastTTL(i + 1) + if n, err := p.WriteTo(wb, nil, tt.grp); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if n, _, _, err := p.ReadFrom(rb); err != nil { + t.Fatal(err) + } else { + m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) + if err != nil { + t.Fatal(err) + } + switch { + case m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1 + case m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0 + default: + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) + } + } + } + } +} + +var rawConnReadWriteMulticastICMPTests = []struct { + grp, src *net.IPAddr +}{ + {&net.IPAddr{IP: net.IPv4(224, 0, 0, 254)}, nil}, // see RFC 4727 + + {&net.IPAddr{IP: net.IPv4(232, 0, 1, 254)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 +} + +func TestRawConnReadWriteMulticastICMP(t *testing.T) { + if testing.Short() { + t.Skip("to avoid external network") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range rawConnReadWriteMulticastICMPTests { + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + defer r.Close() + if tt.src == nil { + if err := r.JoinGroup(ifi, tt.grp); err != nil { + t.Fatal(err) + } + defer r.LeaveGroup(ifi, tt.grp) + } else { + if err := r.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support IGMPv2/3 fail here + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + defer r.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) + } + if err := r.SetMulticastInterface(ifi); err != nil { + t.Fatal(err) + } + if _, err := r.MulticastInterface(); err != nil { + t.Fatal(err) + } + if err := r.SetMulticastLoopback(true); err != nil { + t.Fatal(err) + } + if _, err := r.MulticastLoopback(); err != nil { + t.Fatal(err) + } + cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface + + for i, toggle := range []bool{true, false, true} { + wb, err := (&icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(nil) + if err != nil { + t.Fatal(err) + } + wh := &ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TOS: i + 1, + TotalLen: ipv4.HeaderLen + len(wb), + Protocol: 1, + Dst: tt.grp.IP, + } + if err := r.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := r.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatal(err) + } + r.SetMulticastTTL(i + 1) + if err := r.WriteTo(wh, wb, nil); err != nil { + t.Fatal(err) + } + rb := make([]byte, ipv4.HeaderLen+128) + if rh, b, _, err := r.ReadFrom(rb); err != nil { + t.Fatal(err) + } else { + m, err := icmp.ParseMessage(iana.ProtocolICMP, b) + if err != nil { + t.Fatal(err) + } + switch { + case (rh.Dst.IsLoopback() || rh.Dst.IsLinkLocalUnicast() || rh.Dst.IsGlobalUnicast()) && m.Type == ipv4.ICMPTypeEchoReply && m.Code == 0: // net.inet.icmp.bmcastecho=1 + case rh.Dst.IsMulticast() && m.Type == ipv4.ICMPTypeEcho && m.Code == 0: // net.inet.icmp.bmcastecho=0 + default: + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) + } + } + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/multicastlistener_test.go b/vendor/src/golang.org/x/net/ipv4/multicastlistener_test.go new file mode 100644 index 00000000..e43fbbe0 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/multicastlistener_test.go @@ -0,0 +1,265 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +var udpMultipleGroupListenerTests = []net.Addr{ + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, // see RFC 4727 + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 250)}, + &net.UDPAddr{IP: net.IPv4(224, 0, 0, 254)}, +} + +func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if testing.Short() { + t.Skip("to avoid external network") + } + + for _, gaddr := range udpMultipleGroupListenerTests { + c, err := net.ListenPacket("udp4", "0.0.0.0:0") // wildcard address with no reusable port + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv4.NewPacketConn(c) + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok { + continue + } + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatal(err) + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatal(err) + } + } + } +} + +func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if testing.Short() { + t.Skip("to avoid external network") + } + + for _, gaddr := range udpMultipleGroupListenerTests { + c1, err := net.ListenPacket("udp4", "224.0.0.0:0") // wildcard address with reusable port + if err != nil { + t.Fatal(err) + } + defer c1.Close() + _, port, err := net.SplitHostPort(c1.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + c2, err := net.ListenPacket("udp4", net.JoinHostPort("224.0.0.0", port)) // wildcard address with reusable port + if err != nil { + t.Fatal(err) + } + defer c2.Close() + + var ps [2]*ipv4.PacketConn + ps[0] = ipv4.NewPacketConn(c1) + ps[1] = ipv4.NewPacketConn(c2) + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok { + continue + } + for _, p := range ps { + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatal(err) + } + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + for _, p := range ps { + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatal(err) + } + } + } + } +} + +func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if testing.Short() { + t.Skip("to avoid external network") + } + + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + type ml struct { + c *ipv4.PacketConn + ifi *net.Interface + } + var mlt []*ml + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + port := "0" + for i, ifi := range ift { + ip, ok := nettest.IsMulticastCapable("ip4", &ifi) + if !ok { + continue + } + c, err := net.ListenPacket("udp4", net.JoinHostPort(ip.String(), port)) // unicast address with non-reusable port + if err != nil { + // The listen may fail when the serivce is + // already in use, but it's fine because the + // purpose of this is not to test the + // bookkeeping of IP control block inside the + // kernel. + t.Log(err) + continue + } + defer c.Close() + if port == "0" { + _, port, err = net.SplitHostPort(c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + } + p := ipv4.NewPacketConn(c) + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mlt = append(mlt, &ml{p, &ift[i]}) + } + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} + +func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if testing.Short() { + t.Skip("to avoid external network") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // wildcard address + if err != nil { + t.Fatal(err) + } + defer c.Close() + + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip4", &ifi); !ok { + continue + } + if err := r.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + if err := r.LeaveGroup(ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} + +func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if testing.Short() { + t.Skip("to avoid external network") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + gaddr := net.IPAddr{IP: net.IPv4(224, 0, 0, 254)} // see RFC 4727 + type ml struct { + c *ipv4.RawConn + ifi *net.Interface + } + var mlt []*ml + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + ip, ok := nettest.IsMulticastCapable("ip4", &ifi) + if !ok { + continue + } + c, err := net.ListenPacket("ip4:253", ip.String()) // unicast address + if err != nil { + t.Fatal(err) + } + defer c.Close() + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + if err := r.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mlt = append(mlt, &ml{r, &ift[i]}) + } + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/multicastsockopt_test.go b/vendor/src/golang.org/x/net/ipv4/multicastsockopt_test.go new file mode 100644 index 00000000..f7efac24 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/multicastsockopt_test.go @@ -0,0 +1,195 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +var packetConnMulticastSocketOptionTests = []struct { + net, proto, addr string + grp, src net.Addr +}{ + {"udp4", "", "224.0.0.0:0", &net.UDPAddr{IP: net.IPv4(224, 0, 0, 249)}, nil}, // see RFC 4727 + {"ip4", ":icmp", "0.0.0.0", &net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}, nil}, // see RFC 4727 + + {"udp4", "", "232.0.0.0:0", &net.UDPAddr{IP: net.IPv4(232, 0, 1, 249)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 + {"ip4", ":icmp", "0.0.0.0", &net.IPAddr{IP: net.IPv4(232, 0, 1, 250)}, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 +} + +func TestPacketConnMulticastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + m, ok := nettest.SupportsRawIPSocket() + for _, tt := range packetConnMulticastSocketOptionTests { + if tt.net == "ip4" && !ok { + t.Log(m) + continue + } + c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + defer p.Close() + + if tt.src == nil { + testMulticastSocketOptions(t, p, ifi, tt.grp) + } else { + testSourceSpecificMulticastSocketOptions(t, p, ifi, tt.grp, tt.src) + } + } +} + +var rawConnMulticastSocketOptionTests = []struct { + grp, src net.Addr +}{ + {&net.IPAddr{IP: net.IPv4(224, 0, 0, 250)}, nil}, // see RFC 4727 + + {&net.IPAddr{IP: net.IPv4(232, 0, 1, 250)}, &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}}, // see RFC 5771 +} + +func TestRawConnMulticastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9": + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range rawConnMulticastSocketOptionTests { + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + t.Fatal(err) + } + defer c.Close() + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + defer r.Close() + + if tt.src == nil { + testMulticastSocketOptions(t, r, ifi, tt.grp) + } else { + testSourceSpecificMulticastSocketOptions(t, r, ifi, tt.grp, tt.src) + } + } +} + +type testIPv4MulticastConn interface { + MulticastTTL() (int, error) + SetMulticastTTL(ttl int) error + MulticastLoopback() (bool, error) + SetMulticastLoopback(bool) error + JoinGroup(*net.Interface, net.Addr) error + LeaveGroup(*net.Interface, net.Addr) error + JoinSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + LeaveSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + ExcludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + IncludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error +} + +func testMulticastSocketOptions(t *testing.T, c testIPv4MulticastConn, ifi *net.Interface, grp net.Addr) { + const ttl = 255 + if err := c.SetMulticastTTL(ttl); err != nil { + t.Error(err) + return + } + if v, err := c.MulticastTTL(); err != nil { + t.Error(err) + return + } else if v != ttl { + t.Errorf("got %v; want %v", v, ttl) + return + } + + for _, toggle := range []bool{true, false} { + if err := c.SetMulticastLoopback(toggle); err != nil { + t.Error(err) + return + } + if v, err := c.MulticastLoopback(); err != nil { + t.Error(err) + return + } else if v != toggle { + t.Errorf("got %v; want %v", v, toggle) + return + } + } + + if err := c.JoinGroup(ifi, grp); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } +} + +func testSourceSpecificMulticastSocketOptions(t *testing.T, c testIPv4MulticastConn, ifi *net.Interface, grp, src net.Addr) { + // MCAST_JOIN_GROUP -> MCAST_BLOCK_SOURCE -> MCAST_UNBLOCK_SOURCE -> MCAST_LEAVE_GROUP + if err := c.JoinGroup(ifi, grp); err != nil { + t.Error(err) + return + } + if err := c.ExcludeSourceSpecificGroup(ifi, grp, src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support IGMPv2/3 fail here + t.Logf("not supported on %s", runtime.GOOS) + return + } + t.Error(err) + return + } + if err := c.IncludeSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } + + // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_SOURCE_GROUP + if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + + // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_GROUP + if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/packet.go b/vendor/src/golang.org/x/net/ipv4/packet.go new file mode 100644 index 00000000..f00f5b05 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/packet.go @@ -0,0 +1,69 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the ReadFrom and WriteTo methods of RawConn +// are not implemented. + +// A packetHandler represents the IPv4 datagram handler. +type packetHandler struct { + *net.IPConn + *socket.Conn + rawOpt +} + +func (c *packetHandler) ok() bool { return c != nil && c.IPConn != nil && c.Conn != nil } + +// ReadFrom reads an IPv4 datagram from the endpoint c, copying the +// datagram into b. It returns the received datagram as the IPv4 +// header h, the payload p and the control message cm. +func (c *packetHandler) ReadFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) { + if !c.ok() { + return nil, nil, nil, syscall.EINVAL + } + return c.readFrom(b) +} + +func slicePacket(b []byte) (h, p []byte, err error) { + if len(b) < HeaderLen { + return nil, nil, errHeaderTooShort + } + hdrlen := int(b[0]&0x0f) << 2 + return b[:hdrlen], b[hdrlen:], nil +} + +// WriteTo writes an IPv4 datagram through the endpoint c, copying the +// datagram from the IPv4 header h and the payload p. The control +// message cm allows the datagram path and the outgoing interface to be +// specified. Currently only Darwin and Linux support this. The cm +// may be nil if control of the outgoing datagram is not required. +// +// The IPv4 header h must contain appropriate fields that include: +// +// Version = +// Len = +// TOS = +// TotalLen = +// ID = platform sets an appropriate value if ID is zero +// FragOff = +// TTL = +// Protocol = +// Checksum = platform sets an appropriate value if Checksum is zero +// Src = platform sets an appropriate value if Src is nil +// Dst = +// Options = optional +func (c *packetHandler) WriteTo(h *Header, p []byte, cm *ControlMessage) error { + if !c.ok() { + return syscall.EINVAL + } + return c.writeTo(h, p, cm) +} diff --git a/vendor/src/golang.org/x/net/ipv4/packet_go1_8.go b/vendor/src/golang.org/x/net/ipv4/packet_go1_8.go new file mode 100644 index 00000000..b47d1868 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/packet_go1_8.go @@ -0,0 +1,56 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package ipv4 + +import "net" + +func (c *packetHandler) readFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) { + c.rawOpt.RLock() + oob := NewControlMessage(c.rawOpt.cflags) + c.rawOpt.RUnlock() + n, nn, _, src, err := c.ReadMsgIP(b, oob) + if err != nil { + return nil, nil, nil, err + } + var hs []byte + if hs, p, err = slicePacket(b[:n]); err != nil { + return nil, nil, nil, err + } + if h, err = ParseHeader(hs); err != nil { + return nil, nil, nil, err + } + if nn > 0 { + cm = new(ControlMessage) + if err := cm.Parse(oob[:nn]); err != nil { + return nil, nil, nil, err + } + } + if src != nil && cm != nil { + cm.Src = src.IP + } + return +} + +func (c *packetHandler) writeTo(h *Header, p []byte, cm *ControlMessage) error { + oob := cm.Marshal() + wh, err := h.Marshal() + if err != nil { + return err + } + dst := new(net.IPAddr) + if cm != nil { + if ip := cm.Dst.To4(); ip != nil { + dst.IP = ip + } + } + if dst.IP == nil { + dst.IP = h.Dst + } + wh = append(wh, p...) + _, _, err = c.WriteMsgIP(wh, oob, dst) + return err +} diff --git a/vendor/src/golang.org/x/net/ipv4/packet_go1_9.go b/vendor/src/golang.org/x/net/ipv4/packet_go1_9.go new file mode 100644 index 00000000..082c36d7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/packet_go1_9.go @@ -0,0 +1,67 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (c *packetHandler) readFrom(b []byte) (h *Header, p []byte, cm *ControlMessage, err error) { + c.rawOpt.RLock() + m := socket.Message{ + Buffers: [][]byte{b}, + OOB: NewControlMessage(c.rawOpt.cflags), + } + c.rawOpt.RUnlock() + if err := c.RecvMsg(&m, 0); err != nil { + return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + var hs []byte + if hs, p, err = slicePacket(b[:m.N]); err != nil { + return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + if h, err = ParseHeader(hs); err != nil { + return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + if m.NN > 0 { + cm = new(ControlMessage) + if err := cm.Parse(m.OOB[:m.NN]); err != nil { + return nil, nil, nil, &net.OpError{Op: "read", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Err: err} + } + } + if src, ok := m.Addr.(*net.IPAddr); ok && cm != nil { + cm.Src = src.IP + } + return +} + +func (c *packetHandler) writeTo(h *Header, p []byte, cm *ControlMessage) error { + m := socket.Message{ + OOB: cm.Marshal(), + } + wh, err := h.Marshal() + if err != nil { + return err + } + m.Buffers = [][]byte{wh, p} + dst := new(net.IPAddr) + if cm != nil { + if ip := cm.Dst.To4(); ip != nil { + dst.IP = ip + } + } + if dst.IP == nil { + dst.IP = h.Dst + } + m.Addr = dst + if err := c.SendMsg(&m, 0); err != nil { + return &net.OpError{Op: "write", Net: c.IPConn.LocalAddr().Network(), Source: c.IPConn.LocalAddr(), Addr: opAddr(dst), Err: err} + } + return nil +} diff --git a/vendor/src/golang.org/x/net/ipv4/payload.go b/vendor/src/golang.org/x/net/ipv4/payload.go new file mode 100644 index 00000000..f95f811a --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/payload.go @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo +// methods of PacketConn is not implemented. + +// A payloadHandler represents the IPv4 datagram payload handler. +type payloadHandler struct { + net.PacketConn + *socket.Conn + rawOpt +} + +func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil && c.Conn != nil } diff --git a/vendor/src/golang.org/x/net/ipv4/payload_cmsg.go b/vendor/src/golang.org/x/net/ipv4/payload_cmsg.go new file mode 100644 index 00000000..3f06d760 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/payload_cmsg.go @@ -0,0 +1,36 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !nacl,!plan9,!windows + +package ipv4 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv4 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + return c.readFrom(b) +} + +// WriteTo writes a payload of the IPv4 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the datagram path and the outgoing interface to be specified. +// Currently only Darwin and Linux support this. The cm may be nil if +// control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + return c.writeTo(b, cm, dst) +} diff --git a/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_8.go b/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_8.go new file mode 100644 index 00000000..d26ccd90 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_8.go @@ -0,0 +1,59 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 +// +build !nacl,!plan9,!windows + +package ipv4 + +import "net" + +func (c *payloadHandler) readFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + c.rawOpt.RLock() + oob := NewControlMessage(c.rawOpt.cflags) + c.rawOpt.RUnlock() + var nn int + switch c := c.PacketConn.(type) { + case *net.UDPConn: + if n, nn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { + return 0, nil, nil, err + } + case *net.IPConn: + nb := make([]byte, maxHeaderLen+len(b)) + if n, nn, _, src, err = c.ReadMsgIP(nb, oob); err != nil { + return 0, nil, nil, err + } + hdrlen := int(nb[0]&0x0f) << 2 + copy(b, nb[hdrlen:]) + n -= hdrlen + default: + return 0, nil, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Err: errInvalidConnType} + } + if nn > 0 { + cm = new(ControlMessage) + if err = cm.Parse(oob[:nn]); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + } + if cm != nil { + cm.Src = netAddrToIP4(src) + } + return +} + +func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + oob := cm.Marshal() + if dst == nil { + return 0, &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errMissingAddress} + } + switch c := c.PacketConn.(type) { + case *net.UDPConn: + n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) + case *net.IPConn: + n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) + default: + return 0, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: opAddr(dst), Err: errInvalidConnType} + } + return +} diff --git a/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_9.go b/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_9.go new file mode 100644 index 00000000..2f193118 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/payload_cmsg_go1_9.go @@ -0,0 +1,67 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build !nacl,!plan9,!windows + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (c *payloadHandler) readFrom(b []byte) (int, *ControlMessage, net.Addr, error) { + c.rawOpt.RLock() + m := socket.Message{ + OOB: NewControlMessage(c.rawOpt.cflags), + } + c.rawOpt.RUnlock() + switch c.PacketConn.(type) { + case *net.UDPConn: + m.Buffers = [][]byte{b} + if err := c.RecvMsg(&m, 0); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + case *net.IPConn: + h := make([]byte, HeaderLen) + m.Buffers = [][]byte{h, b} + if err := c.RecvMsg(&m, 0); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + hdrlen := int(h[0]&0x0f) << 2 + if hdrlen > len(h) { + d := hdrlen - len(h) + copy(b, b[d:]) + m.N -= d + } else { + m.N -= hdrlen + } + default: + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errInvalidConnType} + } + var cm *ControlMessage + if m.NN > 0 { + cm = new(ControlMessage) + if err := cm.Parse(m.OOB[:m.NN]); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + cm.Src = netAddrToIP4(m.Addr) + } + return m.N, cm, m.Addr, nil +} + +func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (int, error) { + m := socket.Message{ + Buffers: [][]byte{b}, + OOB: cm.Marshal(), + Addr: dst, + } + err := c.SendMsg(&m, 0) + if err != nil { + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Addr: opAddr(dst), Err: err} + } + return m.N, err +} diff --git a/vendor/src/golang.org/x/net/ipv4/payload_nocmsg.go b/vendor/src/golang.org/x/net/ipv4/payload_nocmsg.go new file mode 100644 index 00000000..3926de70 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/payload_nocmsg.go @@ -0,0 +1,42 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl plan9 windows + +package ipv4 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv4 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + if n, src, err = c.PacketConn.ReadFrom(b); err != nil { + return 0, nil, nil, err + } + return +} + +// WriteTo writes a payload of the IPv4 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the datagram path and the outgoing interface to be specified. +// Currently only Darwin and Linux support this. The cm may be nil if +// control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + if dst == nil { + return 0, errMissingAddress + } + return c.PacketConn.WriteTo(b, dst) +} diff --git a/vendor/src/golang.org/x/net/ipv4/readwrite_go1_8_test.go b/vendor/src/golang.org/x/net/ipv4/readwrite_go1_8_test.go new file mode 100644 index 00000000..1cd926e7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/readwrite_go1_8_test.go @@ -0,0 +1,248 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package ipv4_test + +import ( + "bytes" + "fmt" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +func BenchmarkPacketConnReadWriteUnicast(b *testing.B) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + b.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph, err := (&ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TotalLen: ipv4.HeaderLen + len(payload), + TTL: 1, + Protocol: iana.ProtocolReserved, + Src: net.IPv4(192, 0, 2, 1), + Dst: net.IPv4(192, 0, 2, 254), + }).Marshal() + if err != nil { + b.Fatal(err) + } + greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + bb := make([]byte, 128) + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); ifi != nil { + cm.IfIndex = ifi.Index + } + + b.Run("UDP", func(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv4.FlagTTL | ipv4.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(payload, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(payload, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + }) + b.Run("IP", func(b *testing.B) { + switch runtime.GOOS { + case "netbsd": + b.Skip("need to configure gre on netbsd") + case "openbsd": + b.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv4.FlagTTL | ipv4.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(datagram, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(datagram, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + }) +} + +func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph, err := (&ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TotalLen: ipv4.HeaderLen + len(payload), + TTL: 1, + Protocol: iana.ProtocolReserved, + Src: net.IPv4(192, 0, 2, 1), + Dst: net.IPv4(192, 0, 2, 254), + }).Marshal() + if err != nil { + t.Fatal(err) + } + greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + + t.Run("UDP", func(t *testing.T) { + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr()) + }) + }) + t.Run("IP", func(t *testing.T) { + switch runtime.GOOS { + case "netbsd": + t.Skip("need to configure gre on netbsd") + case "openbsd": + t.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr()) + }) + }) +} + +func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr) { + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + b := make([]byte, 128) + n, cm, _, err := p.ReadFrom(b) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(b[:n], data) { + t.Errorf("got %#v; want %#v", b[:n], data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + n, err := p.WriteTo(data, &cm, dst) + if err != nil { + t.Error(err) + return + } + if n != len(data) { + t.Errorf("got %d; want %d", n, len(data)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + go writer(i%2 != 0) + + } + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv4/readwrite_go1_9_test.go b/vendor/src/golang.org/x/net/ipv4/readwrite_go1_9_test.go new file mode 100644 index 00000000..365de022 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/readwrite_go1_9_test.go @@ -0,0 +1,388 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package ipv4_test + +import ( + "bytes" + "fmt" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +func BenchmarkPacketConnReadWriteUnicast(b *testing.B) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + b.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph, err := (&ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TotalLen: ipv4.HeaderLen + len(payload), + TTL: 1, + Protocol: iana.ProtocolReserved, + Src: net.IPv4(192, 0, 2, 1), + Dst: net.IPv4(192, 0, 2, 254), + }).Marshal() + if err != nil { + b.Fatal(err) + } + greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + bb := make([]byte, 128) + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback); ifi != nil { + cm.IfIndex = ifi.Index + } + + b.Run("UDP", func(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv4.FlagTTL | ipv4.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + wms := []ipv4.Message{ + { + Buffers: [][]byte{payload}, + Addr: dst, + OOB: cm.Marshal(), + }, + } + rms := []ipv4.Message{ + { + Buffers: [][]byte{bb}, + OOB: ipv4.NewControlMessage(cf), + }, + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(payload, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(payload, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("Batch", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteBatch(wms, 0); err != nil { + b.Fatal(err) + } + if _, err := p.ReadBatch(rms, 0); err != nil { + b.Fatal(err) + } + } + }) + }) + b.Run("IP", func(b *testing.B) { + switch runtime.GOOS { + case "netbsd": + b.Skip("need to configure gre on netbsd") + case "openbsd": + b.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv4.FlagTTL | ipv4.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + wms := []ipv4.Message{ + { + Buffers: [][]byte{datagram}, + Addr: dst, + OOB: cm.Marshal(), + }, + } + rms := []ipv4.Message{ + { + Buffers: [][]byte{bb}, + OOB: ipv4.NewControlMessage(cf), + }, + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(datagram, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(datagram, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("Batch", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteBatch(wms, 0); err != nil { + b.Fatal(err) + } + if _, err := p.ReadBatch(rms, 0); err != nil { + b.Fatal(err) + } + } + }) + }) +} + +func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph, err := (&ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TotalLen: ipv4.HeaderLen + len(payload), + TTL: 1, + Protocol: iana.ProtocolReserved, + Src: net.IPv4(192, 0, 2, 1), + Dst: net.IPv4(192, 0, 2, 254), + }).Marshal() + if err != nil { + t.Fatal(err) + } + greh := []byte{0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + + t.Run("UDP", func(t *testing.T) { + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false) + }) + t.Run("Batch", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true) + }) + }) + t.Run("IP", func(t *testing.T) { + switch runtime.GOOS { + case "netbsd": + t.Skip("need to configure gre on netbsd") + case "openbsd": + t.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip4:%d", iana.ProtocolGRE), "127.0.0.1") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false) + }) + t.Run("Batch", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true) + }) + }) +} + +func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv4.PacketConn, data []byte, dst net.Addr, batch bool) { + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + b := make([]byte, 128) + n, cm, _, err := p.ReadFrom(b) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(b[:n], data) { + t.Errorf("got %#v; want %#v", b[:n], data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + batchReader := func() { + defer wg.Done() + ms := []ipv4.Message{ + { + Buffers: [][]byte{make([]byte, 128)}, + OOB: ipv4.NewControlMessage(cf), + }, + } + n, err := p.ReadBatch(ms, 0) + if err != nil { + t.Error(err) + return + } + if n != len(ms) { + t.Errorf("got %d; want %d", n, len(ms)) + return + } + var cm ipv4.ControlMessage + if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil { + t.Error(err) + return + } + var b []byte + if _, ok := dst.(*net.IPAddr); ok { + var h ipv4.Header + if err := h.Parse(ms[0].Buffers[0][:ms[0].N]); err != nil { + t.Error(err) + return + } + b = ms[0].Buffers[0][h.Len:ms[0].N] + } else { + b = ms[0].Buffers[0][:ms[0].N] + } + if !bytes.Equal(b, data) { + t.Errorf("got %#v; want %#v", b, data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + n, err := p.WriteTo(data, &cm, dst) + if err != nil { + t.Error(err) + return + } + if n != len(data) { + t.Errorf("got %d; want %d", n, len(data)) + return + } + } + batchWriter := func(toggle bool) { + defer wg.Done() + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + ms := []ipv4.Message{ + { + Buffers: [][]byte{data}, + OOB: cm.Marshal(), + Addr: dst, + }, + } + n, err := p.WriteBatch(ms, 0) + if err != nil { + t.Error(err) + return + } + if n != len(ms) { + t.Errorf("got %d; want %d", n, len(ms)) + return + } + if ms[0].N != len(data) { + t.Errorf("got %d; want %d", ms[0].N, len(data)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + if batch { + go batchReader() + } else { + go reader() + } + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + if batch { + go batchWriter(i%2 != 0) + } else { + go writer(i%2 != 0) + } + + } + wg.Add(N) + for i := 0; i < N; i++ { + if batch { + go batchReader() + } else { + go reader() + } + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv4/readwrite_test.go b/vendor/src/golang.org/x/net/ipv4/readwrite_test.go new file mode 100644 index 00000000..3896a8ae --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/readwrite_test.go @@ -0,0 +1,140 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "bytes" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +func BenchmarkReadWriteUnicast(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + + dst := c.LocalAddr() + wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) + + b.Run("NetUDP", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(wb, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(rb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("IPv4UDP", func(b *testing.B) { + p := ipv4.NewPacketConn(c) + cf := ipv4.FlagTTL | ipv4.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + cm := ipv4.ControlMessage{TTL: 1} + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi != nil { + cm.IfIndex = ifi.Index + } + + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(wb, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(rb); err != nil { + b.Fatal(err) + } + } + }) +} + +func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + defer p.Close() + + dst := c.LocalAddr() + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + cf := ipv4.FlagTTL | ipv4.FlagSrc | ipv4.FlagDst | ipv4.FlagInterface + wb := []byte("HELLO-R-U-THERE") + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + rb := make([]byte, 128) + if n, cm, _, err := p.ReadFrom(rb); err != nil { + t.Error(err) + return + } else if !bytes.Equal(rb[:n], wb) { + t.Errorf("got %v; want %v", rb[:n], wb) + return + } else { + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + } + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv4.ControlMessage{ + Src: net.IPv4(127, 0, 0, 1), + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + t.Error(err) + return + } else if n != len(wb) { + t.Errorf("got %d; want %d", n, len(wb)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + go writer(i%2 != 0) + } + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv4/sockopt.go b/vendor/src/golang.org/x/net/ipv4/sockopt.go new file mode 100644 index 00000000..22e90c03 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sockopt.go @@ -0,0 +1,44 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import "golang.org/x/net/internal/socket" + +// Sticky socket options +const ( + ssoTOS = iota // header field for unicast packet + ssoTTL // header field for unicast packet + ssoMulticastTTL // header field for multicast packet + ssoMulticastInterface // outbound interface for multicast packet + ssoMulticastLoopback // loopback for multicast packet + ssoReceiveTTL // header field on received packet + ssoReceiveDst // header field on received packet + ssoReceiveInterface // inbound interface on received packet + ssoPacketInfo // incbound or outbound packet path + ssoHeaderPrepend // ipv4 header prepend + ssoStripHeader // strip ipv4 header + ssoICMPFilter // icmp filter + ssoJoinGroup // any-source multicast + ssoLeaveGroup // any-source multicast + ssoJoinSourceGroup // source-specific multicast + ssoLeaveSourceGroup // source-specific multicast + ssoBlockSourceGroup // any-source or source-specific multicast + ssoUnblockSourceGroup // any-source or source-specific multicast + ssoAttachFilter // attach BPF for filtering inbound traffic +) + +// Sticky socket option value types +const ( + ssoTypeIPMreq = iota + 1 + ssoTypeIPMreqn + ssoTypeGroupReq + ssoTypeGroupSourceReq +) + +// A sockOpt represents a binding for sticky socket option. +type sockOpt struct { + socket.Option + typ int // hint for option value type; optional +} diff --git a/vendor/src/golang.org/x/net/ipv4/sockopt_posix.go b/vendor/src/golang.org/x/net/ipv4/sockopt_posix.go new file mode 100644 index 00000000..e96955bc --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sockopt_posix.go @@ -0,0 +1,71 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package ipv4 + +import ( + "net" + "unsafe" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getMulticastInterface(c *socket.Conn) (*net.Interface, error) { + switch so.typ { + case ssoTypeIPMreqn: + return so.getIPMreqn(c) + default: + return so.getMulticastIf(c) + } +} + +func (so *sockOpt) setMulticastInterface(c *socket.Conn, ifi *net.Interface) error { + switch so.typ { + case ssoTypeIPMreqn: + return so.setIPMreqn(c, ifi, nil) + default: + return so.setMulticastIf(c, ifi) + } +} + +func (so *sockOpt) getICMPFilter(c *socket.Conn) (*ICMPFilter, error) { + b := make([]byte, so.Len) + n, err := so.Get(c, b) + if err != nil { + return nil, err + } + if n != sizeofICMPFilter { + return nil, errOpNoSupport + } + return (*ICMPFilter)(unsafe.Pointer(&b[0])), nil +} + +func (so *sockOpt) setICMPFilter(c *socket.Conn, f *ICMPFilter) error { + b := (*[sizeofICMPFilter]byte)(unsafe.Pointer(f))[:sizeofICMPFilter] + return so.Set(c, b) +} + +func (so *sockOpt) setGroup(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + switch so.typ { + case ssoTypeIPMreq: + return so.setIPMreq(c, ifi, grp) + case ssoTypeIPMreqn: + return so.setIPMreqn(c, ifi, grp) + case ssoTypeGroupReq: + return so.setGroupReq(c, ifi, grp) + default: + return errOpNoSupport + } +} + +func (so *sockOpt) setSourceGroup(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return so.setGroupSourceReq(c, ifi, grp, src) +} + +func (so *sockOpt) setBPF(c *socket.Conn, f []bpf.RawInstruction) error { + return so.setAttachFilter(c, f) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sockopt_stub.go b/vendor/src/golang.org/x/net/ipv4/sockopt_stub.go new file mode 100644 index 00000000..23249b78 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sockopt_stub.go @@ -0,0 +1,42 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv4 + +import ( + "net" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getMulticastInterface(c *socket.Conn) (*net.Interface, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setMulticastInterface(c *socket.Conn, ifi *net.Interface) error { + return errOpNoSupport +} + +func (so *sockOpt) getICMPFilter(c *socket.Conn) (*ICMPFilter, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setICMPFilter(c *socket.Conn, f *ICMPFilter) error { + return errOpNoSupport +} + +func (so *sockOpt) setGroup(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setSourceGroup(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setBPF(c *socket.Conn, f []bpf.RawInstruction) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_asmreq.go b/vendor/src/golang.org/x/net/ipv4/sys_asmreq.go new file mode 100644 index 00000000..0388cba0 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_asmreq.go @@ -0,0 +1,119 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd solaris windows + +package ipv4 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + mreq := ipMreq{Multiaddr: [4]byte{grp[0], grp[1], grp[2], grp[3]}} + if err := setIPMreqInterface(&mreq, ifi); err != nil { + return err + } + b := (*[sizeofIPMreq]byte)(unsafe.Pointer(&mreq))[:sizeofIPMreq] + return so.Set(c, b) +} + +func (so *sockOpt) getMulticastIf(c *socket.Conn) (*net.Interface, error) { + var b [4]byte + if _, err := so.Get(c, b[:]); err != nil { + return nil, err + } + ifi, err := netIP4ToInterface(net.IPv4(b[0], b[1], b[2], b[3])) + if err != nil { + return nil, err + } + return ifi, nil +} + +func (so *sockOpt) setMulticastIf(c *socket.Conn, ifi *net.Interface) error { + ip, err := netInterfaceToIP4(ifi) + if err != nil { + return err + } + var b [4]byte + copy(b[:], ip) + return so.Set(c, b[:]) +} + +func setIPMreqInterface(mreq *ipMreq, ifi *net.Interface) error { + if ifi == nil { + return nil + } + ifat, err := ifi.Addrs() + if err != nil { + return err + } + for _, ifa := range ifat { + switch ifa := ifa.(type) { + case *net.IPAddr: + if ip := ifa.IP.To4(); ip != nil { + copy(mreq.Interface[:], ip) + return nil + } + case *net.IPNet: + if ip := ifa.IP.To4(); ip != nil { + copy(mreq.Interface[:], ip) + return nil + } + } + } + return errNoSuchInterface +} + +func netIP4ToInterface(ip net.IP) (*net.Interface, error) { + ift, err := net.Interfaces() + if err != nil { + return nil, err + } + for _, ifi := range ift { + ifat, err := ifi.Addrs() + if err != nil { + return nil, err + } + for _, ifa := range ifat { + switch ifa := ifa.(type) { + case *net.IPAddr: + if ip.Equal(ifa.IP) { + return &ifi, nil + } + case *net.IPNet: + if ip.Equal(ifa.IP) { + return &ifi, nil + } + } + } + } + return nil, errNoSuchInterface +} + +func netInterfaceToIP4(ifi *net.Interface) (net.IP, error) { + if ifi == nil { + return net.IPv4zero.To4(), nil + } + ifat, err := ifi.Addrs() + if err != nil { + return nil, err + } + for _, ifa := range ifat { + switch ifa := ifa.(type) { + case *net.IPAddr: + if ip := ifa.IP.To4(); ip != nil { + return ip, nil + } + case *net.IPNet: + if ip := ifa.IP.To4(); ip != nil { + return ip, nil + } + } + } + return nil, errNoSuchInterface +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_asmreq_stub.go b/vendor/src/golang.org/x/net/ipv4/sys_asmreq_stub.go new file mode 100644 index 00000000..f3919208 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_asmreq_stub.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) getMulticastIf(c *socket.Conn) (*net.Interface, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setMulticastIf(c *socket.Conn, ifi *net.Interface) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_asmreqn.go b/vendor/src/golang.org/x/net/ipv4/sys_asmreqn.go new file mode 100644 index 00000000..1f24f69f --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_asmreqn.go @@ -0,0 +1,42 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux + +package ipv4 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getIPMreqn(c *socket.Conn) (*net.Interface, error) { + b := make([]byte, so.Len) + if _, err := so.Get(c, b); err != nil { + return nil, err + } + mreqn := (*ipMreqn)(unsafe.Pointer(&b[0])) + if mreqn.Ifindex == 0 { + return nil, nil + } + ifi, err := net.InterfaceByIndex(int(mreqn.Ifindex)) + if err != nil { + return nil, err + } + return ifi, nil +} + +func (so *sockOpt) setIPMreqn(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + var mreqn ipMreqn + if ifi != nil { + mreqn.Ifindex = int32(ifi.Index) + } + if grp != nil { + mreqn.Multiaddr = [4]byte{grp[0], grp[1], grp[2], grp[3]} + } + b := (*[sizeofIPMreqn]byte)(unsafe.Pointer(&mreqn))[:sizeofIPMreqn] + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_asmreqn_stub.go b/vendor/src/golang.org/x/net/ipv4/sys_asmreqn_stub.go new file mode 100644 index 00000000..0711d3d7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_asmreqn_stub.go @@ -0,0 +1,21 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!freebsd,!linux + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getIPMreqn(c *socket.Conn) (*net.Interface, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setIPMreqn(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_bpf.go b/vendor/src/golang.org/x/net/ipv4/sys_bpf.go new file mode 100644 index 00000000..9f30b730 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_bpf.go @@ -0,0 +1,23 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package ipv4 + +import ( + "unsafe" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error { + prog := sockFProg{ + Len: uint16(len(f)), + Filter: (*sockFilter)(unsafe.Pointer(&f[0])), + } + b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog] + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_bpf_stub.go b/vendor/src/golang.org/x/net/ipv4/sys_bpf_stub.go new file mode 100644 index 00000000..9a213209 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_bpf_stub.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !linux + +package ipv4 + +import ( + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_bsd.go b/vendor/src/golang.org/x/net/ipv4/sys_bsd.go new file mode 100644 index 00000000..58256dd9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_bsd.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build netbsd openbsd + +package ipv4 + +import ( + "net" + "syscall" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL}, + ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst}, + ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface}, + } + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 1}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoReceiveDst: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVDSTADDR, Len: 4}}, + ssoReceiveInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVIF, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + } +) diff --git a/vendor/src/golang.org/x/net/ipv4/sys_darwin.go b/vendor/src/golang.org/x/net/ipv4/sys_darwin.go new file mode 100644 index 00000000..e8fb1916 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_darwin.go @@ -0,0 +1,93 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "strconv" + "strings" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL}, + ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst}, + ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface}, + } + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoReceiveDst: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVDSTADDR, Len: 4}}, + ssoReceiveInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVIF, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoStripHeader: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_STRIPHDR, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + } +) + +func init() { + // Seems like kern.osreldate is veiled on latest OS X. We use + // kern.osrelease instead. + s, err := syscall.Sysctl("kern.osrelease") + if err != nil { + return + } + ss := strings.Split(s, ".") + if len(ss) == 0 { + return + } + // The IP_PKTINFO and protocol-independent multicast API were + // introduced in OS X 10.7 (Darwin 11). But it looks like + // those features require OS X 10.8 (Darwin 12) or above. + // See http://support.apple.com/kb/HT1633. + if mjver, err := strconv.Atoi(ss[0]); err != nil || mjver < 12 { + return + } + ctlOpts[ctlPacketInfo].name = sysIP_PKTINFO + ctlOpts[ctlPacketInfo].length = sizeofInetPktinfo + ctlOpts[ctlPacketInfo].marshal = marshalPacketInfo + ctlOpts[ctlPacketInfo].parse = parsePacketInfo + sockOpts[ssoPacketInfo] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVPKTINFO, Len: 4}} + sockOpts[ssoMulticastInterface] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: sizeofIPMreqn}, typ: ssoTypeIPMreqn} + sockOpts[ssoJoinGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq} + sockOpts[ssoLeaveGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq} + sockOpts[ssoJoinSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoLeaveSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoBlockSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoUnblockSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} +} + +func (pi *inetPktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 132)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_dragonfly.go b/vendor/src/golang.org/x/net/ipv4/sys_dragonfly.go new file mode 100644 index 00000000..859764f3 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_dragonfly.go @@ -0,0 +1,35 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL}, + ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst}, + ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface}, + } + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoReceiveDst: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVDSTADDR, Len: 4}}, + ssoReceiveInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVIF, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + } +) diff --git a/vendor/src/golang.org/x/net/ipv4/sys_freebsd.go b/vendor/src/golang.org/x/net/ipv4/sys_freebsd.go new file mode 100644 index 00000000..b8003245 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_freebsd.go @@ -0,0 +1,76 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "runtime" + "strings" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_RECVTTL, 1, marshalTTL, parseTTL}, + ctlDst: {sysIP_RECVDSTADDR, net.IPv4len, marshalDst, parseDst}, + ctlInterface: {sysIP_RECVIF, syscall.SizeofSockaddrDatalink, marshalInterface, parseInterface}, + } + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoReceiveDst: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVDSTADDR, Len: 4}}, + ssoReceiveInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVIF, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + } +) + +func init() { + freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate") + if freebsdVersion >= 1000000 { + sockOpts[ssoMulticastInterface] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: sizeofIPMreqn}, typ: ssoTypeIPMreqn} + } + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + archs, _ := syscall.Sysctl("kern.supported_archs") + for _, s := range strings.Fields(archs) { + if s == "amd64" { + freebsd32o64 = true + break + } + } + } +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(&gr.Group)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(&gsr.Group)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sockaddrInet)(unsafe.Pointer(&gsr.Source)) + sa.Len = sizeofSockaddrInet + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_linux.go b/vendor/src/golang.org/x/net/ipv4/sys_linux.go new file mode 100644 index 00000000..60defe13 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_linux.go @@ -0,0 +1,59 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_TTL, 1, marshalTTL, parseTTL}, + ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo}, + } + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: sizeofIPMreqn}, typ: ssoTypeIPMreqn}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoPacketInfo: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_PKTINFO, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolReserved, Name: sysICMP_FILTER, Len: sizeofICMPFilter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}}, + } +) + +func (pi *inetPktinfo) setIfindex(i int) { + pi.Ifindex = int32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(&gr.Group)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(&gsr.Group)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sockaddrInet)(unsafe.Pointer(&gsr.Source)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_solaris.go b/vendor/src/golang.org/x/net/ipv4/sys_solaris.go new file mode 100644 index 00000000..832fef1e --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_solaris.go @@ -0,0 +1,57 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTTL: {sysIP_RECVTTL, 4, marshalTTL, parseTTL}, + ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo}, + } + + sockOpts = map[int]sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 1}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 1}}, + ssoReceiveTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVTTL, Len: 4}}, + ssoPacketInfo: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_RECVPKTINFO, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + } +) + +func (pi *inetPktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], grp) + sa = (*sockaddrInet)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 260)) + sa.Family = syscall.AF_INET + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_ssmreq.go b/vendor/src/golang.org/x/net/ipv4/sys_ssmreq.go new file mode 100644 index 00000000..ae5704e7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_ssmreq.go @@ -0,0 +1,54 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux solaris + +package ipv4 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/socket" +) + +var freebsd32o64 bool + +func (so *sockOpt) setGroupReq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + var gr groupReq + if ifi != nil { + gr.Interface = uint32(ifi.Index) + } + gr.setGroup(grp) + var b []byte + if freebsd32o64 { + var d [sizeofGroupReq + 4]byte + s := (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr)) + copy(d[:4], s[:4]) + copy(d[8:], s[4:]) + b = d[:] + } else { + b = (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr))[:sizeofGroupReq] + } + return so.Set(c, b) +} + +func (so *sockOpt) setGroupSourceReq(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + var gsr groupSourceReq + if ifi != nil { + gsr.Interface = uint32(ifi.Index) + } + gsr.setSourceGroup(grp, src) + var b []byte + if freebsd32o64 { + var d [sizeofGroupSourceReq + 4]byte + s := (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr)) + copy(d[:4], s[:4]) + copy(d[8:], s[4:]) + b = d[:] + } else { + b = (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))[:sizeofGroupSourceReq] + } + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_ssmreq_stub.go b/vendor/src/golang.org/x/net/ipv4/sys_ssmreq_stub.go new file mode 100644 index 00000000..e6b7623d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_ssmreq_stub.go @@ -0,0 +1,21 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!freebsd,!linux,!solaris + +package ipv4 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setGroupReq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setGroupSourceReq(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv4/sys_stub.go b/vendor/src/golang.org/x/net/ipv4/sys_stub.go new file mode 100644 index 00000000..4f076473 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_stub.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv4 + +var ( + ctlOpts = [ctlMax]ctlOpt{} + + sockOpts = map[int]*sockOpt{} +) diff --git a/vendor/src/golang.org/x/net/ipv4/sys_windows.go b/vendor/src/golang.org/x/net/ipv4/sys_windows.go new file mode 100644 index 00000000..b0913d53 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/sys_windows.go @@ -0,0 +1,67 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4 + +import ( + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +const ( + // See ws2tcpip.h. + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + sysIP_DONTFRAGMENT = 0xe + sysIP_ADD_SOURCE_MEMBERSHIP = 0xf + sysIP_DROP_SOURCE_MEMBERSHIP = 0x10 + sysIP_PKTINFO = 0x13 + + sizeofInetPktinfo = 0x8 + sizeofIPMreq = 0x8 + sizeofIPMreqSource = 0xc +) + +type inetPktinfo struct { + Addr [4]byte + Ifindex int32 +} + +type ipMreq struct { + Multiaddr [4]byte + Interface [4]byte +} + +type ipMreqSource struct { + Multiaddr [4]byte + Sourceaddr [4]byte + Interface [4]byte +} + +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx +var ( + ctlOpts = [ctlMax]ctlOpt{} + + sockOpts = map[int]*sockOpt{ + ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, + ssoTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TTL, Len: 4}}, + ssoMulticastTTL: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_TTL, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_IF, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_MULTICAST_LOOP, Len: 4}}, + ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + } +) + +func (pi *inetPktinfo) setIfindex(i int) { + pi.Ifindex = int32(i) +} diff --git a/vendor/src/golang.org/x/net/ipv4/unicast_test.go b/vendor/src/golang.org/x/net/ipv4/unicast_test.go new file mode 100644 index 00000000..02c089f0 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/unicast_test.go @@ -0,0 +1,247 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "bytes" + "net" + "os" + "runtime" + "testing" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +func TestPacketConnReadWriteUnicastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + c, err := nettest.NewLocalPacketListener("udp4") + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv4.NewPacketConn(c) + defer p.Close() + + dst := c.LocalAddr() + cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface + wb := []byte("HELLO-R-U-THERE") + + for i, toggle := range []bool{true, false, true} { + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + p.SetTTL(i + 1) + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, err := p.WriteTo(wb, nil, dst); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, _, _, err := p.ReadFrom(rb); err != nil { + t.Fatal(err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; want %v", rb[:n], wb) + } + } +} + +func TestPacketConnReadWriteUnicastICMP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") + if err != nil { + t.Fatal(err) + } + p := ipv4.NewPacketConn(c) + defer p.Close() + cf := ipv4.FlagDst | ipv4.FlagInterface + if runtime.GOOS != "solaris" { + // Solaris never allows to modify ICMP properties. + cf |= ipv4.FlagTTL + } + + for i, toggle := range []bool{true, false, true} { + wb, err := (&icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(nil) + if err != nil { + t.Fatal(err) + } + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + p.SetTTL(i + 1) + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, err := p.WriteTo(wb, nil, dst); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + loop: + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, _, _, err := p.ReadFrom(rb); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } else { + m, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) + if err != nil { + t.Fatal(err) + } + if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { + // On Linux we must handle own sent packets. + goto loop + } + if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) + } + } + } +} + +func TestRawConnReadWriteUnicastICMP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + dst, err := net.ResolveIPAddr("ip4", "127.0.0.1") + if err != nil { + t.Fatal(err) + } + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + defer r.Close() + cf := ipv4.FlagTTL | ipv4.FlagDst | ipv4.FlagInterface + + for i, toggle := range []bool{true, false, true} { + wb, err := (&icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(nil) + if err != nil { + t.Fatal(err) + } + wh := &ipv4.Header{ + Version: ipv4.Version, + Len: ipv4.HeaderLen, + TOS: i + 1, + TotalLen: ipv4.HeaderLen + len(wb), + TTL: i + 1, + Protocol: 1, + Dst: dst.IP, + } + if err := r.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := r.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if err := r.WriteTo(wh, wb, nil); err != nil { + t.Fatal(err) + } + rb := make([]byte, ipv4.HeaderLen+128) + loop: + if err := r.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if _, b, _, err := r.ReadFrom(rb); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } else { + m, err := icmp.ParseMessage(iana.ProtocolICMP, b) + if err != nil { + t.Fatal(err) + } + if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho { + // On Linux we must handle own sent packets. + goto loop + } + if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 { + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0) + } + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/unicastsockopt_test.go b/vendor/src/golang.org/x/net/ipv4/unicastsockopt_test.go new file mode 100644 index 00000000..db5213b9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/unicastsockopt_test.go @@ -0,0 +1,148 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv4_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv4" +) + +func TestConnUnicastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + ln, err := net.Listen("tcp4", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func() { + c, err := ln.Accept() + if err != nil { + errc <- err + return + } + errc <- c.Close() + }() + + c, err := net.Dial("tcp4", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + testUnicastSocketOptions(t, ipv4.NewConn(c)) + + if err := <-errc; err != nil { + t.Errorf("server: %v", err) + } +} + +var packetConnUnicastSocketOptionTests = []struct { + net, proto, addr string +}{ + {"udp4", "", "127.0.0.1:0"}, + {"ip4", ":icmp", "127.0.0.1"}, +} + +func TestPacketConnUnicastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + m, ok := nettest.SupportsRawIPSocket() + for _, tt := range packetConnUnicastSocketOptionTests { + if tt.net == "ip4" && !ok { + t.Log(m) + continue + } + c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + testUnicastSocketOptions(t, ipv4.NewPacketConn(c)) + } +} + +func TestRawConnUnicastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + c, err := net.ListenPacket("ip4:icmp", "127.0.0.1") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + r, err := ipv4.NewRawConn(c) + if err != nil { + t.Fatal(err) + } + + testUnicastSocketOptions(t, r) +} + +type testIPv4UnicastConn interface { + TOS() (int, error) + SetTOS(int) error + TTL() (int, error) + SetTTL(int) error +} + +func testUnicastSocketOptions(t *testing.T, c testIPv4UnicastConn) { + tos := iana.DiffServCS0 | iana.NotECNTransport + switch runtime.GOOS { + case "windows": + // IP_TOS option is supported on Windows 8 and beyond. + t.Skipf("not supported on %s", runtime.GOOS) + } + + if err := c.SetTOS(tos); err != nil { + t.Fatal(err) + } + if v, err := c.TOS(); err != nil { + t.Fatal(err) + } else if v != tos { + t.Fatalf("got %v; want %v", v, tos) + } + const ttl = 255 + if err := c.SetTTL(ttl); err != nil { + t.Fatal(err) + } + if v, err := c.TTL(); err != nil { + t.Fatal(err) + } else if v != ttl { + t.Fatalf("got %v; want %v", v, ttl) + } +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_darwin.go b/vendor/src/golang.org/x/net/ipv4/zsys_darwin.go new file mode 100644 index 00000000..c07cc883 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_darwin.go @@ -0,0 +1,99 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_STRIPHDR = 0x17 + sysIP_RECVTTL = 0x18 + sysIP_BOUND_IF = 0x19 + sysIP_PKTINFO = 0x1a + sysIP_RECVPKTINFO = 0x1a + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + sysIP_MULTICAST_VIF = 0xe + sysIP_MULTICAST_IFINDEX = 0x42 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x46 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x47 + sysIP_BLOCK_SOURCE = 0x48 + sysIP_UNBLOCK_SOURCE = 0x49 + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type inetPktinfo struct { + Ifindex uint32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [128]byte +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [128]byte + Pad_cgo_1 [128]byte +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_dragonfly.go b/vendor/src/golang.org/x/net/ipv4/zsys_dragonfly.go new file mode 100644 index 00000000..c4365e9e --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_dragonfly.go @@ -0,0 +1,31 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_dragonfly.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_RECVTTL = 0x41 + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_MULTICAST_VIF = 0xe + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + + sizeofIPMreq = 0x8 +) + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_386.go b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_386.go new file mode 100644 index 00000000..8c4aec94 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_386.go @@ -0,0 +1,93 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_SENDSRCADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_ONESBCAST = 0x17 + sysIP_BINDANY = 0x18 + sysIP_RECVTTL = 0x41 + sysIP_MINTTL = 0x42 + sysIP_DONTFRAG = 0x43 + sysIP_RECVTOS = 0x44 + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + sysIP_MULTICAST_VIF = 0xe + sysIP_ADD_SOURCE_MEMBERSHIP = 0x46 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x47 + sysIP_BLOCK_SOURCE = 0x48 + sysIP_UNBLOCK_SOURCE = 0x49 + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group sockaddrStorage + Source sockaddrStorage +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_amd64.go b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_amd64.go new file mode 100644 index 00000000..4b10b7c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_amd64.go @@ -0,0 +1,95 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_SENDSRCADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_ONESBCAST = 0x17 + sysIP_BINDANY = 0x18 + sysIP_RECVTTL = 0x41 + sysIP_MINTTL = 0x42 + sysIP_DONTFRAG = 0x43 + sysIP_RECVTOS = 0x44 + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + sysIP_MULTICAST_VIF = 0xe + sysIP_ADD_SOURCE_MEMBERSHIP = 0x46 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x47 + sysIP_BLOCK_SOURCE = 0x48 + sysIP_UNBLOCK_SOURCE = 0x49 + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage + Source sockaddrStorage +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_arm.go b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_arm.go new file mode 100644 index 00000000..4b10b7c5 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_freebsd_arm.go @@ -0,0 +1,95 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_SENDSRCADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_ONESBCAST = 0x17 + sysIP_BINDANY = 0x18 + sysIP_RECVTTL = 0x41 + sysIP_MINTTL = 0x42 + sysIP_DONTFRAG = 0x43 + sysIP_RECVTOS = 0x44 + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + sysIP_MULTICAST_VIF = 0xe + sysIP_ADD_SOURCE_MEMBERSHIP = 0x46 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x47 + sysIP_BLOCK_SOURCE = 0x48 + sysIP_UNBLOCK_SOURCE = 0x49 + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage + Source sockaddrStorage +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_386.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_386.go new file mode 100644 index 00000000..c0260f0c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_386.go @@ -0,0 +1,148 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_amd64.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_amd64.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_amd64.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm.go new file mode 100644 index 00000000..c0260f0c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm.go @@ -0,0 +1,148 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm64.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm64.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_arm64.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips.go new file mode 100644 index 00000000..c0260f0c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips.go @@ -0,0 +1,148 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64le.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64le.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mips64le.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go new file mode 100644 index 00000000..c0260f0c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_mipsle.go @@ -0,0 +1,148 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc.go new file mode 100644 index 00000000..f65bd9a7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc.go @@ -0,0 +1,148 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]uint8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64le.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64le.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_ppc64le.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_linux_s390x.go b/vendor/src/golang.org/x/net/ipv4/zsys_linux_s390x.go new file mode 100644 index 00000000..9c967eaa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_linux_s390x.go @@ -0,0 +1,150 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv4 + +const ( + sysIP_TOS = 0x1 + sysIP_TTL = 0x2 + sysIP_HDRINCL = 0x3 + sysIP_OPTIONS = 0x4 + sysIP_ROUTER_ALERT = 0x5 + sysIP_RECVOPTS = 0x6 + sysIP_RETOPTS = 0x7 + sysIP_PKTINFO = 0x8 + sysIP_PKTOPTIONS = 0x9 + sysIP_MTU_DISCOVER = 0xa + sysIP_RECVERR = 0xb + sysIP_RECVTTL = 0xc + sysIP_RECVTOS = 0xd + sysIP_MTU = 0xe + sysIP_FREEBIND = 0xf + sysIP_TRANSPARENT = 0x13 + sysIP_RECVRETOPTS = 0x7 + sysIP_ORIGDSTADDR = 0x14 + sysIP_RECVORIGDSTADDR = 0x14 + sysIP_MINTTL = 0x15 + sysIP_NODEFRAG = 0x16 + sysIP_UNICAST_IF = 0x32 + + sysIP_MULTICAST_IF = 0x20 + sysIP_MULTICAST_TTL = 0x21 + sysIP_MULTICAST_LOOP = 0x22 + sysIP_ADD_MEMBERSHIP = 0x23 + sysIP_DROP_MEMBERSHIP = 0x24 + sysIP_UNBLOCK_SOURCE = 0x25 + sysIP_BLOCK_SOURCE = 0x26 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x27 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x28 + sysIP_MSFILTER = 0x29 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIP_MULTICAST_ALL = 0x31 + + sysICMP_FILTER = 0x1 + + sysSO_EE_ORIGIN_NONE = 0x0 + sysSO_EE_ORIGIN_LOCAL = 0x1 + sysSO_EE_ORIGIN_ICMP = 0x2 + sysSO_EE_ORIGIN_ICMP6 = 0x3 + sysSO_EE_ORIGIN_TXSTATUS = 0x4 + sysSO_EE_ORIGIN_TIMESTAMPING = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + sizeofSockExtendedErr = 0x10 + + sizeofIPMreq = 0x8 + sizeofIPMreqn = 0xc + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPFilter = 0x4 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + X__pad [8]uint8 +} + +type inetPktinfo struct { + Ifindex int32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type sockExtendedErr struct { + Errno uint32 + Origin uint8 + Type uint8 + Code uint8 + Pad uint8 + Info uint32 + Data uint32 +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqn struct { + Multiaddr [4]byte /* in_addr */ + Address [4]byte /* in_addr */ + Ifindex int32 +} + +type ipMreqSource struct { + Multiaddr uint32 + Interface uint32 + Sourceaddr uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpFilter struct { + Data uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_netbsd.go b/vendor/src/golang.org/x/net/ipv4/zsys_netbsd.go new file mode 100644 index 00000000..fd3624d9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_netbsd.go @@ -0,0 +1,30 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x14 + sysIP_RECVTTL = 0x17 + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + + sizeofIPMreq = 0x8 +) + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_openbsd.go b/vendor/src/golang.org/x/net/ipv4/zsys_openbsd.go new file mode 100644 index 00000000..12f36be7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_openbsd.go @@ -0,0 +1,30 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x1e + sysIP_RECVTTL = 0x1f + + sysIP_MULTICAST_IF = 0x9 + sysIP_MULTICAST_TTL = 0xa + sysIP_MULTICAST_LOOP = 0xb + sysIP_ADD_MEMBERSHIP = 0xc + sysIP_DROP_MEMBERSHIP = 0xd + + sizeofIPMreq = 0x8 +) + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} diff --git a/vendor/src/golang.org/x/net/ipv4/zsys_solaris.go b/vendor/src/golang.org/x/net/ipv4/zsys_solaris.go new file mode 100644 index 00000000..0a3875cc --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv4/zsys_solaris.go @@ -0,0 +1,100 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_solaris.go + +package ipv4 + +const ( + sysIP_OPTIONS = 0x1 + sysIP_HDRINCL = 0x2 + sysIP_TOS = 0x3 + sysIP_TTL = 0x4 + sysIP_RECVOPTS = 0x5 + sysIP_RECVRETOPTS = 0x6 + sysIP_RECVDSTADDR = 0x7 + sysIP_RETOPTS = 0x8 + sysIP_RECVIF = 0x9 + sysIP_RECVSLLA = 0xa + sysIP_RECVTTL = 0xb + + sysIP_MULTICAST_IF = 0x10 + sysIP_MULTICAST_TTL = 0x11 + sysIP_MULTICAST_LOOP = 0x12 + sysIP_ADD_MEMBERSHIP = 0x13 + sysIP_DROP_MEMBERSHIP = 0x14 + sysIP_BLOCK_SOURCE = 0x15 + sysIP_UNBLOCK_SOURCE = 0x16 + sysIP_ADD_SOURCE_MEMBERSHIP = 0x17 + sysIP_DROP_SOURCE_MEMBERSHIP = 0x18 + sysIP_NEXTHOP = 0x19 + + sysIP_PKTINFO = 0x1a + sysIP_RECVPKTINFO = 0x1a + sysIP_DONTFRAG = 0x1b + + sysIP_BOUND_IF = 0x41 + sysIP_UNSPEC_SRC = 0x42 + sysIP_BROADCAST_TTL = 0x43 + sysIP_DHCPINIT_IF = 0x45 + + sysIP_REUSEADDR = 0x104 + sysIP_DONTROUTE = 0x105 + sysIP_BROADCAST = 0x106 + + sysMCAST_JOIN_GROUP = 0x29 + sysMCAST_LEAVE_GROUP = 0x2a + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_JOIN_SOURCE_GROUP = 0x2d + sysMCAST_LEAVE_SOURCE_GROUP = 0x2e + + sizeofSockaddrStorage = 0x100 + sizeofSockaddrInet = 0x10 + sizeofInetPktinfo = 0xc + + sizeofIPMreq = 0x8 + sizeofIPMreqSource = 0xc + sizeofGroupReq = 0x104 + sizeofGroupSourceReq = 0x204 +) + +type sockaddrStorage struct { + Family uint16 + X_ss_pad1 [6]int8 + X_ss_align float64 + X_ss_pad2 [240]int8 +} + +type sockaddrInet struct { + Family uint16 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type inetPktinfo struct { + Ifindex uint32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type ipMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type ipMreqSource struct { + Multiaddr [4]byte /* in_addr */ + Sourceaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [256]byte +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [256]byte + Pad_cgo_1 [256]byte +} diff --git a/vendor/src/golang.org/x/net/ipv6/batch.go b/vendor/src/golang.org/x/net/ipv6/batch.go new file mode 100644 index 00000000..4f5fe683 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/batch.go @@ -0,0 +1,119 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package ipv6 + +import ( + "net" + "runtime" + "syscall" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the ReadBatch and WriteBatch methods of +// PacketConn are not implemented. + +// A Message represents an IO message. +// +// type Message struct { +// Buffers [][]byte +// OOB []byte +// Addr net.Addr +// N int +// NN int +// Flags int +// } +// +// The Buffers fields represents a list of contiguous buffers, which +// can be used for vectored IO, for example, putting a header and a +// payload in each slice. +// When writing, the Buffers field must contain at least one byte to +// write. +// When reading, the Buffers field will always contain a byte to read. +// +// The OOB field contains protocol-specific control or miscellaneous +// ancillary data known as out-of-band data. +// It can be nil when not required. +// +// The Addr field specifies a destination address when writing. +// It can be nil when the underlying protocol of the endpoint uses +// connection-oriented communication. +// After a successful read, it may contain the source address on the +// received packet. +// +// The N field indicates the number of bytes read or written from/to +// Buffers. +// +// The NN field indicates the number of bytes read or written from/to +// OOB. +// +// The Flags field contains protocol-specific information on the +// received message. +type Message = socket.Message + +// ReadBatch reads a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_PEEK. +// +// On a successful read it returns the number of messages received, up +// to len(ms). +// +// On Linux, a batch read will be optimized. +// On other platforms, this method will read only a single message. +func (c *payloadHandler) ReadBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.RecvMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.RecvMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + } +} + +// WriteBatch writes a batch of messages. +// +// The provided flags is a set of platform-dependent flags, such as +// syscall.MSG_DONTROUTE. +// +// It returns the number of messages written on a successful write. +// +// On Linux, a batch write will be optimized. +// On other platforms, this method will write only a single message. +func (c *payloadHandler) WriteBatch(ms []Message, flags int) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + switch runtime.GOOS { + case "linux": + n, err := c.SendMsgs([]socket.Message(ms), flags) + if err != nil { + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + default: + n := 1 + err := c.SendMsg(&ms[0], flags) + if err != nil { + n = 0 + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + return n, err + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/bpf_test.go b/vendor/src/golang.org/x/net/ipv6/bpf_test.go new file mode 100644 index 00000000..8253e1f4 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/bpf_test.go @@ -0,0 +1,96 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "runtime" + "testing" + "time" + + "golang.org/x/net/bpf" + "golang.org/x/net/ipv6" +) + +func TestBPF(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + l, err := net.ListenPacket("udp6", "[::1]:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + + p := ipv6.NewPacketConn(l) + + // This filter accepts UDP packets whose first payload byte is + // even. + prog, err := bpf.Assemble([]bpf.Instruction{ + // Load the first byte of the payload (skipping UDP header). + bpf.LoadAbsolute{Off: 8, Size: 1}, + // Select LSB of the byte. + bpf.ALUOpConstant{Op: bpf.ALUOpAnd, Val: 1}, + // Byte is even? + bpf.JumpIf{Cond: bpf.JumpEqual, Val: 0, SkipFalse: 1}, + // Accept. + bpf.RetConstant{Val: 4096}, + // Ignore. + bpf.RetConstant{Val: 0}, + }) + if err != nil { + t.Fatalf("compiling BPF: %s", err) + } + + if err = p.SetBPF(prog); err != nil { + t.Fatalf("attaching filter to Conn: %s", err) + } + + s, err := net.Dial("udp6", l.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + defer s.Close() + go func() { + for i := byte(0); i < 10; i++ { + s.Write([]byte{i}) + } + }() + + l.SetDeadline(time.Now().Add(2 * time.Second)) + seen := make([]bool, 5) + for { + var b [512]byte + n, _, err := l.ReadFrom(b[:]) + if err != nil { + t.Fatalf("reading from listener: %s", err) + } + if n != 1 { + t.Fatalf("unexpected packet length, want 1, got %d", n) + } + if b[0] >= 10 { + t.Fatalf("unexpected byte, want 0-9, got %d", b[0]) + } + if b[0]%2 != 0 { + t.Fatalf("got odd byte %d, wanted only even bytes", b[0]) + } + seen[b[0]/2] = true + + seenAll := true + for _, v := range seen { + if !v { + seenAll = false + break + } + } + if seenAll { + break + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/control.go b/vendor/src/golang.org/x/net/ipv6/control.go new file mode 100644 index 00000000..2da64441 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control.go @@ -0,0 +1,187 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "fmt" + "net" + "sync" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +// Note that RFC 3542 obsoletes RFC 2292 but OS X Snow Leopard and the +// former still support RFC 2292 only. Please be aware that almost +// all protocol implementations prohibit using a combination of RFC +// 2292 and RFC 3542 for some practical reasons. + +type rawOpt struct { + sync.RWMutex + cflags ControlFlags +} + +func (c *rawOpt) set(f ControlFlags) { c.cflags |= f } +func (c *rawOpt) clear(f ControlFlags) { c.cflags &^= f } +func (c *rawOpt) isset(f ControlFlags) bool { return c.cflags&f != 0 } + +// A ControlFlags represents per packet basis IP-level socket option +// control flags. +type ControlFlags uint + +const ( + FlagTrafficClass ControlFlags = 1 << iota // pass the traffic class on the received packet + FlagHopLimit // pass the hop limit on the received packet + FlagSrc // pass the source address on the received packet + FlagDst // pass the destination address on the received packet + FlagInterface // pass the interface index on the received packet + FlagPathMTU // pass the path MTU on the received packet path +) + +const flagPacketInfo = FlagDst | FlagInterface + +// A ControlMessage represents per packet basis IP-level socket +// options. +type ControlMessage struct { + // Receiving socket options: SetControlMessage allows to + // receive the options from the protocol stack using ReadFrom + // method of PacketConn. + // + // Specifying socket options: ControlMessage for WriteTo + // method of PacketConn allows to send the options to the + // protocol stack. + // + TrafficClass int // traffic class, must be 1 <= value <= 255 when specifying + HopLimit int // hop limit, must be 1 <= value <= 255 when specifying + Src net.IP // source address, specifying only + Dst net.IP // destination address, receiving only + IfIndex int // interface index, must be 1 <= value when specifying + NextHop net.IP // next hop address, specifying only + MTU int // path MTU, receiving only +} + +func (cm *ControlMessage) String() string { + if cm == nil { + return "" + } + return fmt.Sprintf("tclass=%#x hoplim=%d src=%v dst=%v ifindex=%d nexthop=%v mtu=%d", cm.TrafficClass, cm.HopLimit, cm.Src, cm.Dst, cm.IfIndex, cm.NextHop, cm.MTU) +} + +// Marshal returns the binary encoding of cm. +func (cm *ControlMessage) Marshal() []byte { + if cm == nil { + return nil + } + var l int + tclass := false + if ctlOpts[ctlTrafficClass].name > 0 && cm.TrafficClass > 0 { + tclass = true + l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length) + } + hoplimit := false + if ctlOpts[ctlHopLimit].name > 0 && cm.HopLimit > 0 { + hoplimit = true + l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length) + } + pktinfo := false + if ctlOpts[ctlPacketInfo].name > 0 && (cm.Src.To16() != nil && cm.Src.To4() == nil || cm.IfIndex > 0) { + pktinfo = true + l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length) + } + nexthop := false + if ctlOpts[ctlNextHop].name > 0 && cm.NextHop.To16() != nil && cm.NextHop.To4() == nil { + nexthop = true + l += socket.ControlMessageSpace(ctlOpts[ctlNextHop].length) + } + var b []byte + if l > 0 { + b = make([]byte, l) + bb := b + if tclass { + bb = ctlOpts[ctlTrafficClass].marshal(bb, cm) + } + if hoplimit { + bb = ctlOpts[ctlHopLimit].marshal(bb, cm) + } + if pktinfo { + bb = ctlOpts[ctlPacketInfo].marshal(bb, cm) + } + if nexthop { + bb = ctlOpts[ctlNextHop].marshal(bb, cm) + } + } + return b +} + +// Parse parses b as a control message and stores the result in cm. +func (cm *ControlMessage) Parse(b []byte) error { + ms, err := socket.ControlMessage(b).Parse() + if err != nil { + return err + } + for _, m := range ms { + lvl, typ, l, err := m.ParseHeader() + if err != nil { + return err + } + if lvl != iana.ProtocolIPv6 { + continue + } + switch { + case typ == ctlOpts[ctlTrafficClass].name && l >= ctlOpts[ctlTrafficClass].length: + ctlOpts[ctlTrafficClass].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlHopLimit].name && l >= ctlOpts[ctlHopLimit].length: + ctlOpts[ctlHopLimit].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlPacketInfo].name && l >= ctlOpts[ctlPacketInfo].length: + ctlOpts[ctlPacketInfo].parse(cm, m.Data(l)) + case typ == ctlOpts[ctlPathMTU].name && l >= ctlOpts[ctlPathMTU].length: + ctlOpts[ctlPathMTU].parse(cm, m.Data(l)) + } + } + return nil +} + +// NewControlMessage returns a new control message. +// +// The returned message is large enough for options specified by cf. +func NewControlMessage(cf ControlFlags) []byte { + opt := rawOpt{cflags: cf} + var l int + if opt.isset(FlagTrafficClass) && ctlOpts[ctlTrafficClass].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlTrafficClass].length) + } + if opt.isset(FlagHopLimit) && ctlOpts[ctlHopLimit].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlHopLimit].length) + } + if opt.isset(flagPacketInfo) && ctlOpts[ctlPacketInfo].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlPacketInfo].length) + } + if opt.isset(FlagPathMTU) && ctlOpts[ctlPathMTU].name > 0 { + l += socket.ControlMessageSpace(ctlOpts[ctlPathMTU].length) + } + var b []byte + if l > 0 { + b = make([]byte, l) + } + return b +} + +// Ancillary data socket options +const ( + ctlTrafficClass = iota // header field + ctlHopLimit // header field + ctlPacketInfo // inbound or outbound packet path + ctlNextHop // nexthop + ctlPathMTU // path mtu + ctlMax +) + +// A ctlOpt represents a binding for ancillary data socket option. +type ctlOpt struct { + name int // option name, must be equal or greater than 1 + length int // option length + marshal func([]byte, *ControlMessage) []byte + parse func(*ControlMessage, []byte) +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_rfc2292_unix.go b/vendor/src/golang.org/x/net/ipv6/control_rfc2292_unix.go new file mode 100644 index 00000000..9fd9eb15 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_rfc2292_unix.go @@ -0,0 +1,48 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package ipv6 + +import ( + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +func marshal2292HopLimit(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292HOPLIMIT, 4) + if cm != nil { + socket.NativeEndian.PutUint32(m.Data(4), uint32(cm.HopLimit)) + } + return m.Next(4) +} + +func marshal2292PacketInfo(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292PKTINFO, sizeofInet6Pktinfo) + if cm != nil { + pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0])) + if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { + copy(pi.Addr[:], ip) + } + if cm.IfIndex > 0 { + pi.setIfindex(cm.IfIndex) + } + } + return m.Next(sizeofInet6Pktinfo) +} + +func marshal2292NextHop(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_2292NEXTHOP, sizeofSockaddrInet6) + if cm != nil { + sa := (*sockaddrInet6)(unsafe.Pointer(&m.Data(sizeofSockaddrInet6)[0])) + sa.setSockaddr(cm.NextHop, cm.IfIndex) + } + return m.Next(sizeofSockaddrInet6) +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_rfc3542_unix.go b/vendor/src/golang.org/x/net/ipv6/control_rfc3542_unix.go new file mode 100644 index 00000000..eec529c2 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_rfc3542_unix.go @@ -0,0 +1,94 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package ipv6 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +func marshalTrafficClass(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_TCLASS, 4) + if cm != nil { + socket.NativeEndian.PutUint32(m.Data(4), uint32(cm.TrafficClass)) + } + return m.Next(4) +} + +func parseTrafficClass(cm *ControlMessage, b []byte) { + cm.TrafficClass = int(socket.NativeEndian.Uint32(b[:4])) +} + +func marshalHopLimit(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_HOPLIMIT, 4) + if cm != nil { + socket.NativeEndian.PutUint32(m.Data(4), uint32(cm.HopLimit)) + } + return m.Next(4) +} + +func parseHopLimit(cm *ControlMessage, b []byte) { + cm.HopLimit = int(socket.NativeEndian.Uint32(b[:4])) +} + +func marshalPacketInfo(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_PKTINFO, sizeofInet6Pktinfo) + if cm != nil { + pi := (*inet6Pktinfo)(unsafe.Pointer(&m.Data(sizeofInet6Pktinfo)[0])) + if ip := cm.Src.To16(); ip != nil && ip.To4() == nil { + copy(pi.Addr[:], ip) + } + if cm.IfIndex > 0 { + pi.setIfindex(cm.IfIndex) + } + } + return m.Next(sizeofInet6Pktinfo) +} + +func parsePacketInfo(cm *ControlMessage, b []byte) { + pi := (*inet6Pktinfo)(unsafe.Pointer(&b[0])) + if len(cm.Dst) < net.IPv6len { + cm.Dst = make(net.IP, net.IPv6len) + } + copy(cm.Dst, pi.Addr[:]) + cm.IfIndex = int(pi.Ifindex) +} + +func marshalNextHop(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_NEXTHOP, sizeofSockaddrInet6) + if cm != nil { + sa := (*sockaddrInet6)(unsafe.Pointer(&m.Data(sizeofSockaddrInet6)[0])) + sa.setSockaddr(cm.NextHop, cm.IfIndex) + } + return m.Next(sizeofSockaddrInet6) +} + +func parseNextHop(cm *ControlMessage, b []byte) { +} + +func marshalPathMTU(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIPv6, sysIPV6_PATHMTU, sizeofIPv6Mtuinfo) + return m.Next(sizeofIPv6Mtuinfo) +} + +func parsePathMTU(cm *ControlMessage, b []byte) { + mi := (*ipv6Mtuinfo)(unsafe.Pointer(&b[0])) + if len(cm.Dst) < net.IPv6len { + cm.Dst = make(net.IP, net.IPv6len) + } + copy(cm.Dst, mi.Addr.Addr[:]) + cm.IfIndex = int(mi.Addr.Scope_id) + cm.MTU = int(mi.Mtu) +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_stub.go b/vendor/src/golang.org/x/net/ipv6/control_stub.go new file mode 100644 index 00000000..a045f28f --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_stub.go @@ -0,0 +1,13 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv6 + +import "golang.org/x/net/internal/socket" + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_test.go b/vendor/src/golang.org/x/net/ipv6/control_test.go new file mode 100644 index 00000000..c186ca99 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_test.go @@ -0,0 +1,21 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "testing" + + "golang.org/x/net/ipv6" +) + +func TestControlMessageParseWithFuzz(t *testing.T) { + var cm ipv6.ControlMessage + for _, fuzz := range []string{ + "\f\x00\x00\x00)\x00\x00\x00.\x00\x00\x00", + "\f\x00\x00\x00)\x00\x00\x00,\x00\x00\x00", + } { + cm.Parse([]byte(fuzz)) + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_unix.go b/vendor/src/golang.org/x/net/ipv6/control_unix.go new file mode 100644 index 00000000..66515060 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_unix.go @@ -0,0 +1,55 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package ipv6 + +import "golang.org/x/net/internal/socket" + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + opt.Lock() + defer opt.Unlock() + if so, ok := sockOpts[ssoReceiveTrafficClass]; ok && cf&FlagTrafficClass != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagTrafficClass) + } else { + opt.clear(FlagTrafficClass) + } + } + if so, ok := sockOpts[ssoReceiveHopLimit]; ok && cf&FlagHopLimit != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagHopLimit) + } else { + opt.clear(FlagHopLimit) + } + } + if so, ok := sockOpts[ssoReceivePacketInfo]; ok && cf&flagPacketInfo != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(cf & flagPacketInfo) + } else { + opt.clear(cf & flagPacketInfo) + } + } + if so, ok := sockOpts[ssoReceivePathMTU]; ok && cf&FlagPathMTU != 0 { + if err := so.SetInt(c, boolint(on)); err != nil { + return err + } + if on { + opt.set(FlagPathMTU) + } else { + opt.clear(FlagPathMTU) + } + } + return nil +} diff --git a/vendor/src/golang.org/x/net/ipv6/control_windows.go b/vendor/src/golang.org/x/net/ipv6/control_windows.go new file mode 100644 index 00000000..ef2563b3 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/control_windows.go @@ -0,0 +1,16 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "syscall" + + "golang.org/x/net/internal/socket" +) + +func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error { + // TODO(mikio): implement this + return syscall.EWINDOWS +} diff --git a/vendor/src/golang.org/x/net/ipv6/defs_darwin.go b/vendor/src/golang.org/x/net/ipv6/defs_darwin.go new file mode 100644 index 00000000..55ddc116 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_darwin.go @@ -0,0 +1,112 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#define __APPLE_USE_RFC_3542 +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + + sysIPV6_PORTRANGE = C.IPV6_PORTRANGE + sysICMP6_FILTER = C.ICMP6_FILTER + sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO + sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT + sysIPV6_2292NEXTHOP = C.IPV6_2292NEXTHOP + sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS + sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS + sysIPV6_2292RTHDR = C.IPV6_2292RTHDR + + sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY + + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + sysIPV6_TCLASS = C.IPV6_TCLASS + + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + + sysIPV6_PATHMTU = C.IPV6_PATHMTU + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RTHDR = C.IPV6_RTHDR + + sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL + + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + + sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR + + sysIPV6_MSFILTER = C.IPV6_MSFILTER + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + + sysIPV6_BOUND_IF = C.IPV6_BOUND_IF + + sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT + sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH + sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type icmpv6Filter C.struct_icmp6_filter + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req diff --git a/vendor/src/golang.org/x/net/ipv6/defs_dragonfly.go b/vendor/src/golang.org/x/net/ipv6/defs_dragonfly.go new file mode 100644 index 00000000..a4c383a5 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_dragonfly.go @@ -0,0 +1,84 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include +#include + +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + sysIPV6_PORTRANGE = C.IPV6_PORTRANGE + sysICMP6_FILTER = C.ICMP6_FILTER + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY + + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + + sysIPV6_PATHMTU = C.IPV6_PATHMTU + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RTHDR = C.IPV6_RTHDR + + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + + sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL + + sysIPV6_TCLASS = C.IPV6_TCLASS + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + + sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR + + sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT + sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH + sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW + + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type icmpv6Filter C.struct_icmp6_filter diff --git a/vendor/src/golang.org/x/net/ipv6/defs_freebsd.go b/vendor/src/golang.org/x/net/ipv6/defs_freebsd.go new file mode 100644 index 00000000..53e62538 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_freebsd.go @@ -0,0 +1,105 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include +#include + +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + sysIPV6_PORTRANGE = C.IPV6_PORTRANGE + sysICMP6_FILTER = C.ICMP6_FILTER + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY + + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + + sysIPV6_PATHMTU = C.IPV6_PATHMTU + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RTHDR = C.IPV6_RTHDR + + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + + sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL + + sysIPV6_TCLASS = C.IPV6_TCLASS + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + + sysIPV6_PREFER_TEMPADDR = C.IPV6_PREFER_TEMPADDR + + sysIPV6_BINDANY = C.IPV6_BINDANY + + sysIPV6_MSFILTER = C.IPV6_MSFILTER + + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + + sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT + sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH + sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req + +type icmpv6Filter C.struct_icmp6_filter diff --git a/vendor/src/golang.org/x/net/ipv6/defs_linux.go b/vendor/src/golang.org/x/net/ipv6/defs_linux.go new file mode 100644 index 00000000..3308cb2c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_linux.go @@ -0,0 +1,147 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include +#include +#include +#include +#include +#include +*/ +import "C" + +const ( + sysIPV6_ADDRFORM = C.IPV6_ADDRFORM + sysIPV6_2292PKTINFO = C.IPV6_2292PKTINFO + sysIPV6_2292HOPOPTS = C.IPV6_2292HOPOPTS + sysIPV6_2292DSTOPTS = C.IPV6_2292DSTOPTS + sysIPV6_2292RTHDR = C.IPV6_2292RTHDR + sysIPV6_2292PKTOPTIONS = C.IPV6_2292PKTOPTIONS + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_2292HOPLIMIT = C.IPV6_2292HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_FLOWINFO = C.IPV6_FLOWINFO + + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_ADD_MEMBERSHIP = C.IPV6_ADD_MEMBERSHIP + sysIPV6_DROP_MEMBERSHIP = C.IPV6_DROP_MEMBERSHIP + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + sysMCAST_MSFILTER = C.MCAST_MSFILTER + sysIPV6_ROUTER_ALERT = C.IPV6_ROUTER_ALERT + sysIPV6_MTU_DISCOVER = C.IPV6_MTU_DISCOVER + sysIPV6_MTU = C.IPV6_MTU + sysIPV6_RECVERR = C.IPV6_RECVERR + sysIPV6_V6ONLY = C.IPV6_V6ONLY + sysIPV6_JOIN_ANYCAST = C.IPV6_JOIN_ANYCAST + sysIPV6_LEAVE_ANYCAST = C.IPV6_LEAVE_ANYCAST + + //sysIPV6_PMTUDISC_DONT = C.IPV6_PMTUDISC_DONT + //sysIPV6_PMTUDISC_WANT = C.IPV6_PMTUDISC_WANT + //sysIPV6_PMTUDISC_DO = C.IPV6_PMTUDISC_DO + //sysIPV6_PMTUDISC_PROBE = C.IPV6_PMTUDISC_PROBE + //sysIPV6_PMTUDISC_INTERFACE = C.IPV6_PMTUDISC_INTERFACE + //sysIPV6_PMTUDISC_OMIT = C.IPV6_PMTUDISC_OMIT + + sysIPV6_FLOWLABEL_MGR = C.IPV6_FLOWLABEL_MGR + sysIPV6_FLOWINFO_SEND = C.IPV6_FLOWINFO_SEND + + sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY + sysIPV6_XFRM_POLICY = C.IPV6_XFRM_POLICY + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RTHDR = C.IPV6_RTHDR + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + sysIPV6_PATHMTU = C.IPV6_PATHMTU + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + sysIPV6_TCLASS = C.IPV6_TCLASS + + sysIPV6_ADDR_PREFERENCES = C.IPV6_ADDR_PREFERENCES + + sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP + sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = C.IPV6_PREFER_SRC_PUBTMP_DEFAULT + sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA + sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME + sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA + sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA + + sysIPV6_MINHOPCOUNT = C.IPV6_MINHOPCOUNT + + sysIPV6_ORIGDSTADDR = C.IPV6_ORIGDSTADDR + sysIPV6_RECVORIGDSTADDR = C.IPV6_RECVORIGDSTADDR + sysIPV6_TRANSPARENT = C.IPV6_TRANSPARENT + sysIPV6_UNICAST_IF = C.IPV6_UNICAST_IF + + sysICMPV6_FILTER = C.ICMPV6_FILTER + + sysICMPV6_FILTER_BLOCK = C.ICMPV6_FILTER_BLOCK + sysICMPV6_FILTER_PASS = C.ICMPV6_FILTER_PASS + sysICMPV6_FILTER_BLOCKOTHERS = C.ICMPV6_FILTER_BLOCKOTHERS + sysICMPV6_FILTER_PASSONLY = C.ICMPV6_FILTER_PASSONLY + + sysSOL_SOCKET = C.SOL_SOCKET + sysSO_ATTACH_FILTER = C.SO_ATTACH_FILTER + + sizeofKernelSockaddrStorage = C.sizeof_struct___kernel_sockaddr_storage + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + sizeofIPv6FlowlabelReq = C.sizeof_struct_in6_flowlabel_req + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter + + sizeofSockFprog = C.sizeof_struct_sock_fprog +) + +type kernelSockaddrStorage C.struct___kernel_sockaddr_storage + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6FlowlabelReq C.struct_in6_flowlabel_req + +type ipv6Mreq C.struct_ipv6_mreq + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req + +type icmpv6Filter C.struct_icmp6_filter + +type sockFProg C.struct_sock_fprog + +type sockFilter C.struct_sock_filter diff --git a/vendor/src/golang.org/x/net/ipv6/defs_netbsd.go b/vendor/src/golang.org/x/net/ipv6/defs_netbsd.go new file mode 100644 index 00000000..be9ceb9c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_netbsd.go @@ -0,0 +1,80 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include +#include + +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + sysIPV6_PORTRANGE = C.IPV6_PORTRANGE + sysICMP6_FILTER = C.ICMP6_FILTER + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_IPSEC_POLICY = C.IPV6_IPSEC_POLICY + + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + sysIPV6_PATHMTU = C.IPV6_PATHMTU + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RTHDR = C.IPV6_RTHDR + + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + + sysIPV6_TCLASS = C.IPV6_TCLASS + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + + sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT + sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH + sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW + + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type icmpv6Filter C.struct_icmp6_filter diff --git a/vendor/src/golang.org/x/net/ipv6/defs_openbsd.go b/vendor/src/golang.org/x/net/ipv6/defs_openbsd.go new file mode 100644 index 00000000..177ddf87 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_openbsd.go @@ -0,0 +1,89 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include +#include + +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + sysIPV6_PORTRANGE = C.IPV6_PORTRANGE + sysICMP6_FILTER = C.ICMP6_FILTER + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + + sysIPV6_PATHMTU = C.IPV6_PATHMTU + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + sysIPV6_RTHDR = C.IPV6_RTHDR + + sysIPV6_AUTH_LEVEL = C.IPV6_AUTH_LEVEL + sysIPV6_ESP_TRANS_LEVEL = C.IPV6_ESP_TRANS_LEVEL + sysIPV6_ESP_NETWORK_LEVEL = C.IPV6_ESP_NETWORK_LEVEL + sysIPSEC6_OUTSA = C.IPSEC6_OUTSA + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + + sysIPV6_AUTOFLOWLABEL = C.IPV6_AUTOFLOWLABEL + sysIPV6_IPCOMP_LEVEL = C.IPV6_IPCOMP_LEVEL + + sysIPV6_TCLASS = C.IPV6_TCLASS + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + sysIPV6_PIPEX = C.IPV6_PIPEX + + sysIPV6_RTABLE = C.IPV6_RTABLE + + sysIPV6_PORTRANGE_DEFAULT = C.IPV6_PORTRANGE_DEFAULT + sysIPV6_PORTRANGE_HIGH = C.IPV6_PORTRANGE_HIGH + sysIPV6_PORTRANGE_LOW = C.IPV6_PORTRANGE_LOW + + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type icmpv6Filter C.struct_icmp6_filter diff --git a/vendor/src/golang.org/x/net/ipv6/defs_solaris.go b/vendor/src/golang.org/x/net/ipv6/defs_solaris.go new file mode 100644 index 00000000..0f8ce2b4 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/defs_solaris.go @@ -0,0 +1,114 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// +godefs map struct_in6_addr [16]byte /* in6_addr */ + +package ipv6 + +/* +#include + +#include +#include +*/ +import "C" + +const ( + sysIPV6_UNICAST_HOPS = C.IPV6_UNICAST_HOPS + sysIPV6_MULTICAST_IF = C.IPV6_MULTICAST_IF + sysIPV6_MULTICAST_HOPS = C.IPV6_MULTICAST_HOPS + sysIPV6_MULTICAST_LOOP = C.IPV6_MULTICAST_LOOP + sysIPV6_JOIN_GROUP = C.IPV6_JOIN_GROUP + sysIPV6_LEAVE_GROUP = C.IPV6_LEAVE_GROUP + + sysIPV6_PKTINFO = C.IPV6_PKTINFO + + sysIPV6_HOPLIMIT = C.IPV6_HOPLIMIT + sysIPV6_NEXTHOP = C.IPV6_NEXTHOP + sysIPV6_HOPOPTS = C.IPV6_HOPOPTS + sysIPV6_DSTOPTS = C.IPV6_DSTOPTS + + sysIPV6_RTHDR = C.IPV6_RTHDR + sysIPV6_RTHDRDSTOPTS = C.IPV6_RTHDRDSTOPTS + + sysIPV6_RECVPKTINFO = C.IPV6_RECVPKTINFO + sysIPV6_RECVHOPLIMIT = C.IPV6_RECVHOPLIMIT + sysIPV6_RECVHOPOPTS = C.IPV6_RECVHOPOPTS + + sysIPV6_RECVRTHDR = C.IPV6_RECVRTHDR + + sysIPV6_RECVRTHDRDSTOPTS = C.IPV6_RECVRTHDRDSTOPTS + + sysIPV6_CHECKSUM = C.IPV6_CHECKSUM + sysIPV6_RECVTCLASS = C.IPV6_RECVTCLASS + sysIPV6_USE_MIN_MTU = C.IPV6_USE_MIN_MTU + sysIPV6_DONTFRAG = C.IPV6_DONTFRAG + sysIPV6_SEC_OPT = C.IPV6_SEC_OPT + sysIPV6_SRC_PREFERENCES = C.IPV6_SRC_PREFERENCES + sysIPV6_RECVPATHMTU = C.IPV6_RECVPATHMTU + sysIPV6_PATHMTU = C.IPV6_PATHMTU + sysIPV6_TCLASS = C.IPV6_TCLASS + sysIPV6_V6ONLY = C.IPV6_V6ONLY + + sysIPV6_RECVDSTOPTS = C.IPV6_RECVDSTOPTS + + sysMCAST_JOIN_GROUP = C.MCAST_JOIN_GROUP + sysMCAST_LEAVE_GROUP = C.MCAST_LEAVE_GROUP + sysMCAST_BLOCK_SOURCE = C.MCAST_BLOCK_SOURCE + sysMCAST_UNBLOCK_SOURCE = C.MCAST_UNBLOCK_SOURCE + sysMCAST_JOIN_SOURCE_GROUP = C.MCAST_JOIN_SOURCE_GROUP + sysMCAST_LEAVE_SOURCE_GROUP = C.MCAST_LEAVE_SOURCE_GROUP + + sysIPV6_PREFER_SRC_HOME = C.IPV6_PREFER_SRC_HOME + sysIPV6_PREFER_SRC_COA = C.IPV6_PREFER_SRC_COA + sysIPV6_PREFER_SRC_PUBLIC = C.IPV6_PREFER_SRC_PUBLIC + sysIPV6_PREFER_SRC_TMP = C.IPV6_PREFER_SRC_TMP + sysIPV6_PREFER_SRC_NONCGA = C.IPV6_PREFER_SRC_NONCGA + sysIPV6_PREFER_SRC_CGA = C.IPV6_PREFER_SRC_CGA + + sysIPV6_PREFER_SRC_MIPMASK = C.IPV6_PREFER_SRC_MIPMASK + sysIPV6_PREFER_SRC_MIPDEFAULT = C.IPV6_PREFER_SRC_MIPDEFAULT + sysIPV6_PREFER_SRC_TMPMASK = C.IPV6_PREFER_SRC_TMPMASK + sysIPV6_PREFER_SRC_TMPDEFAULT = C.IPV6_PREFER_SRC_TMPDEFAULT + sysIPV6_PREFER_SRC_CGAMASK = C.IPV6_PREFER_SRC_CGAMASK + sysIPV6_PREFER_SRC_CGADEFAULT = C.IPV6_PREFER_SRC_CGADEFAULT + + sysIPV6_PREFER_SRC_MASK = C.IPV6_PREFER_SRC_MASK + + sysIPV6_PREFER_SRC_DEFAULT = C.IPV6_PREFER_SRC_DEFAULT + + sysIPV6_BOUND_IF = C.IPV6_BOUND_IF + sysIPV6_UNSPEC_SRC = C.IPV6_UNSPEC_SRC + + sysICMP6_FILTER = C.ICMP6_FILTER + + sizeofSockaddrStorage = C.sizeof_struct_sockaddr_storage + sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6 + sizeofInet6Pktinfo = C.sizeof_struct_in6_pktinfo + sizeofIPv6Mtuinfo = C.sizeof_struct_ip6_mtuinfo + + sizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq + sizeofGroupReq = C.sizeof_struct_group_req + sizeofGroupSourceReq = C.sizeof_struct_group_source_req + + sizeofICMPv6Filter = C.sizeof_struct_icmp6_filter +) + +type sockaddrStorage C.struct_sockaddr_storage + +type sockaddrInet6 C.struct_sockaddr_in6 + +type inet6Pktinfo C.struct_in6_pktinfo + +type ipv6Mtuinfo C.struct_ip6_mtuinfo + +type ipv6Mreq C.struct_ipv6_mreq + +type groupReq C.struct_group_req + +type groupSourceReq C.struct_group_source_req + +type icmpv6Filter C.struct_icmp6_filter diff --git a/vendor/src/golang.org/x/net/ipv6/dgramopt.go b/vendor/src/golang.org/x/net/ipv6/dgramopt.go new file mode 100644 index 00000000..703dafe8 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/dgramopt.go @@ -0,0 +1,302 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" + + "golang.org/x/net/bpf" +) + +// MulticastHopLimit returns the hop limit field value for outgoing +// multicast packets. +func (c *dgramOpt) MulticastHopLimit() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastHopLimit] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetMulticastHopLimit sets the hop limit field value for future +// outgoing multicast packets. +func (c *dgramOpt) SetMulticastHopLimit(hoplim int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastHopLimit] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, hoplim) +} + +// MulticastInterface returns the default interface for multicast +// packet transmissions. +func (c *dgramOpt) MulticastInterface() (*net.Interface, error) { + if !c.ok() { + return nil, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastInterface] + if !ok { + return nil, errOpNoSupport + } + return so.getMulticastInterface(c.Conn) +} + +// SetMulticastInterface sets the default interface for future +// multicast packet transmissions. +func (c *dgramOpt) SetMulticastInterface(ifi *net.Interface) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastInterface] + if !ok { + return errOpNoSupport + } + return so.setMulticastInterface(c.Conn, ifi) +} + +// MulticastLoopback reports whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) MulticastLoopback() (bool, error) { + if !c.ok() { + return false, syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastLoopback] + if !ok { + return false, errOpNoSupport + } + on, err := so.GetInt(c.Conn) + if err != nil { + return false, err + } + return on == 1, nil +} + +// SetMulticastLoopback sets whether transmitted multicast packets +// should be copied and send back to the originator. +func (c *dgramOpt) SetMulticastLoopback(on bool) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoMulticastLoopback] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, boolint(on)) +} + +// JoinGroup joins the group address group on the interface ifi. +// By default all sources that can cast data to group are accepted. +// It's possible to mute and unmute data transmission from a specific +// source by using ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup. +// JoinGroup uses the system assigned multicast interface when ifi is +// nil, although this is not recommended because the assignment +// depends on platforms and sometimes it might require routing +// configuration. +func (c *dgramOpt) JoinGroup(ifi *net.Interface, group net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoJoinGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + return so.setGroup(c.Conn, ifi, grp) +} + +// LeaveGroup leaves the group address group on the interface ifi +// regardless of whether the group is any-source group or +// source-specific group. +func (c *dgramOpt) LeaveGroup(ifi *net.Interface, group net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoLeaveGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + return so.setGroup(c.Conn, ifi, grp) +} + +// JoinSourceSpecificGroup joins the source-specific group comprising +// group and source on the interface ifi. +// JoinSourceSpecificGroup uses the system assigned multicast +// interface when ifi is nil, although this is not recommended because +// the assignment depends on platforms and sometimes it might require +// routing configuration. +func (c *dgramOpt) JoinSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoJoinSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP16(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// LeaveSourceSpecificGroup leaves the source-specific group on the +// interface ifi. +func (c *dgramOpt) LeaveSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoLeaveSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP16(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// ExcludeSourceSpecificGroup excludes the source-specific group from +// the already joined any-source groups by JoinGroup on the interface +// ifi. +func (c *dgramOpt) ExcludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoBlockSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP16(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// IncludeSourceSpecificGroup includes the excluded source-specific +// group by ExcludeSourceSpecificGroup again on the interface ifi. +func (c *dgramOpt) IncludeSourceSpecificGroup(ifi *net.Interface, group, source net.Addr) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoUnblockSourceGroup] + if !ok { + return errOpNoSupport + } + grp := netAddrToIP16(group) + if grp == nil { + return errMissingAddress + } + src := netAddrToIP16(source) + if src == nil { + return errMissingAddress + } + return so.setSourceGroup(c.Conn, ifi, grp, src) +} + +// Checksum reports whether the kernel will compute, store or verify a +// checksum for both incoming and outgoing packets. If on is true, it +// returns an offset in bytes into the data of where the checksum +// field is located. +func (c *dgramOpt) Checksum() (on bool, offset int, err error) { + if !c.ok() { + return false, 0, syscall.EINVAL + } + so, ok := sockOpts[ssoChecksum] + if !ok { + return false, 0, errOpNoSupport + } + offset, err = so.GetInt(c.Conn) + if err != nil { + return false, 0, err + } + if offset < 0 { + return false, 0, nil + } + return true, offset, nil +} + +// SetChecksum enables the kernel checksum processing. If on is ture, +// the offset should be an offset in bytes into the data of where the +// checksum field is located. +func (c *dgramOpt) SetChecksum(on bool, offset int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoChecksum] + if !ok { + return errOpNoSupport + } + if !on { + offset = -1 + } + return so.SetInt(c.Conn, offset) +} + +// ICMPFilter returns an ICMP filter. +func (c *dgramOpt) ICMPFilter() (*ICMPFilter, error) { + if !c.ok() { + return nil, syscall.EINVAL + } + so, ok := sockOpts[ssoICMPFilter] + if !ok { + return nil, errOpNoSupport + } + return so.getICMPFilter(c.Conn) +} + +// SetICMPFilter deploys the ICMP filter. +func (c *dgramOpt) SetICMPFilter(f *ICMPFilter) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoICMPFilter] + if !ok { + return errOpNoSupport + } + return so.setICMPFilter(c.Conn, f) +} + +// SetBPF attaches a BPF program to the connection. +// +// Only supported on Linux. +func (c *dgramOpt) SetBPF(filter []bpf.RawInstruction) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoAttachFilter] + if !ok { + return errOpNoSupport + } + return so.setBPF(c.Conn, filter) +} diff --git a/vendor/src/golang.org/x/net/ipv6/doc.go b/vendor/src/golang.org/x/net/ipv6/doc.go new file mode 100644 index 00000000..664a97de --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/doc.go @@ -0,0 +1,243 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ipv6 implements IP-level socket options for the Internet +// Protocol version 6. +// +// The package provides IP-level socket options that allow +// manipulation of IPv6 facilities. +// +// The IPv6 protocol is defined in RFC 8200. +// Socket interface extensions are defined in RFC 3493, RFC 3542 and +// RFC 3678. +// MLDv1 and MLDv2 are defined in RFC 2710 and RFC 3810. +// Source-specific multicast is defined in RFC 4607. +// +// On Darwin, this package requires OS X Mavericks version 10.9 or +// above, or equivalent. +// +// +// Unicasting +// +// The options for unicasting are available for net.TCPConn, +// net.UDPConn and net.IPConn which are created as network connections +// that use the IPv6 transport. When a single TCP connection carrying +// a data flow of multiple packets needs to indicate the flow is +// important, Conn is used to set the traffic class field on the IPv6 +// header for each packet. +// +// ln, err := net.Listen("tcp6", "[::]:1024") +// if err != nil { +// // error handling +// } +// defer ln.Close() +// for { +// c, err := ln.Accept() +// if err != nil { +// // error handling +// } +// go func(c net.Conn) { +// defer c.Close() +// +// The outgoing packets will be labeled DiffServ assured forwarding +// class 1 low drop precedence, known as AF11 packets. +// +// if err := ipv6.NewConn(c).SetTrafficClass(0x28); err != nil { +// // error handling +// } +// if _, err := c.Write(data); err != nil { +// // error handling +// } +// }(c) +// } +// +// +// Multicasting +// +// The options for multicasting are available for net.UDPConn and +// net.IPconn which are created as network connections that use the +// IPv6 transport. A few network facilities must be prepared before +// you begin multicasting, at a minimum joining network interfaces and +// multicast groups. +// +// en0, err := net.InterfaceByName("en0") +// if err != nil { +// // error handling +// } +// en1, err := net.InterfaceByIndex(911) +// if err != nil { +// // error handling +// } +// group := net.ParseIP("ff02::114") +// +// First, an application listens to an appropriate address with an +// appropriate service port. +// +// c, err := net.ListenPacket("udp6", "[::]:1024") +// if err != nil { +// // error handling +// } +// defer c.Close() +// +// Second, the application joins multicast groups, starts listening to +// the groups on the specified network interfaces. Note that the +// service port for transport layer protocol does not matter with this +// operation as joining groups affects only network and link layer +// protocols, such as IPv6 and Ethernet. +// +// p := ipv6.NewPacketConn(c) +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: group}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en1, &net.UDPAddr{IP: group}); err != nil { +// // error handling +// } +// +// The application might set per packet control message transmissions +// between the protocol stack within the kernel. When the application +// needs a destination address on an incoming packet, +// SetControlMessage of PacketConn is used to enable control message +// transmissions. +// +// if err := p.SetControlMessage(ipv6.FlagDst, true); err != nil { +// // error handling +// } +// +// The application could identify whether the received packets are +// of interest by using the control message that contains the +// destination address of the received packet. +// +// b := make([]byte, 1500) +// for { +// n, rcm, src, err := p.ReadFrom(b) +// if err != nil { +// // error handling +// } +// if rcm.Dst.IsMulticast() { +// if rcm.Dst.Equal(group) { +// // joined group, do something +// } else { +// // unknown group, discard +// continue +// } +// } +// +// The application can also send both unicast and multicast packets. +// +// p.SetTrafficClass(0x0) +// p.SetHopLimit(16) +// if _, err := p.WriteTo(data[:n], nil, src); err != nil { +// // error handling +// } +// dst := &net.UDPAddr{IP: group, Port: 1024} +// wcm := ipv6.ControlMessage{TrafficClass: 0xe0, HopLimit: 1} +// for _, ifi := range []*net.Interface{en0, en1} { +// wcm.IfIndex = ifi.Index +// if _, err := p.WriteTo(data[:n], &wcm, dst); err != nil { +// // error handling +// } +// } +// } +// +// +// More multicasting +// +// An application that uses PacketConn may join multiple multicast +// groups. For example, a UDP listener with port 1024 might join two +// different groups across over two different network interfaces by +// using: +// +// c, err := net.ListenPacket("udp6", "[::]:1024") +// if err != nil { +// // error handling +// } +// defer c.Close() +// p := ipv6.NewPacketConn(c) +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en1, &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}); err != nil { +// // error handling +// } +// +// It is possible for multiple UDP listeners that listen on the same +// UDP port to join the same multicast group. The net package will +// provide a socket that listens to a wildcard address with reusable +// UDP port when an appropriate multicast address prefix is passed to +// the net.ListenPacket or net.ListenUDP. +// +// c1, err := net.ListenPacket("udp6", "[ff02::]:1024") +// if err != nil { +// // error handling +// } +// defer c1.Close() +// c2, err := net.ListenPacket("udp6", "[ff02::]:1024") +// if err != nil { +// // error handling +// } +// defer c2.Close() +// p1 := ipv6.NewPacketConn(c1) +// if err := p1.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { +// // error handling +// } +// p2 := ipv6.NewPacketConn(c2) +// if err := p2.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { +// // error handling +// } +// +// Also it is possible for the application to leave or rejoin a +// multicast group on the network interface. +// +// if err := p.LeaveGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff02::114")}); err != nil { +// // error handling +// } +// if err := p.JoinGroup(en0, &net.UDPAddr{IP: net.ParseIP("ff01::114")}); err != nil { +// // error handling +// } +// +// +// Source-specific multicasting +// +// An application that uses PacketConn on MLDv2 supported platform is +// able to join source-specific multicast groups. +// The application may use JoinSourceSpecificGroup and +// LeaveSourceSpecificGroup for the operation known as "include" mode, +// +// ssmgroup := net.UDPAddr{IP: net.ParseIP("ff32::8000:9")} +// ssmsource := net.UDPAddr{IP: net.ParseIP("fe80::cafe")} +// if err := p.JoinSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil { +// // error handling +// } +// if err := p.LeaveSourceSpecificGroup(en0, &ssmgroup, &ssmsource); err != nil { +// // error handling +// } +// +// or JoinGroup, ExcludeSourceSpecificGroup, +// IncludeSourceSpecificGroup and LeaveGroup for the operation known +// as "exclude" mode. +// +// exclsource := net.UDPAddr{IP: net.ParseIP("fe80::dead")} +// if err := p.JoinGroup(en0, &ssmgroup); err != nil { +// // error handling +// } +// if err := p.ExcludeSourceSpecificGroup(en0, &ssmgroup, &exclsource); err != nil { +// // error handling +// } +// if err := p.LeaveGroup(en0, &ssmgroup); err != nil { +// // error handling +// } +// +// Note that it depends on each platform implementation what happens +// when an application which runs on MLDv2 unsupported platform uses +// JoinSourceSpecificGroup and LeaveSourceSpecificGroup. +// In general the platform tries to fall back to conversations using +// MLDv1 and starts to listen to multicast traffic. +// In the fallback case, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup may return an error. +package ipv6 // import "golang.org/x/net/ipv6" + +// BUG(mikio): This package is not implemented on NaCl and Plan 9. diff --git a/vendor/src/golang.org/x/net/ipv6/endpoint.go b/vendor/src/golang.org/x/net/ipv6/endpoint.go new file mode 100644 index 00000000..0624c174 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/endpoint.go @@ -0,0 +1,128 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" + "time" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the JoinSourceSpecificGroup, +// LeaveSourceSpecificGroup, ExcludeSourceSpecificGroup and +// IncludeSourceSpecificGroup methods of PacketConn are not +// implemented. + +// A Conn represents a network endpoint that uses IPv6 transport. +// It allows to set basic IP-level socket options such as traffic +// class and hop limit. +type Conn struct { + genericOpt +} + +type genericOpt struct { + *socket.Conn +} + +func (c *genericOpt) ok() bool { return c != nil && c.Conn != nil } + +// PathMTU returns a path MTU value for the destination associated +// with the endpoint. +func (c *Conn) PathMTU() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoPathMTU] + if !ok { + return 0, errOpNoSupport + } + _, mtu, err := so.getMTUInfo(c.Conn) + if err != nil { + return 0, err + } + return mtu, nil +} + +// NewConn returns a new Conn. +func NewConn(c net.Conn) *Conn { + cc, _ := socket.NewConn(c) + return &Conn{ + genericOpt: genericOpt{Conn: cc}, + } +} + +// A PacketConn represents a packet network endpoint that uses IPv6 +// transport. It is used to control several IP-level socket options +// including IPv6 header manipulation. It also provides datagram +// based network I/O methods specific to the IPv6 and higher layer +// protocols such as OSPF, GRE, and UDP. +type PacketConn struct { + genericOpt + dgramOpt + payloadHandler +} + +type dgramOpt struct { + *socket.Conn +} + +func (c *dgramOpt) ok() bool { return c != nil && c.Conn != nil } + +// SetControlMessage allows to receive the per packet basis IP-level +// socket options. +func (c *PacketConn) SetControlMessage(cf ControlFlags, on bool) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return setControlMessage(c.dgramOpt.Conn, &c.payloadHandler.rawOpt, cf, on) +} + +// SetDeadline sets the read and write deadlines associated with the +// endpoint. +func (c *PacketConn) SetDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline associated with the +// endpoint. +func (c *PacketConn) SetReadDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline associated with the +// endpoint. +func (c *PacketConn) SetWriteDeadline(t time.Time) error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.SetWriteDeadline(t) +} + +// Close closes the endpoint. +func (c *PacketConn) Close() error { + if !c.payloadHandler.ok() { + return syscall.EINVAL + } + return c.payloadHandler.Close() +} + +// NewPacketConn returns a new PacketConn using c as its underlying +// transport. +func NewPacketConn(c net.PacketConn) *PacketConn { + cc, _ := socket.NewConn(c.(net.Conn)) + return &PacketConn{ + genericOpt: genericOpt{Conn: cc}, + dgramOpt: dgramOpt{Conn: cc}, + payloadHandler: payloadHandler{PacketConn: c, Conn: cc}, + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/example_test.go b/vendor/src/golang.org/x/net/ipv6/example_test.go new file mode 100644 index 00000000..e761aa2a --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/example_test.go @@ -0,0 +1,216 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "fmt" + "log" + "net" + "os" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/ipv6" +) + +func ExampleConn_markingTCP() { + ln, err := net.Listen("tcp", "[::]:1024") + if err != nil { + log.Fatal(err) + } + defer ln.Close() + + for { + c, err := ln.Accept() + if err != nil { + log.Fatal(err) + } + go func(c net.Conn) { + defer c.Close() + if c.RemoteAddr().(*net.TCPAddr).IP.To16() != nil && c.RemoteAddr().(*net.TCPAddr).IP.To4() == nil { + p := ipv6.NewConn(c) + if err := p.SetTrafficClass(0x28); err != nil { // DSCP AF11 + log.Fatal(err) + } + if err := p.SetHopLimit(128); err != nil { + log.Fatal(err) + } + } + if _, err := c.Write([]byte("HELLO-R-U-THERE-ACK")); err != nil { + log.Fatal(err) + } + }(c) + } +} + +func ExamplePacketConn_servingOneShotMulticastDNS() { + c, err := net.ListenPacket("udp6", "[::]:5353") // mDNS over UDP + if err != nil { + log.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + + en0, err := net.InterfaceByName("en0") + if err != nil { + log.Fatal(err) + } + mDNSLinkLocal := net.UDPAddr{IP: net.ParseIP("ff02::fb")} + if err := p.JoinGroup(en0, &mDNSLinkLocal); err != nil { + log.Fatal(err) + } + defer p.LeaveGroup(en0, &mDNSLinkLocal) + if err := p.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true); err != nil { + log.Fatal(err) + } + + var wcm ipv6.ControlMessage + b := make([]byte, 1500) + for { + _, rcm, peer, err := p.ReadFrom(b) + if err != nil { + log.Fatal(err) + } + if !rcm.Dst.IsMulticast() || !rcm.Dst.Equal(mDNSLinkLocal.IP) { + continue + } + wcm.IfIndex = rcm.IfIndex + answers := []byte("FAKE-MDNS-ANSWERS") // fake mDNS answers, you need to implement this + if _, err := p.WriteTo(answers, &wcm, peer); err != nil { + log.Fatal(err) + } + } +} + +func ExamplePacketConn_tracingIPPacketRoute() { + // Tracing an IP packet route to www.google.com. + + const host = "www.google.com" + ips, err := net.LookupIP(host) + if err != nil { + log.Fatal(err) + } + var dst net.IPAddr + for _, ip := range ips { + if ip.To16() != nil && ip.To4() == nil { + dst.IP = ip + fmt.Printf("using %v for tracing an IP packet route to %s\n", dst.IP, host) + break + } + } + if dst.IP == nil { + log.Fatal("no AAAA record found") + } + + c, err := net.ListenPacket("ip6:58", "::") // ICMP for IPv6 + if err != nil { + log.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + + if err := p.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true); err != nil { + log.Fatal(err) + } + wm := icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, + Data: []byte("HELLO-R-U-THERE"), + }, + } + var f ipv6.ICMPFilter + f.SetAll(true) + f.Accept(ipv6.ICMPTypeTimeExceeded) + f.Accept(ipv6.ICMPTypeEchoReply) + if err := p.SetICMPFilter(&f); err != nil { + log.Fatal(err) + } + + var wcm ipv6.ControlMessage + rb := make([]byte, 1500) + for i := 1; i <= 64; i++ { // up to 64 hops + wm.Body.(*icmp.Echo).Seq = i + wb, err := wm.Marshal(nil) + if err != nil { + log.Fatal(err) + } + + // In the real world usually there are several + // multiple traffic-engineered paths for each hop. + // You may need to probe a few times to each hop. + begin := time.Now() + wcm.HopLimit = i + if _, err := p.WriteTo(wb, &wcm, &dst); err != nil { + log.Fatal(err) + } + if err := p.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil { + log.Fatal(err) + } + n, rcm, peer, err := p.ReadFrom(rb) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + fmt.Printf("%v\t*\n", i) + continue + } + log.Fatal(err) + } + rm, err := icmp.ParseMessage(58, rb[:n]) + if err != nil { + log.Fatal(err) + } + rtt := time.Since(begin) + + // In the real world you need to determine whether the + // received message is yours using ControlMessage.Src, + // ControlMesage.Dst, icmp.Echo.ID and icmp.Echo.Seq. + switch rm.Type { + case ipv6.ICMPTypeTimeExceeded: + names, _ := net.LookupAddr(peer.String()) + fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm) + case ipv6.ICMPTypeEchoReply: + names, _ := net.LookupAddr(peer.String()) + fmt.Printf("%d\t%v %+v %v\n\t%+v\n", i, peer, names, rtt, rcm) + return + } + } +} + +func ExamplePacketConn_advertisingOSPFHello() { + c, err := net.ListenPacket("ip6:89", "::") // OSPF for IPv6 + if err != nil { + log.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + + en0, err := net.InterfaceByName("en0") + if err != nil { + log.Fatal(err) + } + allSPFRouters := net.IPAddr{IP: net.ParseIP("ff02::5")} + if err := p.JoinGroup(en0, &allSPFRouters); err != nil { + log.Fatal(err) + } + defer p.LeaveGroup(en0, &allSPFRouters) + + hello := make([]byte, 24) // fake hello data, you need to implement this + ospf := make([]byte, 16) // fake ospf header, you need to implement this + ospf[0] = 3 // version 3 + ospf[1] = 1 // hello packet + ospf = append(ospf, hello...) + if err := p.SetChecksum(true, 12); err != nil { + log.Fatal(err) + } + + cm := ipv6.ControlMessage{ + TrafficClass: 0xc0, // DSCP CS6 + HopLimit: 1, + IfIndex: en0.Index, + } + if _, err := p.WriteTo(ospf, &cm, &allSPFRouters); err != nil { + log.Fatal(err) + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/gen.go b/vendor/src/golang.org/x/net/ipv6/gen.go new file mode 100644 index 00000000..41886ec7 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/gen.go @@ -0,0 +1,199 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +//go:generate go run gen.go + +// This program generates system adaptation constants and types, +// internet protocol constants and tables by reading template files +// and IANA protocol registries. +package main + +import ( + "bytes" + "encoding/xml" + "fmt" + "go/format" + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "runtime" + "strconv" + "strings" +) + +func main() { + if err := genzsys(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + if err := geniana(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func genzsys() error { + defs := "defs_" + runtime.GOOS + ".go" + f, err := os.Open(defs) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + f.Close() + cmd := exec.Command("go", "tool", "cgo", "-godefs", defs) + b, err := cmd.Output() + if err != nil { + return err + } + b, err = format.Source(b) + if err != nil { + return err + } + zsys := "zsys_" + runtime.GOOS + ".go" + switch runtime.GOOS { + case "freebsd", "linux": + zsys = "zsys_" + runtime.GOOS + "_" + runtime.GOARCH + ".go" + } + if err := ioutil.WriteFile(zsys, b, 0644); err != nil { + return err + } + return nil +} + +var registries = []struct { + url string + parse func(io.Writer, io.Reader) error +}{ + { + "http://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml", + parseICMPv6Parameters, + }, +} + +func geniana() error { + var bb bytes.Buffer + fmt.Fprintf(&bb, "// go generate gen.go\n") + fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n") + fmt.Fprintf(&bb, "package ipv6\n\n") + for _, r := range registries { + resp, err := http.Get(r.url) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("got HTTP status code %v for %v\n", resp.StatusCode, r.url) + } + if err := r.parse(&bb, resp.Body); err != nil { + return err + } + fmt.Fprintf(&bb, "\n") + } + b, err := format.Source(bb.Bytes()) + if err != nil { + return err + } + if err := ioutil.WriteFile("iana.go", b, 0644); err != nil { + return err + } + return nil +} + +func parseICMPv6Parameters(w io.Writer, r io.Reader) error { + dec := xml.NewDecoder(r) + var icp icmpv6Parameters + if err := dec.Decode(&icp); err != nil { + return err + } + prs := icp.escape() + fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) + fmt.Fprintf(w, "const (\n") + for _, pr := range prs { + if pr.Name == "" { + continue + } + fmt.Fprintf(w, "ICMPType%s ICMPType = %d", pr.Name, pr.Value) + fmt.Fprintf(w, "// %s\n", pr.OrigName) + } + fmt.Fprintf(w, ")\n\n") + fmt.Fprintf(w, "// %s, Updated: %s\n", icp.Title, icp.Updated) + fmt.Fprintf(w, "var icmpTypes = map[ICMPType]string{\n") + for _, pr := range prs { + if pr.Name == "" { + continue + } + fmt.Fprintf(w, "%d: %q,\n", pr.Value, strings.ToLower(pr.OrigName)) + } + fmt.Fprintf(w, "}\n") + return nil +} + +type icmpv6Parameters struct { + XMLName xml.Name `xml:"registry"` + Title string `xml:"title"` + Updated string `xml:"updated"` + Registries []struct { + Title string `xml:"title"` + Records []struct { + Value string `xml:"value"` + Name string `xml:"name"` + } `xml:"record"` + } `xml:"registry"` +} + +type canonICMPv6ParamRecord struct { + OrigName string + Name string + Value int +} + +func (icp *icmpv6Parameters) escape() []canonICMPv6ParamRecord { + id := -1 + for i, r := range icp.Registries { + if strings.Contains(r.Title, "Type") || strings.Contains(r.Title, "type") { + id = i + break + } + } + if id < 0 { + return nil + } + prs := make([]canonICMPv6ParamRecord, len(icp.Registries[id].Records)) + sr := strings.NewReplacer( + "Messages", "", + "Message", "", + "ICMP", "", + "+", "P", + "-", "", + "/", "", + ".", "", + " ", "", + ) + for i, pr := range icp.Registries[id].Records { + if strings.Contains(pr.Name, "Reserved") || + strings.Contains(pr.Name, "Unassigned") || + strings.Contains(pr.Name, "Deprecated") || + strings.Contains(pr.Name, "Experiment") || + strings.Contains(pr.Name, "experiment") { + continue + } + ss := strings.Split(pr.Name, "\n") + if len(ss) > 1 { + prs[i].Name = strings.Join(ss, " ") + } else { + prs[i].Name = ss[0] + } + s := strings.TrimSpace(prs[i].Name) + prs[i].OrigName = s + prs[i].Name = sr.Replace(s) + prs[i].Value, _ = strconv.Atoi(pr.Value) + } + return prs +} diff --git a/vendor/src/golang.org/x/net/ipv6/genericopt.go b/vendor/src/golang.org/x/net/ipv6/genericopt.go new file mode 100644 index 00000000..e9dbc2e1 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/genericopt.go @@ -0,0 +1,58 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import "syscall" + +// TrafficClass returns the traffic class field value for outgoing +// packets. +func (c *genericOpt) TrafficClass() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoTrafficClass] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetTrafficClass sets the traffic class field value for future +// outgoing packets. +func (c *genericOpt) SetTrafficClass(tclass int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoTrafficClass] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, tclass) +} + +// HopLimit returns the hop limit field value for outgoing packets. +func (c *genericOpt) HopLimit() (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + so, ok := sockOpts[ssoHopLimit] + if !ok { + return 0, errOpNoSupport + } + return so.GetInt(c.Conn) +} + +// SetHopLimit sets the hop limit field value for future outgoing +// packets. +func (c *genericOpt) SetHopLimit(hoplim int) error { + if !c.ok() { + return syscall.EINVAL + } + so, ok := sockOpts[ssoHopLimit] + if !ok { + return errOpNoSupport + } + return so.SetInt(c.Conn, hoplim) +} diff --git a/vendor/src/golang.org/x/net/ipv6/header.go b/vendor/src/golang.org/x/net/ipv6/header.go new file mode 100644 index 00000000..e05cb08b --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/header.go @@ -0,0 +1,55 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "encoding/binary" + "fmt" + "net" +) + +const ( + Version = 6 // protocol version + HeaderLen = 40 // header length +) + +// A Header represents an IPv6 base header. +type Header struct { + Version int // protocol version + TrafficClass int // traffic class + FlowLabel int // flow label + PayloadLen int // payload length + NextHeader int // next header + HopLimit int // hop limit + Src net.IP // source address + Dst net.IP // destination address +} + +func (h *Header) String() string { + if h == nil { + return "" + } + return fmt.Sprintf("ver=%d tclass=%#x flowlbl=%#x payloadlen=%d nxthdr=%d hoplim=%d src=%v dst=%v", h.Version, h.TrafficClass, h.FlowLabel, h.PayloadLen, h.NextHeader, h.HopLimit, h.Src, h.Dst) +} + +// ParseHeader parses b as an IPv6 base header. +func ParseHeader(b []byte) (*Header, error) { + if len(b) < HeaderLen { + return nil, errHeaderTooShort + } + h := &Header{ + Version: int(b[0]) >> 4, + TrafficClass: int(b[0]&0x0f)<<4 | int(b[1])>>4, + FlowLabel: int(b[1]&0x0f)<<16 | int(b[2])<<8 | int(b[3]), + PayloadLen: int(binary.BigEndian.Uint16(b[4:6])), + NextHeader: int(b[6]), + HopLimit: int(b[7]), + } + h.Src = make(net.IP, net.IPv6len) + copy(h.Src, b[8:24]) + h.Dst = make(net.IP, net.IPv6len) + copy(h.Dst, b[24:40]) + return h, nil +} diff --git a/vendor/src/golang.org/x/net/ipv6/header_test.go b/vendor/src/golang.org/x/net/ipv6/header_test.go new file mode 100644 index 00000000..ca11dc23 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/header_test.go @@ -0,0 +1,55 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "reflect" + "strings" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/ipv6" +) + +var ( + wireHeaderFromKernel = [ipv6.HeaderLen]byte{ + 0x69, 0x8b, 0xee, 0xf1, + 0xca, 0xfe, 0x2c, 0x01, + 0x20, 0x01, 0x0d, 0xb8, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x20, 0x01, 0x0d, 0xb8, + 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + } + + testHeader = &ipv6.Header{ + Version: ipv6.Version, + TrafficClass: iana.DiffServAF43, + FlowLabel: 0xbeef1, + PayloadLen: 0xcafe, + NextHeader: iana.ProtocolIPv6Frag, + HopLimit: 1, + Src: net.ParseIP("2001:db8:1::1"), + Dst: net.ParseIP("2001:db8:2::1"), + } +) + +func TestParseHeader(t *testing.T) { + h, err := ipv6.ParseHeader(wireHeaderFromKernel[:]) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(h, testHeader) { + t.Fatalf("got %#v; want %#v", h, testHeader) + } + s := h.String() + if strings.Contains(s, ",") { + t.Fatalf("should be space-separated values: %s", s) + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/helper.go b/vendor/src/golang.org/x/net/ipv6/helper.go new file mode 100644 index 00000000..25974013 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/helper.go @@ -0,0 +1,57 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "errors" + "net" +) + +var ( + errMissingAddress = errors.New("missing address") + errHeaderTooShort = errors.New("header too short") + errInvalidConnType = errors.New("invalid conn type") + errOpNoSupport = errors.New("operation not supported") + errNoSuchInterface = errors.New("no such interface") +) + +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +func netAddrToIP16(a net.Addr) net.IP { + switch v := a.(type) { + case *net.UDPAddr: + if ip := v.IP.To16(); ip != nil && ip.To4() == nil { + return ip + } + case *net.IPAddr: + if ip := v.IP.To16(); ip != nil && ip.To4() == nil { + return ip + } + } + return nil +} + +func opAddr(a net.Addr) net.Addr { + switch a.(type) { + case *net.TCPAddr: + if a == nil { + return nil + } + case *net.UDPAddr: + if a == nil { + return nil + } + case *net.IPAddr: + if a == nil { + return nil + } + } + return a +} diff --git a/vendor/src/golang.org/x/net/ipv6/iana.go b/vendor/src/golang.org/x/net/ipv6/iana.go new file mode 100644 index 00000000..3c6214fb --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/iana.go @@ -0,0 +1,82 @@ +// go generate gen.go +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package ipv6 + +// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07 +const ( + ICMPTypeDestinationUnreachable ICMPType = 1 // Destination Unreachable + ICMPTypePacketTooBig ICMPType = 2 // Packet Too Big + ICMPTypeTimeExceeded ICMPType = 3 // Time Exceeded + ICMPTypeParameterProblem ICMPType = 4 // Parameter Problem + ICMPTypeEchoRequest ICMPType = 128 // Echo Request + ICMPTypeEchoReply ICMPType = 129 // Echo Reply + ICMPTypeMulticastListenerQuery ICMPType = 130 // Multicast Listener Query + ICMPTypeMulticastListenerReport ICMPType = 131 // Multicast Listener Report + ICMPTypeMulticastListenerDone ICMPType = 132 // Multicast Listener Done + ICMPTypeRouterSolicitation ICMPType = 133 // Router Solicitation + ICMPTypeRouterAdvertisement ICMPType = 134 // Router Advertisement + ICMPTypeNeighborSolicitation ICMPType = 135 // Neighbor Solicitation + ICMPTypeNeighborAdvertisement ICMPType = 136 // Neighbor Advertisement + ICMPTypeRedirect ICMPType = 137 // Redirect Message + ICMPTypeRouterRenumbering ICMPType = 138 // Router Renumbering + ICMPTypeNodeInformationQuery ICMPType = 139 // ICMP Node Information Query + ICMPTypeNodeInformationResponse ICMPType = 140 // ICMP Node Information Response + ICMPTypeInverseNeighborDiscoverySolicitation ICMPType = 141 // Inverse Neighbor Discovery Solicitation Message + ICMPTypeInverseNeighborDiscoveryAdvertisement ICMPType = 142 // Inverse Neighbor Discovery Advertisement Message + ICMPTypeVersion2MulticastListenerReport ICMPType = 143 // Version 2 Multicast Listener Report + ICMPTypeHomeAgentAddressDiscoveryRequest ICMPType = 144 // Home Agent Address Discovery Request Message + ICMPTypeHomeAgentAddressDiscoveryReply ICMPType = 145 // Home Agent Address Discovery Reply Message + ICMPTypeMobilePrefixSolicitation ICMPType = 146 // Mobile Prefix Solicitation + ICMPTypeMobilePrefixAdvertisement ICMPType = 147 // Mobile Prefix Advertisement + ICMPTypeCertificationPathSolicitation ICMPType = 148 // Certification Path Solicitation Message + ICMPTypeCertificationPathAdvertisement ICMPType = 149 // Certification Path Advertisement Message + ICMPTypeMulticastRouterAdvertisement ICMPType = 151 // Multicast Router Advertisement + ICMPTypeMulticastRouterSolicitation ICMPType = 152 // Multicast Router Solicitation + ICMPTypeMulticastRouterTermination ICMPType = 153 // Multicast Router Termination + ICMPTypeFMIPv6 ICMPType = 154 // FMIPv6 Messages + ICMPTypeRPLControl ICMPType = 155 // RPL Control Message + ICMPTypeILNPv6LocatorUpdate ICMPType = 156 // ILNPv6 Locator Update Message + ICMPTypeDuplicateAddressRequest ICMPType = 157 // Duplicate Address Request + ICMPTypeDuplicateAddressConfirmation ICMPType = 158 // Duplicate Address Confirmation + ICMPTypeMPLControl ICMPType = 159 // MPL Control Message +) + +// Internet Control Message Protocol version 6 (ICMPv6) Parameters, Updated: 2015-07-07 +var icmpTypes = map[ICMPType]string{ + 1: "destination unreachable", + 2: "packet too big", + 3: "time exceeded", + 4: "parameter problem", + 128: "echo request", + 129: "echo reply", + 130: "multicast listener query", + 131: "multicast listener report", + 132: "multicast listener done", + 133: "router solicitation", + 134: "router advertisement", + 135: "neighbor solicitation", + 136: "neighbor advertisement", + 137: "redirect message", + 138: "router renumbering", + 139: "icmp node information query", + 140: "icmp node information response", + 141: "inverse neighbor discovery solicitation message", + 142: "inverse neighbor discovery advertisement message", + 143: "version 2 multicast listener report", + 144: "home agent address discovery request message", + 145: "home agent address discovery reply message", + 146: "mobile prefix solicitation", + 147: "mobile prefix advertisement", + 148: "certification path solicitation message", + 149: "certification path advertisement message", + 151: "multicast router advertisement", + 152: "multicast router solicitation", + 153: "multicast router termination", + 154: "fmipv6 messages", + 155: "rpl control message", + 156: "ilnpv6 locator update message", + 157: "duplicate address request", + 158: "duplicate address confirmation", + 159: "mpl control message", +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp.go b/vendor/src/golang.org/x/net/ipv6/icmp.go new file mode 100644 index 00000000..b7f48e27 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp.go @@ -0,0 +1,60 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import "golang.org/x/net/internal/iana" + +// BUG(mikio): On Windows, methods related to ICMPFilter are not +// implemented. + +// An ICMPType represents a type of ICMP message. +type ICMPType int + +func (typ ICMPType) String() string { + s, ok := icmpTypes[typ] + if !ok { + return "" + } + return s +} + +// Protocol returns the ICMPv6 protocol number. +func (typ ICMPType) Protocol() int { + return iana.ProtocolIPv6ICMP +} + +// An ICMPFilter represents an ICMP message filter for incoming +// packets. The filter belongs to a packet delivery path on a host and +// it cannot interact with forwarding packets or tunnel-outer packets. +// +// Note: RFC 8200 defines a reasonable role model. A node means a +// device that implements IP. A router means a node that forwards IP +// packets not explicitly addressed to itself, and a host means a node +// that is not a router. +type ICMPFilter struct { + icmpv6Filter +} + +// Accept accepts incoming ICMP packets including the type field value +// typ. +func (f *ICMPFilter) Accept(typ ICMPType) { + f.accept(typ) +} + +// Block blocks incoming ICMP packets including the type field value +// typ. +func (f *ICMPFilter) Block(typ ICMPType) { + f.block(typ) +} + +// SetAll sets the filter action to the filter. +func (f *ICMPFilter) SetAll(block bool) { + f.setAll(block) +} + +// WillBlock reports whether the ICMP type will be blocked. +func (f *ICMPFilter) WillBlock(typ ICMPType) bool { + return f.willBlock(typ) +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_bsd.go b/vendor/src/golang.org/x/net/ipv6/icmp_bsd.go new file mode 100644 index 00000000..e1a791de --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_bsd.go @@ -0,0 +1,29 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package ipv6 + +func (f *icmpv6Filter) accept(typ ICMPType) { + f.Filt[typ>>5] |= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) block(typ ICMPType) { + f.Filt[typ>>5] &^= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) setAll(block bool) { + for i := range f.Filt { + if block { + f.Filt[i] = 0 + } else { + f.Filt[i] = 1<<32 - 1 + } + } +} + +func (f *icmpv6Filter) willBlock(typ ICMPType) bool { + return f.Filt[typ>>5]&(1<<(uint32(typ)&31)) == 0 +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_linux.go b/vendor/src/golang.org/x/net/ipv6/icmp_linux.go new file mode 100644 index 00000000..647f6b44 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_linux.go @@ -0,0 +1,27 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +func (f *icmpv6Filter) accept(typ ICMPType) { + f.Data[typ>>5] &^= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) block(typ ICMPType) { + f.Data[typ>>5] |= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) setAll(block bool) { + for i := range f.Data { + if block { + f.Data[i] = 1<<32 - 1 + } else { + f.Data[i] = 0 + } + } +} + +func (f *icmpv6Filter) willBlock(typ ICMPType) bool { + return f.Data[typ>>5]&(1<<(uint32(typ)&31)) != 0 +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_solaris.go b/vendor/src/golang.org/x/net/ipv6/icmp_solaris.go new file mode 100644 index 00000000..7c23bb1c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_solaris.go @@ -0,0 +1,27 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +func (f *icmpv6Filter) accept(typ ICMPType) { + f.X__icmp6_filt[typ>>5] |= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) block(typ ICMPType) { + f.X__icmp6_filt[typ>>5] &^= 1 << (uint32(typ) & 31) +} + +func (f *icmpv6Filter) setAll(block bool) { + for i := range f.X__icmp6_filt { + if block { + f.X__icmp6_filt[i] = 0 + } else { + f.X__icmp6_filt[i] = 1<<32 - 1 + } + } +} + +func (f *icmpv6Filter) willBlock(typ ICMPType) bool { + return f.X__icmp6_filt[typ>>5]&(1<<(uint32(typ)&31)) == 0 +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_stub.go b/vendor/src/golang.org/x/net/ipv6/icmp_stub.go new file mode 100644 index 00000000..c4b9be6d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_stub.go @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv6 + +type icmpv6Filter struct { +} + +func (f *icmpv6Filter) accept(typ ICMPType) { +} + +func (f *icmpv6Filter) block(typ ICMPType) { +} + +func (f *icmpv6Filter) setAll(block bool) { +} + +func (f *icmpv6Filter) willBlock(typ ICMPType) bool { + return false +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_test.go b/vendor/src/golang.org/x/net/ipv6/icmp_test.go new file mode 100644 index 00000000..d8e9675d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_test.go @@ -0,0 +1,96 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "reflect" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +var icmpStringTests = []struct { + in ipv6.ICMPType + out string +}{ + {ipv6.ICMPTypeDestinationUnreachable, "destination unreachable"}, + + {256, ""}, +} + +func TestICMPString(t *testing.T) { + for _, tt := range icmpStringTests { + s := tt.in.String() + if s != tt.out { + t.Errorf("got %s; want %s", s, tt.out) + } + } +} + +func TestICMPFilter(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + var f ipv6.ICMPFilter + for _, toggle := range []bool{false, true} { + f.SetAll(toggle) + for _, typ := range []ipv6.ICMPType{ + ipv6.ICMPTypeDestinationUnreachable, + ipv6.ICMPTypeEchoReply, + ipv6.ICMPTypeNeighborSolicitation, + ipv6.ICMPTypeDuplicateAddressConfirmation, + } { + f.Accept(typ) + if f.WillBlock(typ) { + t.Errorf("ipv6.ICMPFilter.Set(%v, false) failed", typ) + } + f.Block(typ) + if !f.WillBlock(typ) { + t.Errorf("ipv6.ICMPFilter.Set(%v, true) failed", typ) + } + } + } +} + +func TestSetICMPFilter(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket("ip6:ipv6-icmp", "::1") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv6.NewPacketConn(c) + + var f ipv6.ICMPFilter + f.SetAll(true) + f.Accept(ipv6.ICMPTypeEchoRequest) + f.Accept(ipv6.ICMPTypeEchoReply) + if err := p.SetICMPFilter(&f); err != nil { + t.Fatal(err) + } + kf, err := p.ICMPFilter() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(kf, &f) { + t.Fatalf("got %#v; want %#v", kf, f) + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/icmp_windows.go b/vendor/src/golang.org/x/net/ipv6/icmp_windows.go new file mode 100644 index 00000000..443cd073 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/icmp_windows.go @@ -0,0 +1,22 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +func (f *icmpv6Filter) accept(typ ICMPType) { + // TODO(mikio): implement this +} + +func (f *icmpv6Filter) block(typ ICMPType) { + // TODO(mikio): implement this +} + +func (f *icmpv6Filter) setAll(block bool) { + // TODO(mikio): implement this +} + +func (f *icmpv6Filter) willBlock(typ ICMPType) bool { + // TODO(mikio): implement this + return false +} diff --git a/vendor/src/golang.org/x/net/ipv6/mocktransponder_test.go b/vendor/src/golang.org/x/net/ipv6/mocktransponder_test.go new file mode 100644 index 00000000..6efe56c6 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/mocktransponder_test.go @@ -0,0 +1,32 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "testing" +) + +func connector(t *testing.T, network, addr string, done chan<- bool) { + defer func() { done <- true }() + + c, err := net.Dial(network, addr) + if err != nil { + t.Error(err) + return + } + c.Close() +} + +func acceptor(t *testing.T, ln net.Listener, done chan<- bool) { + defer func() { done <- true }() + + c, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + c.Close() +} diff --git a/vendor/src/golang.org/x/net/ipv6/multicast_test.go b/vendor/src/golang.org/x/net/ipv6/multicast_test.go new file mode 100644 index 00000000..69a21cd3 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/multicast_test.go @@ -0,0 +1,264 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "bytes" + "net" + "os" + "runtime" + "testing" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +var packetConnReadWriteMulticastUDPTests = []struct { + addr string + grp, src *net.UDPAddr +}{ + {"[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727 + + {"[ff30::8000:0]:0", &net.UDPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.UDPAddr{IP: net.IPv6loopback}}, // see RFC 5771 +} + +func TestPacketConnReadWriteMulticastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if !nettest.SupportsIPv6MulticastDeliveryOnLoopback() { + t.Skipf("multicast delivery doesn't work correctly on %s", runtime.GOOS) + } + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range packetConnReadWriteMulticastUDPTests { + c, err := net.ListenPacket("udp6", tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + grp := *tt.grp + grp.Port = c.LocalAddr().(*net.UDPAddr).Port + p := ipv6.NewPacketConn(c) + defer p.Close() + if tt.src == nil { + if err := p.JoinGroup(ifi, &grp); err != nil { + t.Fatal(err) + } + defer p.LeaveGroup(ifi, &grp) + } else { + if err := p.JoinSourceSpecificGroup(ifi, &grp, tt.src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support MLDv2 fail here + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + defer p.LeaveSourceSpecificGroup(ifi, &grp, tt.src) + } + if err := p.SetMulticastInterface(ifi); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastInterface(); err != nil { + t.Fatal(err) + } + if err := p.SetMulticastLoopback(true); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatal(err) + } + + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + Src: net.IPv6loopback, + IfIndex: ifi.Index, + } + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + wb := []byte("HELLO-R-U-THERE") + + for i, toggle := range []bool{true, false, true} { + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatal(err) + } + cm.HopLimit = i + 1 + if n, err := p.WriteTo(wb, &cm, &grp); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatal(err) + } + rb := make([]byte, 128) + if n, _, _, err := p.ReadFrom(rb); err != nil { + t.Fatal(err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; want %v", rb[:n], wb) + } + } + } +} + +var packetConnReadWriteMulticastICMPTests = []struct { + grp, src *net.IPAddr +}{ + {&net.IPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727 + + {&net.IPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.IPAddr{IP: net.IPv6loopback}}, // see RFC 5771 +} + +func TestPacketConnReadWriteMulticastICMP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if !nettest.SupportsIPv6MulticastDeliveryOnLoopback() { + t.Skipf("multicast delivery doesn't work correctly on %s", runtime.GOOS) + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + for _, tt := range packetConnReadWriteMulticastICMPTests { + c, err := net.ListenPacket("ip6:ipv6-icmp", "::") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, tt.grp.IP) + p := ipv6.NewPacketConn(c) + defer p.Close() + if tt.src == nil { + if err := p.JoinGroup(ifi, tt.grp); err != nil { + t.Fatal(err) + } + defer p.LeaveGroup(ifi, tt.grp) + } else { + if err := p.JoinSourceSpecificGroup(ifi, tt.grp, tt.src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support MLDv2 fail here + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + defer p.LeaveSourceSpecificGroup(ifi, tt.grp, tt.src) + } + if err := p.SetMulticastInterface(ifi); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastInterface(); err != nil { + t.Fatal(err) + } + if err := p.SetMulticastLoopback(true); err != nil { + t.Fatal(err) + } + if _, err := p.MulticastLoopback(); err != nil { + t.Fatal(err) + } + + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + Src: net.IPv6loopback, + IfIndex: ifi.Index, + } + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + + var f ipv6.ICMPFilter + f.SetAll(true) + f.Accept(ipv6.ICMPTypeEchoReply) + if err := p.SetICMPFilter(&f); err != nil { + t.Fatal(err) + } + + var psh []byte + for i, toggle := range []bool{true, false, true} { + if toggle { + psh = nil + if err := p.SetChecksum(true, 2); err != nil { + // Solaris never allows to + // modify ICMP properties. + if runtime.GOOS != "solaris" { + t.Fatal(err) + } + } + } else { + psh = pshicmp + // Some platforms never allow to + // disable the kernel checksum + // processing. + p.SetChecksum(false, -1) + } + wb, err := (&icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(psh) + if err != nil { + t.Fatal(err) + } + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + if err := p.SetDeadline(time.Now().Add(200 * time.Millisecond)); err != nil { + t.Fatal(err) + } + cm.HopLimit = i + 1 + if n, err := p.WriteTo(wb, &cm, tt.grp); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if n, _, _, err := p.ReadFrom(rb); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } else { + if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil { + t.Fatal(err) + } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) + } + } + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/multicastlistener_test.go b/vendor/src/golang.org/x/net/ipv6/multicastlistener_test.go new file mode 100644 index 00000000..b27713e2 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/multicastlistener_test.go @@ -0,0 +1,261 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +var udpMultipleGroupListenerTests = []net.Addr{ + &net.UDPAddr{IP: net.ParseIP("ff02::114")}, // see RFC 4727 + &net.UDPAddr{IP: net.ParseIP("ff02::1:114")}, + &net.UDPAddr{IP: net.ParseIP("ff02::2:114")}, +} + +func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + for _, gaddr := range udpMultipleGroupListenerTests { + c, err := net.ListenPacket("udp6", "[::]:0") // wildcard address with non-reusable port + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv6.NewPacketConn(c) + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok { + continue + } + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatal(err) + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatal(err) + } + } + } +} + +func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + for _, gaddr := range udpMultipleGroupListenerTests { + c1, err := net.ListenPacket("udp6", "[ff02::]:0") // wildcard address with reusable port + if err != nil { + t.Fatal(err) + } + defer c1.Close() + _, port, err := net.SplitHostPort(c1.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + c2, err := net.ListenPacket("udp6", net.JoinHostPort("ff02::", port)) // wildcard address with reusable port + if err != nil { + t.Fatal(err) + } + defer c2.Close() + + var ps [2]*ipv6.PacketConn + ps[0] = ipv6.NewPacketConn(c1) + ps[1] = ipv6.NewPacketConn(c2) + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok { + continue + } + for _, p := range ps { + if err := p.JoinGroup(&ifi, gaddr); err != nil { + t.Fatal(err) + } + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + for _, p := range ps { + if err := p.LeaveGroup(ifi, gaddr); err != nil { + t.Fatal(err) + } + } + } + } +} + +func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + type ml struct { + c *ipv6.PacketConn + ifi *net.Interface + } + var mlt []*ml + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + port := "0" + for i, ifi := range ift { + ip, ok := nettest.IsMulticastCapable("ip6", &ifi) + if !ok { + continue + } + c, err := net.ListenPacket("udp6", net.JoinHostPort(ip.String()+"%"+ifi.Name, port)) // unicast address with non-reusable port + if err != nil { + // The listen may fail when the serivce is + // already in use, but it's fine because the + // purpose of this is not to test the + // bookkeeping of IP control block inside the + // kernel. + t.Log(err) + continue + } + defer c.Close() + if port == "0" { + _, port, err = net.SplitHostPort(c.LocalAddr().String()) + if err != nil { + t.Fatal(err) + } + } + p := ipv6.NewPacketConn(c) + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mlt = append(mlt, &ml{p, &ift[i]}) + } + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} + +func TestIPSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket("ip6:ipv6-icmp", "::") // wildcard address + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv6.NewPacketConn(c) + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + var mift []*net.Interface + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + if _, ok := nettest.IsMulticastCapable("ip6", &ifi); !ok { + continue + } + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mift = append(mift, &ift[i]) + } + for _, ifi := range mift { + if err := p.LeaveGroup(ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} + +func TestIPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) { + switch runtime.GOOS { + case "darwin", "dragonfly", "openbsd": // platforms that return fe80::1%lo0: bind: can't assign requested address + t.Skipf("not supported on %s", runtime.GOOS) + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + gaddr := net.IPAddr{IP: net.ParseIP("ff02::114")} // see RFC 4727 + type ml struct { + c *ipv6.PacketConn + ifi *net.Interface + } + var mlt []*ml + + ift, err := net.Interfaces() + if err != nil { + t.Fatal(err) + } + for i, ifi := range ift { + ip, ok := nettest.IsMulticastCapable("ip6", &ifi) + if !ok { + continue + } + c, err := net.ListenPacket("ip6:ipv6-icmp", ip.String()+"%"+ifi.Name) // unicast address + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + if err := p.JoinGroup(&ifi, &gaddr); err != nil { + t.Fatal(err) + } + mlt = append(mlt, &ml{p, &ift[i]}) + } + for _, m := range mlt { + if err := m.c.LeaveGroup(m.ifi, &gaddr); err != nil { + t.Fatal(err) + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/multicastsockopt_test.go b/vendor/src/golang.org/x/net/ipv6/multicastsockopt_test.go new file mode 100644 index 00000000..9e6b902d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/multicastsockopt_test.go @@ -0,0 +1,157 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +var packetConnMulticastSocketOptionTests = []struct { + net, proto, addr string + grp, src net.Addr +}{ + {"udp6", "", "[ff02::]:0", &net.UDPAddr{IP: net.ParseIP("ff02::114")}, nil}, // see RFC 4727 + {"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff02::115")}, nil}, // see RFC 4727 + + {"udp6", "", "[ff30::8000:0]:0", &net.UDPAddr{IP: net.ParseIP("ff30::8000:1")}, &net.UDPAddr{IP: net.IPv6loopback}}, // see RFC 5771 + {"ip6", ":ipv6-icmp", "::", &net.IPAddr{IP: net.ParseIP("ff30::8000:2")}, &net.IPAddr{IP: net.IPv6loopback}}, // see RFC 5771 +} + +func TestPacketConnMulticastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagMulticast|net.FlagLoopback) + if ifi == nil { + t.Skipf("not available on %s", runtime.GOOS) + } + + m, ok := nettest.SupportsRawIPSocket() + for _, tt := range packetConnMulticastSocketOptionTests { + if tt.net == "ip6" && !ok { + t.Log(m) + continue + } + c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() + + if tt.src == nil { + testMulticastSocketOptions(t, p, ifi, tt.grp) + } else { + testSourceSpecificMulticastSocketOptions(t, p, ifi, tt.grp, tt.src) + } + } +} + +type testIPv6MulticastConn interface { + MulticastHopLimit() (int, error) + SetMulticastHopLimit(ttl int) error + MulticastLoopback() (bool, error) + SetMulticastLoopback(bool) error + JoinGroup(*net.Interface, net.Addr) error + LeaveGroup(*net.Interface, net.Addr) error + JoinSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + LeaveSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + ExcludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error + IncludeSourceSpecificGroup(*net.Interface, net.Addr, net.Addr) error +} + +func testMulticastSocketOptions(t *testing.T, c testIPv6MulticastConn, ifi *net.Interface, grp net.Addr) { + const hoplim = 255 + if err := c.SetMulticastHopLimit(hoplim); err != nil { + t.Error(err) + return + } + if v, err := c.MulticastHopLimit(); err != nil { + t.Error(err) + return + } else if v != hoplim { + t.Errorf("got %v; want %v", v, hoplim) + return + } + + for _, toggle := range []bool{true, false} { + if err := c.SetMulticastLoopback(toggle); err != nil { + t.Error(err) + return + } + if v, err := c.MulticastLoopback(); err != nil { + t.Error(err) + return + } else if v != toggle { + t.Errorf("got %v; want %v", v, toggle) + return + } + } + + if err := c.JoinGroup(ifi, grp); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } +} + +func testSourceSpecificMulticastSocketOptions(t *testing.T, c testIPv6MulticastConn, ifi *net.Interface, grp, src net.Addr) { + // MCAST_JOIN_GROUP -> MCAST_BLOCK_SOURCE -> MCAST_UNBLOCK_SOURCE -> MCAST_LEAVE_GROUP + if err := c.JoinGroup(ifi, grp); err != nil { + t.Error(err) + return + } + if err := c.ExcludeSourceSpecificGroup(ifi, grp, src); err != nil { + switch runtime.GOOS { + case "freebsd", "linux": + default: // platforms that don't support MLDv2 fail here + t.Logf("not supported on %s", runtime.GOOS) + return + } + t.Error(err) + return + } + if err := c.IncludeSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } + + // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_SOURCE_GROUP + if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + + // MCAST_JOIN_SOURCE_GROUP -> MCAST_LEAVE_GROUP + if err := c.JoinSourceSpecificGroup(ifi, grp, src); err != nil { + t.Error(err) + return + } + if err := c.LeaveGroup(ifi, grp); err != nil { + t.Error(err) + return + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/payload.go b/vendor/src/golang.org/x/net/ipv6/payload.go new file mode 100644 index 00000000..a8197f16 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/payload.go @@ -0,0 +1,23 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo +// methods of PacketConn is not implemented. + +// A payloadHandler represents the IPv6 datagram payload handler. +type payloadHandler struct { + net.PacketConn + *socket.Conn + rawOpt +} + +func (c *payloadHandler) ok() bool { return c != nil && c.PacketConn != nil && c.Conn != nil } diff --git a/vendor/src/golang.org/x/net/ipv6/payload_cmsg.go b/vendor/src/golang.org/x/net/ipv6/payload_cmsg.go new file mode 100644 index 00000000..4ee4b062 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/payload_cmsg.go @@ -0,0 +1,35 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !nacl,!plan9,!windows + +package ipv6 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv6 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + return c.readFrom(b) +} + +// WriteTo writes a payload of the IPv6 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the IPv6 header fields and the datagram path to be specified. The +// cm may be nil if control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + return c.writeTo(b, cm, dst) +} diff --git a/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_8.go b/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_8.go new file mode 100644 index 00000000..fdc6c399 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_8.go @@ -0,0 +1,55 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 +// +build !nacl,!plan9,!windows + +package ipv6 + +import "net" + +func (c *payloadHandler) readFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + c.rawOpt.RLock() + oob := NewControlMessage(c.rawOpt.cflags) + c.rawOpt.RUnlock() + var nn int + switch c := c.PacketConn.(type) { + case *net.UDPConn: + if n, nn, _, src, err = c.ReadMsgUDP(b, oob); err != nil { + return 0, nil, nil, err + } + case *net.IPConn: + if n, nn, _, src, err = c.ReadMsgIP(b, oob); err != nil { + return 0, nil, nil, err + } + default: + return 0, nil, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Err: errInvalidConnType} + } + if nn > 0 { + cm = new(ControlMessage) + if err = cm.Parse(oob[:nn]); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + } + if cm != nil { + cm.Src = netAddrToIP16(src) + } + return +} + +func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + oob := cm.Marshal() + if dst == nil { + return 0, &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errMissingAddress} + } + switch c := c.PacketConn.(type) { + case *net.UDPConn: + n, _, err = c.WriteMsgUDP(b, oob, dst.(*net.UDPAddr)) + case *net.IPConn: + n, _, err = c.WriteMsgIP(b, oob, dst.(*net.IPAddr)) + default: + return 0, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: opAddr(dst), Err: errInvalidConnType} + } + return +} diff --git a/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_9.go b/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_9.go new file mode 100644 index 00000000..8f6d02e2 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/payload_cmsg_go1_9.go @@ -0,0 +1,57 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 +// +build !nacl,!plan9,!windows + +package ipv6 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (c *payloadHandler) readFrom(b []byte) (int, *ControlMessage, net.Addr, error) { + c.rawOpt.RLock() + m := socket.Message{ + Buffers: [][]byte{b}, + OOB: NewControlMessage(c.rawOpt.cflags), + } + c.rawOpt.RUnlock() + switch c.PacketConn.(type) { + case *net.UDPConn: + if err := c.RecvMsg(&m, 0); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + case *net.IPConn: + if err := c.RecvMsg(&m, 0); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + default: + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: errInvalidConnType} + } + var cm *ControlMessage + if m.NN > 0 { + cm = new(ControlMessage) + if err := cm.Parse(m.OOB[:m.NN]); err != nil { + return 0, nil, nil, &net.OpError{Op: "read", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Err: err} + } + cm.Src = netAddrToIP16(m.Addr) + } + return m.N, cm, m.Addr, nil +} + +func (c *payloadHandler) writeTo(b []byte, cm *ControlMessage, dst net.Addr) (int, error) { + m := socket.Message{ + Buffers: [][]byte{b}, + OOB: cm.Marshal(), + Addr: dst, + } + err := c.SendMsg(&m, 0) + if err != nil { + err = &net.OpError{Op: "write", Net: c.PacketConn.LocalAddr().Network(), Source: c.PacketConn.LocalAddr(), Addr: opAddr(dst), Err: err} + } + return m.N, err +} diff --git a/vendor/src/golang.org/x/net/ipv6/payload_nocmsg.go b/vendor/src/golang.org/x/net/ipv6/payload_nocmsg.go new file mode 100644 index 00000000..99a43542 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/payload_nocmsg.go @@ -0,0 +1,41 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl plan9 windows + +package ipv6 + +import ( + "net" + "syscall" +) + +// ReadFrom reads a payload of the received IPv6 datagram, from the +// endpoint c, copying the payload into b. It returns the number of +// bytes copied into b, the control message cm and the source address +// src of the received datagram. +func (c *payloadHandler) ReadFrom(b []byte) (n int, cm *ControlMessage, src net.Addr, err error) { + if !c.ok() { + return 0, nil, nil, syscall.EINVAL + } + if n, src, err = c.PacketConn.ReadFrom(b); err != nil { + return 0, nil, nil, err + } + return +} + +// WriteTo writes a payload of the IPv6 datagram, to the destination +// address dst through the endpoint c, copying the payload from b. It +// returns the number of bytes written. The control message cm allows +// the IPv6 header fields and the datagram path to be specified. The +// cm may be nil if control of the outgoing datagram is not required. +func (c *payloadHandler) WriteTo(b []byte, cm *ControlMessage, dst net.Addr) (n int, err error) { + if !c.ok() { + return 0, syscall.EINVAL + } + if dst == nil { + return 0, errMissingAddress + } + return c.PacketConn.WriteTo(b, dst) +} diff --git a/vendor/src/golang.org/x/net/ipv6/readwrite_go1_8_test.go b/vendor/src/golang.org/x/net/ipv6/readwrite_go1_8_test.go new file mode 100644 index 00000000..c11d92ae --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/readwrite_go1_8_test.go @@ -0,0 +1,242 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package ipv6_test + +import ( + "bytes" + "fmt" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +func BenchmarkPacketConnReadWriteUnicast(b *testing.B) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + b.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph := []byte{ + 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + } + greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + bb := make([]byte, 128) + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + Src: net.IPv6loopback, + } + if ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback); ifi != nil { + cm.IfIndex = ifi.Index + } + + b.Run("UDP", func(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv6.FlagHopLimit | ipv6.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(payload, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(payload, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + }) + b.Run("IP", func(b *testing.B) { + switch runtime.GOOS { + case "netbsd": + b.Skip("need to configure gre on netbsd") + case "openbsd": + b.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(datagram, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(datagram, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + }) +} + +func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph := []byte{ + 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + } + greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + + t.Run("UDP", func(t *testing.T) { + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr()) + }) + }) + t.Run("IP", func(t *testing.T) { + switch runtime.GOOS { + case "netbsd": + t.Skip("need to configure gre on netbsd") + case "openbsd": + t.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr()) + }) + }) +} + +func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr) { + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + b := make([]byte, 128) + n, cm, _, err := p.ReadFrom(b) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(b[:n], data) { + t.Errorf("got %#v; want %#v", b[:n], data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + Src: net.IPv6loopback, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + n, err := p.WriteTo(data, &cm, dst) + if err != nil { + t.Error(err) + return + } + if n != len(data) { + t.Errorf("got %d; want %d", n, len(data)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + go writer(i%2 != 0) + + } + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv6/readwrite_go1_9_test.go b/vendor/src/golang.org/x/net/ipv6/readwrite_go1_9_test.go new file mode 100644 index 00000000..e2fd7337 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/readwrite_go1_9_test.go @@ -0,0 +1,373 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package ipv6_test + +import ( + "bytes" + "fmt" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +func BenchmarkPacketConnReadWriteUnicast(b *testing.B) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + b.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph := []byte{ + 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + } + greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + bb := make([]byte, 128) + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + Src: net.IPv6loopback, + } + if ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback); ifi != nil { + cm.IfIndex = ifi.Index + } + + b.Run("UDP", func(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv6.FlagHopLimit | ipv6.FlagInterface + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + wms := []ipv6.Message{ + { + Buffers: [][]byte{payload}, + Addr: dst, + OOB: cm.Marshal(), + }, + } + rms := []ipv6.Message{ + { + Buffers: [][]byte{bb}, + OOB: ipv6.NewControlMessage(cf), + }, + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(payload, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(payload, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("Batch", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteBatch(wms, 0); err != nil { + b.Fatal(err) + } + if _, err := p.ReadBatch(rms, 0); err != nil { + b.Fatal(err) + } + } + }) + }) + b.Run("IP", func(b *testing.B) { + switch runtime.GOOS { + case "netbsd": + b.Skip("need to configure gre on netbsd") + case "openbsd": + b.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + dst := c.LocalAddr() + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + wms := []ipv6.Message{ + { + Buffers: [][]byte{datagram}, + Addr: dst, + OOB: cm.Marshal(), + }, + } + rms := []ipv6.Message{ + { + Buffers: [][]byte{bb}, + OOB: ipv6.NewControlMessage(cf), + }, + } + b.Run("Net", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(datagram, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("ToFrom", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(datagram, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(bb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("Batch", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := p.WriteBatch(wms, 0); err != nil { + b.Fatal(err) + } + if _, err := p.ReadBatch(rms, 0); err != nil { + b.Fatal(err) + } + } + }) + }) +} + +func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + + payload := []byte("HELLO-R-U-THERE") + iph := []byte{ + 0x69, 0x8b, 0xee, 0xf1, 0xca, 0xfe, 0xff, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + } + greh := []byte{0x00, 0x00, 0x86, 0xdd, 0x00, 0x00, 0x00, 0x00} + datagram := append(greh, append(iph, payload...)...) + + t.Run("UDP", func(t *testing.T) { + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), false) + }) + t.Run("Batch", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, payload, c.LocalAddr(), true) + }) + }) + t.Run("IP", func(t *testing.T) { + switch runtime.GOOS { + case "netbsd": + t.Skip("need to configure gre on netbsd") + case "openbsd": + t.Skip("net.inet.gre.allow=0 by default on openbsd") + } + + c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolGRE), "::1") + if err != nil { + t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + t.Run("ToFrom", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), false) + }) + t.Run("Batch", func(t *testing.T) { + testPacketConnConcurrentReadWriteUnicast(t, p, datagram, c.LocalAddr(), true) + }) + }) +} + +func testPacketConnConcurrentReadWriteUnicast(t *testing.T, p *ipv6.PacketConn, data []byte, dst net.Addr, batch bool) { + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + b := make([]byte, 128) + n, cm, _, err := p.ReadFrom(b) + if err != nil { + t.Error(err) + return + } + if !bytes.Equal(b[:n], data) { + t.Errorf("got %#v; want %#v", b[:n], data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + batchReader := func() { + defer wg.Done() + ms := []ipv6.Message{ + { + Buffers: [][]byte{make([]byte, 128)}, + OOB: ipv6.NewControlMessage(cf), + }, + } + n, err := p.ReadBatch(ms, 0) + if err != nil { + t.Error(err) + return + } + if n != len(ms) { + t.Errorf("got %d; want %d", n, len(ms)) + return + } + var cm ipv6.ControlMessage + if err := cm.Parse(ms[0].OOB[:ms[0].NN]); err != nil { + t.Error(err) + return + } + b := ms[0].Buffers[0][:ms[0].N] + if !bytes.Equal(b, data) { + t.Errorf("got %#v; want %#v", b, data) + return + } + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + return + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + Src: net.IPv6loopback, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + n, err := p.WriteTo(data, &cm, dst) + if err != nil { + t.Error(err) + return + } + if n != len(data) { + t.Errorf("got %d; want %d", n, len(data)) + return + } + } + batchWriter := func(toggle bool) { + defer wg.Done() + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + Src: net.IPv6loopback, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + ms := []ipv6.Message{ + { + Buffers: [][]byte{data}, + OOB: cm.Marshal(), + Addr: dst, + }, + } + n, err := p.WriteBatch(ms, 0) + if err != nil { + t.Error(err) + return + } + if n != len(ms) { + t.Errorf("got %d; want %d", n, len(ms)) + return + } + if ms[0].N != len(data) { + t.Errorf("got %d; want %d", ms[0].N, len(data)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + if batch { + go batchReader() + } else { + go reader() + } + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + if batch { + go batchWriter(i%2 != 0) + } else { + go writer(i%2 != 0) + } + } + wg.Add(N) + for i := 0; i < N; i++ { + if batch { + go batchReader() + } else { + go reader() + } + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv6/readwrite_test.go b/vendor/src/golang.org/x/net/ipv6/readwrite_test.go new file mode 100644 index 00000000..206b915c --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/readwrite_test.go @@ -0,0 +1,148 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "bytes" + "net" + "runtime" + "strings" + "sync" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +func BenchmarkReadWriteUnicast(b *testing.B) { + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) + } + defer c.Close() + + dst := c.LocalAddr() + wb, rb := []byte("HELLO-R-U-THERE"), make([]byte, 128) + + b.Run("NetUDP", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := c.WriteTo(wb, dst); err != nil { + b.Fatal(err) + } + if _, _, err := c.ReadFrom(rb); err != nil { + b.Fatal(err) + } + } + }) + b.Run("IPv6UDP", func(b *testing.B) { + p := ipv6.NewPacketConn(c) + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + if err := p.SetControlMessage(cf, true); err != nil { + b.Fatal(err) + } + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + HopLimit: 1, + } + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + if ifi != nil { + cm.IfIndex = ifi.Index + } + + for i := 0; i < b.N; i++ { + if _, err := p.WriteTo(wb, &cm, dst); err != nil { + b.Fatal(err) + } + if _, _, _, err := p.ReadFrom(rb); err != nil { + b.Fatal(err) + } + } + }) +} + +func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() + + dst := c.LocalAddr() + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + wb := []byte("HELLO-R-U-THERE") + + if err := p.SetControlMessage(cf, true); err != nil { // probe before test + if nettest.ProtocolNotSupported(err) { + t.Skipf("not supported on %s", runtime.GOOS) + } + t.Fatal(err) + } + + var wg sync.WaitGroup + reader := func() { + defer wg.Done() + rb := make([]byte, 128) + if n, cm, _, err := p.ReadFrom(rb); err != nil { + t.Error(err) + return + } else if !bytes.Equal(rb[:n], wb) { + t.Errorf("got %v; want %v", rb[:n], wb) + return + } else { + s := cm.String() + if strings.Contains(s, ",") { + t.Errorf("should be space-separated values: %s", s) + } + } + } + writer := func(toggle bool) { + defer wg.Done() + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + Src: net.IPv6loopback, + } + if ifi != nil { + cm.IfIndex = ifi.Index + } + if err := p.SetControlMessage(cf, toggle); err != nil { + t.Error(err) + return + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + t.Error(err) + return + } else if n != len(wb) { + t.Errorf("got %d; want %d", n, len(wb)) + return + } + } + + const N = 10 + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Add(2 * N) + for i := 0; i < 2*N; i++ { + go writer(i%2 != 0) + } + wg.Add(N) + for i := 0; i < N; i++ { + go reader() + } + wg.Wait() +} diff --git a/vendor/src/golang.org/x/net/ipv6/sockopt.go b/vendor/src/golang.org/x/net/ipv6/sockopt.go new file mode 100644 index 00000000..cc3907df --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sockopt.go @@ -0,0 +1,43 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import "golang.org/x/net/internal/socket" + +// Sticky socket options +const ( + ssoTrafficClass = iota // header field for unicast packet, RFC 3542 + ssoHopLimit // header field for unicast packet, RFC 3493 + ssoMulticastInterface // outbound interface for multicast packet, RFC 3493 + ssoMulticastHopLimit // header field for multicast packet, RFC 3493 + ssoMulticastLoopback // loopback for multicast packet, RFC 3493 + ssoReceiveTrafficClass // header field on received packet, RFC 3542 + ssoReceiveHopLimit // header field on received packet, RFC 2292 or 3542 + ssoReceivePacketInfo // incbound or outbound packet path, RFC 2292 or 3542 + ssoReceivePathMTU // path mtu, RFC 3542 + ssoPathMTU // path mtu, RFC 3542 + ssoChecksum // packet checksum, RFC 2292 or 3542 + ssoICMPFilter // icmp filter, RFC 2292 or 3542 + ssoJoinGroup // any-source multicast, RFC 3493 + ssoLeaveGroup // any-source multicast, RFC 3493 + ssoJoinSourceGroup // source-specific multicast + ssoLeaveSourceGroup // source-specific multicast + ssoBlockSourceGroup // any-source or source-specific multicast + ssoUnblockSourceGroup // any-source or source-specific multicast + ssoAttachFilter // attach BPF for filtering inbound traffic +) + +// Sticky socket option value types +const ( + ssoTypeIPMreq = iota + 1 + ssoTypeGroupReq + ssoTypeGroupSourceReq +) + +// A sockOpt represents a binding for sticky socket option. +type sockOpt struct { + socket.Option + typ int // hint for option value type; optional +} diff --git a/vendor/src/golang.org/x/net/ipv6/sockopt_posix.go b/vendor/src/golang.org/x/net/ipv6/sockopt_posix.go new file mode 100644 index 00000000..0eac86eb --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sockopt_posix.go @@ -0,0 +1,87 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package ipv6 + +import ( + "net" + "unsafe" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getMulticastInterface(c *socket.Conn) (*net.Interface, error) { + n, err := so.GetInt(c) + if err != nil { + return nil, err + } + return net.InterfaceByIndex(n) +} + +func (so *sockOpt) setMulticastInterface(c *socket.Conn, ifi *net.Interface) error { + var n int + if ifi != nil { + n = ifi.Index + } + return so.SetInt(c, n) +} + +func (so *sockOpt) getICMPFilter(c *socket.Conn) (*ICMPFilter, error) { + b := make([]byte, so.Len) + n, err := so.Get(c, b) + if err != nil { + return nil, err + } + if n != sizeofICMPv6Filter { + return nil, errOpNoSupport + } + return (*ICMPFilter)(unsafe.Pointer(&b[0])), nil +} + +func (so *sockOpt) setICMPFilter(c *socket.Conn, f *ICMPFilter) error { + b := (*[sizeofICMPv6Filter]byte)(unsafe.Pointer(f))[:sizeofICMPv6Filter] + return so.Set(c, b) +} + +func (so *sockOpt) getMTUInfo(c *socket.Conn) (*net.Interface, int, error) { + b := make([]byte, so.Len) + n, err := so.Get(c, b) + if err != nil { + return nil, 0, err + } + if n != sizeofIPv6Mtuinfo { + return nil, 0, errOpNoSupport + } + mi := (*ipv6Mtuinfo)(unsafe.Pointer(&b[0])) + if mi.Addr.Scope_id == 0 { + return nil, int(mi.Mtu), nil + } + ifi, err := net.InterfaceByIndex(int(mi.Addr.Scope_id)) + if err != nil { + return nil, 0, err + } + return ifi, int(mi.Mtu), nil +} + +func (so *sockOpt) setGroup(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + switch so.typ { + case ssoTypeIPMreq: + return so.setIPMreq(c, ifi, grp) + case ssoTypeGroupReq: + return so.setGroupReq(c, ifi, grp) + default: + return errOpNoSupport + } +} + +func (so *sockOpt) setSourceGroup(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return so.setGroupSourceReq(c, ifi, grp, src) +} + +func (so *sockOpt) setBPF(c *socket.Conn, f []bpf.RawInstruction) error { + return so.setAttachFilter(c, f) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sockopt_stub.go b/vendor/src/golang.org/x/net/ipv6/sockopt_stub.go new file mode 100644 index 00000000..1f4a273e --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sockopt_stub.go @@ -0,0 +1,46 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv6 + +import ( + "net" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) getMulticastInterface(c *socket.Conn) (*net.Interface, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setMulticastInterface(c *socket.Conn, ifi *net.Interface) error { + return errOpNoSupport +} + +func (so *sockOpt) getICMPFilter(c *socket.Conn) (*ICMPFilter, error) { + return nil, errOpNoSupport +} + +func (so *sockOpt) setICMPFilter(c *socket.Conn, f *ICMPFilter) error { + return errOpNoSupport +} + +func (so *sockOpt) getMTUInfo(c *socket.Conn) (*net.Interface, int, error) { + return nil, 0, errOpNoSupport +} + +func (so *sockOpt) setGroup(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setSourceGroup(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setBPF(c *socket.Conn, f []bpf.RawInstruction) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv6/sockopt_test.go b/vendor/src/golang.org/x/net/ipv6/sockopt_test.go new file mode 100644 index 00000000..774338db --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sockopt_test.go @@ -0,0 +1,133 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "fmt" + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +var supportsIPv6 bool = nettest.SupportsIPv6() + +func TestConnInitiatorPathMTU(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + ln, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + done := make(chan bool) + go acceptor(t, ln, done) + + c, err := net.Dial("tcp6", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels don't support IPV6_PATHMTU option + t.Logf("not supported on %s", runtime.GOOS) + default: + t.Fatal(err) + } + } else { + t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu) + } + + <-done +} + +func TestConnResponderPathMTU(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + ln, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + done := make(chan bool) + go connector(t, "tcp6", ln.Addr().String(), done) + + c, err := ln.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + if pmtu, err := ipv6.NewConn(c).PathMTU(); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels don't support IPV6_PATHMTU option + t.Logf("not supported on %s", runtime.GOOS) + default: + t.Fatal(err) + } + } else { + t.Logf("path mtu for %v: %v", c.RemoteAddr(), pmtu) + } + + <-done +} + +func TestPacketConnChecksum(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket(fmt.Sprintf("ip6:%d", iana.ProtocolOSPFIGP), "::") // OSPF for IPv6 + if err != nil { + t.Fatal(err) + } + defer c.Close() + + p := ipv6.NewPacketConn(c) + offset := 12 // see RFC 5340 + + for _, toggle := range []bool{false, true} { + if err := p.SetChecksum(toggle, offset); err != nil { + if toggle { + t.Fatalf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err) + } else { + // Some platforms never allow to disable the kernel + // checksum processing. + t.Logf("ipv6.PacketConn.SetChecksum(%v, %v) failed: %v", toggle, offset, err) + } + } + if on, offset, err := p.Checksum(); err != nil { + t.Fatal(err) + } else { + t.Logf("kernel checksum processing enabled=%v, offset=%v", on, offset) + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_asmreq.go b/vendor/src/golang.org/x/net/ipv6/sys_asmreq.go new file mode 100644 index 00000000..b0510c0b --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_asmreq.go @@ -0,0 +1,24 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows + +package ipv6 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + var mreq ipv6Mreq + copy(mreq.Multiaddr[:], grp) + if ifi != nil { + mreq.setIfindex(ifi.Index) + } + b := (*[sizeofIPv6Mreq]byte)(unsafe.Pointer(&mreq))[:sizeofIPv6Mreq] + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_asmreq_stub.go b/vendor/src/golang.org/x/net/ipv6/sys_asmreq_stub.go new file mode 100644 index 00000000..eece9618 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_asmreq_stub.go @@ -0,0 +1,17 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv6 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setIPMreq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_bpf.go b/vendor/src/golang.org/x/net/ipv6/sys_bpf.go new file mode 100644 index 00000000..b2dbcb2f --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_bpf.go @@ -0,0 +1,23 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package ipv6 + +import ( + "unsafe" + + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error { + prog := sockFProg{ + Len: uint16(len(f)), + Filter: (*sockFilter)(unsafe.Pointer(&f[0])), + } + b := (*[sizeofSockFprog]byte)(unsafe.Pointer(&prog))[:sizeofSockFprog] + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_bpf_stub.go b/vendor/src/golang.org/x/net/ipv6/sys_bpf_stub.go new file mode 100644 index 00000000..676bea55 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_bpf_stub.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !linux + +package ipv6 + +import ( + "golang.org/x/net/bpf" + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setAttachFilter(c *socket.Conn, f []bpf.RawInstruction) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_bsd.go b/vendor/src/golang.org/x/net/ipv6/sys_bsd.go new file mode 100644 index 00000000..e416eaa1 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_bsd.go @@ -0,0 +1,57 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dragonfly netbsd openbsd + +package ipv6 + +import ( + "net" + "syscall" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass}, + ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit}, + ctlPacketInfo: {sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo}, + ctlNextHop: {sysIPV6_NEXTHOP, sizeofSockaddrInet6, marshalNextHop, parseNextHop}, + ctlPathMTU: {sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU}, + } + + sockOpts = map[int]*sockOpt{ + ssoTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}}, + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}}, + ssoReceiveHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}}, + ssoReceivePacketInfo: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}}, + ssoReceivePathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}}, + ssoPathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_PATHMTU, Len: sizeofIPv6Mtuinfo}}, + ssoChecksum: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_CHECKSUM, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMP6_FILTER, Len: sizeofICMPv6Filter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_JOIN_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_LEAVE_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + } +) + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (pi *inet6Pktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Interface = uint32(i) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_darwin.go b/vendor/src/golang.org/x/net/ipv6/sys_darwin.go new file mode 100644 index 00000000..e3d04439 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_darwin.go @@ -0,0 +1,106 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "strconv" + "strings" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlHopLimit: {sysIPV6_2292HOPLIMIT, 4, marshal2292HopLimit, parseHopLimit}, + ctlPacketInfo: {sysIPV6_2292PKTINFO, sizeofInet6Pktinfo, marshal2292PacketInfo, parsePacketInfo}, + } + + sockOpts = map[int]*sockOpt{ + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoReceiveHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_2292HOPLIMIT, Len: 4}}, + ssoReceivePacketInfo: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_2292PKTINFO, Len: 4}}, + ssoChecksum: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_CHECKSUM, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMP6_FILTER, Len: sizeofICMPv6Filter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_JOIN_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_LEAVE_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + } +) + +func init() { + // Seems like kern.osreldate is veiled on latest OS X. We use + // kern.osrelease instead. + s, err := syscall.Sysctl("kern.osrelease") + if err != nil { + return + } + ss := strings.Split(s, ".") + if len(ss) == 0 { + return + } + // The IP_PKTINFO and protocol-independent multicast API were + // introduced in OS X 10.7 (Darwin 11). But it looks like + // those features require OS X 10.8 (Darwin 12) or above. + // See http://support.apple.com/kb/HT1633. + if mjver, err := strconv.Atoi(ss[0]); err != nil || mjver < 12 { + return + } + ctlOpts[ctlTrafficClass] = ctlOpt{sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass} + ctlOpts[ctlHopLimit] = ctlOpt{sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit} + ctlOpts[ctlPacketInfo] = ctlOpt{sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo} + ctlOpts[ctlNextHop] = ctlOpt{sysIPV6_NEXTHOP, sizeofSockaddrInet6, marshalNextHop, parseNextHop} + ctlOpts[ctlPathMTU] = ctlOpt{sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU} + sockOpts[ssoTrafficClass] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}} + sockOpts[ssoReceiveTrafficClass] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}} + sockOpts[ssoReceiveHopLimit] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}} + sockOpts[ssoReceivePacketInfo] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}} + sockOpts[ssoReceivePathMTU] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}} + sockOpts[ssoPathMTU] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_PATHMTU, Len: sizeofIPv6Mtuinfo}} + sockOpts[ssoJoinGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq} + sockOpts[ssoLeaveGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq} + sockOpts[ssoJoinSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoLeaveSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoBlockSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} + sockOpts[ssoUnblockSourceGroup] = &sockOpt{Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq} +} + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (pi *inet6Pktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Interface = uint32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) + sa = (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 132)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_freebsd.go b/vendor/src/golang.org/x/net/ipv6/sys_freebsd.go new file mode 100644 index 00000000..e9349dc2 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_freebsd.go @@ -0,0 +1,92 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "runtime" + "strings" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass}, + ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit}, + ctlPacketInfo: {sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo}, + ctlNextHop: {sysIPV6_NEXTHOP, sizeofSockaddrInet6, marshalNextHop, parseNextHop}, + ctlPathMTU: {sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU}, + } + + sockOpts = map[int]sockOpt{ + ssoTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}}, + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}}, + ssoReceiveHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}}, + ssoReceivePacketInfo: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}}, + ssoReceivePathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}}, + ssoPathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_PATHMTU, Len: sizeofIPv6Mtuinfo}}, + ssoChecksum: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_CHECKSUM, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMP6_FILTER, Len: sizeofICMPv6Filter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + } +) + +func init() { + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + archs, _ := syscall.Sysctl("kern.supported_archs") + for _, s := range strings.Fields(archs) { + if s == "amd64" { + freebsd32o64 = true + break + } + } + } +} + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (pi *inet6Pktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Interface = uint32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(&gr.Group)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(&gsr.Group)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) + sa = (*sockaddrInet6)(unsafe.Pointer(&gsr.Source)) + sa.Len = sizeofSockaddrInet6 + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_linux.go b/vendor/src/golang.org/x/net/ipv6/sys_linux.go new file mode 100644 index 00000000..bc218103 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_linux.go @@ -0,0 +1,74 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass}, + ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit}, + ctlPacketInfo: {sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo}, + ctlPathMTU: {sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU}, + } + + sockOpts = map[int]*sockOpt{ + ssoTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}}, + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}}, + ssoReceiveHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}}, + ssoReceivePacketInfo: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}}, + ssoReceivePathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}}, + ssoPathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_PATHMTU, Len: sizeofIPv6Mtuinfo}}, + ssoChecksum: {Option: socket.Option{Level: iana.ProtocolReserved, Name: sysIPV6_CHECKSUM, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMPV6_FILTER, Len: sizeofICMPv6Filter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoAttachFilter: {Option: socket.Option{Level: sysSOL_SOCKET, Name: sysSO_ATTACH_FILTER, Len: sizeofSockFprog}}, + } +) + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (pi *inet6Pktinfo) setIfindex(i int) { + pi.Ifindex = int32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Ifindex = int32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(&gr.Group)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(&gsr.Group)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) + sa = (*sockaddrInet6)(unsafe.Pointer(&gsr.Source)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_solaris.go b/vendor/src/golang.org/x/net/ipv6/sys_solaris.go new file mode 100644 index 00000000..d348b5f6 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_solaris.go @@ -0,0 +1,74 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +var ( + ctlOpts = [ctlMax]ctlOpt{ + ctlTrafficClass: {sysIPV6_TCLASS, 4, marshalTrafficClass, parseTrafficClass}, + ctlHopLimit: {sysIPV6_HOPLIMIT, 4, marshalHopLimit, parseHopLimit}, + ctlPacketInfo: {sysIPV6_PKTINFO, sizeofInet6Pktinfo, marshalPacketInfo, parsePacketInfo}, + ctlNextHop: {sysIPV6_NEXTHOP, sizeofSockaddrInet6, marshalNextHop, parseNextHop}, + ctlPathMTU: {sysIPV6_PATHMTU, sizeofIPv6Mtuinfo, marshalPathMTU, parsePathMTU}, + } + + sockOpts = map[int]*sockOpt{ + ssoTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_TCLASS, Len: 4}}, + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoReceiveTrafficClass: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVTCLASS, Len: 4}}, + ssoReceiveHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVHOPLIMIT, Len: 4}}, + ssoReceivePacketInfo: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPKTINFO, Len: 4}}, + ssoReceivePathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_RECVPATHMTU, Len: 4}}, + ssoPathMTU: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_PATHMTU, Len: sizeofIPv6Mtuinfo}}, + ssoChecksum: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_CHECKSUM, Len: 4}}, + ssoICMPFilter: {Option: socket.Option{Level: iana.ProtocolIPv6ICMP, Name: sysICMP6_FILTER, Len: sizeofICMPv6Filter}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_GROUP, Len: sizeofGroupReq}, typ: ssoTypeGroupReq}, + ssoJoinSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_JOIN_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoLeaveSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_LEAVE_SOURCE_GROUP, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoBlockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_BLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + ssoUnblockSourceGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysMCAST_UNBLOCK_SOURCE, Len: sizeofGroupSourceReq}, typ: ssoTypeGroupSourceReq}, + } +) + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (pi *inet6Pktinfo) setIfindex(i int) { + pi.Ifindex = uint32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Interface = uint32(i) +} + +func (gr *groupReq) setGroup(grp net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gr)) + 4)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) +} + +func (gsr *groupSourceReq) setSourceGroup(grp, src net.IP) { + sa := (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 4)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], grp) + sa = (*sockaddrInet6)(unsafe.Pointer(uintptr(unsafe.Pointer(gsr)) + 260)) + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], src) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_ssmreq.go b/vendor/src/golang.org/x/net/ipv6/sys_ssmreq.go new file mode 100644 index 00000000..add8ccc0 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_ssmreq.go @@ -0,0 +1,54 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd linux solaris + +package ipv6 + +import ( + "net" + "unsafe" + + "golang.org/x/net/internal/socket" +) + +var freebsd32o64 bool + +func (so *sockOpt) setGroupReq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + var gr groupReq + if ifi != nil { + gr.Interface = uint32(ifi.Index) + } + gr.setGroup(grp) + var b []byte + if freebsd32o64 { + var d [sizeofGroupReq + 4]byte + s := (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr)) + copy(d[:4], s[:4]) + copy(d[8:], s[4:]) + b = d[:] + } else { + b = (*[sizeofGroupReq]byte)(unsafe.Pointer(&gr))[:sizeofGroupReq] + } + return so.Set(c, b) +} + +func (so *sockOpt) setGroupSourceReq(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + var gsr groupSourceReq + if ifi != nil { + gsr.Interface = uint32(ifi.Index) + } + gsr.setSourceGroup(grp, src) + var b []byte + if freebsd32o64 { + var d [sizeofGroupSourceReq + 4]byte + s := (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr)) + copy(d[:4], s[:4]) + copy(d[8:], s[4:]) + b = d[:] + } else { + b = (*[sizeofGroupSourceReq]byte)(unsafe.Pointer(&gsr))[:sizeofGroupSourceReq] + } + return so.Set(c, b) +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_ssmreq_stub.go b/vendor/src/golang.org/x/net/ipv6/sys_ssmreq_stub.go new file mode 100644 index 00000000..581ee490 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_ssmreq_stub.go @@ -0,0 +1,21 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!freebsd,!linux,!solaris + +package ipv6 + +import ( + "net" + + "golang.org/x/net/internal/socket" +) + +func (so *sockOpt) setGroupReq(c *socket.Conn, ifi *net.Interface, grp net.IP) error { + return errOpNoSupport +} + +func (so *sockOpt) setGroupSourceReq(c *socket.Conn, ifi *net.Interface, grp, src net.IP) error { + return errOpNoSupport +} diff --git a/vendor/src/golang.org/x/net/ipv6/sys_stub.go b/vendor/src/golang.org/x/net/ipv6/sys_stub.go new file mode 100644 index 00000000..b845388e --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_stub.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows + +package ipv6 + +var ( + ctlOpts = [ctlMax]ctlOpt{} + + sockOpts = map[int]*sockOpt{} +) diff --git a/vendor/src/golang.org/x/net/ipv6/sys_windows.go b/vendor/src/golang.org/x/net/ipv6/sys_windows.go new file mode 100644 index 00000000..fc36b018 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/sys_windows.go @@ -0,0 +1,75 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6 + +import ( + "net" + "syscall" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/socket" +) + +const ( + // See ws2tcpip.h. + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PKTINFO = 0x13 + + sizeofSockaddrInet6 = 0x1c + + sizeofIPv6Mreq = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofICMPv6Filter = 0 +) + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type icmpv6Filter struct { + // TODO(mikio): implement this +} + +var ( + ctlOpts = [ctlMax]ctlOpt{} + + sockOpts = map[int]*sockOpt{ + ssoHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_UNICAST_HOPS, Len: 4}}, + ssoMulticastInterface: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_IF, Len: 4}}, + ssoMulticastHopLimit: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_HOPS, Len: 4}}, + ssoMulticastLoopback: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_MULTICAST_LOOP, Len: 4}}, + ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_JOIN_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIPv6, Name: sysIPV6_LEAVE_GROUP, Len: sizeofIPv6Mreq}, typ: ssoTypeIPMreq}, + } +) + +func (sa *sockaddrInet6) setSockaddr(ip net.IP, i int) { + sa.Family = syscall.AF_INET6 + copy(sa.Addr[:], ip) + sa.Scope_id = uint32(i) +} + +func (mreq *ipv6Mreq) setIfindex(i int) { + mreq.Interface = uint32(i) +} diff --git a/vendor/src/golang.org/x/net/ipv6/unicast_test.go b/vendor/src/golang.org/x/net/ipv6/unicast_test.go new file mode 100644 index 00000000..a0b7d955 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/unicast_test.go @@ -0,0 +1,184 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "bytes" + "net" + "os" + "runtime" + "testing" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +func TestPacketConnReadWriteUnicastUDP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + c, err := nettest.NewLocalPacketListener("udp6") + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() + + dst := c.LocalAddr() + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + Src: net.IPv6loopback, + } + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + if ifi != nil { + cm.IfIndex = ifi.Index + } + wb := []byte("HELLO-R-U-THERE") + + for i, toggle := range []bool{true, false, true} { + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + cm.HopLimit = i + 1 + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, _, _, err := p.ReadFrom(rb); err != nil { + t.Fatal(err) + } else if !bytes.Equal(rb[:n], wb) { + t.Fatalf("got %v; want %v", rb[:n], wb) + } + } +} + +func TestPacketConnReadWriteUnicastICMP(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + if m, ok := nettest.SupportsRawIPSocket(); !ok { + t.Skip(m) + } + + c, err := net.ListenPacket("ip6:ipv6-icmp", "::1") + if err != nil { + t.Fatal(err) + } + defer c.Close() + p := ipv6.NewPacketConn(c) + defer p.Close() + + dst, err := net.ResolveIPAddr("ip6", "::1") + if err != nil { + t.Fatal(err) + } + + pshicmp := icmp.IPv6PseudoHeader(c.LocalAddr().(*net.IPAddr).IP, dst.IP) + cm := ipv6.ControlMessage{ + TrafficClass: iana.DiffServAF11 | iana.CongestionExperienced, + Src: net.IPv6loopback, + } + cf := ipv6.FlagTrafficClass | ipv6.FlagHopLimit | ipv6.FlagSrc | ipv6.FlagDst | ipv6.FlagInterface | ipv6.FlagPathMTU + ifi := nettest.RoutedInterface("ip6", net.FlagUp|net.FlagLoopback) + if ifi != nil { + cm.IfIndex = ifi.Index + } + + var f ipv6.ICMPFilter + f.SetAll(true) + f.Accept(ipv6.ICMPTypeEchoReply) + if err := p.SetICMPFilter(&f); err != nil { + t.Fatal(err) + } + + var psh []byte + for i, toggle := range []bool{true, false, true} { + if toggle { + psh = nil + if err := p.SetChecksum(true, 2); err != nil { + // Solaris never allows to modify + // ICMP properties. + if runtime.GOOS != "solaris" { + t.Fatal(err) + } + } + } else { + psh = pshicmp + // Some platforms never allow to disable the + // kernel checksum processing. + p.SetChecksum(false, -1) + } + wb, err := (&icmp.Message{ + Type: ipv6.ICMPTypeEchoRequest, Code: 0, + Body: &icmp.Echo{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("HELLO-R-U-THERE"), + }, + }).Marshal(psh) + if err != nil { + t.Fatal(err) + } + if err := p.SetControlMessage(cf, toggle); err != nil { + if nettest.ProtocolNotSupported(err) { + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } + cm.HopLimit = i + 1 + if err := p.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, err := p.WriteTo(wb, &cm, dst); err != nil { + t.Fatal(err) + } else if n != len(wb) { + t.Fatalf("got %v; want %v", n, len(wb)) + } + rb := make([]byte, 128) + if err := p.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil { + t.Fatal(err) + } + if n, _, _, err := p.ReadFrom(rb); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels have some limitation on receiving icmp packet through raw socket + t.Logf("not supported on %s", runtime.GOOS) + continue + } + t.Fatal(err) + } else { + if m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, rb[:n]); err != nil { + t.Fatal(err) + } else if m.Type != ipv6.ICMPTypeEchoReply || m.Code != 0 { + t.Fatalf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv6.ICMPTypeEchoReply, 0) + } + } + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/unicastsockopt_test.go b/vendor/src/golang.org/x/net/ipv6/unicastsockopt_test.go new file mode 100644 index 00000000..e175dccf --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/unicastsockopt_test.go @@ -0,0 +1,120 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ipv6_test + +import ( + "net" + "runtime" + "testing" + + "golang.org/x/net/internal/iana" + "golang.org/x/net/internal/nettest" + "golang.org/x/net/ipv6" +) + +func TestConnUnicastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + ln, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func() { + c, err := ln.Accept() + if err != nil { + errc <- err + return + } + errc <- c.Close() + }() + + c, err := net.Dial("tcp6", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + testUnicastSocketOptions(t, ipv6.NewConn(c)) + + if err := <-errc; err != nil { + t.Errorf("server: %v", err) + } +} + +var packetConnUnicastSocketOptionTests = []struct { + net, proto, addr string +}{ + {"udp6", "", "[::1]:0"}, + {"ip6", ":ipv6-icmp", "::1"}, +} + +func TestPacketConnUnicastSocketOptions(t *testing.T) { + switch runtime.GOOS { + case "nacl", "plan9", "windows": + t.Skipf("not supported on %s", runtime.GOOS) + } + if !supportsIPv6 { + t.Skip("ipv6 is not supported") + } + + m, ok := nettest.SupportsRawIPSocket() + for _, tt := range packetConnUnicastSocketOptionTests { + if tt.net == "ip6" && !ok { + t.Log(m) + continue + } + c, err := net.ListenPacket(tt.net+tt.proto, tt.addr) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + testUnicastSocketOptions(t, ipv6.NewPacketConn(c)) + } +} + +type testIPv6UnicastConn interface { + TrafficClass() (int, error) + SetTrafficClass(int) error + HopLimit() (int, error) + SetHopLimit(int) error +} + +func testUnicastSocketOptions(t *testing.T, c testIPv6UnicastConn) { + tclass := iana.DiffServCS0 | iana.NotECNTransport + if err := c.SetTrafficClass(tclass); err != nil { + switch runtime.GOOS { + case "darwin": // older darwin kernels don't support IPV6_TCLASS option + t.Logf("not supported on %s", runtime.GOOS) + goto next + } + t.Fatal(err) + } + if v, err := c.TrafficClass(); err != nil { + t.Fatal(err) + } else if v != tclass { + t.Fatalf("got %v; want %v", v, tclass) + } + +next: + hoplim := 255 + if err := c.SetHopLimit(hoplim); err != nil { + t.Fatal(err) + } + if v, err := c.HopLimit(); err != nil { + t.Fatal(err) + } else if v != hoplim { + t.Fatalf("got %v; want %v", v, hoplim) + } +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_darwin.go b/vendor/src/golang.org/x/net/ipv6/zsys_darwin.go new file mode 100644 index 00000000..6aab1dfa --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_darwin.go @@ -0,0 +1,131 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_darwin.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + sysIPV6_2292PKTINFO = 0x13 + sysIPV6_2292HOPLIMIT = 0x14 + sysIPV6_2292NEXTHOP = 0x15 + sysIPV6_2292HOPOPTS = 0x16 + sysIPV6_2292DSTOPTS = 0x17 + sysIPV6_2292RTHDR = 0x18 + + sysIPV6_2292PKTOPTIONS = 0x19 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RECVTCLASS = 0x23 + sysIPV6_TCLASS = 0x24 + + sysIPV6_RTHDRDSTOPTS = 0x39 + + sysIPV6_RECVPKTINFO = 0x3d + + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_AUTOFLOWLABEL = 0x3b + + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PREFER_TEMPADDR = 0x3f + + sysIPV6_MSFILTER = 0x4a + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sysIPV6_BOUND_IF = 0x7d + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type icmpv6Filter struct { + Filt [8]uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [128]byte +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [128]byte + Pad_cgo_1 [128]byte +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_dragonfly.go b/vendor/src/golang.org/x/net/ipv6/zsys_dragonfly.go new file mode 100644 index 00000000..d2de804d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_dragonfly.go @@ -0,0 +1,88 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_dragonfly.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RTHDRDSTOPTS = 0x23 + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_AUTOFLOWLABEL = 0x3b + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PREFER_TEMPADDR = 0x3f + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_386.go b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_386.go new file mode 100644 index 00000000..919e572d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_386.go @@ -0,0 +1,122 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RTHDRDSTOPTS = 0x23 + + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_AUTOFLOWLABEL = 0x3b + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PREFER_TEMPADDR = 0x3f + + sysIPV6_BINDANY = 0x40 + + sysIPV6_MSFILTER = 0x4a + + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type groupReq struct { + Interface uint32 + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group sockaddrStorage + Source sockaddrStorage +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_amd64.go b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_amd64.go new file mode 100644 index 00000000..cb8141f9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_amd64.go @@ -0,0 +1,124 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RTHDRDSTOPTS = 0x23 + + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_AUTOFLOWLABEL = 0x3b + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PREFER_TEMPADDR = 0x3f + + sysIPV6_BINDANY = 0x40 + + sysIPV6_MSFILTER = 0x4a + + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage + Source sockaddrStorage +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_arm.go b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_arm.go new file mode 100644 index 00000000..cb8141f9 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_freebsd_arm.go @@ -0,0 +1,124 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_freebsd.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RTHDRDSTOPTS = 0x23 + + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_AUTOFLOWLABEL = 0x3b + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PREFER_TEMPADDR = 0x3f + + sysIPV6_BINDANY = 0x40 + + sysIPV6_MSFILTER = 0x4a + + sysMCAST_JOIN_GROUP = 0x50 + sysMCAST_LEAVE_GROUP = 0x51 + sysMCAST_JOIN_SOURCE_GROUP = 0x52 + sysMCAST_LEAVE_SOURCE_GROUP = 0x53 + sysMCAST_BLOCK_SOURCE = 0x54 + sysMCAST_UNBLOCK_SOURCE = 0x55 + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Len uint8 + Family uint8 + X__ss_pad1 [6]int8 + X__ss_align int64 + X__ss_pad2 [112]int8 +} + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group sockaddrStorage + Source sockaddrStorage +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_386.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_386.go new file mode 100644 index 00000000..73aa8c6d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_386.go @@ -0,0 +1,170 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_amd64.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_amd64.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_amd64.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm.go new file mode 100644 index 00000000..73aa8c6d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm.go @@ -0,0 +1,170 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm64.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm64.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_arm64.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips.go new file mode 100644 index 00000000..73aa8c6d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips.go @@ -0,0 +1,170 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64le.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64le.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mips64le.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go new file mode 100644 index 00000000..73aa8c6d --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_mipsle.go @@ -0,0 +1,170 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc.go new file mode 100644 index 00000000..c9bf6a87 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc.go @@ -0,0 +1,170 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x84 + sizeofGroupSourceReq = 0x104 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x8 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]uint8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [2]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64le.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64le.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_ppc64le.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_linux_s390x.go b/vendor/src/golang.org/x/net/ipv6/zsys_linux_s390x.go new file mode 100644 index 00000000..b64f0157 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_linux_s390x.go @@ -0,0 +1,172 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_linux.go + +package ipv6 + +const ( + sysIPV6_ADDRFORM = 0x1 + sysIPV6_2292PKTINFO = 0x2 + sysIPV6_2292HOPOPTS = 0x3 + sysIPV6_2292DSTOPTS = 0x4 + sysIPV6_2292RTHDR = 0x5 + sysIPV6_2292PKTOPTIONS = 0x6 + sysIPV6_CHECKSUM = 0x7 + sysIPV6_2292HOPLIMIT = 0x8 + sysIPV6_NEXTHOP = 0x9 + sysIPV6_FLOWINFO = 0xb + + sysIPV6_UNICAST_HOPS = 0x10 + sysIPV6_MULTICAST_IF = 0x11 + sysIPV6_MULTICAST_HOPS = 0x12 + sysIPV6_MULTICAST_LOOP = 0x13 + sysIPV6_ADD_MEMBERSHIP = 0x14 + sysIPV6_DROP_MEMBERSHIP = 0x15 + sysMCAST_JOIN_GROUP = 0x2a + sysMCAST_LEAVE_GROUP = 0x2d + sysMCAST_JOIN_SOURCE_GROUP = 0x2e + sysMCAST_LEAVE_SOURCE_GROUP = 0x2f + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_MSFILTER = 0x30 + sysIPV6_ROUTER_ALERT = 0x16 + sysIPV6_MTU_DISCOVER = 0x17 + sysIPV6_MTU = 0x18 + sysIPV6_RECVERR = 0x19 + sysIPV6_V6ONLY = 0x1a + sysIPV6_JOIN_ANYCAST = 0x1b + sysIPV6_LEAVE_ANYCAST = 0x1c + + sysIPV6_FLOWLABEL_MGR = 0x20 + sysIPV6_FLOWINFO_SEND = 0x21 + + sysIPV6_IPSEC_POLICY = 0x22 + sysIPV6_XFRM_POLICY = 0x23 + + sysIPV6_RECVPKTINFO = 0x31 + sysIPV6_PKTINFO = 0x32 + sysIPV6_RECVHOPLIMIT = 0x33 + sysIPV6_HOPLIMIT = 0x34 + sysIPV6_RECVHOPOPTS = 0x35 + sysIPV6_HOPOPTS = 0x36 + sysIPV6_RTHDRDSTOPTS = 0x37 + sysIPV6_RECVRTHDR = 0x38 + sysIPV6_RTHDR = 0x39 + sysIPV6_RECVDSTOPTS = 0x3a + sysIPV6_DSTOPTS = 0x3b + sysIPV6_RECVPATHMTU = 0x3c + sysIPV6_PATHMTU = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_RECVTCLASS = 0x42 + sysIPV6_TCLASS = 0x43 + + sysIPV6_ADDR_PREFERENCES = 0x48 + + sysIPV6_PREFER_SRC_TMP = 0x1 + sysIPV6_PREFER_SRC_PUBLIC = 0x2 + sysIPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x100 + sysIPV6_PREFER_SRC_COA = 0x4 + sysIPV6_PREFER_SRC_HOME = 0x400 + sysIPV6_PREFER_SRC_CGA = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x800 + + sysIPV6_MINHOPCOUNT = 0x49 + + sysIPV6_ORIGDSTADDR = 0x4a + sysIPV6_RECVORIGDSTADDR = 0x4a + sysIPV6_TRANSPARENT = 0x4b + sysIPV6_UNICAST_IF = 0x4c + + sysICMPV6_FILTER = 0x1 + + sysICMPV6_FILTER_BLOCK = 0x1 + sysICMPV6_FILTER_PASS = 0x2 + sysICMPV6_FILTER_BLOCKOTHERS = 0x3 + sysICMPV6_FILTER_PASSONLY = 0x4 + + sysSOL_SOCKET = 0x1 + sysSO_ATTACH_FILTER = 0x1a + + sizeofKernelSockaddrStorage = 0x80 + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + sizeofIPv6FlowlabelReq = 0x20 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x88 + sizeofGroupSourceReq = 0x108 + + sizeofICMPv6Filter = 0x20 + + sizeofSockFprog = 0x10 +) + +type kernelSockaddrStorage struct { + Family uint16 + X__data [126]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex int32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6FlowlabelReq struct { + Dst [16]byte /* in6_addr */ + Label uint32 + Action uint8 + Share uint8 + Flags uint16 + Expires uint16 + Linger uint16 + X__flr_pad uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Ifindex int32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [4]byte + Group kernelSockaddrStorage + Source kernelSockaddrStorage +} + +type icmpv6Filter struct { + Data [8]uint32 +} + +type sockFProg struct { + Len uint16 + Pad_cgo_0 [6]byte + Filter *sockFilter +} + +type sockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_netbsd.go b/vendor/src/golang.org/x/net/ipv6/zsys_netbsd.go new file mode 100644 index 00000000..bcada13b --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_netbsd.go @@ -0,0 +1,84 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_netbsd.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_IPSEC_POLICY = 0x1c + + sysIPV6_RTHDRDSTOPTS = 0x23 + + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_openbsd.go b/vendor/src/golang.org/x/net/ipv6/zsys_openbsd.go new file mode 100644 index 00000000..86cf3c63 --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_openbsd.go @@ -0,0 +1,93 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_openbsd.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x4 + sysIPV6_MULTICAST_IF = 0x9 + sysIPV6_MULTICAST_HOPS = 0xa + sysIPV6_MULTICAST_LOOP = 0xb + sysIPV6_JOIN_GROUP = 0xc + sysIPV6_LEAVE_GROUP = 0xd + sysIPV6_PORTRANGE = 0xe + sysICMP6_FILTER = 0x12 + + sysIPV6_CHECKSUM = 0x1a + sysIPV6_V6ONLY = 0x1b + + sysIPV6_RTHDRDSTOPTS = 0x23 + + sysIPV6_RECVPKTINFO = 0x24 + sysIPV6_RECVHOPLIMIT = 0x25 + sysIPV6_RECVRTHDR = 0x26 + sysIPV6_RECVHOPOPTS = 0x27 + sysIPV6_RECVDSTOPTS = 0x28 + + sysIPV6_USE_MIN_MTU = 0x2a + sysIPV6_RECVPATHMTU = 0x2b + + sysIPV6_PATHMTU = 0x2c + + sysIPV6_PKTINFO = 0x2e + sysIPV6_HOPLIMIT = 0x2f + sysIPV6_NEXTHOP = 0x30 + sysIPV6_HOPOPTS = 0x31 + sysIPV6_DSTOPTS = 0x32 + sysIPV6_RTHDR = 0x33 + + sysIPV6_AUTH_LEVEL = 0x35 + sysIPV6_ESP_TRANS_LEVEL = 0x36 + sysIPV6_ESP_NETWORK_LEVEL = 0x37 + sysIPSEC6_OUTSA = 0x38 + sysIPV6_RECVTCLASS = 0x39 + + sysIPV6_AUTOFLOWLABEL = 0x3b + sysIPV6_IPCOMP_LEVEL = 0x3c + + sysIPV6_TCLASS = 0x3d + sysIPV6_DONTFRAG = 0x3e + sysIPV6_PIPEX = 0x3f + + sysIPV6_RTABLE = 0x1021 + + sysIPV6_PORTRANGE_DEFAULT = 0x0 + sysIPV6_PORTRANGE_HIGH = 0x1 + sysIPV6_PORTRANGE_LOW = 0x2 + + sizeofSockaddrInet6 = 0x1c + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x20 + + sizeofIPv6Mreq = 0x14 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type icmpv6Filter struct { + Filt [8]uint32 +} diff --git a/vendor/src/golang.org/x/net/ipv6/zsys_solaris.go b/vendor/src/golang.org/x/net/ipv6/zsys_solaris.go new file mode 100644 index 00000000..cf1837dd --- /dev/null +++ b/vendor/src/golang.org/x/net/ipv6/zsys_solaris.go @@ -0,0 +1,131 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs defs_solaris.go + +package ipv6 + +const ( + sysIPV6_UNICAST_HOPS = 0x5 + sysIPV6_MULTICAST_IF = 0x6 + sysIPV6_MULTICAST_HOPS = 0x7 + sysIPV6_MULTICAST_LOOP = 0x8 + sysIPV6_JOIN_GROUP = 0x9 + sysIPV6_LEAVE_GROUP = 0xa + + sysIPV6_PKTINFO = 0xb + + sysIPV6_HOPLIMIT = 0xc + sysIPV6_NEXTHOP = 0xd + sysIPV6_HOPOPTS = 0xe + sysIPV6_DSTOPTS = 0xf + + sysIPV6_RTHDR = 0x10 + sysIPV6_RTHDRDSTOPTS = 0x11 + + sysIPV6_RECVPKTINFO = 0x12 + sysIPV6_RECVHOPLIMIT = 0x13 + sysIPV6_RECVHOPOPTS = 0x14 + + sysIPV6_RECVRTHDR = 0x16 + + sysIPV6_RECVRTHDRDSTOPTS = 0x17 + + sysIPV6_CHECKSUM = 0x18 + sysIPV6_RECVTCLASS = 0x19 + sysIPV6_USE_MIN_MTU = 0x20 + sysIPV6_DONTFRAG = 0x21 + sysIPV6_SEC_OPT = 0x22 + sysIPV6_SRC_PREFERENCES = 0x23 + sysIPV6_RECVPATHMTU = 0x24 + sysIPV6_PATHMTU = 0x25 + sysIPV6_TCLASS = 0x26 + sysIPV6_V6ONLY = 0x27 + + sysIPV6_RECVDSTOPTS = 0x28 + + sysMCAST_JOIN_GROUP = 0x29 + sysMCAST_LEAVE_GROUP = 0x2a + sysMCAST_BLOCK_SOURCE = 0x2b + sysMCAST_UNBLOCK_SOURCE = 0x2c + sysMCAST_JOIN_SOURCE_GROUP = 0x2d + sysMCAST_LEAVE_SOURCE_GROUP = 0x2e + + sysIPV6_PREFER_SRC_HOME = 0x1 + sysIPV6_PREFER_SRC_COA = 0x2 + sysIPV6_PREFER_SRC_PUBLIC = 0x4 + sysIPV6_PREFER_SRC_TMP = 0x8 + sysIPV6_PREFER_SRC_NONCGA = 0x10 + sysIPV6_PREFER_SRC_CGA = 0x20 + + sysIPV6_PREFER_SRC_MIPMASK = 0x3 + sysIPV6_PREFER_SRC_MIPDEFAULT = 0x1 + sysIPV6_PREFER_SRC_TMPMASK = 0xc + sysIPV6_PREFER_SRC_TMPDEFAULT = 0x4 + sysIPV6_PREFER_SRC_CGAMASK = 0x30 + sysIPV6_PREFER_SRC_CGADEFAULT = 0x10 + + sysIPV6_PREFER_SRC_MASK = 0x3f + + sysIPV6_PREFER_SRC_DEFAULT = 0x15 + + sysIPV6_BOUND_IF = 0x41 + sysIPV6_UNSPEC_SRC = 0x42 + + sysICMP6_FILTER = 0x1 + + sizeofSockaddrStorage = 0x100 + sizeofSockaddrInet6 = 0x20 + sizeofInet6Pktinfo = 0x14 + sizeofIPv6Mtuinfo = 0x24 + + sizeofIPv6Mreq = 0x14 + sizeofGroupReq = 0x104 + sizeofGroupSourceReq = 0x204 + + sizeofICMPv6Filter = 0x20 +) + +type sockaddrStorage struct { + Family uint16 + X_ss_pad1 [6]int8 + X_ss_align float64 + X_ss_pad2 [240]int8 +} + +type sockaddrInet6 struct { + Family uint16 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 + X__sin6_src_id uint32 +} + +type inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type ipv6Mtuinfo struct { + Addr sockaddrInet6 + Mtu uint32 +} + +type ipv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type groupReq struct { + Interface uint32 + Pad_cgo_0 [256]byte +} + +type groupSourceReq struct { + Interface uint32 + Pad_cgo_0 [256]byte + Pad_cgo_1 [256]byte +} + +type icmpv6Filter struct { + X__icmp6_filt [8]uint32 +}